start quad tree for bounding boxes

This commit is contained in:
Hannes Janetzek 2014-02-07 15:09:21 +01:00
parent 59e6240766
commit 3ea697763f
6 changed files with 506 additions and 54 deletions

View File

@ -30,6 +30,10 @@ import org.oscim.utils.quadtree.Node;
*/
public class MapTile extends Tile {
public static class TileNode extends Node<TileNode, MapTile> {
}
/**
* To be removed: used by GWT backend
* */
@ -98,7 +102,7 @@ public class MapTile extends Tile {
/**
* Pointer to access relatives in QuadTree
*/
public Node<MapTile> node;
public TileNode node;
/**
* to avoid drawing a tile twice per frame

View File

@ -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<MapTile> mIndex = new QuadTree<MapTile>() {
private final QuadTree<TileNode, MapTile> mIndex = new QuadTree<TileNode, MapTile>() {
@Override
public MapTile create(int x, int y, int z) {
Node<MapTile> 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--;
}

View File

@ -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<MapTile> r = tile.node;
TileNode r = tile.node;
MapTile proxy;
if (!preferParent) {

View File

@ -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<Box extends BoxItem<E>, E>
extends QuadTree<BoxNode<Box>, Box> {
final static Logger log = LoggerFactory.getLogger(BoxTree.class);
static boolean dbg = false;
int extents;
int maxDepth;
public static class BoxNode<T extends BoxItem<?>> extends Node<BoxNode<T>, T> {
// for non-recursive traversal
BoxNode<T> next;
public int x;
public int y;
public int size;
//BoxItem<T> 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<T> extends Inlist<BoxItem<T>> {
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<T> it) {
return (x1 < it.x2)
&& (it.x1 < x2)
&& (y1 < it.y2)
&& (it.y1 < y2);
}
}
public interface Visitor<T> {
boolean process(T item);
}
// public class NodeVistor<T> implements Visitor<BoxNode<T>> {
//
// @Override
// public boolean process(BoxNode<T> 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<Box> create() {
BoxNode<Box> node = new BoxNode<Box>();
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<Box> cur, c;
BoxNode<Box> 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<Box> node);
public int all() {
return all(root);
}
public int all(BoxNode<Box> node) {
BoxNode<Box> cur, c;
BoxNode<Box> 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<Box> create(BoxNode<Box> parent, int i) {
BoxNode<Box> node = new BoxNode<Box>();
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<Box> cur = root;
BoxNode<Box> 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<Box> 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<Integer> {
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<Item, Integer> tree = new BoxTree<Item, Integer>(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<Item> 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<Item> 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();
// }
}

View File

@ -16,14 +16,14 @@
*/
package org.oscim.utils.quadtree;
public class Node<T> {
public Node<T> parent;
public Node<T> child00;
public Node<T> child10;
public Node<T> child01;
public Node<T> child11;
public class Node<T extends Node<T, E>, 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<T> {
// 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;

View File

@ -16,32 +16,38 @@
*/
package org.oscim.utils.quadtree;
public abstract class QuadTree<T> {
/**
* A quad tree for the standard map tiling schema.
*/
public abstract class QuadTree<T extends Node<T, E>, E> {
Node<T> pool;
protected final T root;
Node<T> root;
protected T pool;
public QuadTree() {
root = new Node<T>();
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<T> 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<T> leaf = root;
T leaf = root;
for (int level = z - 1; level >= 0; level--) {
@ -49,7 +55,7 @@ public abstract class QuadTree<T> {
leaf.refs++;
Node<T> cur = null;
T cur = null;
switch (id) {
case 0:
@ -75,7 +81,7 @@ public abstract class QuadTree<T> {
cur = pool;
pool = pool.parent;
} else {
cur = new Node<T>();
cur = create();
}
cur.refs = 0;
@ -105,11 +111,11 @@ public abstract class QuadTree<T> {
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<T> leaf = root;
T leaf = root;
for (int level = z - 1; level >= 0; level--) {
@ -140,14 +146,14 @@ public abstract class QuadTree<T> {
return null;
}
public boolean remove(Node<T> item) {
public boolean remove(T item) {
Node<T> cur = item;
Node<T> 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;