2121 */
2222public 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 )
0 commit comments