diff --git a/vtm/src/org/oscim/tiling/MapTile.java b/vtm/src/org/oscim/tiling/MapTile.java index 5a172396..b68544bc 100644 --- a/vtm/src/org/oscim/tiling/MapTile.java +++ b/vtm/src/org/oscim/tiling/MapTile.java @@ -30,6 +30,10 @@ import org.oscim.utils.quadtree.Node; */ public class MapTile extends Tile { + public static class TileNode extends Node { + + } + /** * To be removed: used by GWT backend * */ @@ -98,7 +102,7 @@ public class MapTile extends Tile { /** * Pointer to access relatives in QuadTree */ - public Node node; + public TileNode node; /** * to avoid drawing a tile twice per frame diff --git a/vtm/src/org/oscim/tiling/TileManager.java b/vtm/src/org/oscim/tiling/TileManager.java index 801bfa86..c476c7da 100644 --- a/vtm/src/org/oscim/tiling/TileManager.java +++ b/vtm/src/org/oscim/tiling/TileManager.java @@ -29,9 +29,9 @@ import org.oscim.core.Tile; import org.oscim.map.Map; import org.oscim.map.Viewport; import org.oscim.renderer.BufferObject; +import org.oscim.tiling.MapTile.TileNode; import org.oscim.utils.FastMath; import org.oscim.utils.ScanBox; -import org.oscim.utils.quadtree.Node; import org.oscim.utils.quadtree.QuadTree; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,11 +85,11 @@ public class TileManager { // job queue filled in TileManager and polled by TileLoaders private final JobQueue jobQueue; - private final QuadTree mIndex = new QuadTree() { + private final QuadTree mIndex = new QuadTree() { @Override public MapTile create(int x, int y, int z) { - Node t = super.add(x, y, z); + TileNode t = super.add(x, y, z); t.item = new MapTile(x, y, (byte) z); t.item.node = t; @@ -97,7 +97,7 @@ public class TileManager { } @Override - public void remove(MapTile t) { + public void removeItem(MapTile t) { if (t.node == null) { log.debug("BUG already removed " + t); return; @@ -108,6 +108,11 @@ public class TileManager { t.node.item = null; t.node = null; } + + @Override + public TileNode create() { + return new TileNode(); + } }; private final float[] mMapPlane = new float[8]; @@ -324,11 +329,9 @@ public class TileManager { tileSet.releaseTiles(); } - /* package */MapTile addTile(int x, int y, int zoomLevel) { - MapTile tile; - - // tile = QuadTree.getTile(x, y, zoomLevel); - tile = mIndex.getTile(x, y, zoomLevel); + /* package */ + MapTile addTile(int x, int y, int zoomLevel) { + MapTile tile = mIndex.getTile(x, y, zoomLevel); if (tile == null) { tile = mIndex.create(x, y, zoomLevel); @@ -393,7 +396,7 @@ public class TileManager { t.clear(); t.state = STATE_CANCEL; - mIndex.remove(t); + mIndex.removeItem(t); } mTilesCount--; } diff --git a/vtm/src/org/oscim/tiling/TileRenderer.java b/vtm/src/org/oscim/tiling/TileRenderer.java index c94f3588..5ad968a5 100644 --- a/vtm/src/org/oscim/tiling/TileRenderer.java +++ b/vtm/src/org/oscim/tiling/TileRenderer.java @@ -40,9 +40,9 @@ import org.oscim.renderer.elements.LineTexLayer; import org.oscim.renderer.elements.MeshLayer; import org.oscim.renderer.elements.PolygonLayer; import org.oscim.renderer.elements.RenderElement; +import org.oscim.tiling.MapTile.TileNode; import org.oscim.utils.FastMath; import org.oscim.utils.ScanBox; -import org.oscim.utils.quadtree.Node; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -91,7 +91,7 @@ public class TileRenderer extends LayerRenderer { * synced with clearTiles, setOverdrawColor and setBitmapAlpha */ @Override - protected synchronized void update(MapPosition pos, boolean positionChanged, Matrices m) { + protected synchronized void update(MapPosition pos, boolean changed, Matrices m) { if (mAlpha == 0) { mTileManager.releaseTiles(mDrawTiles); @@ -123,7 +123,7 @@ public class TileRenderer extends LayerRenderer { int tileCnt = mDrawTiles.cnt; MapTile[] tiles = mDrawTiles.tiles; - if (tilesChanged || positionChanged) { + if (tilesChanged || changed) { updateTileVisibility(pos, m.mapPlane); } @@ -162,17 +162,17 @@ public class TileRenderer extends LayerRenderer { if (mClipMode > 1) { mClipMode = 3; //GL.glClear(GL20.GL_DEPTH_BUFFER_BIT); - GL.glDepthFunc(GL20.GL_LESS); - double scale = pos.getZoomScale(); + /** draw child or parent proxies */ + boolean preferParent = (pos.getZoomScale() < 1.5) + || (pos.zoomLevel < tiles[0].zoomLevel); + for (int i = 0; i < tileCnt; i++) { MapTile t = tiles[i]; if (t.isVisible && (t.state != STATE_READY) && (t.holder == null)) { - boolean preferParent = (scale > 1.5) - || (pos.zoomLevel - t.zoomLevel < 0); drawProxyTile(t, pos, true, preferParent); } } @@ -235,9 +235,9 @@ public class TileRenderer extends LayerRenderer { // check near relatives than can serve as proxy if ((tile.proxies & MapTile.PROXY_PARENT) != 0) { - MapTile rel = tile.node.parent.item; - if (rel.state == STATE_NEW_DATA) - uploadCnt += uploadTileData(rel); + MapTile t = tile.node.parent.item; + if (t.state == STATE_NEW_DATA) + uploadCnt += uploadTileData(t); // dont load child proxies continue; @@ -247,9 +247,9 @@ public class TileRenderer extends LayerRenderer { if ((tile.proxies & 1 << c) == 0) continue; - MapTile rel = tile.node.child(i); - if (rel != null && rel.state == STATE_NEW_DATA) - uploadCnt += uploadTileData(rel); + MapTile t = tile.node.child(i); + if (t != null && t.state == STATE_NEW_DATA) + uploadCnt += uploadTileData(t); } } return uploadCnt; @@ -270,7 +270,7 @@ public class TileRenderer extends LayerRenderer { tile.layers.vbo = BufferObject.get(GL20.GL_ARRAY_BUFFER, newSize); if (!ElementRenderer.uploadLayers(tile.layers, newSize, true)) { - log.debug("BUG uploadTileData " + tile + " failed!"); + log.error("{} uploadTileData failed!", tile); tile.layers.vbo = BufferObject.release(tile.layers.vbo); tile.layers.clear(); @@ -550,10 +550,10 @@ public class TileRenderer extends LayerRenderer { return drawn; } - private void drawProxyTile(MapTile tile, MapPosition pos, boolean parent, - boolean preferParent) { + protected void drawProxyTile(MapTile tile, MapPosition pos, + boolean parent, boolean preferParent) { - Node r = tile.node; + TileNode r = tile.node; MapTile proxy; if (!preferParent) { diff --git a/vtm/src/org/oscim/utils/quadtree/BoxTree.java b/vtm/src/org/oscim/utils/quadtree/BoxTree.java new file mode 100644 index 00000000..2fd4c468 --- /dev/null +++ b/vtm/src/org/oscim/utils/quadtree/BoxTree.java @@ -0,0 +1,424 @@ +package org.oscim.utils.quadtree; + +import org.oscim.utils.pool.Inlist; +import org.oscim.utils.quadtree.BoxTree.BoxItem; +import org.oscim.utils.quadtree.BoxTree.BoxNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A BoxTree is made of BoxNodes which hold a list of + * generic BoxItems which can hold a custom data item. + * + * ... in case this generic isnt obvious at first sight. + * */ +public abstract class BoxTree, E> + extends QuadTree, Box> { + + final static Logger log = LoggerFactory.getLogger(BoxTree.class); + static boolean dbg = false; + + int extents; + int maxDepth; + + public static class BoxNode> extends Node, T> { + // for non-recursive traversal + BoxNode next; + public int x; + public int y; + public int size; + + //BoxItem list; + + public boolean overlaps(T it) { + return (x < it.x2) && + (y < it.y2) && + (it.x1 < x + size) && + (it.y1 < y + size); + } + + @Override + public String toString() { + return x + ":" + y + ":" + size; + } + } + + public static class BoxItem extends Inlist> { + public BoxItem(int x1, int y1, int x2, int y2) { + this.x1 = x1; + this.x2 = x2; + this.y1 = y1; + this.y2 = y2; + } + + @Override + public String toString() { + return x1 + "," + y1 + "/" + x2 + "," + y2; + } + + public T item; + + public BoxItem() { + + } + + public int x1; + public int x2; + public int y1; + public int y2; + + public boolean overlaps(BoxItem it) { + return (x1 < it.x2) + && (it.x1 < x2) + && (y1 < it.y2) + && (it.y1 < y2); + } + } + + public interface Visitor { + boolean process(T item); + } + + // public class NodeVistor implements Visitor> { + // + // @Override + // public boolean process(BoxNode item) { + // + // return false; + // } + // + // } + + public BoxTree(int extents, int maxDepth) { + super(); + // size is -extents to +extents + this.root.size = extents * 2; + this.root.x = -extents; + this.root.y = -extents; + + this.extents = extents; + this.maxDepth = maxDepth; + } + + @Override + public Box create(int x, int y, int z) { + return null; + } + + @Override + public BoxNode create() { + BoxNode node = new BoxNode(); + return node; + } + + @Override + public void removeItem(Box item) { + + } + + @SuppressWarnings("unchecked") + public int query(Box box) { + if (box.x1 > box.x2 || box.y1 > box.y2) + throw new IllegalArgumentException(); + + int x1 = box.x1; + int x2 = box.x2; + int y1 = box.y1; + int y2 = box.y2; + + BoxNode cur, c; + BoxNode stack = root; + int result = 0; + + boolean drop = false; + + //O: + while (stack != null) { + + /** pop cur from stack */ + cur = stack; + stack = stack.next; + + /** process overlapping items from cur node */ + Box prev = cur.item; + + for (Box it = cur.item; it != null; it = (Box) it.next) { + if ((x1 <= it.x2) && (x2 >= it.x1) && + (y1 <= it.y2) && (y2 >= it.y1)) { + + result = process(box, it); + + if (result > 0) { + if (dbg) + log.debug("{} overlap {} {}", result, box, it); + drop = true; + //break O; + } + + if (result < 0) { + result = 0; + if (dbg) + log.debug("remove overlap {} {}", box, it); + // remove this itemchild = cur.child11; + //cur.item = Inlist.remove(cur.item, it); + if (it == cur.item) + prev = cur.item = it; + else + prev.next = it.next; + + continue; + } + } + prev = it; + } + + /** put children on stack which overlap with box */ + if ((c = cur.child00) != null && c.overlaps(box)) { + c.next = stack; + stack = c; + } + + if ((c = cur.child01) != null && c.overlaps(box)) { + c.next = stack; + stack = c; + } + + if ((c = cur.child10) != null && c.overlaps(box)) { + c.next = stack; + stack = c; + } + + if ((c = cur.child11) != null && c.overlaps(box)) { + c.next = stack; + stack = c; + } + } + + /** dont keep dangling references */ + while (stack != null) + stack = stack.next; + + return drop ? 1 : 0; + } + + public abstract boolean collectAll(BoxNode node); + + public int all() { + return all(root); + } + + public int all(BoxNode node) { + + BoxNode cur, c; + BoxNode stack = node; + + while (stack != null) { + cur = stack; + stack = stack.next; + + if (cur.item != null && !collectAll(cur)) + break; + + if ((c = cur.child00) != null) { + c.next = stack; + stack = c; + } + if ((c = cur.child01) != null) { + c.next = stack; + stack = c; + } + if ((c = cur.child10) != null) { + c.next = stack; + stack = c; + } + if ((c = cur.child11) != null) { + c.next = stack; + stack = c; + } + } + + // dont keep dangling references + while (stack != null) + stack = stack.next; + + return 0; + } + + public abstract int process(Box box, Box it); + + public BoxNode create(BoxNode parent, int i) { + BoxNode node = new BoxNode(); + int size = parent.size >> 1; + node.x = parent.x; + node.y = parent.y; + node.size = size; + + if (i == 0) { + // top-left + parent.child00 = node; + } else if (i == 1) { + // bottom-left + parent.child10 = node; + node.y += size; + } else if (i == 2) { + // top-right + parent.child01 = node; + node.x += size; + } else { + // bottom-right + parent.child11 = node; + node.x += size; + node.y += size; + } + + node.parent = parent; + + return node; + } + + public void insert(Box box) { + if (box.x1 > box.x2 || box.y1 > box.y2) + throw new IllegalArgumentException(); + + BoxNode cur = root; + BoxNode child = null; + + // tile position in tree + //int px = 0, py = 0; + int idX = 0, idY = 0; + int x1 = box.x1; + int x2 = box.x2; + int y1 = box.y1; + int y2 = box.y2; + + for (int level = 0; level <= maxDepth; level++) { + // half size of tile at current z + //int hsize = (extents >> level); + int hsize = cur.size >> 1; + + // center of tile (shift by -extents) + //int cx = px + hsize - extents; + //int cy = py + hsize - extents; + int cx = cur.x + hsize; + int cy = cur.y + hsize; + + child = null; + //int childPos = -1; + //log.debug(cx + ":" + cy + " " + hsize); + if (x2 <= cx) { + if (y2 <= cy) { + if ((child = cur.child00) == null) + child = create(cur, 0); + } + // should be else? + if (y1 >= cy) { + if ((child = cur.child10) == null) + child = create(cur, 1); + idX++; + } + } + if (x1 >= cx) { + if (y2 <= cy) { + if ((child = cur.child01) == null) + child = create(cur, 2); + idY++; + } + if (y1 >= cy) { + if ((child = cur.child11) == null) + child = create(cur, 3); + idX++; + idY++; + } + } + + //log.debug("child {}", child); + + if (child == null || level == maxDepth) { + // push item onto list of this node + box.next = cur.item; + cur.item = box; + + if (dbg) + log.debug("insert at: " + level + " / " + idX + ":" + + idY + " -- " + x1 + ":" + y1 + + " /" + (x2) + "x" + (y2)); + + break; + } + cur = child; + } + + } + + public abstract boolean process(BoxNode nodes); + + public void clear() { + root.child00 = null; + root.child01 = null; + root.child10 = null; + root.child11 = null; + root.item = null; + + //root = create(); + //root.parent = root; + } + + static class Item extends BoxItem { + public Item(int x1, int y1, int x2, int y2) { + super(x1, y1, x2, y2); + } + + public Item() { + // TODO Auto-generated constructor stub + } + } + + // static { + // System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE"); + //} + // public static void main(String[] args) { + // + // BoxTree tree = new BoxTree(4096, 12) { + // + // @Override + // public int process(Item box, Item it) { + // System.out.println("found ... " + box + "\t\t" + it); + // return 1; + // } + // + // @Override + // public boolean process(BoxNode nodes) { + // System.out.println("found ... "); + // //for (BoxItem it = nodes.item; it != null; it = it.next) { + // // System.out.println("it: " + it.x1 + "/" + it.y1); + // //} + // + // // TODO Auto-generated method stub + // return false; + // } + // + // @Override + // public boolean collectAll(BoxNode nodes) { + // for (Item it = nodes.item; it != null; it = (Item) it.next) { + // System.out.println("all: " + it); + // } + // return false; + // } + // + // }; + // //[VtmAsyncExecutor] DEBUG org.oscim.utils.quadtree.BoxTree - insert at: 8 / 9:0 -- -631:266 /106x187 + // + // tree.insert(new Item(-631, 266, -631 + 106, 266 + 187)); + // + // //tree.insert(new Item(-40, -40, -32, -32)); + // // tree.insert(new Item(-60, -60, -40, -40)); + // // tree.insert(new Item(100, 10, 200, 100)); + // // tree.insert(new Item(100, 200, 200, 300)); + // // tree.insert(new Item(100, 200, 1000, 1000)); + // // + // // tree.query(new Item(-100, -100, 10, 10)); + // //tree.query(new Item(10, 10, 100, 100)); + // + // tree.all(); + // } +} diff --git a/vtm/src/org/oscim/utils/quadtree/Node.java b/vtm/src/org/oscim/utils/quadtree/Node.java index 3bd722e4..0efcbb19 100644 --- a/vtm/src/org/oscim/utils/quadtree/Node.java +++ b/vtm/src/org/oscim/utils/quadtree/Node.java @@ -16,14 +16,14 @@ */ package org.oscim.utils.quadtree; -public class Node { - public Node parent; - public Node child00; - public Node child10; - public Node child01; - public Node child11; +public class Node, E> { + public T parent; + public T child00; + public T child10; + public T child01; + public T child11; - public T item; + public E item; // id of this child relative to parent byte id; @@ -31,11 +31,26 @@ public class Node { // number of children and grandchildren int refs = 0; - public T parent() { + // public byte getId() { + // if (parent.child00 == this) + // return 0; + // if (parent.child01 == this) + // return 1; + // if (parent.child10 == this) + // return 2; + // if (parent.child00 == this) + // return 3; + // + // // is root node + // //if (parent == this) + // return -1; + // } + + public E parent() { return parent.item; } - public T child(int i) { + public E child(int i) { switch (i) { case 0: return (child00 != null) ? child00.item : null; diff --git a/vtm/src/org/oscim/utils/quadtree/QuadTree.java b/vtm/src/org/oscim/utils/quadtree/QuadTree.java index d6f4c23a..6029f2df 100644 --- a/vtm/src/org/oscim/utils/quadtree/QuadTree.java +++ b/vtm/src/org/oscim/utils/quadtree/QuadTree.java @@ -16,32 +16,38 @@ */ package org.oscim.utils.quadtree; -public abstract class QuadTree { +/** + * A quad tree for the standard map tiling schema. + */ +public abstract class QuadTree, E> { - Node pool; + protected final T root; - Node root; + protected T pool; public QuadTree() { - root = new Node(); + root = create(); root.parent = root; } static void checkIndex(int x, int y, int max) { if (x < 0 || x >= max || y < 0 || y >= max) { - throw new IllegalArgumentException("invalid position " + x + '/' + y + '/' + (max >> 1)); + throw new IllegalArgumentException("invalid position " + + x + '/' + y + '/' + (max >> 1)); } } - public abstract T create(int x, int y, int z); + public abstract E create(int x, int y, int z); - public abstract void remove(T item); + public abstract T create(); - public Node add(int x, int y, int z) { + public abstract void removeItem(E item); + + public T add(int x, int y, int z) { checkIndex(x, y, 1 << z); - Node leaf = root; + T leaf = root; for (int level = z - 1; level >= 0; level--) { @@ -49,7 +55,7 @@ public abstract class QuadTree { leaf.refs++; - Node cur = null; + T cur = null; switch (id) { case 0: @@ -75,7 +81,7 @@ public abstract class QuadTree { cur = pool; pool = pool.parent; } else { - cur = new Node(); + cur = create(); } cur.refs = 0; @@ -105,11 +111,11 @@ public abstract class QuadTree { return leaf; } - public T getTile(int x, int y, int z) { + public E getTile(int x, int y, int z) { checkIndex(x, y, 1 << z); - Node leaf = root; + T leaf = root; for (int level = z - 1; level >= 0; level--) { @@ -140,14 +146,14 @@ public abstract class QuadTree { return null; } - public boolean remove(Node item) { + public boolean remove(T item) { - Node cur = item; - Node next; + T cur = item; + T next; while (cur != root) { if (cur == null) - throw new IllegalArgumentException("QuadTree.remove: item not in index"); + throw new IllegalArgumentException("item not in index"); // keep pointer to parent next = cur.parent;