From a6590c2314b4d0d7bed4118d5f94d8b98683a926 Mon Sep 17 00:00:00 2001
From: Radek Oslejsek <oslejsek@fi.muni.cz>
Date: Fri, 26 Feb 2021 10:41:30 +0100
Subject: [PATCH] Finished concurrent implementation of visitorts and their
 tests

---
 .../mesh/HausdorffDistMeshTriVisitor.java     |  58 +++-
 .../mesh/HausdorffDistMeshVisitor.java        |   8 +-
 .../visitors/mesh/Point2MeshTriVisitor.java   |   4 +-
 .../mesh/HausdorffDistMeshTriVisitorTest.java |  96 ++----
 .../mesh/HausdorffDistMeshVisitorTest.java    |  95 +++---
 .../mesh/Point2MeshTriVisitorTest.java        | 294 +++++-------------
 .../visitors/mesh/Point2MeshVisitorTest.java  |  71 ++---
 7 files changed, 244 insertions(+), 382 deletions(-)

diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshTriVisitor.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshTriVisitor.java
index 947ce338..bd98bfd2 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshTriVisitor.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshTriVisitor.java
@@ -1,10 +1,16 @@
 package cz.fidentis.analyst.visitors.mesh;
 
+import cz.fidentis.analyst.mesh.MeshVisitor;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
+import cz.fidentis.analyst.mesh.core.MeshModel;
 import cz.fidentis.analyst.mesh.core.MeshPoint;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 /**
  * Visitor for Hausdorff distance. 
@@ -45,20 +51,58 @@ public class HausdorffDistMeshTriVisitor extends HausdorffDistMeshVisitor {
         super(mainFacet, relativeDistance, concurrently);
     }
     
+    /**
+     * @param mainModel The mesh model with primary facets of which distance to 
+     * others is to be computed. Must not be {@code null} or empty.
+     * @param relativeDistance If true, then the visitor calculates the relative distances with respect 
+     * to the normal vectors of source facets (normal vectors have to present), 
+     * i.e., we can get negative distances.
+     * @param concurrently If {@code true} and this visitor is thread-safe, then
+     * the visitor is applied concurrently when inspecting multiple mesh facets.
+     * @throws IllegalArgumentException if some parametr is wrong
+     */
+    public HausdorffDistMeshTriVisitor(MeshModel mainModel, boolean relativeDistance, boolean concurrently) {
+        super(mainModel, relativeDistance, concurrently);
+    }
+    
     @Override
     protected void visitMeshFacet(MeshFacet comparedFacet) {
+        int threads = Runtime.getRuntime().availableProcessors();
+        ExecutorService executor = Executors.newFixedThreadPool(threads);
+        List<Future<MeshVisitor>> results = new LinkedList<>();
+        
         for (Map.Entry<MeshFacet, List<Double>> entry: getDistMap().entrySet()) {
             List<MeshPoint> vertices = entry.getKey().getVertices();
             List<Double> distList = entry.getValue();
             
-            boolean firstComparison = distList.isEmpty();
-            
-            for (int i = 0; i < vertices.size(); i++) {
-                Point2MeshTriVisitor visitor = new Point2MeshTriVisitor(vertices.get(i), isRelativeDistance(), concurrently());
-                comparedFacet.accept(visitor);
-                double dist = visitor.getDistance();
-                updateDistances(distList, firstComparison, dist, i);
+            if (concurrently()) {
+                for (int i = 0; i < vertices.size(); i++) {
+                    Point2MeshVisitor visitor = new Point2MeshTriVisitor(vertices.get(i), isRelativeDistance(), true);
+                    comparedFacet.accept(visitor);
+                    Future<MeshVisitor> result = executor.submit(visitor); // fork and continue
+                    results.add(result);
+                    //updateFacetDistancesConcurrently(distList, results);
+                }
+                updateFacetDistancesConcurrently(distList, results);
+            } else {
+                updateFacetDistancesSequentially(vertices, distList, comparedFacet);
             }
         }
     }
+    
+    @Override
+    protected synchronized void updateFacetDistancesSequentially(
+            List<MeshPoint> facetVertices, 
+            List<Double> facetDistances, 
+            MeshFacet comparedFacet) {
+        
+        boolean firstComparison = facetDistances.isEmpty();
+            
+        for (int i = 0; i < facetVertices.size(); i++) {
+            Point2MeshVisitor visitor = new Point2MeshTriVisitor(facetVertices.get(i), isRelativeDistance(), false);
+            comparedFacet.accept(visitor);
+            double dist = visitor.getDistance();
+            updateDistances(facetDistances, firstComparison, dist, i);
+        }
+    }
 }
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshVisitor.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshVisitor.java
index 237cae9a..d45c35dc 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshVisitor.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshVisitor.java
@@ -105,6 +105,7 @@ public class HausdorffDistMeshVisitor extends MeshVisitor {
                     comparedFacet.accept(visitor);
                     Future<MeshVisitor> result = executor.submit(visitor); // fork and continue
                     results.add(result);
+                    //updateFacetDistancesConcurrently(distList, results);
                 }
                 updateFacetDistancesConcurrently(distList, results);
             } else {
@@ -114,7 +115,7 @@ public class HausdorffDistMeshVisitor extends MeshVisitor {
     }
     
     /**
-     * Sequentially updates distances of particular mesh. The update has to be 
+     * Sequentially updates distances of particular mesh facet. The update has to be 
      * exclusive (sychronized with multiple threads)
      */
     protected synchronized void updateFacetDistancesSequentially(
@@ -133,8 +134,9 @@ public class HausdorffDistMeshVisitor extends MeshVisitor {
     }
     
     /**
-     * Sequentially updates distances of particular mesh. The update has to be 
-     * exclusive (sychronized with multiple threads)
+     * Updates distances of a particular mesh facet computed concurrently by 
+     * multiple independent visitors (one for each vertex of the source facet). 
+     * Only this update has to be exclusive (sychronized with multiple threads)
      */
     protected synchronized void updateFacetDistancesConcurrently(
             List<Double> facetDistances, 
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/Point2MeshTriVisitor.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/Point2MeshTriVisitor.java
index 2c748280..c6786e0e 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/Point2MeshTriVisitor.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/Point2MeshTriVisitor.java
@@ -24,11 +24,11 @@ import javax.vecmath.Vector3d;
  * Then iterate into the n-th triangle of facet {@code getClosestFacets().get(i)} to get 
  * the triangle instance.
  * </p>
-* <p>
+ * <p>
  * This visitor is thread-safe. A single instance of the visitor can be used 
  * to inspect multiple mesh models or facets simultaneously.
  * </p>
-  * 
+ * 
  * @author Matej Lukes
  * @author Radek Oslejsek
  */
diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshTriVisitorTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshTriVisitorTest.java
index a7c2f5c6..a93bd2b6 100644
--- a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshTriVisitorTest.java
+++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshTriVisitorTest.java
@@ -1,89 +1,53 @@
 package cz.fidentis.analyst.visitors.mesh;
 
 import cz.fidentis.analyst.visitors.mesh.HausdorffDistMeshTriVisitor;
-import cz.fidentis.analyst.mesh.core.CornerTableRow;
-import cz.fidentis.analyst.mesh.core.MeshFacet;
-import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
-import cz.fidentis.analyst.mesh.core.MeshPointImpl;
-import java.util.List;
-import java.util.Map;
-import javax.vecmath.Vector3d;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import cz.fidentis.analyst.mesh.core.MeshModel;
 import org.junit.jupiter.api.Test;
 
-public class HausdorffDistMeshTriVisitorTest {
-
-    private MeshFacet getTrivialFacet(double offset, double size) {
-        MeshFacet facet = new MeshFacetImpl();
-        facet.addVertex(new MeshPointImpl(new Vector3d(0, 0, offset), new Vector3d(0, 0, 1), new Vector3d()));
-        facet.addVertex(new MeshPointImpl(new Vector3d(size, 0, offset), new Vector3d(0, 0, 1), new Vector3d()));
-        facet.addVertex(new MeshPointImpl(new Vector3d(0, size, offset), new Vector3d(0, 0, 1), new Vector3d()));
-
-        facet.getCornerTable().addRow(new CornerTableRow(0, -1));
-        facet.getCornerTable().addRow(new CornerTableRow(1, -1));
-        facet.getCornerTable().addRow(new CornerTableRow(2, -1));
-
-        return facet;
-    }
+public class HausdorffDistMeshTriVisitorTest extends HausdorffDistMeshVisitorTest {
 
     @Test
+    @Override
     public void visitToVerticesTest() {
-        MeshFacet mainFacet = getTrivialFacet(1, 1);
-        MeshFacet comparedFacet = getTrivialFacet(1.5, 1);
-
-        HausdorffDistMeshTriVisitor hausdorffDistance = new HausdorffDistMeshTriVisitor(mainFacet, false, false);
-        hausdorffDistance.visitMeshFacet(comparedFacet);
-        
-        Map<MeshFacet, List<Double>> map = hausdorffDistance.getDistances();
-        List<Double> results = map.get(mainFacet);
-        for (int i = 0; i < mainFacet.getNumberOfVertices(); i++) {
-            assertEquals(0.5, results.get(i));
-        }
+        MeshModel firstModel = getTrivialModel(1, 1);
+        MeshModel secondModel = getTrivialModel(1.5, 1);
+        testDist(new HausdorffDistMeshTriVisitor(firstModel, false, false), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistMeshTriVisitor(secondModel, false, false), secondModel, firstModel, 0.5);
     }
 
     @Test
     public void visitToVerticesBehindMeshTest() {
-        MeshFacet mainFacet = getTrivialFacet(1, 1);
-        MeshFacet comparedFacet = getTrivialFacet(-1.5, 1);
-
-        HausdorffDistMeshTriVisitor hausdorffDistance = new HausdorffDistMeshTriVisitor(mainFacet, false, false);
-        hausdorffDistance.visitMeshFacet(comparedFacet);
-
-        Map<MeshFacet, List<Double>> map = hausdorffDistance.getDistances();
-        List<Double> results = map.get(mainFacet);
-        for (int i = 0; i < mainFacet.getNumberOfVertices(); i++) {
-            assertEquals(2.5, results.get(i));
-        }
+        MeshModel firstModel = getTrivialModel(1, 1);
+        MeshModel secondModel = getTrivialModel(-1.5, 1);
+        testDist(new HausdorffDistMeshTriVisitor(firstModel, false, false), firstModel, secondModel, 2.5);
+        testDist(new HausdorffDistMeshTriVisitor(secondModel, false, false), secondModel, firstModel, 2.5);
     }
 
     @Test
+    @Override
     public void visitToVerticesRelativeDistanceTest() {
-        MeshFacet mainFacet = getTrivialFacet(1, 1);
-        MeshFacet comparedFacet = getTrivialFacet(1.5, 1);
-
-        HausdorffDistMeshTriVisitor hausdorffDistance = new HausdorffDistMeshTriVisitor(mainFacet, true, false);
-        hausdorffDistance.visitMeshFacet(comparedFacet);
-
-        Map<MeshFacet, List<Double>> map = hausdorffDistance.getDistances();
-        List<Double> results = map.get(mainFacet);
-        for (int i = 0; i < mainFacet.getNumberOfVertices(); i++) {
-            assertEquals(0.5, results.get(i));
-        }
+        MeshModel firstModel = getTrivialModel(1, 1);
+        MeshModel secondModel = getTrivialModel(1.5, 1);
+        testDist(new HausdorffDistMeshTriVisitor(firstModel, true, false), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistMeshTriVisitor(secondModel, true, false), secondModel, firstModel, -0.5);
     }
 
     @Test
+    @Override
     public void visitToVerticesBehindMeshRelativeDistanceTest() {
-        MeshFacet mainFacet = getTrivialFacet(1, 1);
-        MeshFacet comparedFacet = getTrivialFacet(-1.5, 1);
-
-        HausdorffDistMeshTriVisitor hausdorffDistance = new HausdorffDistMeshTriVisitor(mainFacet, true, false);
-        hausdorffDistance.visitMeshFacet(comparedFacet);
-
-        Map<MeshFacet, List<Double>> map = hausdorffDistance.getDistances();
-        List<Double> results = map.get(mainFacet);
-        for (int i = 0; i < mainFacet.getNumberOfVertices(); i++) {
-            assertEquals(-2.5, results.get(i));
-        }
+        MeshModel firstModel = getTrivialModel(1, 1);
+        MeshModel secondModel = getTrivialModel(-1.5, 1);
+        testDist(new HausdorffDistMeshTriVisitor(firstModel, true, false), firstModel, secondModel, -2.5);
+        testDist(new HausdorffDistMeshTriVisitor(secondModel, true, false), secondModel, firstModel, 2.5);
+    }
+    
+    @Test
+    @Override
+    public void concurrencyTest() {
+        MeshModel firstModel = getTrivialModel(1, 1);
+        MeshModel secondModel = getTrivialModel(1.5, 1);
+        testDist(new HausdorffDistMeshTriVisitor(firstModel, false, true), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistMeshTriVisitor(secondModel, false, true), secondModel, firstModel, 0.5);
     }
     
 }
diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshVisitorTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshVisitorTest.java
index 5ad6f5d4..d365d017 100644
--- a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshVisitorTest.java
+++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistMeshVisitorTest.java
@@ -1,19 +1,20 @@
 package cz.fidentis.analyst.visitors.mesh;
 
-import cz.fidentis.analyst.visitors.mesh.HausdorffDistMeshVisitor;
 import cz.fidentis.analyst.mesh.core.CornerTableRow;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
 import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
+import cz.fidentis.analyst.mesh.core.MeshModel;
 import cz.fidentis.analyst.mesh.core.MeshPointImpl;
 import java.util.List;
 import java.util.Map;
 import javax.vecmath.Vector3d;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import org.junit.jupiter.api.Test;
 
 public class HausdorffDistMeshVisitorTest {
 
-    private MeshFacet getTrivialFacet(double offset, double size) {
+    protected MeshFacet getTrivialFacet(double offset, double size) {
         MeshFacet facet = new MeshFacetImpl();
         facet.addVertex(new MeshPointImpl(new Vector3d(0, 0, offset), new Vector3d(0, 0, 1), new Vector3d()));
         facet.addVertex(new MeshPointImpl(new Vector3d(size, 0, offset), new Vector3d(0, 0, 1), new Vector3d()));
@@ -25,65 +26,65 @@ public class HausdorffDistMeshVisitorTest {
 
         return facet;
     }
-
-    @Test
-    public void visitToVerticesTest() {
-        MeshFacet mainFacet = getTrivialFacet(1, 1);
-        MeshFacet comparedFacet = getTrivialFacet(1.5, 1);
-
-        HausdorffDistMeshVisitor hausdorffDistance = new HausdorffDistMeshVisitor(mainFacet, false, false);
-        hausdorffDistance.visitMeshFacet(comparedFacet);
+    
+    protected MeshModel getTrivialModel(double offset, double size) {
+        MeshModel model = new MeshModel();
+        model.addFacet(getTrivialFacet(offset, size));
+        return model;
+    }
+    
+    protected void testDist(HausdorffDistMeshVisitor vis, MeshModel firstModel, MeshModel secondModel, double expectedDist) {
+        MeshFacet firstFacet = firstModel.getFacets().get(0);
         
-        Map<MeshFacet, List<Double>> map = hausdorffDistance.getDistances();
-        List<Double> results = map.get(mainFacet);
-        for (int i = 0; i < mainFacet.getNumberOfVertices(); i++) {
-            assertEquals(0.5, results.get(i));
+        secondModel.compute(vis);
+        Map<MeshFacet, List<Double>> map = vis.getDistances();
+        
+        assertTrue(map.containsKey(firstFacet));
+        List<Double> results = map.get(firstFacet);
+        for (int i = 0; i < firstFacet.getNumberOfVertices(); i++) {
+            assertEquals(expectedDist, results.get(i));
         }
     }
+    
+    
+    @Test
+    public void visitToVerticesTest() {
+        MeshModel firstModel = getTrivialModel(1, 1);
+        MeshModel secondModel = getTrivialModel(1.5, 1);
+        testDist(new HausdorffDistMeshVisitor(firstModel, false, false), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistMeshVisitor(secondModel, false, false), secondModel, firstModel, 0.5);
+    }
 
     @Test
     public void visitToVerticesBehindMeshTest() {
-        MeshFacet mainFacet = getTrivialFacet(1, 1);
-        MeshFacet comparedFacet = getTrivialFacet(-1.5, 1);
-
-        HausdorffDistMeshVisitor hausdorffDistance = new HausdorffDistMeshVisitor(mainFacet, false, false);
-        hausdorffDistance.visitMeshFacet(comparedFacet);
-
-        Map<MeshFacet, List<Double>> map = hausdorffDistance.getDistances();
-        List<Double> results = map.get(mainFacet);
-        for (int i = 0; i < mainFacet.getNumberOfVertices(); i++) {
-            assertEquals(2.5, results.get(i));
-        }
+        MeshModel firstModel = getTrivialModel(1, 1);
+        MeshModel secondModel = getTrivialModel(-1.5, 1);
+        testDist(new HausdorffDistMeshVisitor(firstModel, false, false), firstModel, secondModel, 2.5);
+        testDist(new HausdorffDistMeshVisitor(secondModel, false, false), secondModel, firstModel, 2.5);
     }
 
     @Test
     public void visitToVerticesRelativeDistanceTest() {
-        MeshFacet mainFacet = getTrivialFacet(1, 1);
-        MeshFacet comparedFacet = getTrivialFacet(1.5, 1);
-
-        HausdorffDistMeshVisitor hausdorffDistance = new HausdorffDistMeshVisitor(mainFacet, true, false);
-        hausdorffDistance.visitMeshFacet(comparedFacet);
-
-        Map<MeshFacet, List<Double>> map = hausdorffDistance.getDistances();
-        List<Double> results = map.get(mainFacet);
-        for (int i = 0; i < mainFacet.getNumberOfVertices(); i++) {
-            assertEquals(0.5, results.get(i));
-        }
+        MeshModel firstModel = getTrivialModel(1, 1);
+        MeshModel secondModel = getTrivialModel(1.5, 1);
+        testDist(new HausdorffDistMeshVisitor(firstModel, true, false), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistMeshVisitor(secondModel, true, false), secondModel, firstModel, -0.5);
     }
 
     @Test
     public void visitToVerticesBehindMeshRelativeDistanceTest() {
-        MeshFacet mainFacet = getTrivialFacet(1, 1);
-        MeshFacet comparedFacet = getTrivialFacet(-1.5, 1);
-
-        HausdorffDistMeshVisitor hausdorffDistance = new HausdorffDistMeshVisitor(mainFacet, true, false);
-        hausdorffDistance.visitMeshFacet(comparedFacet);
-
-        Map<MeshFacet, List<Double>> map = hausdorffDistance.getDistances();
-        List<Double> results = map.get(mainFacet);
-        for (int i = 0; i < mainFacet.getNumberOfVertices(); i++) {
-            assertEquals(-2.5, results.get(i));
-        }
+        MeshModel firstModel = getTrivialModel(1, 1);
+        MeshModel secondModel = getTrivialModel(-1.5, 1);
+        testDist(new HausdorffDistMeshVisitor(firstModel, true, false), firstModel, secondModel, -2.5);
+        testDist(new HausdorffDistMeshVisitor(secondModel, true, false), secondModel, firstModel, 2.5);
+    }
+    
+    @Test
+    public void concurrencyTest() {
+        MeshModel firstModel = getTrivialModel(1, 1);
+        MeshModel secondModel = getTrivialModel(1.5, 1);
+        testDist(new HausdorffDistMeshVisitor(firstModel, false, true), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistMeshVisitor(secondModel, false, true), secondModel, firstModel, 0.5);
     }
 
 }
diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/Point2MeshTriVisitorTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/Point2MeshTriVisitorTest.java
index dbc1a722..911a0fa1 100644
--- a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/Point2MeshTriVisitorTest.java
+++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/Point2MeshTriVisitorTest.java
@@ -1,246 +1,141 @@
 package cz.fidentis.analyst.visitors.mesh;
 
-import cz.fidentis.analyst.mesh.core.CornerTableRow;
-import cz.fidentis.analyst.mesh.core.MeshFacet;
-import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
 import cz.fidentis.analyst.mesh.core.MeshPoint;
 import cz.fidentis.analyst.mesh.core.MeshPointImpl;
 import cz.fidentis.analyst.mesh.core.MeshTriangle;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.util.List;
 import javax.vecmath.Vector3d;
 import org.junit.jupiter.api.Assertions;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
 import org.junit.jupiter.api.Test;
 
 /**
  *
  * @author oslejsek
  */
-public class Point2MeshTriVisitorTest {
-    private MeshFacet getTrivialFacet(double offset, double size) {
-        MeshFacet facet = new MeshFacetImpl();
-        facet.addVertex(new MeshPointImpl(new Vector3d(0, 0, offset), new Vector3d(0, 0, 1), new Vector3d()));
-        facet.addVertex(new MeshPointImpl(new Vector3d(size, 0, offset), new Vector3d(0, 0, 1), new Vector3d()));
-        facet.addVertex(new MeshPointImpl(new Vector3d(0, size, offset), new Vector3d(0, 0, 1), new Vector3d()));
+public class Point2MeshTriVisitorTest extends Point2MeshVisitorTest {
+    
+    protected Object testTriangleMethod(String methodName, Vector3d point)
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException 
+    {    
+        Class<?>[] argClasses = new Class[2];
+        argClasses[0] = Vector3d.class;
+        argClasses[1] = MeshTriangle.class;
+        Method method = Point2MeshTriVisitor.class.getDeclaredMethod(methodName, argClasses);
+        method.setAccessible(true);
+        Object[] args = new Object[2];
 
-        facet.getCornerTable().addRow(new CornerTableRow(0, -1));
-        facet.getCornerTable().addRow(new CornerTableRow(1, -1));
-        facet.getCornerTable().addRow(new CornerTableRow(2, -1));
+        args[0] = point;
+        MeshTriangle triangle = new MeshTriangle(
+                new MeshPointImpl(new Vector3d(1, 0, 0), null, null),
+                new MeshPointImpl(new Vector3d(1, 2, 0), null, null),
+                new MeshPointImpl(new Vector3d(2, 0, 0), null, null)
+        );
+        args[1] = triangle;
 
-        return facet;
+        Point2MeshTriVisitor vis = new Point2MeshTriVisitor(new MeshPointImpl(new Vector3d(), null, null), false, false);
+        return method.invoke(vis, args);
     }
+    
+    protected Object testEdgeMethod(String methodName, Vector3d point1, Vector3d point2, Vector3d point3)
+            throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        Class<?>[] argClasses = new Class[3];
+        argClasses[0] = Vector3d.class;
+        argClasses[1] = Vector3d.class;
+        argClasses[2] = Vector3d.class;
+        Method method = Point2MeshTriVisitor.class.getDeclaredMethod(methodName, argClasses);
+        method.setAccessible(true);
+        Object[] args = new Object[3];
+
+        args[0] = point1;
+        args[1] = point2;
+        args[2] = point3;
 
+        Point2MeshTriVisitor vis = new Point2MeshTriVisitor(new MeshPointImpl(new Vector3d(), null, null), false, false);
+        return method.invoke(vis, args);
+    }
+    
     @Test
+    @Override
     public void absoluteDistTest() {
-        MeshFacet facet = getTrivialFacet(1.5, 1);
         MeshPoint point = new MeshPointImpl(new Vector3d(0,0,0), new Vector3d(0,0,-1), new Vector3d());
-        
-        Point2MeshVisitor vis = new Point2MeshTriVisitor(point, false, false);
-        facet.accept(vis);
-        assertEquals(1.5, vis.getDistance());
-        
-        List<Integer> closestPoints = vis.getIndices();
-        List<MeshFacet> closestMeshes = vis.getClosestFacets();
-        assertNotNull(closestPoints);
-        assertNotNull(closestMeshes);
-        assertEquals(1, closestPoints.size());
-        assertEquals(1, closestMeshes.size());
-        assertEquals(facet, closestMeshes.get(0));
-        assertEquals(0, closestPoints.get(0));
+        Point2MeshVisitor vis = new Point2MeshTriVisitor(point, false, false); // sequentially
+        distTest(1.5, vis);
     }
-    
+
     @Test
+    @Override
     public void relativeDistTest() {
-        MeshFacet facet = getTrivialFacet(1.5, 1);
         MeshPoint point = new MeshPointImpl(new Vector3d(0,0,0), new Vector3d(0,0,-1), new Vector3d());
-        
-        Point2MeshVisitor vis = new Point2MeshTriVisitor(point, true, false);
-        facet.accept(vis);
-        assertEquals(-1.5, vis.getDistance());
-        
-        List<Integer> closestPoints = vis.getIndices();
-        List<MeshFacet> closestMeshes = vis.getClosestFacets();
-        assertNotNull(closestPoints);
-        assertNotNull(closestMeshes);
-        assertEquals(1, closestPoints.size());
-        assertEquals(1, closestMeshes.size());
-        assertEquals(facet, closestMeshes.get(0));
-        assertEquals(0, closestPoints.get(0));
+        Point2MeshVisitor vis = new Point2MeshTriVisitor(point, true, false); // sequentially
+        distTest(-1.5, vis);
     }
     
     @Test
+    @Override
     public void exactMatchTest() {
-        MeshFacet facet = getTrivialFacet(1.5, 1);
         MeshPoint point = new MeshPointImpl(new Vector3d(0, 0, 1.5), new Vector3d(0, 0, 1), new Vector3d());
-        
-        System.out.println(facet.getVertices());
-        
-        Point2MeshVisitor vis = new Point2MeshTriVisitor(point, false, false);
-        facet.accept(vis);
-        assertEquals(0, vis.getDistance());
-        
-        System.out.println(facet.getVertices());
-        
-        List<Integer> closestPoints = vis.getIndices();
-        List<MeshFacet> closestMeshes = vis.getClosestFacets();
-        assertNotNull(closestPoints);
-        assertNotNull(closestMeshes);
-        assertEquals(1, closestPoints.size());
-        assertEquals(1, closestMeshes.size());
-        assertEquals(facet, closestMeshes.get(0));
-        assertEquals(0, closestPoints.get(0));
+        Point2MeshVisitor vis = new Point2MeshTriVisitor(point, true, false); // relative dist, sequentially
+        distTest(0, vis);
+        vis = new Point2MeshVisitor(point, false, false); // abosolute dist, sequentially
+        distTest(0, vis);
+    }
+    
+    @Test
+    @Override
+    public void concurrencyTest() {
+        MeshPoint point = new MeshPointImpl(new Vector3d(0,0,0), new Vector3d(0,0,-1), new Vector3d());
+        Point2MeshVisitor vis = new Point2MeshTriVisitor(point, false, true); // absolute dist, concurrently
+        distTest(1.5, vis);
+        vis = new Point2MeshVisitor(point, true, true); // relative dist, concurrently
+        distTest(-1.5, vis);
     }
     
     @Test
     public void getProjectionToTrianglePlaneTest()
             throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-        Class<?>[] argClasses = new Class[2];
-        argClasses[0] = Vector3d.class;
-        argClasses[1] = MeshTriangle.class;
-        Method method = Point2MeshTriVisitor.class.getDeclaredMethod("getProjectionToTrianglePlane", argClasses);
-        method.setAccessible(true);
-        Object[] args = new Object[2];
-
-        args[0] = new Vector3d(0, 0.5, 1);
-        MeshTriangle triangle = new MeshTriangle(
-                new MeshPointImpl(new Vector3d(1, 0, 0), null, null),
-                new MeshPointImpl(new Vector3d(1, 2, 0), null, null),
-                new MeshPointImpl(new Vector3d(2, 0, 0), null, null)
-        );
-        args[1] = triangle;
-
-        Point2MeshTriVisitor hausdorffDistance = new Point2MeshTriVisitor(new MeshPointImpl(new Vector3d(), null, null), false, false);
-        Vector3d result = (Vector3d) method.invoke(hausdorffDistance, args);
-
+        Vector3d result = (Vector3d) testTriangleMethod("getProjectionToTrianglePlane", new Vector3d(0, 0.5, 1));
         Assertions.assertEquals(0d, result.x);
         Assertions.assertEquals(0.5d, result.y);
         Assertions.assertEquals(0d, result.z);
     }
 
     @Test
-    public void getProjectionToTrianglePlaneBelowPLaneTest()
+    public void getProjectionToTrianglePlaneBelowPlaneTest()
             throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-        Class<?>[] argClasses = new Class[2];
-        argClasses[0] = Vector3d.class;
-        argClasses[1] = MeshTriangle.class;
-        Method method = Point2MeshTriVisitor.class.getDeclaredMethod("getProjectionToTrianglePlane", argClasses);
-        method.setAccessible(true);
-        Object[] args = new Object[2];
-
-        args[0] = new Vector3d(0, 0.5, 1);
-        MeshTriangle triangle = new MeshTriangle(
-                new MeshPointImpl(new Vector3d(1, 0, 0), null, null),
-                new MeshPointImpl(new Vector3d(1, 2, 0), null, null),
-                new MeshPointImpl(new Vector3d(2, 0, 0), null, null)
-        );
-        args[1] = triangle;
-
-        Point2MeshTriVisitor hausdorffDistance = new Point2MeshTriVisitor(new MeshPointImpl(new Vector3d(), null, null), false, false);
-        Vector3d result = (Vector3d) method.invoke(hausdorffDistance, args);
-
+        Vector3d result = (Vector3d) testTriangleMethod("getProjectionToTrianglePlane", new Vector3d(0, -0.5, 1));
         Assertions.assertEquals(0d, result.x);
-        Assertions.assertEquals(0.5d, result.y);
+        Assertions.assertEquals(-0.5d, result.y);
         Assertions.assertEquals(0d, result.z);
     }
 
     @Test
     public void isPointInTriangleInsideTriangleTest()
             throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-        Class<?>[] argClasses = new Class[2];
-        argClasses[0] = Vector3d.class;
-        argClasses[1] = MeshTriangle.class;
-        Method method = Point2MeshTriVisitor.class.getDeclaredMethod("isPointInTriangle", argClasses);
-        method.setAccessible(true);
-        Object[] args = new Object[2];
-
-        args[0] = new Vector3d(1.2, 0.2, 0);
-        MeshTriangle triangle = new MeshTriangle(
-                new MeshPointImpl(new Vector3d(1, 0, 0), null, null),
-                new MeshPointImpl(new Vector3d(1, 2, 0), null, null),
-                new MeshPointImpl(new Vector3d(2, 0, 0), null, null)
-        );
-        args[1] = triangle;
-
-        Point2MeshTriVisitor hausdorffDistance = new Point2MeshTriVisitor(new MeshPointImpl(new Vector3d(), null, null), false, false);
-        boolean result = (boolean) method.invoke(hausdorffDistance, args);
-
+        boolean result = (boolean) testTriangleMethod("isPointInTriangle", new Vector3d(1.2, 0.2, 0));
         Assertions.assertTrue(result);
     }
 
     @Test
     public void isPointInTriangleOutsideTriangleTest()
             throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-        Class<?>[] argClasses = new Class[2];
-        argClasses[0] = Vector3d.class;
-        argClasses[1] = MeshTriangle.class;
-        Method method = Point2MeshTriVisitor.class.getDeclaredMethod("isPointInTriangle", argClasses);
-        method.setAccessible(true);
-        Object[] args = new Object[2];
-
-        args[0] = new Vector3d(0, 0.5, 0);
-        MeshTriangle triangle = new MeshTriangle(
-                new MeshPointImpl(new Vector3d(1, 0, 0), null, null),
-                new MeshPointImpl(new Vector3d(1, 2, 0), null, null),
-                new MeshPointImpl(new Vector3d(2, 0, 0), null, null)
-        );
-        args[1] = triangle;
-
-        Point2MeshTriVisitor hausdorffDistance = new Point2MeshTriVisitor(new MeshPointImpl(new Vector3d(), null, null), false, false);
-        boolean result = (boolean) method.invoke(hausdorffDistance, args);
-
+        boolean result = (boolean) testTriangleMethod("isPointInTriangle", new Vector3d(0, 0.5, 0));
         Assertions.assertFalse(result);
     }
 
     @Test
     public void getProjectionToClosestEdgeTest()
             throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-        Class<?>[] argClasses = new Class[2];
-        argClasses[0] = Vector3d.class;
-        argClasses[1] = MeshTriangle.class;
-        Method method = Point2MeshTriVisitor.class.getDeclaredMethod("getProjectionToClosestEdge", argClasses);
-        method.setAccessible(true);
-        Object[] args = new Object[2];
-
-        args[0] = new Vector3d(0, 0.5, 0);
-        MeshTriangle triangle = new MeshTriangle(
-                new MeshPointImpl(new Vector3d(1, 0, 0), null, null),
-                new MeshPointImpl(new Vector3d(1, 2, 0), null, null),
-                new MeshPointImpl(new Vector3d(2, 0, 0), null, null)
-        );
-        args[1] = triangle;
-
-        Point2MeshTriVisitor hausdorffDistance = new Point2MeshTriVisitor(new MeshPointImpl(new Vector3d(), null, null), false, false);
-        Vector3d result = (Vector3d) method.invoke(hausdorffDistance, args);
-
+        Vector3d result = (Vector3d) testTriangleMethod("getProjectionToClosestEdge", new Vector3d(0, 0.5, 0));
         Assertions.assertEquals(1d, result.x);
         Assertions.assertEquals(0.5d, result.y);
         Assertions.assertEquals(0d, result.z);
     }
 
     @Test
-    public void getProjectionToClosestEdgeNormalProjectionOutsideEdgeTestTest()
+    public void getProjectionToClosestEdgeNormalProjectionOutsideEdgeTest()
             throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-        Class<?>[] argClasses = new Class[2];
-        argClasses[0] = Vector3d.class;
-        argClasses[1] = MeshTriangle.class;
-        Method method = Point2MeshTriVisitor.class.getDeclaredMethod("getProjectionToClosestEdge", argClasses);
-        method.setAccessible(true);
-        Object[] args = new Object[2];
-
-        args[0] = new Vector3d(0, -0.5, 0);
-        MeshTriangle triangle = new MeshTriangle(
-                new MeshPointImpl(new Vector3d(1, 0, 0), null, null),
-                new MeshPointImpl(new Vector3d(1, 2, 0), null, null),
-                new MeshPointImpl(new Vector3d(2, 0, 0), null, null)
-        );
-        args[1] = triangle;
-
-        Point2MeshTriVisitor hausdorffDistance = new Point2MeshTriVisitor(new MeshPointImpl(new Vector3d(), null, null), false, false);
-        Vector3d result = (Vector3d) method.invoke(hausdorffDistance, args);
-
+        Vector3d result = (Vector3d) testTriangleMethod("getProjectionToClosestEdge", new Vector3d(0, -0.5, 0));
         Assertions.assertEquals(1d, result.x);
         Assertions.assertEquals(0d, result.y);
         Assertions.assertEquals(0d, result.z);
@@ -249,21 +144,8 @@ public class Point2MeshTriVisitorTest {
     @Test
     public void getProjectionToEdgeTest()
             throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-        Class<?>[] argClasses = new Class[3];
-        argClasses[0] = Vector3d.class;
-        argClasses[1] = Vector3d.class;
-        argClasses[2] = Vector3d.class;
-        Method method = Point2MeshTriVisitor.class.getDeclaredMethod("getProjectionToEdge", argClasses);
-        method.setAccessible(true);
-        Object[] args = new Object[3];
-
-        args[0] = new Vector3d(0, 0.5, 0);
-        args[1] = new Vector3d(1, 2, 0);
-        args[2] = new Vector3d(1, 0, 0);
-
-        Point2MeshTriVisitor hausdorffDistance = new Point2MeshTriVisitor(new MeshPointImpl(new Vector3d(), null, null), false, false);
-        Vector3d result = (Vector3d) method.invoke(hausdorffDistance, args);
-
+        Vector3d result = (Vector3d) testEdgeMethod("getProjectionToEdge", 
+                new Vector3d(0, 0.5, 0), new Vector3d(1, 2, 0), new Vector3d(1, 0, 0));
         Assertions.assertEquals(1d, result.x);
         Assertions.assertEquals(0.5d, result.y);
         Assertions.assertEquals(0d, result.z);
@@ -272,21 +154,8 @@ public class Point2MeshTriVisitorTest {
     @Test
     public void getProjectionToEdgeNormalProjectionOutsideEdgeTest()
             throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-        Class<?>[] argClasses = new Class[3];
-        argClasses[0] = Vector3d.class;
-        argClasses[1] = Vector3d.class;
-        argClasses[2] = Vector3d.class;
-        Method method = Point2MeshTriVisitor.class.getDeclaredMethod("getProjectionToEdge", argClasses);
-        method.setAccessible(true);
-        Object[] args = new Object[3];
-
-        args[0] = new Vector3d(0, -0.5, 0);
-        args[1] = new Vector3d(1, 2, 0);
-        args[2] = new Vector3d(1, 0, 0);
-
-        Point2MeshTriVisitor hausdorffDistance = new Point2MeshTriVisitor(new MeshPointImpl(new Vector3d(), null, null), false, false);
-        Vector3d result = (Vector3d) method.invoke(hausdorffDistance, args);
-
+        Vector3d result = (Vector3d) testEdgeMethod("getProjectionToEdge", 
+                new Vector3d(0, -0.5, 0), new Vector3d(1, 2, 0), new Vector3d(1, 0, 0));
         Assertions.assertEquals(1d, result.x);
         Assertions.assertEquals(0d, result.y);
         Assertions.assertEquals(0d, result.z);
@@ -295,21 +164,8 @@ public class Point2MeshTriVisitorTest {
     @Test
     public void getProjectionToEdgeNormalProjectionOutsideEdge2Test()
             throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-        Class<?>[] argClasses = new Class[3];
-        argClasses[0] = Vector3d.class;
-        argClasses[1] = Vector3d.class;
-        argClasses[2] = Vector3d.class;
-        Method method = Point2MeshTriVisitor.class.getDeclaredMethod("getProjectionToEdge", argClasses);
-        method.setAccessible(true);
-        Object[] args = new Object[3];
-
-        args[0] = new Vector3d(0, 2.5, 0);
-        args[1] = new Vector3d(1, 2, 0);
-        args[2] = new Vector3d(1, 0, 0);
-
-        Point2MeshTriVisitor hausdorffDistance = new Point2MeshTriVisitor(new MeshPointImpl(new Vector3d(), null, null), false, false);
-        Vector3d result = (Vector3d) method.invoke(hausdorffDistance, args);
-
+        Vector3d result = (Vector3d) testEdgeMethod("getProjectionToEdge", 
+                new Vector3d(0, 2.5, 0), new Vector3d(1, 2, 0), new Vector3d(1, 0, 0));
         Assertions.assertEquals(1d, result.x);
         Assertions.assertEquals(2d, result.y);
         Assertions.assertEquals(0d, result.z);
diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/Point2MeshVisitorTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/Point2MeshVisitorTest.java
index d36d119d..74a14724 100644
--- a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/Point2MeshVisitorTest.java
+++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/Point2MeshVisitorTest.java
@@ -1,8 +1,10 @@
 package cz.fidentis.analyst.visitors.mesh;
 
+import cz.fidentis.analyst.mesh.MeshVisitor;
 import cz.fidentis.analyst.mesh.core.CornerTableRow;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
 import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
+import cz.fidentis.analyst.mesh.core.MeshModel;
 import cz.fidentis.analyst.mesh.core.MeshPoint;
 import cz.fidentis.analyst.mesh.core.MeshPointImpl;
 import java.util.List;
@@ -12,7 +14,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import org.junit.jupiter.api.Test;
 
 public class Point2MeshVisitorTest {
-    private MeshFacet getTrivialFacet(double offset, double size) {
+    
+    protected MeshFacet getTrivialFacet(double offset, double size) {
         MeshFacet facet = new MeshFacetImpl();
         facet.addVertex(new MeshPointImpl(new Vector3d(0, 0, offset), new Vector3d(0, 0, 1), new Vector3d()));
         facet.addVertex(new MeshPointImpl(new Vector3d(size, 0, offset), new Vector3d(0, 0, 1), new Vector3d()));
@@ -24,15 +27,15 @@ public class Point2MeshVisitorTest {
 
         return facet;
     }
-
-    @Test
-    public void absoluteDistTest() {
+    
+    protected void distTest(double expectedDist, Point2MeshVisitor vis) 
+    {
+        MeshModel model = new MeshModel();
         MeshFacet facet = getTrivialFacet(1.5, 1);
-        MeshPoint point = new MeshPointImpl(new Vector3d(0,0,0), new Vector3d(0,0,-1), new Vector3d());
+        model.addFacet(facet);
         
-        Point2MeshVisitor vis = new Point2MeshVisitor(point, false, false);
-        facet.accept(vis);
-        assertEquals(1.5, vis.getDistance());
+        model.compute(vis);
+        assertEquals(expectedDist, vis.getDistance());
         
         List<Integer> closestPoints = vis.getIndices();
         List<MeshFacet> closestMeshes = vis.getClosestFacets();
@@ -44,43 +47,35 @@ public class Point2MeshVisitorTest {
         assertEquals(0, closestPoints.get(0));
     }
 
+    @Test
+    public void absoluteDistTest() {
+        MeshPoint point = new MeshPointImpl(new Vector3d(0,0,0), new Vector3d(0,0,-1), new Vector3d());
+        Point2MeshVisitor vis = new Point2MeshVisitor(point, false, false); // sequentially
+        distTest(1.5, vis);
+    }
+
     @Test
     public void relativeDistTest() {
-        MeshFacet facet = getTrivialFacet(1.5, 1);
         MeshPoint point = new MeshPointImpl(new Vector3d(0,0,0), new Vector3d(0,0,-1), new Vector3d());
-        
-        Point2MeshVisitor vis = new Point2MeshVisitor(point, true, false);
-        facet.accept(vis);
-        assertEquals(-1.5, vis.getDistance());
-        
-        List<Integer> closestPoints = vis.getIndices();
-        List<MeshFacet> closestMeshes = vis.getClosestFacets();
-        assertNotNull(closestPoints);
-        assertNotNull(closestMeshes);
-        assertEquals(1, closestPoints.size());
-        assertEquals(1, closestMeshes.size());
-        assertEquals(facet, closestMeshes.get(0));
-        assertEquals(0, closestPoints.get(0));
+        Point2MeshVisitor vis = new Point2MeshVisitor(point, true, false); // sequentially
+        distTest(-1.5, vis);
     }
     
     @Test
     public void exactMatchTest() {
-        MeshFacet facet = getTrivialFacet(1.5, 1);
         MeshPoint point = new MeshPointImpl(new Vector3d(0, 0, 1.5), new Vector3d(0, 0, 1), new Vector3d());
-        
-        System.out.println(facet.getVertices());
-        
-        Point2MeshVisitor vis = new Point2MeshVisitor(point, false, false);
-        facet.accept(vis);
-        assertEquals(0, vis.getDistance());
-        
-        List<Integer> closestPoints = vis.getIndices();
-        List<MeshFacet> closestMeshes = vis.getClosestFacets();
-        assertNotNull(closestPoints);
-        assertNotNull(closestMeshes);
-        assertEquals(1, closestPoints.size());
-        assertEquals(1, closestMeshes.size());
-        assertEquals(facet, closestMeshes.get(0));
-        assertEquals(0, closestPoints.get(0));
+        Point2MeshVisitor vis = new Point2MeshVisitor(point, true, false); // relative dist, sequentially
+        distTest(0, vis);
+        vis = new Point2MeshVisitor(point, false, false); // abosolute dist, sequentially
+        distTest(0, vis);
+    }
+    
+    @Test
+    public void concurrencyTest() {
+        MeshPoint point = new MeshPointImpl(new Vector3d(0,0,0), new Vector3d(0,0,-1), new Vector3d());
+        Point2MeshVisitor vis = new Point2MeshVisitor(point, false, true); // absolute dist, concurrently
+        distTest(1.5, vis);
+        vis = new Point2MeshVisitor(point, true, true); // relative dist, concurrently
+        distTest(-1.5, vis);
     }
 }
-- 
GitLab