From 62c8817a70ad6b1ab5d2b53ca49894319479200a Mon Sep 17 00:00:00 2001
From: Daniel Schramm <xschramm@fi.muni.cz>
Date: Mon, 22 Mar 2021 17:07:54 +0100
Subject: [PATCH] ClosestNodeMultipleVisitor repeated tree traversal reduced

---
 .../kdtree/ClosestNodeMultipleVisitor.java    | 63 +++++--------------
 .../ClosestNodeMultipleVisitorTest.java       | 51 +++++++++++++++
 2 files changed, 65 insertions(+), 49 deletions(-)

diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/ClosestNodeMultipleVisitor.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/ClosestNodeMultipleVisitor.java
index 4c510c66..221637b1 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/ClosestNodeMultipleVisitor.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/ClosestNodeMultipleVisitor.java
@@ -59,54 +59,18 @@ public class ClosestNodeMultipleVisitor extends ClosestNodeAbstractVisitor {
             return;
         }
         
-        KdNode foundNode = closestNode(kdTree.getRoot());
-        final double closestDistance = MeshPoint.distance(getPoint3d(), foundNode.getLocation());
-        synchronized (this) {
-            if (updateMinimalDistance(closestDistance)) {
-                closest.clear();
-            }
-        }
-        
-        // There is no closer point than the 'closestFound' in our tree.
-        // Search for other closest points with the same distance.
-        while (foundNode != null) {
-            final double distanceFound = MeshPoint.distance(getPoint3d(), foundNode.getLocation());
-            synchronized (this) {
-                if (distanceFound == getDistance()) {
-                    closest.add(foundNode);
-                } else { // There is a closer point found in some other tree visited by this visitor
-                    return;
-                }
-            }
-            foundNode = closestNodeOmitting(root);
-        }
-    }
-    
-    /*
-     * Find an undiscovered node with its distance equal to 'distance'.
-     *
-     * - Discovered nodes are those stored in the 'closest' set.
-     * - We are not looking for distance closer than 'distance' because
-     *   from the method {@link cz.fidentis.analyst.visitors.kdtree.ClosestNodeMultipleVisitor#visitKdTree}
-     *   we already know distance to the closest node of the visited tree.
-     */
-    private KdNode closestNodeOmitting(KdNode root) {
-        if (root == null) {
-            return null;
-        }
+        KdNode searchedNode = root;
         
         /*
-         * First, find the closest node
+         * Reduce the search space by exploring the area where the closest node
+         * may theoretically be located.
          */
-        KdNode nearNode = null;
-        KdNode searchedNode = root;
-
         while (searchedNode != null) {
             final Vector3d nodePos = searchedNode.getLocation();
             final double dist = MeshPoint.distance(getPoint3d(), nodePos);
             synchronized (this) {
-                if (dist == getDistance() && !closest.contains(searchedNode)) {
-                    nearNode = searchedNode;
+                if (updateMinimalDistance(dist) && !closest.isEmpty()) {
+                    closest.clear();
                 }
             }
 
@@ -118,20 +82,23 @@ public class ClosestNodeMultipleVisitor extends ClosestNodeAbstractVisitor {
         }
 
         /*
-         * Second, search for a vertex that could be potentially closer than
-         * the nearest vertex already found
+         * Search for vertices that could be potentially closer than
+         * the closest distance already found or at the same distance.
          */
         final Queue<KdNode> queue = new LinkedList<>();
         queue.add(root);
 
         while (!queue.isEmpty()) {
             searchedNode = queue.poll();
-            Vector3d nodePos = searchedNode.getLocation();
+            final Vector3d nodePos = searchedNode.getLocation();
 
             final double dist = MeshPoint.distance(getPoint3d(), nodePos);
             synchronized (this) {
-                if (dist == getDistance() && !closest.contains(searchedNode)) {
-                    nearNode = searchedNode;
+                if (updateMinimalDistance(dist)) {
+                    closest.clear();
+                    closest.add(searchedNode);
+                } else if (dist == getDistance()) {
+                    closest.add(searchedNode);
                 }
             }
 
@@ -156,8 +123,6 @@ public class ClosestNodeMultipleVisitor extends ClosestNodeAbstractVisitor {
                 }
             }
         }
-
-        return nearNode;
     }
-    
+
 }
diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/ClosestNodeMultipleVisitorTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/ClosestNodeMultipleVisitorTest.java
index a0ba2374..89e0d65a 100644
--- a/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/ClosestNodeMultipleVisitorTest.java
+++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/ClosestNodeMultipleVisitorTest.java
@@ -110,4 +110,55 @@ public class ClosestNodeMultipleVisitorTest {
         assertEquals(8, closestFound.size());
         testContainsExactlyPositions(closest, closestFound);
     }
+    
+    @Test
+    public void threeTreesSingleClosestTest() {
+        final Set<MeshPoint> closest = getTreeNodesSymmetric(1);
+        final KdTree kdTreeClosest = new KdTree(closest);
+        
+        final KdTree kdTree1 = new KdTree(getTreeNodesSymmetric(2));
+        final KdTree kdTree2 = new KdTree(getTreeNodesSymmetric(3));
+        
+        final ClosestNodeMultipleVisitor visitor = new ClosestNodeMultipleVisitor(centerNode, false);
+        kdTree1.accept(visitor);
+        kdTreeClosest.accept(visitor);
+        kdTree2.accept(visitor);
+        
+        final Set<KdNode> closestFound = visitor.getClosestNodes();
+        assertEquals(8, closestFound.size());
+        testContainsExactlyPositions(closest, closestFound);
+    }
+    
+    @Test
+    public void threeTreesTwoClosestTest() {
+        final Set<MeshPoint> closest = getTreeNodesSymmetric(1);
+        final Set<MeshPoint> closest1 = new HashSet<>();
+        final Set<MeshPoint> closest2 = new HashSet<>();
+        boolean odd = true;
+        for (final MeshPoint point: closest) {
+            if (odd) {
+                closest1.add(point);
+            } else {
+                closest2.add(point);
+            }
+            odd = !odd;
+        }
+        assertEquals(8, closest.size());
+        assertEquals(4, closest1.size());
+        assertEquals(4, closest2.size());
+        
+        final KdTree kdTreeClosest1 = new KdTree(closest1);
+        final KdTree kdTreeClosest2 = new KdTree(closest2);
+        final KdTree kdTree = new KdTree(getTreeNodesSymmetric(2));
+        
+        final ClosestNodeMultipleVisitor visitor = new ClosestNodeMultipleVisitor(centerNode, false);
+        kdTreeClosest1.accept(visitor);
+        kdTree.accept(visitor);
+        kdTreeClosest2.accept(visitor);
+        
+        final Set<KdNode> closestFound = visitor.getClosestNodes();
+        assertEquals(8, closestFound.size());
+        testContainsExactlyPositions(closest, closestFound);
+    }
+    
 }
-- 
GitLab