From 92a5472c8397a3f555eb6ddf7da639f6a2ae985e Mon Sep 17 00:00:00 2001
From: Daniel Schramm <xschramm@fi.muni.cz>
Date: Wed, 8 Sep 2021 23:37:00 +0200
Subject: [PATCH] Variable radii of priority spheres around individual feature
 points

---
 .../face/HausdorffDistancePrioritized.java    | 68 +++++++++++--------
 .../HausdorffDistancePrioritizedTest.java     | 54 +++++++++------
 2 files changed, 74 insertions(+), 48 deletions(-)

diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java
index 83ed7f1c..52f2949e 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java
@@ -54,7 +54,7 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
     
     private final HausdorffDistance distanceVisitor;
     
-    private final Set<FeaturePointType> featurePointTypes;
+    private final Map<FeaturePointType, Double> featurePointTypes;
     
     private final Map<HumanFace, Map<FeaturePointType, Map<MeshFacet, List<Double>>>> priorities = new HashMap<>();
     private final Map<HumanFace, Map<FeaturePointType, Map<MeshFacet, Double>>> featurePointWeights = new HashMap<>();
@@ -65,7 +65,8 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
      * 
      * @param mainFacets Facets to which distance from the visited human face's facets is to be computed.
      * Must not be {@code null}.
-     * @param featurePoints Types of feature points according to which the Hausdorff distances will be prioritized.
+     * @param featurePoints Types of feature points according to which the Hausdorff distances will be prioritized
+     * together with the radii of priority spheres around the feature points of the corresponding type.
      * Must not be {@code null}.
      * @param strategy Strategy of the computation of distance
      * @param relativeDistance If {@code true}, then the visitor calculates the relative distances with respect 
