Skip to content

Commit 25ebc2e

Browse files
author
Russ Weeks
committed
Added implementation of QuadraticPickSeeds; QuadraticPickNext
1 parent 34e9ce0 commit 25ebc2e

2 files changed

Lines changed: 128 additions & 46 deletions

File tree

src/com/newbrightidea/util/RTree.java

Lines changed: 123 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@
2121
*/
2222
public class RTree<T>
2323
{
24+
public enum SeedPicker { LINEAR, QUADRATIC }
2425

2526
private final int maxEntries;
2627
private final int minEntries;
2728
private final int numDims;
2829

2930
private final float[] pointDims;
3031

32+
private final SeedPicker seedPicker;
33+
3134
private Node root;
3235

3336
private volatile int size;
@@ -42,16 +45,22 @@ public class RTree<T>
4245
* @param numDims
4346
* the number of dimensions of the RTree.
4447
*/
45-
public RTree(int maxEntries, int minEntries, int numDims)
48+
public RTree(int maxEntries, int minEntries, int numDims, SeedPicker seedPicker)
4649
{
4750
assert (minEntries <= (maxEntries / 2));
4851
this.numDims = numDims;
4952
this.maxEntries = maxEntries;
5053
this.minEntries = minEntries;
54+
this.seedPicker = seedPicker;
5155
pointDims = new float[numDims];
5256
root = buildRoot(true);
5357
}
5458

59+
public RTree(int maxEntries, int minEntries, int numDims)
60+
{
61+
this(maxEntries, minEntries, numDims, SeedPicker.LINEAR);
62+
}
63+
5564
private Node buildRoot(boolean asLeaf)
5665
{
5766
float[] initCoords = new float[numDims];
@@ -70,7 +79,7 @@ private Node buildRoot(boolean asLeaf)
7079
*/
7180
public RTree()
7281
{
73-
this(50, 2, 2);
82+
this(50, 2, 2, SeedPicker.LINEAR);
7483
}
7584

7685
/**
@@ -375,6 +384,10 @@ private void adjustTree(Node n, Node nn)
375384

376385
private Node[] splitNode(Node n)
377386
{
387+
// TODO: this class probably calls "tighten" a little too often.
388+
// For instance the call at the end of the "while (!cc.isEmpty())" loop
389+
// could be modified and inlined because it's only adjusting for the addition
390+
// of a single node. Left as-is for now for readability.
378391
@SuppressWarnings("unchecked")
379392
Node[] nn = new RTree.Node[]
380393
{ n, new Node(n.coords, n.dimensions, n.leaf) };
@@ -385,32 +398,30 @@ private Node[] splitNode(Node n)
385398
}
386399
LinkedList<Node> cc = new LinkedList<Node>(n.children);
387400
n.children.clear();
388-
Node[] ss = pickSeeds(cc);
401+
Node[] ss = seedPicker == SeedPicker.LINEAR ? lPickSeeds(cc) : qPickSeeds(cc);
389402
nn[0].children.add(ss[0]);
390403
nn[1].children.add(ss[1]);
404+
tighten(nn);
391405
while (!cc.isEmpty())
392406
{
393407
if ((nn[0].children.size() >= minEntries)
394408
&& (nn[1].children.size() + cc.size() == minEntries))
395409
{
396410
nn[1].children.addAll(cc);
397411
cc.clear();
398-
tighten(nn[0]); // Not sure this is required.
399-
tighten(nn[1]);
412+
tighten(nn); // Not sure this is required.
400413
return nn;
401414
}
402415
else if ((nn[1].children.size() >= minEntries)
403416
&& (nn[0].children.size() + cc.size() == minEntries))
404417
{
405418
nn[0].children.addAll(cc);
406419
cc.clear();
407-
tighten(nn[0]); // Not sure this is required.
408-
tighten(nn[1]);
420+
tighten(nn); // Not sure this is required.
409421
return nn;
410422
}
411-
Node c = cc.pop();
423+
Node c = seedPicker == SeedPicker.LINEAR ? lPickNext(cc) : qPickNext(cc, nn);
412424
Node preferred;
413-
// Implementation of linear PickNext
414425
float e0 = getRequiredExpansion(nn[0].coords, nn[0].dimensions, c);
415426
float e1 = getRequiredExpansion(nn[1].coords, nn[1].dimensions, c);
416427
if (e0 < e1)
@@ -450,16 +461,75 @@ else if (nn[0].children.size() > nn[1].children.size())
450461
}
451462
}
452463
preferred.children.add(c);
464+
tighten(preferred);
453465
}
454-
tighten(nn[0]);
455-
tighten(nn[1]);
456466
return nn;
457467
}
458468

469+
// Implementation of Quadratic PickSeeds
470+
private RTree<T>.Node[] qPickSeeds(LinkedList<Node> nn)
471+
{
472+
@SuppressWarnings("unchecked")
473+
RTree<T>.Node[] bestPair = new RTree.Node[2];
474+
float maxWaste = -1.0f * Float.MAX_VALUE;
475+
for (Node n1: nn)
476+
{
477+
for (Node n2: nn)
478+
{
479+
if (n1 == n2) continue;
480+
float n1a = getArea(n1.dimensions);
481+
float n2a = getArea(n2.dimensions);
482+
float ja = 1.0f;
483+
for ( int i = 0; i < numDims; i++ )
484+
{
485+
float jc0 = Math.min(n1.coords[i], n2.coords[i]);
486+
float jc1 = Math.max(n1.coords[i] + n1.dimensions[i], n2.coords[i] + n2.dimensions[i]);
487+
ja *= (jc1 - jc0);
488+
}
489+
float waste = ja - n1a - n2a;
490+
if ( waste > maxWaste )
491+
{
492+
maxWaste = waste;
493+
bestPair[0] = n1;
494+
bestPair[1] = n2;
495+
}
496+
}
497+
}
498+
nn.remove(bestPair[0]);
499+
nn.remove(bestPair[1]);
500+
return bestPair;
501+
}
502+
503+
/**
504+
* Implementation of QuadraticPickNext
505+
* @param cc the children to be divided between the new nodes, one item will be removed from this list.
506+
* @param nn the candidate nodes for the children to be added to.
507+
*/
508+
private Node qPickNext(LinkedList<Node> cc, Node[] nn)
509+
{
510+
float maxDiff = -1.0f * Float.MAX_VALUE;
511+
Node nextC = null;
512+
for ( Node c: cc )
513+
{
514+
float n0Exp = getRequiredExpansion(nn[0].coords, nn[0].dimensions, c);
515+
float n1Exp = getRequiredExpansion(nn[1].coords, nn[1].dimensions, c);
516+
float diff = Math.abs(n1Exp - n0Exp);
517+
if (diff > maxDiff)
518+
{
519+
maxDiff = diff;
520+
nextC = c;
521+
}
522+
}
523+
assert (nextC != null) : "No node selected from qPickNext";
524+
cc.remove(nextC);
525+
return nextC;
526+
}
527+
459528
// Implementation of LinearPickSeeds
460-
private RTree<T>.Node[] pickSeeds(LinkedList<Node> nn)
529+
private RTree<T>.Node[] lPickSeeds(LinkedList<Node> nn)
461530
{
462-
RTree<T>.Node[] bestPair = null;
531+
@SuppressWarnings("unchecked")
532+
RTree<T>.Node[] bestPair = new RTree.Node[2];
463533
float bestSep = 0.0f;
464534
for (int i = 0; i < numDims; i++)
465535
{
@@ -491,8 +561,8 @@ private RTree<T>.Node[] pickSeeds(LinkedList<Node> nn)
491561
Math.abs((dimMinUb - dimMaxLb) / (dimUb - dimLb));
492562
if (sep >= bestSep)
493563
{
494-
bestPair = new RTree.Node[]
495-
{ nMaxLb, nMinUb };
564+
bestPair[0] = nMaxLb;
565+
bestPair[1] = nMinUb;
496566
bestSep = sep;
497567
}
498568
}
@@ -508,39 +578,51 @@ private RTree<T>.Node[] pickSeeds(LinkedList<Node> nn)
508578
return bestPair;
509579
}
510580

511-
private void tighten(Node n)
581+
/**
582+
* Implementation of LinearPickNext
583+
* @param cc the children to be divided between the new nodes, one item will be removed from this list.
584+
*/
585+
private Node lPickNext(LinkedList<Node> cc)
512586
{
513-
assert(n.children.size() > 0) : "tighten() called on empty node!";
514-
float[] minCoords = new float[numDims];
515-
float[] maxCoords = new float[numDims];
516-
for (int i = 0; i < numDims; i++)
517-
{
518-
minCoords[i] = Float.MAX_VALUE;
519-
maxCoords[i] = Float.MIN_VALUE;
587+
return cc.pop();
588+
}
520589

521-
for (Node c : n.children)
590+
private void tighten(Node... nodes)
591+
{
592+
assert(nodes.length >= 1): "Pass some nodes to tighten!";
593+
for (Node n: nodes) {
594+
assert(n.children.size() > 0) : "tighten() called on empty node!";
595+
float[] minCoords = new float[numDims];
596+
float[] maxCoords = new float[numDims];
597+
for (int i = 0; i < numDims; i++)
522598
{
523-
// we may have bulk-added a bunch of children to a node (eg. in
524-
// splitNode)
525-
// so here we just enforce the child->parent relationship.
526-
c.parent = n;
527-
if (c.coords[i] < minCoords[i])
528-
{
529-
minCoords[i] = c.coords[i];
530-
}
531-
if ((c.coords[i] + c.dimensions[i]) > maxCoords[i])
599+
minCoords[i] = Float.MAX_VALUE;
600+
maxCoords[i] = Float.MIN_VALUE;
601+
602+
for (Node c : n.children)
532603
{
533-
maxCoords[i] = (c.coords[i] + c.dimensions[i]);
604+
// we may have bulk-added a bunch of children to a node (eg. in
605+
// splitNode)
606+
// so here we just enforce the child->parent relationship.
607+
c.parent = n;
608+
if (c.coords[i] < minCoords[i])
609+
{
610+
minCoords[i] = c.coords[i];
611+
}
612+
if ((c.coords[i] + c.dimensions[i]) > maxCoords[i])
613+
{
614+
maxCoords[i] = (c.coords[i] + c.dimensions[i]);
615+
}
534616
}
535617
}
618+
for (int i = 0; i < numDims; i++)
619+
{
620+
// Convert max coords to dimensions
621+
maxCoords[i] -= minCoords[i];
622+
}
623+
System.arraycopy(minCoords, 0, n.coords, 0, numDims);
624+
System.arraycopy(maxCoords, 0, n.dimensions, 0, numDims);
536625
}
537-
for (int i = 0; i < numDims; i++)
538-
{
539-
// Convert max coords to dimensions
540-
maxCoords[i] -= minCoords[i];
541-
}
542-
System.arraycopy(minCoords, 0, n.coords, 0, numDims);
543-
System.arraycopy(maxCoords, 0, n.dimensions, 0, numDims);
544626
}
545627

546628
private RTree<T>.Node chooseLeaf(RTree<T>.Node n, RTree<T>.Entry e)

test/com/newbrightidea/util/TestRTree.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@ public void testEmptyResults()
9393
@Test
9494
public void testSplitNodesSmall()
9595
{
96-
RTree<Object> rt = new RTree(2,1,2);
96+
RTree<Object> rt = new RTree(2,1,2, RTree.SeedPicker.QUADRATIC);
9797
float[][] coords = new float[][] { {0.0f, 0.0f}, {1.0f, 1.0f}, {2.0f, 2.0f}, {3.0f, 3.0f} };
9898
float[] dims = new float[]{0.5f, 0.5f};
99-
Object[] entries = new Object[] { new Object(), new Object(), new Object(), new Object() };
99+
Object[] entries = new Object[] { 0, 1, 2, 3 };
100100
for (int i = 0; i < entries.length; i++ )
101101
{
102102
rt.insert(coords[i], dims, entries[i]);
@@ -264,10 +264,10 @@ class DataObject {
264264

265265
for ( int j = 0; j < 500; j++ )
266266
{
267-
RTree<Integer> tree = new RTree<Integer>(10, 2, 3);
267+
RTree<Integer> tree = new RTree<Integer>(10, 2, 3, RTree.SeedPicker.LINEAR);
268268
List<DataObject> rects = new ArrayList<DataObject>();
269269

270-
for (int i = 0; i < 100; i++) {
270+
for (int i = 0; i < 150; i++) {
271271
rects.add(new DataObject(
272272
new float[]{i, i * 2, i * 3},
273273
new float[]{0, 0, 0},
@@ -276,7 +276,7 @@ class DataObject {
276276
tree.insert(dataObject.val, dataObject.dim, dataObject.id);
277277
}
278278

279-
for (int i = 0; i < 100; i++) {
279+
for (int i = 0; i < 150; i++) {
280280
DataObject dataObject = rects.get(i);
281281
boolean deleted = tree.delete(dataObject.val, dataObject.dim, dataObject.id);
282282
assert deleted;

0 commit comments

Comments
 (0)