add QuadTree implementing SpatialIndex
- add SpatialIndex interface
This commit is contained in:
parent
4e94a8f269
commit
b460b4ebaf
234
vtm-tests/test/org/oscim/utils/QuadTreeTest.java
Normal file
234
vtm-tests/test/org/oscim/utils/QuadTreeTest.java
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
package org.oscim.utils;
|
||||||
|
|
||||||
|
import static java.lang.System.currentTimeMillis;
|
||||||
|
import static java.lang.System.out;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.oscim.core.Box;
|
||||||
|
import org.oscim.utils.SpatialIndex.SearchCb;
|
||||||
|
|
||||||
|
public class QuadTreeTest {
|
||||||
|
final static Random rand = new Random((long) (Math.PI * 10000000));
|
||||||
|
|
||||||
|
public class Item {
|
||||||
|
final int val;
|
||||||
|
final Box bbox;;
|
||||||
|
|
||||||
|
Item(Box bbox, int val) {
|
||||||
|
this.val = val;
|
||||||
|
this.bbox = new Box(bbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(val) + ' ' + bbox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<Item> fillRandomTree(SpatialIndex<Item> q, int numItems) {
|
||||||
|
Box box = new Box();
|
||||||
|
ArrayList<Item> items = new ArrayList<Item>(numItems + 16);
|
||||||
|
|
||||||
|
for (int i = 0; i < numItems; i++) {
|
||||||
|
box.minX = (int) (rand.nextDouble() * 10000 - 5000);
|
||||||
|
box.minY = (int) (rand.nextDouble() * 10000 - 5000);
|
||||||
|
box.maxX = (int) (box.minX + rand.nextDouble() * 500);
|
||||||
|
box.maxY = (int) (box.minY + rand.nextDouble() * 500);
|
||||||
|
|
||||||
|
Item it = new Item(box, i);
|
||||||
|
q.insert(box, it);
|
||||||
|
|
||||||
|
items.add(it);
|
||||||
|
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldWork0() {
|
||||||
|
|
||||||
|
SpatialIndex<Item> q = new QuadTree<Item>(Short.MAX_VALUE + 1, 16);
|
||||||
|
//SpatialIndex<Item> q = new RTree<Item>();
|
||||||
|
|
||||||
|
int numItems = 10000;
|
||||||
|
|
||||||
|
List<Item> items = fillRandomTree(q, numItems);
|
||||||
|
|
||||||
|
final int[] found = { 0 };
|
||||||
|
final int[] matched = { 0 };
|
||||||
|
|
||||||
|
for (Item it : items) {
|
||||||
|
int f = matched[0];
|
||||||
|
q.search(it.bbox, new SearchCb<Item>() {
|
||||||
|
@Override
|
||||||
|
public boolean call(Item item, Object context) {
|
||||||
|
found[0]++;
|
||||||
|
if (context == item) {
|
||||||
|
matched[0]++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}, it);
|
||||||
|
|
||||||
|
if (f == matched[0])
|
||||||
|
out.println((it.bbox.maxX - it.bbox.minX)
|
||||||
|
+ " x " + (it.bbox.maxY - it.bbox.minY)
|
||||||
|
+ " ==> " + it);
|
||||||
|
}
|
||||||
|
|
||||||
|
//out.println("m:" + matched[0] + " f:" + found[0]);
|
||||||
|
Assert.assertEquals(numItems, matched[0]);
|
||||||
|
Assert.assertEquals(numItems, q.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldWork1() {
|
||||||
|
long time = currentTimeMillis();
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
shouldWork0();
|
||||||
|
}
|
||||||
|
|
||||||
|
long now = currentTimeMillis();
|
||||||
|
out.println("==>" + (now - time) / 10.0f + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldWork6() {
|
||||||
|
SpatialIndex<Item> q = new QuadTree<Item>(Short.MAX_VALUE + 1, 16);
|
||||||
|
|
||||||
|
Box box = new Box(-4184.653317773969,
|
||||||
|
3183.6174297948446,
|
||||||
|
-4088.3197324911957,
|
||||||
|
3222.7770427421046);
|
||||||
|
|
||||||
|
Item it = new Item(box, 1);
|
||||||
|
q.insert(box, it);
|
||||||
|
|
||||||
|
q.search(it.bbox, new SearchCb<Item>() {
|
||||||
|
@Override
|
||||||
|
public boolean call(Item item, Object context) {
|
||||||
|
out.println("==> " + item + " " + (context == item));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}, it);
|
||||||
|
Assert.assertEquals(1, q.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldWork7() {
|
||||||
|
SpatialIndex<Item> q = new QuadTree<Item>(Short.MAX_VALUE + 1, 14);
|
||||||
|
//SpatialIndex<Item> q = new RTree<Item>();
|
||||||
|
|
||||||
|
int numItems = 10000;
|
||||||
|
|
||||||
|
List<Item> items = fillRandomTree(q, numItems);
|
||||||
|
|
||||||
|
Assert.assertEquals(numItems, q.size());
|
||||||
|
|
||||||
|
int cnt = numItems;
|
||||||
|
for (Item it : items) {
|
||||||
|
if (!q.remove(it.bbox, it)) {
|
||||||
|
out.println((it.bbox.maxX - it.bbox.minX)
|
||||||
|
+ " x " + (it.bbox.maxY - it.bbox.minY)
|
||||||
|
+ " ==> " + it);
|
||||||
|
|
||||||
|
q.search(it.bbox, new SearchCb<Item>() {
|
||||||
|
@Override
|
||||||
|
public boolean call(Item item, Object context) {
|
||||||
|
if (context == item) {
|
||||||
|
out.println("found...");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}, it);
|
||||||
|
}
|
||||||
|
Assert.assertEquals(--cnt, q.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
items = fillRandomTree(q, numItems);
|
||||||
|
|
||||||
|
Assert.assertEquals(numItems, q.size());
|
||||||
|
|
||||||
|
cnt = numItems;
|
||||||
|
for (Item it : items) {
|
||||||
|
if (!q.remove(it.bbox, it))
|
||||||
|
out.println((it.bbox.maxX - it.bbox.minX)
|
||||||
|
+ " x " + (it.bbox.maxY - it.bbox.minY)
|
||||||
|
+ " => " + it);
|
||||||
|
|
||||||
|
Assert.assertEquals(--cnt, q.size());
|
||||||
|
}
|
||||||
|
Assert.assertEquals(0, q.size());
|
||||||
|
out.println("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldWork8() {
|
||||||
|
|
||||||
|
SpatialIndex<Item> q = new QuadTree<Item>(Short.MAX_VALUE + 1, 16);
|
||||||
|
//SpatialIndex<Item> q = new RTree<Item>();
|
||||||
|
|
||||||
|
int numItems = 10000;
|
||||||
|
|
||||||
|
List<Item> items = fillRandomTree(q, numItems);
|
||||||
|
|
||||||
|
final int[] found = { 0 };
|
||||||
|
final int[] matched = { 0 };
|
||||||
|
|
||||||
|
for (Item it : items) {
|
||||||
|
int f = matched[0];
|
||||||
|
int cnt = 0;
|
||||||
|
for (Item it2 : items) {
|
||||||
|
if (it2.bbox.overlap(it.bbox))
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
found[0] = 0;
|
||||||
|
q.search(it.bbox, new SearchCb<Item>() {
|
||||||
|
@Override
|
||||||
|
public boolean call(Item item, Object context) {
|
||||||
|
found[0]++;
|
||||||
|
if (context == item) {
|
||||||
|
matched[0]++;
|
||||||
|
//return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}, it);
|
||||||
|
|
||||||
|
if (found[0] != cnt) {
|
||||||
|
out.println("not found " + (found[0] - cnt));
|
||||||
|
}
|
||||||
|
//Assert.assertEquals(cnt, found[0]);
|
||||||
|
if (f == matched[0])
|
||||||
|
out.println((it.bbox.maxX - it.bbox.minX)
|
||||||
|
+ " x " + (it.bbox.maxY - it.bbox.minY)
|
||||||
|
+ " ==> " + it);
|
||||||
|
}
|
||||||
|
|
||||||
|
//out.println("m:" + matched[0] + " f:" + found[0]);
|
||||||
|
Assert.assertEquals(numItems, matched[0]);
|
||||||
|
Assert.assertEquals(numItems, q.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
QuadTreeTest qt = new QuadTreeTest();
|
||||||
|
|
||||||
|
long time = currentTimeMillis();
|
||||||
|
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
qt.shouldWork0();
|
||||||
|
long now = currentTimeMillis();
|
||||||
|
|
||||||
|
out.println("==>" + (now - time));
|
||||||
|
time = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,7 @@ import org.oscim.layers.tile.vector.VectorTileLoader;
|
|||||||
import org.oscim.layers.tile.vector.labeling.LabelTileLoaderHook;
|
import org.oscim.layers.tile.vector.labeling.LabelTileLoaderHook;
|
||||||
import org.oscim.renderer.elements.ElementLayers;
|
import org.oscim.renderer.elements.ElementLayers;
|
||||||
import org.oscim.utils.pool.Inlist;
|
import org.oscim.utils.pool.Inlist;
|
||||||
import org.oscim.utils.quadtree.Node;
|
import org.oscim.utils.quadtree.TreeNode;
|
||||||
import org.oscim.utils.quadtree.QuadTree;
|
import org.oscim.utils.quadtree.QuadTree;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,7 +32,7 @@ import org.oscim.utils.quadtree.QuadTree;
|
|||||||
*/
|
*/
|
||||||
public class MapTile extends Tile {
|
public class MapTile extends Tile {
|
||||||
|
|
||||||
public static class TileNode extends Node<TileNode, MapTile> {
|
public static class TileNode extends TreeNode<TileNode, MapTile> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
76
vtm/src/org/oscim/utils/QuadTree.java
Normal file
76
vtm/src/org/oscim/utils/QuadTree.java
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package org.oscim.utils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.oscim.core.Box;
|
||||||
|
import org.oscim.utils.pool.Pool;
|
||||||
|
import org.oscim.utils.quadtree.BoxTree;
|
||||||
|
import org.oscim.utils.quadtree.BoxTree.BoxItem;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class QuadTree<T> extends BoxTree<BoxItem<T>, T> implements SpatialIndex<T> {
|
||||||
|
|
||||||
|
public QuadTree(int extents, int maxDepth) {
|
||||||
|
super(extents, maxDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Logger log = LoggerFactory.getLogger(QuadTree.class);
|
||||||
|
|
||||||
|
final Pool<BoxItem<T>> boxPool = new Pool<BoxItem<T>>() {
|
||||||
|
@Override
|
||||||
|
protected BoxItem<T> createItem() {
|
||||||
|
return new BoxItem<T>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private BoxItem<T> getBox(Box box) {
|
||||||
|
BoxItem<T> it = boxPool.get();
|
||||||
|
it.x1 = (int) box.minX;
|
||||||
|
it.y1 = (int) box.minY;
|
||||||
|
it.x2 = (int) box.maxX;
|
||||||
|
it.y2 = (int) box.maxY;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insert(Box box, T item) {
|
||||||
|
insert(new BoxItem<T>(box, item));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Box box, T item) {
|
||||||
|
BoxItem<T> bbox = getBox(box);
|
||||||
|
boolean ok = remove(bbox, item);
|
||||||
|
boxPool.release(bbox);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CollectCb<T> implements SearchCb<T> {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public boolean call(T item, Object context) {
|
||||||
|
List<T> l = (List<T>) context;
|
||||||
|
l.add(item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final CollectCb<T> collectCb = new CollectCb<T>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<T> search(Box bbox, List<T> results) {
|
||||||
|
BoxItem<T> box = getBox(bbox);
|
||||||
|
search(box, collectCb, results);
|
||||||
|
boxPool.release(box);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int search(Box bbox, SearchCb<T> cb, Object context) {
|
||||||
|
BoxItem<T> box = getBox(bbox);
|
||||||
|
search(box, cb, context);
|
||||||
|
boxPool.release(box);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
21
vtm/src/org/oscim/utils/SpatialIndex.java
Normal file
21
vtm/src/org/oscim/utils/SpatialIndex.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package org.oscim.utils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.oscim.core.Box;
|
||||||
|
|
||||||
|
public interface SpatialIndex<T> {
|
||||||
|
public interface SearchCb<T> {
|
||||||
|
boolean call(T item, Object context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insert(Box box, T item);
|
||||||
|
|
||||||
|
public boolean remove(Box box, T item);
|
||||||
|
|
||||||
|
public List<T> search(Box bbox, List<T> results);
|
||||||
|
|
||||||
|
public int search(Box bbox, SearchCb<T> cb, Object context);
|
||||||
|
|
||||||
|
public int size();
|
||||||
|
}
|
@ -1,6 +1,10 @@
|
|||||||
package org.oscim.utils.quadtree;
|
package org.oscim.utils.quadtree;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.oscim.utils.SpatialIndex.SearchCb;
|
||||||
import org.oscim.utils.pool.Inlist;
|
import org.oscim.utils.pool.Inlist;
|
||||||
|
import org.oscim.utils.pool.Pool;
|
||||||
import org.oscim.utils.quadtree.BoxTree.BoxItem;
|
import org.oscim.utils.quadtree.BoxTree.BoxItem;
|
||||||
import org.oscim.utils.quadtree.BoxTree.BoxNode;
|
import org.oscim.utils.quadtree.BoxTree.BoxNode;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -12,28 +16,55 @@ import org.slf4j.LoggerFactory;
|
|||||||
*
|
*
|
||||||
* ... in case this generic isnt obvious at first sight.
|
* ... in case this generic isnt obvious at first sight.
|
||||||
* */
|
* */
|
||||||
public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNode<Box>, Box> {
|
public class BoxTree<T extends BoxItem<E>, E> extends TileIndex<BoxNode<T>, T> {
|
||||||
|
|
||||||
final static Logger log = LoggerFactory.getLogger(BoxTree.class);
|
final static Logger log = LoggerFactory.getLogger(BoxTree.class);
|
||||||
static boolean dbg = false;
|
static boolean dbg = false;
|
||||||
|
|
||||||
protected final int extents;
|
protected final int extents;
|
||||||
protected final int maxDepth;
|
protected final int maxDepth;
|
||||||
|
private final static int MAX_STACK = 32;
|
||||||
|
|
||||||
public static class BoxNode<T extends BoxItem<?>> extends Node<BoxNode<T>, T> {
|
static class Stack<E> extends Inlist<Stack<E>> {
|
||||||
// for non-recursive traversal
|
/** Top of stack index */
|
||||||
BoxNode<T> next;
|
int tos;
|
||||||
// TODO make final? or update to the actual used extent?
|
|
||||||
|
final E[] nodes;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Stack() {
|
||||||
|
nodes = (E[]) new BoxNode[MAX_STACK];
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(E node) {
|
||||||
|
nodes[tos] = node;
|
||||||
|
tos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Pop element off iteration stack (For internal use only) */
|
||||||
|
E pop() {
|
||||||
|
// assert (tos > 0);
|
||||||
|
nodes[tos--] = null;
|
||||||
|
return (E) nodes[tos];
|
||||||
|
}
|
||||||
|
|
||||||
|
E node() {
|
||||||
|
return (E) nodes[tos];
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean empty() {
|
||||||
|
return tos <= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BoxNode<T extends BoxItem<?>> extends TreeNode<BoxNode<T>, T> {
|
||||||
|
// TODO this is redundant - use tile ids
|
||||||
public int x1;
|
public int x1;
|
||||||
public int y1;
|
public int y1;
|
||||||
public int x2;
|
public int x2;
|
||||||
public int y2;
|
public int y2;
|
||||||
|
|
||||||
//BoxItem<T> list;
|
// inherits BoxItem<E> item;
|
||||||
|
|
||||||
public boolean overlaps(T it) {
|
|
||||||
return (x1 < it.x2) && (y1 < it.y2) && (it.x1 < x2) && (it.y1 < y2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
@ -44,11 +75,19 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
|
|||||||
public static class BoxItem<T> extends Inlist<BoxItem<T>> {
|
public static class BoxItem<T> extends Inlist<BoxItem<T>> {
|
||||||
public BoxItem(int x1, int y1, int x2, int y2) {
|
public BoxItem(int x1, int y1, int x2, int y2) {
|
||||||
this.x1 = x1;
|
this.x1 = x1;
|
||||||
this.x2 = x2;
|
|
||||||
this.y1 = y1;
|
this.y1 = y1;
|
||||||
|
this.x2 = x2;
|
||||||
this.y2 = y2;
|
this.y2 = y2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BoxItem(org.oscim.core.Box box, T item) {
|
||||||
|
this.x1 = (int) box.minX;
|
||||||
|
this.y1 = (int) box.minY;
|
||||||
|
this.x2 = (int) box.maxX;
|
||||||
|
this.y2 = (int) box.maxY;
|
||||||
|
this.item = item;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return x1 + "," + y1 + "/" + x2 + "," + y2;
|
return x1 + "," + y1 + "/" + x2 + "," + y2;
|
||||||
@ -66,7 +105,7 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
|
|||||||
public int y2;
|
public int y2;
|
||||||
|
|
||||||
public boolean overlaps(BoxItem<T> it) {
|
public boolean overlaps(BoxItem<T> it) {
|
||||||
return (x1 < it.x2) && (it.x1 < x2) && (y1 < it.y2) && (it.y1 < y2);
|
return !((x1 > it.x2) || (it.x1 > x2) || (y1 > it.y2) || (it.y1 > y2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,19 +113,16 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
|
|||||||
boolean process(T item);
|
boolean process(T item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public class NodeVistor<T> implements Visitor<BoxNode<T>> {
|
boolean isPowerOfTwo(int x) {
|
||||||
//
|
return ((x > 0) && (x & (x - 1)) == 0);
|
||||||
// @Override
|
}
|
||||||
// public boolean process(BoxNode<T> item) {
|
|
||||||
//
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
public BoxTree(int extents, int maxDepth) {
|
public BoxTree(int extents, int maxDepth) {
|
||||||
super();
|
super();
|
||||||
// size is -extents to +extents
|
if (!isPowerOfTwo(extents))
|
||||||
|
throw new IllegalArgumentException("Extents must be power of two!");
|
||||||
|
|
||||||
|
//size is -extents to +extents
|
||||||
this.root.x1 = -extents;
|
this.root.x1 = -extents;
|
||||||
this.root.y1 = -extents;
|
this.root.y1 = -extents;
|
||||||
this.root.x2 = extents;
|
this.root.x2 = extents;
|
||||||
@ -97,169 +133,231 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BoxNode<Box> create() {
|
public BoxNode<T> create() {
|
||||||
return new BoxNode<Box>();
|
return new BoxNode<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeItem(Box item) {
|
public void removeItem(T item) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
Pool<Stack<BoxNode<T>>> stackPool = new Pool<Stack<BoxNode<T>>>() {
|
||||||
public int query(Box box) {
|
@Override
|
||||||
if (box.x1 > box.x2 || box.y1 > box.y2)
|
protected Stack<BoxNode<T>> createItem() {
|
||||||
throw new IllegalArgumentException();
|
return new Stack<BoxNode<T>>();
|
||||||
|
}
|
||||||
|
|
||||||
int x1 = box.x1;
|
protected boolean clearItem(Stack<BoxNode<T>> item) {
|
||||||
int x2 = box.x2;
|
if (item.tos != 0) {
|
||||||
int y1 = box.y1;
|
item.tos = 0;
|
||||||
int y2 = box.y2;
|
Arrays.fill(item.nodes, null);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
BoxNode<Box> cur, c;
|
public boolean search(T box, SearchCb<E> cb, Object ctxt) {
|
||||||
BoxNode<Box> stack = root;
|
BoxNode<T> n;
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
boolean drop = false;
|
Stack<BoxNode<T>> stack = stackPool.get();
|
||||||
|
stack.push(root);
|
||||||
|
|
||||||
O: while (stack != null) {
|
O: while (!stack.empty()) {
|
||||||
|
|
||||||
/** pop cur from stack */
|
n = stack.pop();
|
||||||
cur = stack;
|
|
||||||
stack = stack.next;
|
|
||||||
|
|
||||||
/** process overlapping items from cur node */
|
/* process overlapping items from cur node */
|
||||||
Box prev = cur.item;
|
for (BoxItem<E> it = n.item; it != null; it = it.next) {
|
||||||
|
if (it.overlaps(box)) {
|
||||||
for (Box it = cur.item; it != null; it = (Box) it.next) {
|
if (!cb.call(it.item, ctxt))
|
||||||
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;
|
break O;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BoxNode<T> p = n.parent;
|
||||||
|
|
||||||
|
/* push next node on same level onto stack */
|
||||||
|
switch (n.id) {
|
||||||
|
case 0:
|
||||||
|
if (overlaps(p.child01, box)) {
|
||||||
|
stack.push(p.child01);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case 1:
|
||||||
|
if (overlaps(p.child10, box)) {
|
||||||
|
stack.push(p.child10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if (overlaps(p.child11, box)) {
|
||||||
|
stack.push(p.child11);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* push next level child onto stack */
|
||||||
|
if (overlaps(n.child00, box))
|
||||||
|
stack.push(n.child00);
|
||||||
|
else if (overlaps(n.child01, box))
|
||||||
|
stack.push(n.child01);
|
||||||
|
else if (overlaps(n.child10, box))
|
||||||
|
stack.push(n.child10);
|
||||||
|
else if (overlaps(n.child11, box))
|
||||||
|
stack.push(n.child11);
|
||||||
|
}
|
||||||
|
stackPool.release(stack);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (result < 0) {
|
public interface SearchBoxCb<T extends BoxItem<?>> {
|
||||||
result = 0;
|
boolean call(T item);
|
||||||
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;
|
public boolean search(T box, SearchBoxCb<T> cb) {
|
||||||
|
BoxNode<T> n;
|
||||||
|
BoxNode<T> start = getNode(box, false);
|
||||||
|
if (start == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Stack<BoxNode<T>> stack = stackPool.get();
|
||||||
|
stack.push(start);
|
||||||
|
|
||||||
|
while (!stack.empty()) {
|
||||||
|
n = stack.pop();
|
||||||
|
|
||||||
|
/* process overlapping items from cur node */
|
||||||
|
for (BoxItem<E> it = n.item; it != null; it = it.next) {
|
||||||
|
if (it.overlaps(box)) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T item = (T) it;
|
||||||
|
if (!cb.call(item)) {
|
||||||
|
stackPool.release(stack);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prev = it;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** put children on stack which overlap with box */
|
BoxNode<T> p = n.parent;
|
||||||
if ((c = cur.child00) != null &&
|
|
||||||
(x1 < c.x2) && (y1 < c.y2) &&
|
/* push next node on same level onto stack */
|
||||||
(c.x1 < x2) && (c.y1 < y2)) {
|
switch (n.id) {
|
||||||
c.next = stack;
|
case 0:
|
||||||
stack = c;
|
if (overlaps(p.child01, box)) {
|
||||||
}
|
stack.push(p.child01);
|
||||||
if ((c = cur.child01) != null &&
|
break;
|
||||||
(x1 < c.x2) && (y1 < c.y2) &&
|
}
|
||||||
(c.x1 < x2) && (c.y1 < y2)) {
|
case 1:
|
||||||
c.next = stack;
|
if (overlaps(p.child10, box)) {
|
||||||
stack = c;
|
stack.push(p.child10);
|
||||||
}
|
break;
|
||||||
if ((c = cur.child10) != null &&
|
}
|
||||||
(x1 < c.x2) && (y1 < c.y2) &&
|
case 2:
|
||||||
(c.x1 < x2) && (c.y1 < y2)) {
|
if (overlaps(p.child11, box)) {
|
||||||
c.next = stack;
|
stack.push(p.child11);
|
||||||
stack = c;
|
break;
|
||||||
}
|
}
|
||||||
if ((c = cur.child11) != null &&
|
default:
|
||||||
(x1 < c.x2) && (y1 < c.y2) &&
|
break;
|
||||||
(c.x1 < x2) && (c.y1 < y2)) {
|
|
||||||
c.next = stack;
|
|
||||||
stack = c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* push next level child onto stack */
|
||||||
|
if (overlaps(n.child00, box))
|
||||||
|
stack.push(n.child00);
|
||||||
|
else if (overlaps(n.child01, box))
|
||||||
|
stack.push(n.child01);
|
||||||
|
else if (overlaps(n.child10, box))
|
||||||
|
stack.push(n.child10);
|
||||||
|
else if (overlaps(n.child11, box))
|
||||||
|
stack.push(n.child11);
|
||||||
}
|
}
|
||||||
|
stackPool.release(stack);
|
||||||
|
|
||||||
/** dont keep dangling references */
|
return true;
|
||||||
///* gwt optimizer found this cannot be reached :) */
|
|
||||||
while (stack != null)
|
|
||||||
stack = stack.next;
|
|
||||||
|
|
||||||
return drop ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean collectAll(BoxNode<Box> node);
|
private static boolean overlaps(BoxNode<?> a, BoxItem<?> b) {
|
||||||
|
return a != null && !((a.x1 > b.x2) || (b.x1 > a.x2) || (a.y1 > b.y2) || (b.y1 > a.y2));
|
||||||
public int all() {
|
|
||||||
return all(root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int all(BoxNode<Box> node) {
|
public interface SearchNodeCb<E extends BoxNode<?>> {
|
||||||
|
boolean call(E item);
|
||||||
|
}
|
||||||
|
|
||||||
BoxNode<Box> cur, c;
|
public boolean collect(SearchNodeCb<BoxNode<T>> cb) {
|
||||||
BoxNode<Box> stack = node;
|
BoxNode<T> n;
|
||||||
|
|
||||||
while (stack != null) {
|
Stack<BoxNode<T>> stack = stackPool.get();
|
||||||
cur = stack;
|
|
||||||
stack = stack.next;
|
|
||||||
|
|
||||||
if (cur.item != null && !collectAll(cur))
|
stack.push(root);
|
||||||
break;
|
|
||||||
|
|
||||||
if ((c = cur.child00) != null) {
|
while (!stack.empty()) {
|
||||||
c.next = stack;
|
n = stack.pop();
|
||||||
stack = c;
|
|
||||||
}
|
/* process overlapping items from cur node */
|
||||||
if ((c = cur.child01) != null) {
|
cb.call(n);
|
||||||
c.next = stack;
|
|
||||||
stack = c;
|
BoxNode<T> p = n.parent;
|
||||||
}
|
|
||||||
if ((c = cur.child10) != null) {
|
/* push next node on same level onto stack */
|
||||||
c.next = stack;
|
switch (n.id) {
|
||||||
stack = c;
|
case 0:
|
||||||
}
|
if (p.child01 != null) {
|
||||||
if ((c = cur.child11) != null) {
|
stack.push(p.child01);
|
||||||
c.next = stack;
|
break;
|
||||||
stack = c;
|
}
|
||||||
|
case 1:
|
||||||
|
if (p.child10 != null) {
|
||||||
|
stack.push(p.child10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if (p.child11 != null) {
|
||||||
|
stack.push(p.child11);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* push next level child onto stack */
|
||||||
|
if (n.child00 != null)
|
||||||
|
stack.push(n.child00);
|
||||||
|
else if (n.child01 != null)
|
||||||
|
stack.push(n.child01);
|
||||||
|
else if (n.child10 != null)
|
||||||
|
stack.push(n.child10);
|
||||||
|
else if (n.child11 != null)
|
||||||
|
stack.push(n.child11);
|
||||||
}
|
}
|
||||||
|
stackPool.release(stack);
|
||||||
// dont keep dangling references
|
return false;
|
||||||
while (stack != null)
|
|
||||||
stack = stack.next;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract int process(Box box, Box it);
|
public BoxNode<T> create(BoxNode<T> parent, int i) {
|
||||||
|
BoxNode<T> node;
|
||||||
|
if (pool != null) {
|
||||||
|
node = pool;
|
||||||
|
pool = pool.parent;
|
||||||
|
} else
|
||||||
|
node = new BoxNode<T>();
|
||||||
|
|
||||||
|
node.parent = parent;
|
||||||
|
|
||||||
public BoxNode<Box> create(BoxNode<Box> parent, int i) {
|
|
||||||
BoxNode<Box> node = new BoxNode<Box>();
|
|
||||||
int size = (parent.x2 - parent.x1) >> 1;
|
int size = (parent.x2 - parent.x1) >> 1;
|
||||||
node.x1 = parent.x1;
|
node.x1 = parent.x1;
|
||||||
node.y1 = parent.y1;
|
node.y1 = parent.y1;
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
// top-left
|
|
||||||
parent.child00 = node;
|
parent.child00 = node;
|
||||||
} else if (i == 1) {
|
} else if (i == 1) {
|
||||||
// bottom-left
|
parent.child01 = node;
|
||||||
parent.child10 = node;
|
|
||||||
node.y1 += size;
|
node.y1 += size;
|
||||||
} else if (i == 2) {
|
} else if (i == 2) {
|
||||||
// top-right
|
parent.child10 = node;
|
||||||
parent.child01 = node;
|
|
||||||
node.x1 += size;
|
node.x1 += size;
|
||||||
} else {
|
} else {
|
||||||
// bottom-right
|
|
||||||
parent.child11 = node;
|
parent.child11 = node;
|
||||||
node.x1 += size;
|
node.x1 += size;
|
||||||
node.y1 += size;
|
node.y1 += size;
|
||||||
@ -267,93 +365,196 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
|
|||||||
|
|
||||||
node.x2 = node.x1 + size;
|
node.x2 = node.x1 + size;
|
||||||
node.y2 = node.y1 + size;
|
node.y2 = node.y1 + size;
|
||||||
|
node.id = (byte) i;
|
||||||
node.parent = parent;
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insert(Box box) {
|
public void insert(T box) {
|
||||||
if (box.x1 > box.x2 || box.y1 > box.y2)
|
if (box.x1 > box.x2 || box.y1 > box.y2)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
BoxNode<Box> cur = root;
|
if (box.next != null)
|
||||||
BoxNode<Box> child = null;
|
throw new IllegalStateException("BoxItem is list");
|
||||||
|
|
||||||
|
BoxNode<T> cur = root;
|
||||||
|
BoxNode<T> child = null;
|
||||||
|
|
||||||
// tile position in tree
|
|
||||||
//int px = 0, py = 0;
|
|
||||||
int idX = 0, idY = 0;
|
|
||||||
int x1 = box.x1;
|
int x1 = box.x1;
|
||||||
int x2 = box.x2;
|
int x2 = box.x2;
|
||||||
int y1 = box.y1;
|
int y1 = box.y1;
|
||||||
int y2 = box.y2;
|
int y2 = box.y2;
|
||||||
|
|
||||||
for (int level = 0; level <= maxDepth; level++) {
|
for (int level = 0; level <= maxDepth; level++) {
|
||||||
// half size of tile at current z
|
cur.refs++;
|
||||||
//int hsize = (extents >> level);
|
|
||||||
|
/* half size of tile at current level */
|
||||||
int hsize = (cur.x2 - cur.x1) >> 1;
|
int hsize = (cur.x2 - cur.x1) >> 1;
|
||||||
|
|
||||||
// center of tile (shift by -extents)
|
/* center of tile */
|
||||||
//int cx = px + hsize - extents;
|
|
||||||
//int cy = py + hsize - extents;
|
|
||||||
int cx = cur.x1 + hsize;
|
int cx = cur.x1 + hsize;
|
||||||
int cy = cur.y1 + hsize;
|
int cy = cur.y1 + hsize;
|
||||||
|
|
||||||
child = null;
|
child = null;
|
||||||
//int childPos = -1;
|
|
||||||
//log.debug(cx + ":" + cy + " " + hsize);
|
if (level < maxDepth) {
|
||||||
if (x2 <= cx) {
|
if (x2 < cx) {
|
||||||
if (y2 <= cy) {
|
if (y2 < cy) {
|
||||||
if ((child = cur.child00) == null)
|
if ((child = cur.child00) == null)
|
||||||
child = create(cur, 0);
|
child = create(cur, 0);
|
||||||
|
} else if (y1 >= cy) {
|
||||||
|
if ((child = cur.child01) == null)
|
||||||
|
child = create(cur, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// should be else?
|
if (x1 >= cx) {
|
||||||
if (y1 >= cy) {
|
if (y2 < cy) {
|
||||||
if ((child = cur.child10) == null)
|
if ((child = cur.child10) == null)
|
||||||
child = create(cur, 1);
|
child = create(cur, 2);
|
||||||
idX++;
|
} else if (y1 >= cy) {
|
||||||
|
if ((child = cur.child11) == null)
|
||||||
|
child = create(cur, 3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 (cur == minNode && child != null)
|
if (child == null) {
|
||||||
minNode = cur;
|
/* push item onto list of this node */
|
||||||
|
|
||||||
if (child == null || level == maxDepth) {
|
|
||||||
// push item onto list of this node
|
|
||||||
box.next = cur.item;
|
box.next = cur.item;
|
||||||
cur.item = box;
|
cur.item = box;
|
||||||
|
|
||||||
if (dbg)
|
if (dbg)
|
||||||
log.debug("insert at: " + level + " / " + idX + ":"
|
log.debug("insert: " + level
|
||||||
+ idY + " -- " + x1 + ":" + y1
|
+ " cnt:" + Inlist.size(cur.item) + " " + x1 + ":" + y1
|
||||||
+ " /" + (x2) + "x" + (y2));
|
+ " /" + (x2) + "x" + (y2) + " " + box.item);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cur = child;
|
cur = child;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMinNode(int x1, int y1, int x2, int y2) {
|
public boolean remove(T box, E item) {
|
||||||
/* TODO find lowest node that fully contains the region
|
if (box.x1 > box.x2 || box.y1 > box.y2)
|
||||||
* and set it as start for following queries */
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
|
BoxNode<T> cur = root;
|
||||||
|
BoxNode<T> child = null;
|
||||||
|
|
||||||
|
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 level */
|
||||||
|
int hsize = (cur.x2 - cur.x1) >> 1;
|
||||||
|
|
||||||
|
/* center of tile */
|
||||||
|
int cx = cur.x1 + hsize;
|
||||||
|
int cy = cur.y1 + hsize;
|
||||||
|
|
||||||
|
child = null;
|
||||||
|
if (level < maxDepth) {
|
||||||
|
if (x2 < cx) {
|
||||||
|
if (y2 < cy) {
|
||||||
|
if ((child = cur.child00) == null)
|
||||||
|
return false;
|
||||||
|
} else if (y1 >= cy) {
|
||||||
|
if ((child = cur.child01) == null)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (x1 >= cx) {
|
||||||
|
if (y2 < cy) {
|
||||||
|
if ((child = cur.child10) == null)
|
||||||
|
return false;
|
||||||
|
} else if (y1 >= cy) {
|
||||||
|
if ((child = cur.child11) == null)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (child == null) {
|
||||||
|
/* push item onto list of this node */
|
||||||
|
BoxItem<E> prev = cur.item;
|
||||||
|
|
||||||
|
for (BoxItem<E> it = cur.item; it != null; it = it.next) {
|
||||||
|
if (it.item == item) {
|
||||||
|
if (dbg)
|
||||||
|
log.debug("remove: " + level
|
||||||
|
+ " cnt:" + Inlist.size(cur.item) + " " + x1 + ":" + y1
|
||||||
|
+ " /" + (x2) + "x" + (y2) + " " + item);
|
||||||
|
|
||||||
|
if (cur.item == it) {
|
||||||
|
// FUNKY GENERICS...
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T b = (T) it.next;
|
||||||
|
cur.item = b;
|
||||||
|
} else
|
||||||
|
prev.next = it.next;
|
||||||
|
|
||||||
|
remove(cur);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
prev = it;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = child;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean process(BoxNode<Box> nodes);
|
public BoxNode<T> getNode(T box, boolean create) {
|
||||||
|
if (box.x1 > box.x2 || box.y1 > box.y2)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
|
BoxNode<T> cur = root;
|
||||||
|
BoxNode<T> child = null;
|
||||||
|
|
||||||
|
int x1 = box.x1;
|
||||||
|
int x2 = box.x2;
|
||||||
|
int y1 = box.y1;
|
||||||
|
int y2 = box.y2;
|
||||||
|
|
||||||
|
for (int level = 0; level <= maxDepth; level++) {
|
||||||
|
cur.refs++;
|
||||||
|
|
||||||
|
/* half size of tile at current z */
|
||||||
|
int hsize = (cur.x2 - cur.x1) >> 1;
|
||||||
|
|
||||||
|
/* center of tile (shift by -extents) */
|
||||||
|
int cx = cur.x1 + hsize;
|
||||||
|
int cy = cur.y1 + hsize;
|
||||||
|
|
||||||
|
child = null;
|
||||||
|
|
||||||
|
if (x2 < cx) {
|
||||||
|
if (y2 < cy) {
|
||||||
|
if ((child = cur.child00) == null && create)
|
||||||
|
child = create(cur, 0);
|
||||||
|
} else if (y1 >= cy) {
|
||||||
|
if ((child = cur.child01) == null && create)
|
||||||
|
child = create(cur, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (x1 >= cx) {
|
||||||
|
if (y2 < cy) {
|
||||||
|
if ((child = cur.child10) == null && create)
|
||||||
|
child = create(cur, 2);
|
||||||
|
} else if (y1 >= cy) {
|
||||||
|
if ((child = cur.child11) == null && create)
|
||||||
|
child = create(cur, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child == null || level == maxDepth)
|
||||||
|
return cur;
|
||||||
|
|
||||||
|
cur = child;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
root.child00 = null;
|
root.child00 = null;
|
||||||
@ -361,67 +562,10 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
|
|||||||
root.child10 = null;
|
root.child10 = null;
|
||||||
root.child11 = null;
|
root.child11 = null;
|
||||||
root.item = null;
|
root.item = null;
|
||||||
|
root.refs = 0;
|
||||||
//root = create();
|
|
||||||
//root.parent = root;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Item extends BoxItem<Integer> {
|
public int size() {
|
||||||
public Item(int x1, int y1, int x2, int y2) {
|
return root.refs;
|
||||||
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();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
@ -19,16 +19,17 @@ package org.oscim.utils.quadtree;
|
|||||||
/**
|
/**
|
||||||
* A quad tree for the standard map tiling schema.
|
* A quad tree for the standard map tiling schema.
|
||||||
*/
|
*/
|
||||||
public abstract class QuadTree<T extends Node<T, E>, E> {
|
public abstract class QuadTree<T extends TreeNode<T, E>, E> {
|
||||||
|
|
||||||
protected final T root;
|
protected final T root;
|
||||||
|
|
||||||
protected T minNode;
|
//protected T minNode;
|
||||||
|
|
||||||
protected T pool;
|
protected T pool;
|
||||||
|
|
||||||
public QuadTree() {
|
public QuadTree() {
|
||||||
root = create();
|
root = create();
|
||||||
|
root.id = -1;
|
||||||
root.parent = root;
|
root.parent = root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,15 +154,15 @@ public abstract class QuadTree<T extends Node<T, E>, E> {
|
|||||||
|
|
||||||
while (cur != root) {
|
while (cur != root) {
|
||||||
if (cur == null)
|
if (cur == null)
|
||||||
throw new IllegalArgumentException("item not in index");
|
throw new IllegalStateException("Item not in index");
|
||||||
|
|
||||||
// keep pointer to parent
|
/* keep pointer to parent */
|
||||||
next = cur.parent;
|
next = cur.parent;
|
||||||
cur.refs--;
|
cur.refs--;
|
||||||
|
|
||||||
// if current node has no children
|
/* if current node has no children */
|
||||||
if (cur.refs == 0) {
|
if (cur.refs == 0) {
|
||||||
// unhook from parent
|
/* unhook from parent */
|
||||||
|
|
||||||
switch (cur.id) {
|
switch (cur.id) {
|
||||||
case 0:
|
case 0:
|
||||||
@ -178,7 +179,7 @@ public abstract class QuadTree<T extends Node<T, E>, E> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add item back to pool
|
/* add item back to pool */
|
||||||
cur.parent = pool;
|
cur.parent = pool;
|
||||||
pool = cur;
|
pool = cur;
|
||||||
}
|
}
|
||||||
|
@ -16,36 +16,31 @@
|
|||||||
*/
|
*/
|
||||||
package org.oscim.utils.quadtree;
|
package org.oscim.utils.quadtree;
|
||||||
|
|
||||||
public class Node<T extends Node<T, E>, E> {
|
public class TreeNode<T extends TreeNode<T, E>, E> {
|
||||||
|
|
||||||
public T parent;
|
public T parent;
|
||||||
|
|
||||||
|
/** top-left */
|
||||||
public T child00;
|
public T child00;
|
||||||
public T child10;
|
|
||||||
|
/** bottom-left */
|
||||||
public T child01;
|
public T child01;
|
||||||
|
|
||||||
|
/** top-right */
|
||||||
|
public T child10;
|
||||||
|
|
||||||
|
/** bottom-right */
|
||||||
public T child11;
|
public T child11;
|
||||||
|
|
||||||
|
/** payload */
|
||||||
public E item;
|
public E item;
|
||||||
|
|
||||||
// id of this child relative to parent
|
/** id of this child relative to parent */
|
||||||
byte id;
|
int id;
|
||||||
|
|
||||||
// number of children and grandchildren
|
/** number of children and grandchildren */
|
||||||
int refs = 0;
|
int refs = 0;
|
||||||
|
|
||||||
// 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() {
|
public E parent() {
|
||||||
return parent.item;
|
return parent.item;
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user