@@ -74,11 +75,16 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
      * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public HausdorffDistancePrioritized(Set<MeshFacet> mainFacets, Set<FeaturePointType> featurePoints, Strategy strategy, boolean relativeDistance, boolean parallel) {
+    public HausdorffDistancePrioritized(Set<MeshFacet> mainFacets, Map<FeaturePointType, Double> featurePoints, Strategy strategy, boolean relativeDistance, boolean parallel) {
         distanceVisitor = new HausdorffDistance(mainFacets, strategy, relativeDistance, parallel);
         if (featurePoints == null) {
             throw new IllegalArgumentException("featurePoints");
         }
+        for (final Double radius: featurePoints.values()) {
+            if (radius == null || radius < 0) {
+                throw new IllegalArgumentException("featurePoints");
+            }
+        }
         featurePointTypes = featurePoints;
     }
     
@@ -86,7 +92,8 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
      * Constructor.
      * 
      * @param mainFacet Primary facet to which distance from the visited human face's facets is to be computed. Must not be {@code null}.
-     * @param featurePoints Types of feature points according to which the Hausdorff distances will be prioritized.
+     * @param featurePoints Types of feature points according to which the Hausdorff distances will be prioritized
+     * together with the radii of priority spheres around the feature points of the corresponding type.
      * Must not be {@code null}.
      * @param strategy Strategy of the computation of distance
      * @param relativeDistance If {@code true}, then the visitor calculates the relative distances with respect 
@@ -95,7 +102,7 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
      * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public HausdorffDistancePrioritized(MeshFacet mainFacet, Set<FeaturePointType> featurePoints, Strategy strategy, boolean relativeDistance, boolean parallel) {
+    public HausdorffDistancePrioritized(MeshFacet mainFacet, Map<FeaturePointType, Double> featurePoints, Strategy strategy, boolean relativeDistance, boolean parallel) {
         this(new HashSet<>(Collections.singleton(mainFacet)), featurePoints, strategy, relativeDistance, parallel);
     }
     
@@ -104,7 +111,8 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
      * 
      * @param mainModel The mesh model with primary facets to which distance from the visited human face's facets is to be computed.
      * Must not be {@code null} or empty.
-     * @param featurePoints Types of feature points according to which the Hausdorff distances will be prioritized.
+     * @param featurePoints Types of feature points according to which the Hausdorff distances will be prioritized
+     * together with the radii of priority spheres around the feature points of the corresponding type.
      * Must not be {@code null}.
      * @param strategy Strategy of the computation of distance
      * @param relativeDistance If {@code true}, then the visitor calculates the relative distances with respect 
@@ -113,7 +121,7 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
      * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public HausdorffDistancePrioritized(MeshModel mainModel, Set<FeaturePointType> featurePoints, Strategy strategy, boolean relativeDistance, boolean parallel) {
+    public HausdorffDistancePrioritized(MeshModel mainModel, Map<FeaturePointType, Double> featurePoints, Strategy strategy, boolean relativeDistance, boolean parallel) {
         this(new HashSet<>(mainModel.getFacets()), featurePoints, strategy, relativeDistance, parallel);
     }
     
@@ -121,7 +129,8 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
      * Constructor.
      * 
      * @param face Human face to which distance from other human faces is to be computed. Must not be {@code null}.
-     * @param featurePoints Types of feature points according to which the Hausdorff distances will be prioritized.
+     * @param featurePoints Types of feature points according to which the Hausdorff distances will be prioritized
+     * together with the radii of priority spheres around the feature points of the corresponding type.
      * Must not be {@code null}.
      * @param strategy Strategy of the computation of distance
      * @param relativeDistance If {@code true}, then the visitor calculates the relative distances with respect 
@@ -130,7 +139,7 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
      * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public HausdorffDistancePrioritized(HumanFace face, Set<FeaturePointType> featurePoints, Strategy strategy, boolean relativeDistance, boolean parallel) {
+    public HausdorffDistancePrioritized(HumanFace face, Map<FeaturePointType, Double> featurePoints, Strategy strategy, boolean relativeDistance, boolean parallel) {
         this(face.getMeshModel(), featurePoints, strategy, relativeDistance, parallel);
     }
     
@@ -139,7 +148,8 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
      * 
      * @param mainKdTree The KD tree to which distance from the visited human face's facets is to be computed.
      * Must not be {@code null}.
-     * @param featurePoints Types of feature points according to which the Hausdorff distances will be prioritized.
+     * @param featurePoints Types of feature points according to which the Hausdorff distances will be prioritized
+     * together with the radii of priority spheres around the feature points of the corresponding type.
      * Must not be {@code null}.
      * @param strategy Strategy of the computation of distance
      * @param relativeDistance If {@code true}, then the visitor calculates the relative distances with respect 
@@ -148,23 +158,30 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
      * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public HausdorffDistancePrioritized(KdTree mainKdTree, Set<FeaturePointType> featurePoints, Strategy strategy, boolean relativeDistance, boolean parallel) {
+    public HausdorffDistancePrioritized(KdTree mainKdTree, Map<FeaturePointType, Double> featurePoints, Strategy strategy, boolean relativeDistance, boolean parallel) {
         distanceVisitor = new HausdorffDistance(mainKdTree, strategy, relativeDistance, parallel);
         if (featurePoints == null) {
             throw new IllegalArgumentException("featurePoints");
         }
+        for (final Double radius: featurePoints.values()) {
+            if (radius == null || radius < 0) {
+                throw new IllegalArgumentException("featurePoints");
+            }
+        }
         featurePointTypes = featurePoints;
     }
 
     /**
      * Returns types of feature points according to which the computation of Hausdorff
-     * distance is prioritized.
+     * distance is prioritized together with the radii of priority spheres around
+     * the feature points of the corresponding type.
      * 
      * @return Types of feature points according to which the computation of Hausdorff
-     * distance is prioritized
+     * distance is prioritized together with the radii of priority spheres around
+     * the feature points of the corresponding type
      */
-    public Set<FeaturePointType> getFeaturePointTypes() {
-        return Collections.unmodifiableSet(featurePointTypes);
+    public Map<FeaturePointType, Double> getFeaturePointTypes() {
+        return Collections.unmodifiableMap(featurePointTypes);
     }
     
     /**
@@ -309,15 +326,18 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
         final Map<MeshFacet, List<Double>> hausdorffDistances = distanceVisitor.getDistances();
                 
         // Compute priorities of humanFace's vertices for each of the given feature point types
-        for (final FeaturePointType fpType: featurePointTypes) {
-            final FeaturePoint featurePoint = humanFace.getFeaturePoints().get(fpType.getType()); // Get feature point of desired type
+        for (final Map.Entry<FeaturePointType, Double> fpType: featurePointTypes.entrySet()) {
+            final FeaturePointType featurePointType = fpType.getKey();
+            final double featurePointRadius = fpType.getValue();
             
-            final PrioritySphere priorityVisitor = new PrioritySphere(featurePoint.getPosition(), computeSphereRadius(humanFace));
+            final FeaturePoint featurePoint = humanFace.getFeaturePoints().get(featurePointType.getType()); // Get feature point of desired type
+            
+            final PrioritySphere priorityVisitor = new PrioritySphere(featurePoint.getPosition(), featurePointRadius);
             humanFace.getMeshModel().compute(priorityVisitor, inParallel());
 
             synchronized (this) {
                 priorities.computeIfAbsent(humanFace, face -> new HashMap<>())
-                        .computeIfAbsent(fpType, featurePointType -> new HashMap<>())
+                        .computeIfAbsent(featurePointType, fPointType -> new HashMap<>())
                         .putAll(priorityVisitor.getPriorities());
             }
             
@@ -331,7 +351,7 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
                      */
                     final List<Double> facetDistances = hausdorffDistances.get(facet);
                     featurePointWeights.computeIfAbsent(humanFace, face -> new HashMap<>())
-                            .computeIfAbsent(fpType, featurePointType -> new HashMap<>())
+                            .computeIfAbsent(featurePointType, fPointType -> new HashMap<>())
                             .put(facet, IntStream.range(0, facetDistances.size())
                                     .filter(i -> facetPriorities.get(i) > 0) // Filter out vertices that are outside of the priority sphere
                                     .mapToDouble(i -> facetDistances.get(i) * facetPriorities.get(i))
@@ -357,12 +377,4 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
             }
         }
     }
-    
-    private double computeSphereRadius(HumanFace humanFace) {
-        // TODO TEMPORARY BEGIN
-        // Sphere radius needs to be computed dynamically.
-        // The best way to compute the right radius should be thought out in more depth.
-        return 1;
-        // TODO TEMPORARY END
-    }
 }
diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritizedTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritizedTest.java
index a7315155..90bfef80 100644
--- a/Comparison/src/test/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritizedTest.java
+++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritizedTest.java
@@ -14,11 +14,11 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 import javax.vecmath.Point3d;
 import javax.vecmath.Vector3d;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -106,13 +106,20 @@ public class HausdorffDistancePrioritizedTest {
         return face;
     }
     
+    private static Map<FeaturePointType, Double> zipListsToMap(List<FeaturePointType> fpTypes, List<Double> fpRadii) {
+        assertEquals(fpTypes.size(), fpRadii.size());
+        return IntStream.range(0, fpTypes.size())
+                .boxed()
+                .collect(Collectors.toMap(fpTypes::get, fpRadii::get));
+    }
+    
     protected void performTest(HumanFace face,
             Map<FeaturePointType, Map<MeshFacet, List<Double>>> expectedPriorities,
             Map<FeaturePointType, Map<MeshFacet, Double>> expectedFeaturePointWeights,
             Map<MeshFacet, List<Double>> expectedMergedPriorities,
             HausdorffDistancePrioritized visitor) {
         
-        final Set<FeaturePointType> featurePointTypes = visitor.getFeaturePointTypes();
+        final Set<FeaturePointType> featurePointTypes = visitor.getFeaturePointTypes().keySet();
         final List<FeaturePoint> faceFeaturePoints = face.getFeaturePoints();
         featurePointTypes.forEach(fpType -> assertTrue(faceFeaturePoints.stream()
                 .anyMatch(fPoint -> fPoint.getFeaturePointType().equals(fpType))));
@@ -203,9 +210,11 @@ public class HausdorffDistancePrioritizedTest {
     @Test
     public void singleFeaturePointTest() throws IOException {
         final List<Point3d> featurePoints = List.of(featurePoint1);
-        final List<FeaturePointType> featurePointTypes = List.of(fpType1);
+        final List<FeaturePointType> fpTypes = List.of(fpType1);
+        final Map<FeaturePointType, Double> featurePointTypes = zipListsToMap(fpTypes, List.of(1d));
+        
+        final HumanFace face = getHumanFace(facets, featurePoints, fpTypes);
         
-        final HumanFace face = getHumanFace(facets, featurePoints, featurePointTypes);
         final Map<FeaturePointType, Map<MeshFacet, List<Double>>> expectedPriorities = Map.of(fpType1, expectedPrioritiesMapFP1);
         final Map<FeaturePointType, Map<MeshFacet, Double>> expectedFeaturePointsWeights = Map.of(fpType1, expectedWeightedDistancesMapFP1);
         
@@ -215,7 +224,7 @@ public class HausdorffDistancePrioritizedTest {
         }
         
         final HausdorffDistancePrioritized visitor = new HausdorffDistancePrioritized(getHumanFace(facets2, List.of(), List.of()),
-                new HashSet<>(featurePointTypes),
+                featurePointTypes,
                 POINT_TO_POINT,
                 true,
                 false);
@@ -227,9 +236,10 @@ public class HausdorffDistancePrioritizedTest {
     public void twoFeaturePointsNoFacetIntersecTest() throws IOException {
         final List<Point3d> featurePoints = List.of(featurePoint1, new Point3d(3, 0, 0));
         final FeaturePointType fpType2 = new FeaturePointType(1, null, null, null);
-        final List<FeaturePointType> featurePointTypes = List.of(fpType1, fpType2);
+        final List<FeaturePointType> fpTypes = List.of(fpType1, fpType2);
+        final Map<FeaturePointType, Double> featurePointTypes = zipListsToMap(fpTypes, List.of(1d, 1d));
         
-        final HumanFace face = getHumanFace(facets, featurePoints, featurePointTypes);
+        final HumanFace face = getHumanFace(facets, featurePoints, fpTypes);
         
         final Map<MeshFacet, List<Double>> expectedPrioritiesMapFP2 = new HashMap<>();
         for (final MeshFacet facet: facets) {
@@ -251,7 +261,7 @@ public class HausdorffDistancePrioritizedTest {
         }
         
         final HausdorffDistancePrioritized visitor = new HausdorffDistancePrioritized(getHumanFace(facets2, List.of(), List.of()),
-                new HashSet<>(featurePointTypes),
+                featurePointTypes,
                 POINT_TO_POINT,
                 false,
                 false);
@@ -263,9 +273,10 @@ public class HausdorffDistancePrioritizedTest {
     public void twoFeaturePointsSingleFacetIntersecTest() throws IOException {
         final List<Point3d> featurePoints = List.of(featurePoint1, new Point3d(1, 0, 0));
         final FeaturePointType fpType2 = new FeaturePointType(1, null, null, null);
-        final List<FeaturePointType> featurePointTypes = List.of(fpType1, fpType2);
+        final List<FeaturePointType> fpTypes = List.of(fpType1, fpType2);
+        final Map<FeaturePointType, Double> featurePointTypes = zipListsToMap(fpTypes, List.of(1d, 1d));
         
-        final HumanFace face = getHumanFace(facets, featurePoints, featurePointTypes);
+        final HumanFace face = getHumanFace(facets, featurePoints, fpTypes);
         
         final Map<MeshFacet, List<Double>> expectedPrioritiesMapFP2 = new HashMap<>();
         for (final MeshFacet facet: facets) {
@@ -290,7 +301,7 @@ public class HausdorffDistancePrioritizedTest {
         expectedMergedPrioritiesMap.put(facet1, List.of(1d, 0.9, 0.8, 0.7, 0.6, 0.5, 0.6, 0.7, 0.8, 0.9, 1d, 0.9));
         
         final HausdorffDistancePrioritized visitor = new HausdorffDistancePrioritized(getHumanFace(facets2, List.of(), List.of()),
-                new HashSet<>(featurePointTypes),
+                featurePointTypes,
                 POINT_TO_POINT,
                 true,
                 false);
@@ -304,9 +315,10 @@ public class HausdorffDistancePrioritizedTest {
         final FeaturePointType fpType2 = new FeaturePointType(1, null, null, null);
         final FeaturePointType fpType3 = new FeaturePointType(2, null, null, null);
         final FeaturePointType fpType4 = new FeaturePointType(3, null, null, null);
-        final List<FeaturePointType> featurePointTypes = List.of(fpType1, fpType2, fpType3, fpType4);
+        final List<FeaturePointType> fpTypes = List.of(fpType1, fpType2, fpType3, fpType4);
+        final Map<FeaturePointType, Double> featurePointTypes = zipListsToMap(fpTypes, List.of(1d, 1d, 1d, 1d));
         
-        final HumanFace face = getHumanFace(facets, featurePoints, featurePointTypes);
+        final HumanFace face = getHumanFace(facets, featurePoints, fpTypes);
         
         final Map<MeshFacet, List<Double>> expectedPrioritiesMapFP2 = new HashMap<>();
         final Map<MeshFacet, List<Double>> expectedPrioritiesMapFP3 = new HashMap<>();
@@ -353,7 +365,7 @@ public class HausdorffDistancePrioritizedTest {
         expectedMergedPrioritiesMap.put(facet5, mergedPriorities);
         
         final HausdorffDistancePrioritized visitor = new HausdorffDistancePrioritized(getHumanFace(facets2, List.of(), List.of()),
-                new HashSet<>(featurePointTypes),
+                featurePointTypes,
                 POINT_TO_POINT,
                 true,
                 false);
@@ -366,9 +378,10 @@ public class HausdorffDistancePrioritizedTest {
         final List<Point3d> featurePoints = List.of(featurePoint1, new Point3d(0.999, 0.999, 0), new Point3d(0.999, -0.999, 0));
         final FeaturePointType fpType2 = new FeaturePointType(1, null, null, null);
         final FeaturePointType fpType3 = new FeaturePointType(2, null, null, null);
-        final List<FeaturePointType> featurePointTypes = List.of(fpType1, fpType2, fpType3);
+        final List<FeaturePointType> fpTypes = List.of(fpType1, fpType2, fpType3);
+        final Map<FeaturePointType, Double> featurePointTypes = zipListsToMap(fpTypes, List.of(1d, 1d, 1d));
         
-        final HumanFace face = getHumanFace(facets, featurePoints, featurePointTypes);
+        final HumanFace face = getHumanFace(facets, featurePoints, fpTypes);
         
         final Map<MeshFacet, List<Double>> expectedPrioritiesMapFP2 = new HashMap<>();
         final Map<MeshFacet, List<Double>> expectedPrioritiesMapFP3 = new HashMap<>();
@@ -415,7 +428,7 @@ public class HausdorffDistancePrioritizedTest {
         expectedMergedPrioritiesMap.put(facet4, mergedPriorities);
         
         final HausdorffDistancePrioritized visitor = new HausdorffDistancePrioritized(getHumanFace(facets2, List.of(), List.of()),
-                new HashSet<>(featurePointTypes),
+                featurePointTypes,
                 POINT_TO_POINT,
                 true,
                 false);
@@ -429,9 +442,10 @@ public class HausdorffDistancePrioritizedTest {
         final FeaturePointType fpType2 = new FeaturePointType(1, null, null, null);
         final FeaturePointType fpType3 = new FeaturePointType(2, null, null, null);
         final FeaturePointType fpType4 = new FeaturePointType(3, null, null, null);
-        final List<FeaturePointType> featurePointTypes = List.of(fpType1, fpType2, fpType3, fpType4);
+        final List<FeaturePointType> fpTypes = List.of(fpType1, fpType2, fpType3, fpType4);
+        final Map<FeaturePointType, Double> featurePointTypes = zipListsToMap(fpTypes, List.of(1d, 1d, 1d, 1d));
         
-        final HumanFace face = getHumanFace(facets, featurePoints, featurePointTypes);
+        final HumanFace face = getHumanFace(facets, featurePoints, fpTypes);
         
         final Map<MeshFacet, List<Double>> expectedPrioritiesMapFP2 = new HashMap<>();
         final Map<MeshFacet, List<Double>> expectedPrioritiesMapFP3 = new HashMap<>();
@@ -478,7 +492,7 @@ public class HausdorffDistancePrioritizedTest {
         expectedMergedPrioritiesMap.put(facet5, mergedPriorities);
         
         final HausdorffDistancePrioritized visitor = new HausdorffDistancePrioritized(getHumanFace(facets2, List.of(), List.of()),
-                new HashSet<>(featurePointTypes),
+                featurePointTypes,
                 POINT_TO_POINT,
                 true,
                 true);
-- 
GitLab