/** * Returns a new node. All object references can be assumed to be null. * @return */ public KdTree.Node requestNode() { if( unusedNodes.isEmpty() ) return new KdTree.Node(); return unusedNodes.remove( unusedNodes.size()-1); }
/** * Adds a node to the priority queue. * * @param closestDistanceSq The closest distance that a point in the region could possibly be target */ protected void addToQueue(double closestDistanceSq , KdTree.Node node , P target ) { if( !node.isLeaf() ) { Helper h; if( unused.isEmpty() ) { h = new Helper(); } else { h = unused.remove( unused.size()-1 ); } h.closestPossibleSq = closestDistanceSq; h.node = node; queue.add(h); } else { checkBestDistance(node, target); } }
return; if( node.isLeaf() ) {
return; if( node.isLeaf() ) {
if( node.isLeaf() ) { return;
if( node.isLeaf() ) { return;
/** * Traverse a node down to a leaf. Unexplored branches are added to the priority queue. */ protected void searchNode(P target, KdTree.Node n) { while( n != null) { checkBestDistance(n, target); if( n.isLeaf() ) break; // select the most promising branch to investigate first KdTree.Node nearer,further; double splitValue = distance.valueAt( (P)n.point , n.split ); if( distance.valueAt( target , n.split ) <= splitValue ) { nearer = n.left; further = n.right; } else { nearer = n.right; further = n.left; } // See if it is possible for 'further' to contain a better node double dx = splitValue - distance.valueAt(target, n.split ); if( further != null && canImprove(dx*dx) ) { addToQueue(dx*dx, further, target ); } n = nearer; } }
/** * Traverse a node down to a leaf. Unexplored branches are added to the priority queue. */ protected void searchNode(P target, KdTree.Node n) { while( n != null) { checkBestDistance(n, target); if( n.isLeaf() ) break; // select the most promising branch to investigate first KdTree.Node nearer,further; double splitValue = distance.valueAt( (P)n.point , n.split ); if( distance.valueAt( target , n.split ) <= splitValue ) { nearer = n.left; further = n.right; } else { nearer = n.right; further = n.left; } // See if it is possible for 'further' to contain a better node double dx = splitValue - distance.valueAt(target, n.split ); if( further != null && canImprove(dx*dx) ) { addToQueue(dx*dx, further, target ); } n = nearer; } }
/** * Basic tests to see if it can handle different sized input lists. */ @Test public void construct() { KdTreeConstructor<double[]> alg = new KdTreeConstructor<>(new KdTreeMemory(),createSplitter(1,1,false)); // test an empty list List<double[]> points = new ArrayList<>(); KdTree tree = alg.construct(points,false); assertEquals(2, tree.N); assertNull(tree.root); // add a point points.add( new double[]{1,2}); tree = alg.construct(points,false); assertEquals(2, tree.N); assertSame(tree.root.point, points.get(0)); assertTrue(tree.root.isLeaf()); // add another point. These input points are ignored by the dummy splitter points.add( new double[]{1,2,4,5}); tree = alg.construct(points,false); assertEquals(2, tree.N); assertTrue(tree.root.left.isLeaf()); assertTrue(tree.root.right.isLeaf()); }
@Test public void computeChild() { KdTreeConstructor<double[]> alg = new KdTreeConstructor<>(distance); List<double[]> points = new ArrayList<>(); GrowQueue_I32 data = new GrowQueue_I32(); // empty lists should be null KdTree.Node n = new KdTree.Node(); n.point = new double[2]; n.index = 1; KdTree.Node found = alg.computeChild(points,data); assertNull(found); // add a point points.add( new double[2] ); data.add(2); found = alg.computeChild(points,data); assertTrue(found.isLeaf()); assertSame(found.point, points.get(0)); assertEquals(found.index, data.get(0)); // for all the other cases it will create a branch. testing that will require a bit more work... }
/** * Makes sure a branch is created correctly given the results from the splitter */ @Test public void computeBranch_dontTrack() { DummySplitter splitter = createSplitter(1,1,false); splitter.splitAxis = 1; splitter.splitPoint = new double[]{2,3}; KdTreeConstructor<double[]> alg = new KdTreeConstructor<>(new KdTreeMemory(),splitter); KdTree.Node n = alg.computeBranch(new ArrayList<>(),null); assertSame(n.point, splitter.splitPoint); assertEquals(n.split, splitter.splitAxis); assertEquals(-2, n.index); // default value in splitter assertTrue(n.left.isLeaf()); assertTrue(n.right.isLeaf()); assertSame(n.left.point, splitter.left.get(0)); assertEquals(-1, n.left.index); assertSame(n.right.point, splitter.right.get(0)); assertEquals(-1, n.right.index); }
/** * Adds a node to the priority queue. * * @param closestDistanceSq The closest distance that a point in the region could possibly be target */ protected void addToQueue(double closestDistanceSq , KdTree.Node node , P target ) { if( !node.isLeaf() ) { Helper h; if( unused.isEmpty() ) { h = new Helper(); } else { h = unused.remove( unused.size()-1 ); } h.closestPossibleSq = closestDistanceSq; h.node = node; queue.add(h); } else { checkBestDistance(node, target); } }
/** * See if max distance is being respected */ @Test public void findClosest_maxDistance() { KdTree tree = new KdTree(2); tree.root = new KdTree.Node(new double[]{1,2}); KdTreeSearchN<double[]> alg = createAlg(); alg.setTree( tree ); alg.setMaxDistance(2); found.reset(); alg.findNeighbor(new double[]{11, 8}, 1, found); assertEquals(0, found.size); found.reset(); alg.findNeighbor(new double[]{1, 1.5}, 1, found); assertEquals(1, found.size); assertTrue(found.data[0].node == tree.root); }
/** * The tree is a leaf and should always return the same result */ @Test public void findClosest_leaf() { KdTree tree = new KdTree(2); tree.root = new KdTree.Node(new double[]{1,2}); KdTreeSearchN<double[]> alg =createAlg(); alg.setTree( tree ); found.reset(); alg.findNeighbor(new double[]{11, 8}, 2, found); assertEquals(1,found.size); assertTrue(found.data[0].node == tree.root); found.reset(); alg.findNeighbor(new double[]{2, 5}, 2, found); assertEquals(1,found.size); assertTrue(found.data[0].node == tree.root); }
/** * Same test as above but with associated data */ @Test public void computeBranch_trackIndexes() { DummySplitter splitter = createSplitter(1,1,true); splitter.splitAxis = 1; splitter.splitPoint = new double[]{2,3}; splitter.splitIndex = 2; KdTreeConstructor<double[]> alg = new KdTreeConstructor<>(new KdTreeMemory(),splitter); KdTree.Node n = alg.computeBranch(new ArrayList<>(),new GrowQueue_I32()); assertSame(n.point, splitter.splitPoint); assertEquals(n.split, splitter.splitAxis); assertEquals(n.index, splitter.splitIndex); assertTrue(n.left.isLeaf()); assertTrue(n.right.isLeaf()); assertSame(n.left.point, splitter.left.get(0)); assertEquals(n.left.index, splitter.leftIndex.get(0)); assertSame(n.right.point, splitter.right.get(0)); assertEquals(n.right.index, splitter.rightIndex.get(0)); }
public static KdTree createTreeA() { KdTree tree = new KdTree(2); tree.root = new KdTree.Node(new double[]{1,2}); tree.root.split = 1; tree.root.left = new KdTree.Node(new double[]{-0.21,1}); tree.root.left.split = 0; tree.root.left.left = new KdTree.Node(new double[]{-2,0.5}); tree.root.left.left.split = -1; tree.root.left.right = new KdTree.Node(new double[]{2,0.8}); tree.root.left.right.split = -1; tree.root.right = new KdTree.Node(new double[]{10,5}); tree.root.right.split = 0; tree.root.right.left = new KdTree.Node(new double[]{10,5}); // duplicate tree.root.right.left.split = -1; tree.root.right.right = new KdTree.Node(new double[]{12,10}); tree.root.right.right.split = -1; return tree; }
/** * Provide multiple trees for input and see if it finds the best one */ @Test public void multiTreeSearch() { KdTree forest[] = new KdTree[2]; forest[0] = StandardKdTreeSearch1Tests.createTreeA(); forest[1] = new KdTree(2); forest[1].root = new KdTree.Node(new double[]{12,2}); KdTreeSearch1Bbf<double[]> alg = new KdTreeSearch1Bbf<>(new KdTreeEuclideanSq_F64(2),200); alg.setTrees(forest); KdTree.Node found = alg.findNeighbor(new double[]{12, 3}); // make sure it searched some nodes besides the root ones assertTrue(alg.numNodesSearched>0); // the best node should be the root node in the second forest assertSame(found, forest[1].root); }
/** * Provide multiple trees for input and see if it finds the best one */ @Test public void multiTreeSearch() { KdTree forest[] = new KdTree[2]; forest[0] = StandardKdTreeSearch1Tests.createTreeA(); forest[1] = new KdTree(2); forest[1].root = new KdTree.Node(new double[]{12,2}); BBF alg = new BBF(200,3); alg.setTrees(forest); KdTree.Node found = alg.findClosest(new double[]{12,3}); // make sure it searched some nodes besides the root ones assertTrue(alg.numNodesSearched>0); // the best node should be the root node in the second forest assertTrue(found==forest[1].root); }
@Test public void requestNode_leaf() { // create a node with values that need to be changed KdTree.Node n = new KdTree.Node(); n.point = new double[2]; n.split = 123; n.index = 3; KdTreeMemory alg = new KdTreeMemory(); alg.unusedNodes.add(n); KdTree.Node m = alg.requestNode(new double[]{1,2},4); assertTrue(m==n); assertTrue(((double[])m.point)[0]==1); assertEquals(m.index,4); assertTrue(m.split==-1); }
/** * See if max distance is being respected */ @Test public void findClosest_maxDistance() { KdTree tree = new KdTree(2); tree.root = new KdTree.Node(new double[]{1,2}); KdTreeSearch1<double[]> alg = createAlg(); alg.setTree( tree ); alg.setMaxDistance(2); KdTree.Node found = alg.findNeighbor(new double[]{11, 8}); assertNull(found); found = alg.findNeighbor(new double[]{1, 1.5}); assertSame(found, tree.root); }