diff --git a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java
index 54f54923256c3b9c93ccbd356f56d38e70482b02..65b805f774e65b32f1b2c6c0892c8e2769f6798d 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java
@@ -61,7 +61,7 @@ public class HumanFace implements Serializable {
 
     private List<FeaturePoint> featurePoints;
     
-    private final transient EventBus eventBus = new EventBus();
+    private final transient EventBus eventBus;
     
     private final String id;
     
@@ -85,6 +85,8 @@ public class HumanFace implements Serializable {
         BoundingBox visitor = new BoundingBox();
         meshModel.compute(visitor);
         bbox = visitor.getBoundingBox();
+        
+        eventBus = new EventBus();
     }
     
     /**
@@ -123,7 +125,9 @@ public class HumanFace implements Serializable {
      * @param listener Listener concerned in the human face changes.
      */
     public void registerListener(HumanFaceListener listener) {
-        eventBus.register(listener);
+        if (eventBus != null) { // eventBus is null when the class is deserialized!
+            eventBus.register(listener);
+        }
     }
     
     /**
@@ -132,7 +136,9 @@ public class HumanFace implements Serializable {
      * @param listener Registered listener
      */
     public void unregisterListener(HumanFaceListener listener) {
-        eventBus.unregister(listener);  
+        if (eventBus != null) { // eventBus is null when the class is deserialized!
+            eventBus.unregister(listener);  
+        }
     }
     
     /**
@@ -141,7 +147,7 @@ public class HumanFace implements Serializable {
      * @param evt Event to be triggered.
      */
     public void announceEvent(HumanFaceEvent evt) {
-        if (evt != null) {
+        if (evt != null && eventBus != null) { // eventBus is null when the class is deserialized!
             eventBus.post(evt);
         }
     }
@@ -274,7 +280,9 @@ public class HumanFace implements Serializable {
     public KdTree computeKdTree(boolean recompute) {
         if (kdTree == null || recompute) {
             kdTree = new KdTree(new ArrayList<>(meshModel.getFacets()));
-            eventBus.post(new KdTreeCreated(this, this.getShortName(), this));
+            if (eventBus != null) { // eventBus is null when the class is deserialized!
+                eventBus.post(new KdTreeCreated(this, this.getShortName(), this));
+            }
         }
         return kdTree;
     }
@@ -286,7 +294,9 @@ public class HumanFace implements Serializable {
     public KdTree removeKdTree() {
         KdTree ret = this.kdTree;
         this.kdTree = null;
-        eventBus.post(new KdTreeDestroyed(this, this.getShortName(), this));
+        if (eventBus != null) { // eventBus is null when the class is deserialized!
+            eventBus.post(new KdTreeDestroyed(this, this.getShortName(), this));
+        }
         return ret;
     }
     
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceFactory.java b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceFactory.java
index 2198510572f6393420ed3d8d8e7e0e9db56eb480..df9dd278ee00b8641ea741bd63e9408e80a131e5 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceFactory.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceFactory.java
@@ -6,10 +6,6 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -25,14 +21,29 @@ import java.util.logging.Logger;
  * @author Radek Oslejsek
  */
 public class HumanFaceFactory {
+
+    /**
+     * Memory clean  up, i.e. dumping the file into disk and de-referencing it, is
+     * very fast. Faster real objects removal by JVM. 
+     * Therefore, the {@link #checkMemAndDump()} method removes almost
+     * {@code MAX_DUMP_FACES} at once.
+     */
+    private static final int MAX_DUMP_FACES = 3;
     
     /**
      * Dumping strategies.
      * @author Radek Oslejsek
      */
     public enum Strategy {
-        LRU, // least recently used faces are dumped first
-        MRU  // most recently used faces are dumped first
+        /**
+         * least recently used faces are dumped first
+         */
+        LRU,
+        
+        /**
+         * most recently used faces are dumped first
+         */
+        MRU
     }
     
     /**
@@ -77,6 +88,8 @@ public class HumanFaceFactory {
      */
     private Strategy strategy = Strategy.LRU;
     
+    private boolean reuseDumpFile = false;
+    
     
     /**
      * Private constructor. Use {@code instance()} method instead to get the instance.
@@ -116,6 +129,18 @@ public class HumanFaceFactory {
         return this.strategy;
     }
     
+    /**
+     * I set to {@code true}, then the dump file of a face is created only once
+     * and then reused for every recovery (it never overwritten).
+     * It accelerates the dumping face, but can be used only if the state of
+     * faces never changes between the first dump a consequent recoveries. 
+     * 
+     * @param use If {@code true}, then this optimization is turned on.
+     */
+    public void setReuseDumpFile(boolean use) {
+        this.reuseDumpFile = use;
+    }
+    
     /**
      * Loads new face. If the face is already loaded, then the ID of existing instance
      * is returned. To access the human face instance, use {@link getFace}.
@@ -134,24 +159,19 @@ public class HumanFaceFactory {
             return null;
         }
         
-        // Existing face
+        // Existing face, possibly recovered from a dump file
         HumanFace face = getFace(faceId);
         if (face != null) {
             return faceId;
         }
         
-        // New face -- free the memory and load human face simultaneously:
         try {
-            ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
-            final Future<HumanFace> result = executor.submit(() -> new HumanFace(file)); // read from file
-            executor.submit(() -> checkMemAndDump()); // free the memory, if necessary
-            executor.shutdown();
-            while (!executor.isTerminated()){}
-            face = result.get();
-        } catch (InterruptedException|ExecutionException ex) {
+            face = new HumanFace(file); // read from file
+            checkMemAndDump(); // free the memory, if necessary
+        } catch (IOException ex) {
             Logger.getLogger(HumanFaceFactory.class.getName()).log(Level.SEVERE, null, ex);
             return null;
-        }
+        }        
         
         // Update data structures:
         long time = System.currentTimeMillis();
@@ -176,28 +196,45 @@ public class HumanFaceFactory {
             return null;
         }
          
-        // Free memory and recover human face from dump file silultanously:
+        // Free memory and recover human face from dump file:
         HumanFace face;
         try {
-            ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
-            final Future<HumanFace> result = executor.submit(() -> HumanFace.restoreFromFile(dumpFile)); // recover face from disk
-            executor.submit(() -> checkMemAndDump()); // free the memory, if necessary
-            executor.shutdown();
-            while (!executor.isTerminated()){}
-            face = result.get();
-        } catch (InterruptedException|ExecutionException ex) {
+            face = HumanFace.restoreFromFile(dumpFile); // recover face from disk
+            checkMemAndDump(); // free the memory, if necessary
+            //System.out.println(face.getShortName() + " recovered");
+        } catch (ClassNotFoundException|IOException ex) {
             Logger.getLogger(HumanFaceFactory.class.getName()).log(Level.SEVERE, null, ex);
             return null;
         }
         
         // Update data structures:
         long time = System.currentTimeMillis();
-        dumpedFaces.remove(faceId);
+        while (inMemoryFaces.containsKey(time)) { // wait until we get unique ms
+            time = System.currentTimeMillis();
+        }
+        //dumpedFaces.remove(faceId);
         inMemoryFaces.put(time, face);
         usage.put(faceId, time);
         return face;
     }
     
+    /**
+     * Removes the face from either memory or swap space. To re-initiate the face,
+     * use {@link #loadFace(java.io.File)} again.
+     * 
+     * @param faceId Face ID
+     * @return true if the face existed and was removed.
+     */
+    public boolean removeFace(String faceId) {
+        if (usage.containsKey(faceId)) {
+            inMemoryFaces.remove(usage.remove(faceId));
+            dumpedFaces.remove(faceId);
+            return true; // remove from memory;
+        }
+        
+        return dumpedFaces.remove(faceId) != null;
+    }
+    
     @Override
     public String toString() {
         StringBuilder ret = new StringBuilder();
@@ -206,7 +243,7 @@ public class HumanFaceFactory {
                 append(formatSize(Runtime.getRuntime().maxMemory()));
         ret.append("). In memory: ").
                 append(this.inMemoryFaces.size()).
-                append(", dumped: ").
+                append(", dump files: ").
                 append(this.dumpedFaces.size());
         return ret.toString();
     }
@@ -242,27 +279,36 @@ public class HumanFaceFactory {
      * @return true if some existing face has been dumped to free the memory.
      * @throws IOException on I/O error
      */
-    protected boolean checkMemAndDump() throws IOException {
-        double ratio = (double) presumableFreeMemory() / Runtime.getRuntime().maxMemory();
-        if (ratio > MIN_FREE_MEMORY) {
-            return false;
-        }
+    protected int checkMemAndDump() throws IOException {
+        int ret =  0;
+        int counter = 0;
+        while (counter++ < this.MAX_DUMP_FACES &&
+                (double) presumableFreeMemory() / Runtime.getRuntime().maxMemory() <= MIN_FREE_MEMORY) {
+            
+            Long time = null;
+            switch (strategy) {
+                case MRU:
+                    time = inMemoryFaces.lastKey();
+                    break;
+                default:
+                case LRU:
+                    time = inMemoryFaces.firstKey();
+                    break;
+            }
+            
+            if (time == null) { // no face in the memory
+                break; 
+            }
         
-        Long time = null;
-        switch (strategy) {
-            case MRU:
-                time = inMemoryFaces.lastKey();
-                break;
-            default:
-            case LRU:
-                time = inMemoryFaces.firstKey();
-                break;
+            HumanFace faceToDump = this.inMemoryFaces.remove(time);
+            this.usage.remove(faceToDump.getId());
+            if (!reuseDumpFile || !dumpedFaces.containsKey(faceToDump.getId())) { // dump only if it's required
+                dumpedFaces.put(faceToDump.getId(), faceToDump.dumpToFile());
+            }
+            //System.out.println(faceToDump.getShortName() + " dumped");
         }
         
-        HumanFace faceToDump = this.inMemoryFaces.remove(time);
-        this.usage.remove(faceToDump.getId());
-        dumpedFaces.put(faceToDump.getId(), faceToDump.dumpToFile());
-        return true;
+        return ret;        
     }
         
     protected static String formatSize(long v) {
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/face/events/HausdorffDistanceComputed.java b/Comparison/src/main/java/cz/fidentis/analyst/face/events/HausdorffDistanceComputed.java
index df1c6bbbfedf138a2d3e587ce21d2acd2a476dfa..8e79c7914ec4b79352b889a7cb790288aae69d41 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/face/events/HausdorffDistanceComputed.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/face/events/HausdorffDistanceComputed.java
@@ -77,6 +77,7 @@ public class HausdorffDistanceComputed extends HumanFaceEvent {
                 .stream()
                 .flatMap(List::stream)
                 .mapToDouble(Double::doubleValue)
+                .filter(v -> Double.isFinite(v))
                 .summaryStatistics();
     }
     
@@ -105,6 +106,7 @@ public class HausdorffDistanceComputed extends HumanFaceEvent {
                 .stream()
                 .flatMap(List::stream)
                 .mapToDouble(Double::doubleValue)
+                .filter(v -> Double.isFinite(v))
                 .summaryStatistics();
     }
     
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java
index c4775d97a97b01f297f58712233425a57a094b0f..5fabe728978125bb0bbbc646ed7397880a05fdeb 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java
@@ -240,7 +240,8 @@ public class IcpTransformer extends MeshVisitor   {
                 this.primaryKdTree, 
                 HausdorffDistance.Strategy.POINT_TO_POINT, 
                 false, // relative distance
-                true   // parallel computation
+                true,  // parallel computation
+                false  // auto cut
         );
         
         MeshFacet reducedFacet = new UndersampledMeshFacet(transformedFacet, reductionStrategy);
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeApproxDistanceToTriangles.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeApproxDistanceToTriangles.java
index a1539dd4b459dd1128ae7bf2e08498eac7754ff1..5122a4ea2561ef7369ffb10d66a003bedac0888c 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeApproxDistanceToTriangles.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeApproxDistanceToTriangles.java
@@ -15,7 +15,7 @@ import java.util.Set;
 import javax.vecmath.Point3d;
 
 /**
- * This visitor finds the minimal distance between the given 3D point and triangular
+ * This visitor finds the minimal distance between a given 3D point and triangular
  * meshes with vertices stored in a k-d tree.
  * <p>
  * The minimal distance is computed between the 3D point and triangles of the mesh facets,
@@ -32,7 +32,7 @@ import javax.vecmath.Point3d;
  * explores triangles around the vertex to find the closest triangle. It is more
  * optimal than computing the distance to all triangles, but it works only for rather 
  * flat surfaces. It is okay for models of human faces where the "wrinkly" parts 
- * are considered as noise and we aim to remove them from further processing. 
+ * are considered noise and we aim to remove them from further processing anyway. 
  * </p>
  * <p>
  * This visitor is thread-safe, i.e., a single instance of the visitor can be used 
@@ -46,16 +46,25 @@ public class KdTreeApproxDistanceToTriangles extends KdTreeVisitor implements Di
     private double distance = Double.POSITIVE_INFINITY;
     private final Point3d point3d;
     private Map<MeshFacet, List<Point3d>> nearestPoints = new HashMap<>();
+    private Map<MeshFacet, List<Integer>> nearestVertices = new HashMap<>();
+    private final boolean checkOverlay;
     
     /**
+     * Constructor.
+     * 
      * @param point A 3D point from which distance is computed. Must not be {@code null}
+     * @param checkOverlay If {@code true} and and closest point to the given 3D reference point 
+     *        lies on the boundary of the mesh stored in the k-d tree, 
+     *        then the reference point is considered "outside" of the mesh
+     *        and the distance is set to infinity.
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public KdTreeApproxDistanceToTriangles(Point3d point) {
+    public KdTreeApproxDistanceToTriangles(Point3d point, boolean checkOverlay) {
         if (point == null) {
             throw new IllegalArgumentException("point");
         }
         this.point3d = point;
+        this.checkOverlay = checkOverlay;
     }
     
     @Override
@@ -65,6 +74,18 @@ public class KdTreeApproxDistanceToTriangles extends KdTreeVisitor implements Di
 
     @Override
     public double getDistance() {
+        if (!checkOverlay || distance == Double.POSITIVE_INFINITY) {
+            return distance;
+        }
+        
+        for (MeshFacet f: nearestVertices.keySet()) {
+            for (Integer i: nearestVertices.get(f)) {
+                if (f.getOneRingNeighborhood(i).isBoundary()) {
+                    return Double.POSITIVE_INFINITY;
+                }
+            }
+        }
+        
         return distance;
     }
     
@@ -95,7 +116,9 @@ public class KdTreeApproxDistanceToTriangles extends KdTreeVisitor implements Di
                         nearestPoints.clear();                    
                     }
                     nearestPoints.computeIfAbsent(facet, meshFacet -> new ArrayList<>())
-                        .add(projection);
+                            .add(projection);
+                    nearestVertices.computeIfAbsent(facet, n -> new ArrayList<>())
+                            .add(vIndex);
                 }
             }
         }
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeClosestNode.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeClosestNode.java
index f5ccb8fbcac9d844b060c118ff1fe9eba99a2c16..30ee0599eeaff139ded00748d70f0330b34c4463 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeClosestNode.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeClosestNode.java
@@ -30,6 +30,8 @@ public class KdTreeClosestNode extends KdTreeVisitor {
     private final Set<KdNode> closest = new HashSet<>();
     
     /**
+     * Constructor.
+     * 
      * @param point The 3D location for which the closest nodes are to be computed.
      * @throws IllegalArgumentException if some parameter is wrong
      */
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistance.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistance.java
index ab48a42c6dd51c7ff161eba8501109b2d189ff77..51b0f63349fb2106346271bd3131c4a301f226ee 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistance.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistance.java
@@ -17,13 +17,15 @@ import javax.vecmath.Point3d;
  * @author Daniel Schramm
  * @author Radek Oslejsek
  */
+@Deprecated
 public class KdTreeDistance extends KdTreeVisitor implements Distance {
     
     private double distance = Double.POSITIVE_INFINITY;
-    private Point3d point3d;
+    private final Point3d point3d;
     
     /**
      * Constructor.
+     * 
      * @param point A 3D point from which distance is computed. Must not be {@code null}
      * @throws IllegalArgumentException if some parameter is wrong
      */
@@ -33,8 +35,12 @@ public class KdTreeDistance extends KdTreeVisitor implements Distance {
         }
         this.point3d = point;
     }
-
-    @Override
+    
+    /**
+     * Returns the minimal found distance
+     * 
+     * @return the minimal found distance
+     */
     public double getDistance() {
         return distance;
     }
@@ -50,5 +56,4 @@ public class KdTreeDistance extends KdTreeVisitor implements Distance {
             }
         }
     }
-
 }
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistanceToVertices.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistanceToVertices.java
index 2f8594cba8b8eac151483250077c634ddd230cdc..daaa434f3e628020054f12a64defc116e3b22ffd 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistanceToVertices.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistanceToVertices.java
@@ -4,10 +4,8 @@ import cz.fidentis.analyst.kdtree.KdNode;
 import cz.fidentis.analyst.kdtree.KdTree;
 import cz.fidentis.analyst.kdtree.KdTreeVisitor;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
-import cz.fidentis.analyst.mesh.core.MeshPoint;
 import cz.fidentis.analyst.visitors.DistanceWithNearestPoints;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -31,29 +29,56 @@ public class KdTreeDistanceToVertices extends KdTreeVisitor implements DistanceW
     
     private double distance = Double.POSITIVE_INFINITY;
     private final Point3d point3d;
-    private Map<MeshFacet, List<Point3d>> nearestPoints = new HashMap<>();
+    private Map<MeshFacet, List<Integer>> nearestVertices = new HashMap<>();
+    private final boolean checkOverlay;
     
     /**
+     * Constructor.
+     * 
      * @param point A 3D point from which distance is computed. Must not be {@code null}
+     * @param checkOverlay If {@code true} and and closest point to the given 3D reference point 
+     *        lies on the boundary of the mesh stored in the k-d tree, 
+     *        then the reference point is considered "outside" of the mesh
+     *        and the distance is set to infinity.
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public KdTreeDistanceToVertices(Point3d point) {
+    public KdTreeDistanceToVertices(Point3d point, boolean checkOverlay) {
         if (point == null) {
             throw new IllegalArgumentException("point");
         }
         this.point3d = point;
+        this.checkOverlay = checkOverlay;
     }
     
     @Override
     public Map<MeshFacet, List<Point3d>> getNearestPoints() {
-        return Collections.unmodifiableMap(nearestPoints);
+        Map<MeshFacet, List<Point3d>> ret = new HashMap<>();
+        nearestVertices.keySet().forEach(f -> {
+            nearestVertices.get(f).forEach(i -> {
+                ret.computeIfAbsent(f, n -> new ArrayList<>())
+                        .add(f.getVertex(i).getPosition());
+            });
+        });
+        return ret;
     }
-
+        
     @Override
     public double getDistance() {
+        if (!checkOverlay || distance == Double.POSITIVE_INFINITY) {
+            return distance;
+        }
+        
+        for (MeshFacet f: nearestVertices.keySet()) {
+            for (Integer i: nearestVertices.get(f)) {
+                if (f.getOneRingNeighborhood(i).isBoundary()) {
+                    return Double.POSITIVE_INFINITY;
+                }
+            }
+        }
+        
         return distance;
     }
-    
+
     @Override
     public void visitKdTree(KdTree kdTree) {
         final KdTreeClosestNode visitor = new KdTreeClosestNode(point3d);
@@ -64,7 +89,7 @@ public class KdTreeDistanceToVertices extends KdTreeVisitor implements DistanceW
         synchronized (this) {
             if (dist < distance) {
                 distance = dist;
-                nearestPoints.clear();                    
+                nearestVertices.clear();                    
             }
         }
         
@@ -75,9 +100,8 @@ public class KdTreeDistanceToVertices extends KdTreeVisitor implements DistanceW
                 }
                 for (Entry<MeshFacet, Integer> entry: node.getFacets().entrySet()) {
                     MeshFacet facet = entry.getKey();
-                    MeshPoint point = facet.getVertex(entry.getValue());
-                    nearestPoints.computeIfAbsent(facet, meshFacet -> new ArrayList<>())
-                        .add(point.getPosition());
+                    nearestVertices.computeIfAbsent(facet, n -> new ArrayList<>())
+                            .add(entry.getValue());
                 }
             }
         }
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/BoundingBox.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/BoundingBox.java
index a1d5efe5d9e6d2e5bc3ac4f70f5cb8a4f271fcc8..5bfa197cc70d50c693b7c0b9e37123e50933a48a 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/BoundingBox.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/BoundingBox.java
@@ -3,6 +3,7 @@ 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.MeshPoint;
+import java.io.Serializable;
 import java.util.List;
 import javax.vecmath.Point3d;
 import javax.vecmath.Vector3d;
@@ -15,7 +16,7 @@ import javax.vecmath.Vector3d;
  * 
  * @author Radek Oslejsek
  */
-public class BoundingBox extends MeshVisitor {
+public class BoundingBox extends MeshVisitor implements Serializable {
 
     private BBox bbox;
     
@@ -42,7 +43,7 @@ public class BoundingBox extends MeshVisitor {
      * 
      * @author Natalia Bebjakova
     */
-    public class BBox {
+    public class BBox implements Serializable {
     
         private Point3d maxPoint;
         private Point3d minPoint;
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistance.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistance.java
index 283eff9345f620618674e6675914d42c2b0c57f5..d877c569436d9064fc9bf8ce6d3e4b31473aad52 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistance.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistance.java
@@ -1,5 +1,6 @@
 package cz.fidentis.analyst.visitors.mesh;
 
+import cz.fidentis.analyst.kdtree.KdNode;
 import cz.fidentis.analyst.kdtree.KdTree;
 import cz.fidentis.analyst.kdtree.KdTreeVisitor;
 import cz.fidentis.analyst.mesh.MeshVisitor;
@@ -9,6 +10,7 @@ import cz.fidentis.analyst.mesh.core.MeshPoint;
 import cz.fidentis.analyst.visitors.Distance;
 import cz.fidentis.analyst.visitors.DistanceWithNearestPoints;
 import cz.fidentis.analyst.visitors.kdtree.KdTreeApproxDistanceToTriangles;
+import cz.fidentis.analyst.visitors.kdtree.KdTreeClosestNode;
 import cz.fidentis.analyst.visitors.kdtree.KdTreeDistance;
 import cz.fidentis.analyst.visitors.kdtree.KdTreeDistanceToVertices;
 import java.util.ArrayList;
@@ -53,7 +55,7 @@ import javax.vecmath.Point3d;
  * <p>
  * Hausdorff distance sequentially:
  * <table>
- * <tr><td>2443 msec</td><td>OINT_TO_POINT_DISTANCE_ONLY</td></tr>
+ * <tr><td>2443 msec</td><td>POINT_TO_POINT_DISTANCE_ONLY</td></tr>
  * <tr><td>2495 msec</td><td>POINT_TO_POINT</td></tr>
  * <tr><td>3430 msec</td><td>POINT_TO_TRIANGLE_APPROXIMATE</td></tr>
  * </table>
@@ -61,7 +63,7 @@ import javax.vecmath.Point3d;
  * <p>
  * Hausdorff distance concurrently:
  * <table>
- * <tr><td>1782 msec</td><td>OINT_TO_POINT_DISTANCE_ONLY</td></tr>
+ * <tr><td>1782 msec</td><td>POINT_TO_POINT_DISTANCE_ONLY</td></tr>
  * <tr><td>2248 msec</td><td>POINT_TO_POINT</td></tr>
  * <tr><td>2574 msec</td><td>POINT_TO_TRIANGLE_APPROXIMATE</td></tr>
  * </table>
@@ -131,6 +133,8 @@ import javax.vecmath.Point3d;
     
     private final boolean parallel;
     
+    private final boolean crop;
+    
     /**
      * The source triangular mesh (set of mesh facets) stored in k-d tree.
      */
@@ -145,9 +149,14 @@ import javax.vecmath.Point3d;
      * to the normal vectors of source facets (normal vectors have to present), 
      * i.e., we can get negative distances.
      * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
+     * @param crop If {@code true}, then only parts of the visited secondary faces that overlay the primary face are
+     *        taken into account. Parts (vertices) that are out of the surface of the primary face are ignored 
+     *        (their distance is set to {@code NaN}). 
+     *        This feature makes the Hausdorff distance computation more symmetric.
+     *        This optimization cannot be used for POINT_TO_POINT_DISTANCE_ONLY strategy
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public HausdorffDistance(Set<MeshFacet> mainFacets, Strategy strategy, boolean relativeDistance, boolean parallel) {
+    public HausdorffDistance(Set<MeshFacet> mainFacets, Strategy strategy, boolean relativeDistance, boolean parallel, boolean crop) {
         this.strategy = strategy;
         this.relativeDist = relativeDistance;
         this.parallel = parallel;
@@ -155,9 +164,13 @@ import javax.vecmath.Point3d;
             throw new IllegalArgumentException("mainFacets");
         }
         if (strategy == Strategy.POINT_TO_POINT_DISTANCE_ONLY && relativeDistance) {
-            throw new IllegalArgumentException("Point-to-point strategy cannot be used with relative distance");
+            throw new IllegalArgumentException("POINT_TO_POINT_DISTANCE_ONLY strategy cannot be used with relative distance");
+        }
+        if (strategy == Strategy.POINT_TO_POINT_DISTANCE_ONLY && crop) {
+            throw new IllegalArgumentException("POINT_TO_POINT_DISTANCE_ONLY strategy cannot be used with auto cutting parameter");
         }
         this.kdTree = new KdTree(new ArrayList<>(mainFacets));
+        this.crop = crop;
     }
 
     /**
@@ -169,10 +182,15 @@ import javax.vecmath.Point3d;
      * to the normal vectors of source facets (normal vectors have to present), 
      * i.e., we can get negative distances.
      * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
+     * @param crop If {@code true}, then only parts of the visited secondary faces that overlay the primary face are
+     *        taken into account. Parts (vertices) that are out of the surface of the primary face are ignored 
+     *        (their distance is set to {@code NaN}). 
+     *        This feature makes the Hausdorff distance computation more symmetric.
+     *        This optimization cannot be used for POINT_TO_POINT_DISTANCE_ONLY strategy
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public HausdorffDistance(MeshFacet mainFacet, Strategy strategy, boolean relativeDistance, boolean parallel) {
-        this(new HashSet<>(Collections.singleton(mainFacet)), strategy, relativeDistance, parallel);
+    public HausdorffDistance(MeshFacet mainFacet, Strategy strategy, boolean relativeDistance, boolean parallel, boolean crop) {
+        this(new HashSet<>(Collections.singleton(mainFacet)), strategy, relativeDistance, parallel, crop);
         if (mainFacet == null) {
             throw new IllegalArgumentException("mainFacet");
         }
@@ -188,10 +206,15 @@ import javax.vecmath.Point3d;
      * to the normal vectors of source facets (normal vectors have to present), 
      * i.e., we can get negative distances.
      * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
+     * @param crop If {@code true}, then only parts of the visited secondary faces that overlay the primary face are
+     *        taken into account. Parts (vertices) that are out of the surface of the primary face are ignored 
+     *        (their distance is set to {@code NaN}). 
+     *        This feature makes the Hausdorff distance computation more symmetric.
+     *        This optimization cannot be used for POINT_TO_POINT_DISTANCE_ONLY strategy
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public HausdorffDistance(MeshModel mainModel, Strategy strategy, boolean relativeDistance, boolean parallel) {
-        this(new HashSet<>(mainModel.getFacets()), strategy, relativeDistance, parallel);
+    public HausdorffDistance(MeshModel mainModel, Strategy strategy, boolean relativeDistance, boolean parallel, boolean crop) {
+        this(new HashSet<>(mainModel.getFacets()), strategy, relativeDistance, parallel, crop);
         if (mainModel.getFacets().isEmpty()) {
             throw new IllegalArgumentException("mainModel");
         }
@@ -206,9 +229,14 @@ import javax.vecmath.Point3d;
      * to the normal vectors of source facets (normal vectors have to present), 
      * i.e., we can get negative distances.
      * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
+     * @param crop If {@code true}, then only parts of the visited secondary faces that overlay the primary face are
+     *        taken into account. Parts (vertices) that are out of the surface of the primary face are ignored 
+     *        (their distance is set to {@code NaN}). 
+     *        This feature makes the Hausdorff distance computation more symmetric.
+     *        This optimization cannot be used for POINT_TO_POINT_DISTANCE_ONLY strategy
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public HausdorffDistance(KdTree mainKdTree, Strategy strategy, boolean relativeDistance, boolean parallel) {
+    public HausdorffDistance(KdTree mainKdTree, Strategy strategy, boolean relativeDistance, boolean parallel, boolean crop) {
         this.strategy = strategy;
         this.relativeDist = relativeDistance;
         this.parallel = parallel;
@@ -216,9 +244,13 @@ import javax.vecmath.Point3d;
             throw new IllegalArgumentException("mainKdTree");
         }
         if (strategy == Strategy.POINT_TO_POINT_DISTANCE_ONLY && relativeDistance) {
-            throw new IllegalArgumentException("Point-to-point strategy cannot be used with relative distance");
+            throw new IllegalArgumentException("POINT_TO_POINT_DISTANCE_ONLY strategy cannot be used with relative distance");
+        }
+        if (strategy == Strategy.POINT_TO_POINT_DISTANCE_ONLY && crop) {
+            throw new IllegalArgumentException("POINT_TO_POINT_DISTANCE_ONLY strategy cannot be used with auto cutting parameter");
         }
         this.kdTree = mainKdTree;
+        this.crop = crop;
     }
     
     /**
@@ -293,6 +325,7 @@ import javax.vecmath.Point3d;
                 .stream()
                 .flatMap(List::stream)
                 .mapToDouble(Double::doubleValue)
+                .filter(v -> Double.isFinite(v))
                 .summaryStatistics();
     }
     
@@ -347,10 +380,10 @@ import javax.vecmath.Point3d;
             case POINT_TO_POINT_DISTANCE_ONLY:
                 return new KdTreeDistance(inspectedPoint);
             case POINT_TO_POINT:
-                return new KdTreeDistanceToVertices(inspectedPoint);
+                return new KdTreeDistanceToVertices(inspectedPoint, crop);
             default:
             case POINT_TO_TRIANGLE_APPROXIMATE:
-                return new KdTreeApproxDistanceToTriangles(inspectedPoint);
+                return new KdTreeApproxDistanceToTriangles(inspectedPoint, crop);
         }
     }
 
@@ -401,6 +434,22 @@ import javax.vecmath.Point3d;
         return vis.getNearestPoints().get(myInspectedFacet).get(0);
     }
     
+    /*
+    protected boolean isOnBoundary(Point3d point3d) {
+        KdTreeClosestNode vis = new KdTreeClosestNode(point3d);
+        this.kdTree.accept(vis);
+        for (KdNode node: vis.getClosestNodes()) {
+            for (MeshFacet facet: node.getFacets().keySet()) {
+                int vIndex = node.getFacets().get(facet);
+                if (facet.getOneRingNeighborhood(vIndex).isBoundary()) {
+                    return true; 
+                }
+            }
+        }
+        return false;
+    }
+    */
+    
     /**
      * Helper call for asynchronous invocation of visitors.
      * 
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 49229055ec89e63e0aed8fae5b64eb12b9efb440..e6204b567d9cbe2834403dfd91d6897a1bb8249d 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
@@ -227,7 +227,7 @@ public class HausdorffDistancePrioritizedTest {
         }
         
         final HausdorffDistancePrioritized visitor = new HausdorffDistancePrioritized(
-                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, true, false),
+                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, true, false, false),
                 featurePointTypes);
         
         performTest(face, expectedPriorities, expectedFeaturePointsWeights, expectedMergedPrioritiesMap, visitor);
@@ -262,7 +262,7 @@ public class HausdorffDistancePrioritizedTest {
         }
         
         final HausdorffDistancePrioritized visitor = new HausdorffDistancePrioritized(
-                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, false, false),
+                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, false, false, false),
                 featurePointTypes);
         
         performTest(face, expectedPriorities, expectedFeaturePointsWeights, expectedMergedPrioritiesMap, visitor);
@@ -306,7 +306,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(
-                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, true, false),
+                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, true, false, false),
                 featurePointTypes);
         
         performTest(face, expectedPriorities, expectedFeaturePointsWeights, expectedMergedPrioritiesMap, visitor);
@@ -370,7 +370,7 @@ public class HausdorffDistancePrioritizedTest {
         expectedMergedPrioritiesMap.put(facet5, mergedPriorities);
         
         final HausdorffDistancePrioritized visitor = new HausdorffDistancePrioritized(
-                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, true, false),
+                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, true, false, false),
                 featurePointTypes);
         
         performTest(face, expectedPriorities, expectedFeaturePointsWeights, expectedMergedPrioritiesMap, visitor);
@@ -433,7 +433,7 @@ public class HausdorffDistancePrioritizedTest {
         expectedMergedPrioritiesMap.put(facet4, mergedPriorities);
         
         final HausdorffDistancePrioritized visitor = new HausdorffDistancePrioritized(
-                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, true, false),
+                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, true, false, false),
                 featurePointTypes);
         
         performTest(face, expectedPriorities, expectedFeaturePointsWeights, expectedMergedPrioritiesMap, visitor);
@@ -497,7 +497,7 @@ public class HausdorffDistancePrioritizedTest {
         expectedMergedPrioritiesMap.put(facet5, mergedPriorities);
         
         final HausdorffDistancePrioritized visitor = new HausdorffDistancePrioritized(
-                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, true, true),
+                new HausdorffDistance(getHumanFace(facets2, List.of(), List.of()).getMeshModel(), POINT_TO_POINT, true, true, false),
                 featurePointTypes);
         
         performTest(face, expectedPriorities, expectedFeaturePointsWeights, expectedMergedPrioritiesMap, visitor);
diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/KdTreeApproxDistToTriTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/KdTreeApproxDistToTriTest.java
index 1151119ee965afe396dcb75cc6e3bc92bc6a18b6..6f75b170422364e8dea44b369ec4890e09ec4f27 100644
--- a/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/KdTreeApproxDistToTriTest.java
+++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/KdTreeApproxDistToTriTest.java
@@ -33,13 +33,13 @@ public class KdTreeApproxDistToTriTest {
     
     @Test
     public void distTest() {
-        KdTreeApproxDistanceToTriangles vis = new KdTreeApproxDistanceToTriangles(new Point3d(0,0,0)); // sequentially
+        KdTreeApproxDistanceToTriangles vis = new KdTreeApproxDistanceToTriangles(new Point3d(0,0,0), false); // sequentially
         distTest(1.5, vis);
     }
 
     @Test
     public void exactMatchTest() {
-        KdTreeApproxDistanceToTriangles vis = new KdTreeApproxDistanceToTriangles(new Point3d(0, 0, 1.5)); // relative dist, sequentially
+        KdTreeApproxDistanceToTriangles vis = new KdTreeApproxDistanceToTriangles(new Point3d(0, 0, 1.5), false); // relative dist, sequentially
         distTest(0, vis);
     }
 }
diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistanceToVerticesTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistanceToVerticesTest.java
index 09740fb2c600e02b56e832d5d9ab363d8e77a2c0..f4e5a4b4773d31fd3cc8c4bbe7b1ae59935efe9a 100644
--- a/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistanceToVerticesTest.java
+++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/kdtree/KdTreeDistanceToVerticesTest.java
@@ -34,14 +34,14 @@ public class KdTreeDistanceToVerticesTest {
     
     @Test
     public void distTest() {
-        KdTreeDistanceToVertices vis = new KdTreeDistanceToVertices(new Point3d(0,0,0)); // sequentially
+        KdTreeDistanceToVertices vis = new KdTreeDistanceToVertices(new Point3d(0,0,0), false); // sequentially
         distTest(1.5, vis);
     }
 
     @Test
     public void exactMatchTest() {
         MeshPoint point = new MeshPointImpl(new Point3d(0, 0, 1.5), new Vector3d(0, 0, 1), new Vector3d());
-        KdTreeDistanceToVertices vis = new KdTreeDistanceToVertices(new Point3d(0, 0, 1.5)); // relative dist, sequentially
+        KdTreeDistanceToVertices vis = new KdTreeDistanceToVertices(new Point3d(0, 0, 1.5), false); // relative dist, sequentially
         distTest(0, vis);
     }
 }
diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistanceTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistanceTest.java
index dc114435700f688fa33799081a2a6b7d6308210c..cd04a576af7367d2494380eb44d3179173fc5e37 100644
--- a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistanceTest.java
+++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/HausdorffDistanceTest.java
@@ -69,46 +69,46 @@ public class HausdorffDistanceTest {
     public void visitToVerticesTest() {
         MeshModel firstModel = getTrivialModel(1, 1);
         MeshModel secondModel = getTrivialModel(1.5, 1);
-        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, false), firstModel, secondModel, 0.5);
-        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_POINT, false, false), secondModel, firstModel, 0.5);
-        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, false), firstModel, secondModel, 0.5);
-        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, false), secondModel, firstModel, 0.5);
+        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, false, false), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_POINT, false, false, false), secondModel, firstModel, 0.5);
+        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, false, false), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, false, false), secondModel, firstModel, 0.5);
     }
 
     @Test
     public void visitToVerticesBehindMeshTest() {
         MeshModel firstModel = getTrivialModel(1, 1);
         MeshModel secondModel = getTrivialModel(-1.5, 1);
-        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, false), firstModel, secondModel, 2.5);
-        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_POINT, false, false), secondModel, firstModel, 2.5);
-        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, false), firstModel, secondModel, 2.5);
-        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, false), secondModel, firstModel, 2.5);
+        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, false, false), firstModel, secondModel, 2.5);
+        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_POINT, false, false, false), secondModel, firstModel, 2.5);
+        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, false, false), firstModel, secondModel, 2.5);
+        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, false, false), secondModel, firstModel, 2.5);
     }
 
     @Test
     public void visitToVerticesRelativeDistanceTest() {
         MeshModel firstModel = getTrivialModel(1, 1);
         MeshModel secondModel = getTrivialModel(1.5, 1);
-        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, true, false), firstModel, secondModel, -0.5);
-        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, true, false), secondModel, firstModel, 0.5);
+        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, true, false, false), firstModel, secondModel, -0.5);
+        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, true, false, false), secondModel, firstModel, 0.5);
     }
 
     @Test
     public void visitToVerticesBehindMeshRelativeDistanceTest() {
         MeshModel firstModel = getTrivialModel(1, 1);
         MeshModel secondModel = getTrivialModel(-1.5, 1);
-        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, true, false), firstModel, secondModel, 2.5);
-        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, true, false), secondModel, firstModel, -2.5);
+        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, true, false, false), firstModel, secondModel, 2.5);
+        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, true, false, false), secondModel, firstModel, -2.5);
     }
     
     @Test
     public void concurrencyTest() {
         MeshModel firstModel = getTrivialModel(1, 1);
         MeshModel secondModel = getTrivialModel(1.5, 1);
-        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, true), firstModel, secondModel, 0.5);
-        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_POINT, false, true), secondModel, firstModel, 0.5);
-        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, true), firstModel, secondModel, 0.5);
-        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, true), secondModel, firstModel, 0.5);
+        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, true, false), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_POINT, false, true, false), secondModel, firstModel, 0.5);
+        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, true, false), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistance(secondModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, true, false), secondModel, firstModel, 0.5);
     }
     
     @Test
@@ -118,8 +118,8 @@ public class HausdorffDistanceTest {
         firstModel.addFacet(getTrivialFacet(4, 1));
         MeshModel secondModel = getTrivialModel(1.5, 1);
         
-        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, true), firstModel, secondModel, 0.5);
-        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, true), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, true, false), firstModel, secondModel, 0.5);
+        testDist(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, true, false), firstModel, secondModel, 0.5);
     }
 
 
@@ -128,10 +128,10 @@ public class HausdorffDistanceTest {
         MeshModel firstModel = getTrivialModel(1, 1);
         MeshModel secondModel = getTrivialModel(1, 1);
         MeshModel thirdModel = getTrivialModel(1.5, 1);
-        testNearestPoints(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, false), firstModel, secondModel);
-        testNearestPoints(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, false), firstModel, secondModel);
-        testNearestPoints(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, false), firstModel, thirdModel);
-        testNearestPoints(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, false), firstModel, secondModel);
+        testNearestPoints(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, false, false), firstModel, secondModel);
+        testNearestPoints(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, false, false), firstModel, secondModel);
+        testNearestPoints(new HausdorffDistance(firstModel, Strategy.POINT_TO_POINT, false, false, false), firstModel, thirdModel);
+        testNearestPoints(new HausdorffDistance(firstModel, Strategy.POINT_TO_TRIANGLE_APPROXIMATE, false, false, false), firstModel, secondModel);
     }
 
 }
diff --git a/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java b/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java
index f1c070aee34b55b269eebe7513883c6073281ee8..1c13b9dc58653ed4f7097d2fb08e5d30ba1d8498 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java
@@ -20,6 +20,7 @@ import java.awt.event.ActionEvent;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JTabbedPane;
 import javax.swing.JToggleButton;
@@ -61,6 +62,7 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe
     private boolean relativeDist = false;
     private String heatmapDisplayed = DistancePanel.SEL_STD_HAUSDORFF_DISTANCE;
     private String weightedFPsShow = DistancePanel.SEL_SHOW_NO_SPHERES;
+    private boolean crop = false;
     
     private FeaturePointType hoveredFeaturePoint = null;
     
@@ -132,18 +134,18 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe
                 break;
             case DistancePanel.ACTION_COMMAND_SET_DISTANCE_STRATEGY:
                 strategy = (String) ((JComboBox) ae.getSource()).getSelectedItem();
-                computeAndUpdateHausdorffDistance();
+                computeAndUpdateHausdorffDistance(true);
                 break;
             case DistancePanel.ACTION_COMMAND_RELATIVE_ABSOLUTE_DIST:
                 this.relativeDist = ((String) ((JComboBox) ae.getSource()).getSelectedItem())
                         .equals(DistancePanel.SEL_RELATIVE_DISTANCE);
-                computeAndUpdateHausdorffDistance();
+                computeAndUpdateHausdorffDistance(true);
                 break;
             case FeaturePointsPanel.ACTION_COMMAND_FEATURE_POINT_SELECT:
                 selectFeaturePoint((LoadedActionEvent) ae);
                 setVisibleSpheres();
                 highlightSelectedSpheres();
-                computeAndUpdateHausdorffDistance();
+                computeAndUpdateHausdorffDistance(false);
                 break;
             case DistancePanel.ACTION_COMMAND_FEATURE_POINT_SELECT_ALL:
                 for (int i = 0; i < featurePoints.size(); i++) {
@@ -152,20 +154,20 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe
                 controlPanel.getFeaturePointsListPanel().selectAllFeaturePoints(true);
                 setVisibleSpheres();
                 highlightSelectedSpheres();
-                computeAndUpdateHausdorffDistance();
+                computeAndUpdateHausdorffDistance(false);
                 break;
             case DistancePanel.ACTION_COMMAND_FEATURE_POINT_SELECT_NONE:
                 featurePointTypes.clear();
                 controlPanel.getFeaturePointsListPanel().selectAllFeaturePoints(false);
                 setVisibleSpheres();
                 highlightSelectedSpheres();
-                computeAndUpdateHausdorffDistance();
+                computeAndUpdateHausdorffDistance(false);
                 break;
             case DistancePanel.ACTION_COMMAND_FEATURE_POINT_SELECT_AUTO:
                 for (int i = 0; i < featurePoints.size(); i++) { // select all
                     featurePointTypes.put(featurePoints.get(i).getFeaturePointType(), fpSpheres.getSize(i));
                 }
-                calculateHausdorffDistance(featurePointTypes);
+                calculateHausdorffDistance(featurePointTypes, false);
                 selectSignificantFPs();
                 for (int i = 0; i < featurePoints.size(); i++) { // update the check list
                     controlPanel.getFeaturePointsListPanel().selectFeaturePoint(
@@ -175,7 +177,11 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe
                 }
                 setVisibleSpheres();
                 highlightSelectedSpheres();
-                computeAndUpdateHausdorffDistance();
+                computeAndUpdateHausdorffDistance(false);
+                break;
+            case DistancePanel.ACTION_COMMAND_HD_AUTO_CROP:
+                this.crop = ((JCheckBox) ae.getSource()).isSelected();
+                computeAndUpdateHausdorffDistance(true);
                 break;
             case FeaturePointsPanel.ACTION_COMMAND_FEATURE_POINT_HOVER_IN:
                 hoverFeaturePoint((LoadedActionEvent) ae, true);
@@ -187,7 +193,7 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe
                 resizeFeaturePoint((LoadedActionEvent) ae);
                 break;
             case FeaturePointsPanel.ACTION_COMMAND_DISTANCE_RECOMPUTE:
-                computeAndUpdateHausdorffDistance();
+                computeAndUpdateHausdorffDistance(true);
                 break;
             default:
                 // to nothing
@@ -200,7 +206,7 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe
         if (event instanceof MeshChangedEvent && event.getIssuer() != this) { // recompte (W)HD
             hdVisitor = null;
             whdVisitor = null;
-            computeAndUpdateHausdorffDistance();
+            computeAndUpdateHausdorffDistance(true);
             // Relocate weight speheres:
             final List<FeaturePoint> secondaryFPs = getSecondaryFeaturePoints().getFeaturePoints();
             final List<FeaturePoint> weightedFPs = fpSpheres.getFeaturePoints();
@@ -220,8 +226,8 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe
      * Recalculates the Hausdorff distance and updates all GUI elements
      * of {@link DistancePanel}.
      */
-    private void computeAndUpdateHausdorffDistance() {
-        calculateHausdorffDistance(featurePointTypes);
+    private void computeAndUpdateHausdorffDistance(boolean recomputeHD) {
+        calculateHausdorffDistance(featurePointTypes, recomputeHD);
         setFeaturePointWeigths(); // Updates GUI elements that display the calculated Hausdorff distance metrics
         updateHeatmap();
     }
@@ -256,7 +262,7 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe
      *                      points of the corresponding type.
      *                      Must not be {@code null}.
      */
-    private void calculateHausdorffDistance(Map<FeaturePointType, Double> featurePoints) {
+    private void calculateHausdorffDistance(Map<FeaturePointType, Double> featurePoints, boolean recomputeHD) {
         final Strategy useStrategy;
         switch (strategy) {
             case DistancePanel.SEL_STRATEGY_POINT_TO_POINT:
@@ -269,9 +275,15 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe
                 throw new UnsupportedOperationException(strategy);
         }
         
-        if (hdVisitor == null) {
+        if (hdVisitor == null || recomputeHD) {
             getPrimaryDrawableFace().getHumanFace().computeKdTree(false); // recompute if does not exist
-            hdVisitor = new HausdorffDistance(getPrimaryDrawableFace().getHumanFace().getKdTree(), useStrategy, relativeDist, true);
+            hdVisitor = new HausdorffDistance(
+                    getPrimaryDrawableFace().getHumanFace().getKdTree(), 
+                    useStrategy, 
+                    relativeDist, 
+                    true,
+                    crop
+            );
         }
         whdVisitor = new HausdorffDistancePrioritized(hdVisitor, featurePoints);
         
diff --git a/GUI/src/main/java/cz/fidentis/analyst/distance/DistancePanel.form b/GUI/src/main/java/cz/fidentis/analyst/distance/DistancePanel.form
index e51e857d682fa025e33c2a8bdc4fa9142882e56f..1c20f1877f70d23d91677847a3d819bf687230ed 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/distance/DistancePanel.form
+++ b/GUI/src/main/java/cz/fidentis/analyst/distance/DistancePanel.form
@@ -83,14 +83,18 @@
                   </Group>
                   <EmptySpace max="-2" attributes="0"/>
                   <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
+                      <Group type="102" alignment="0" attributes="0">
                           <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
                           <EmptySpace max="-2" attributes="0"/>
                           <Component id="jComboBox4" min="-2" max="-2" attributes="0"/>
                       </Group>
-                      <Component id="jComboBox3" alignment="0" min="-2" max="-2" attributes="0"/>
+                      <Group type="102" alignment="0" attributes="0">
+                          <Component id="jComboBox3" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/>
+                      </Group>
                   </Group>
-                  <EmptySpace min="-2" pref="102" max="-2" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
               </Group>
           </Group>
         </DimensionLayout>
@@ -102,6 +106,7 @@
                       <Component id="jComboBox2" alignment="3" min="-2" max="-2" attributes="0"/>
                       <Component id="jComboBox3" alignment="3" min="-2" max="-2" attributes="0"/>
                       <Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="jCheckBox1" alignment="3" min="-2" max="-2" attributes="0"/>
                   </Group>
                   <EmptySpace min="-2" pref="15" max="-2" attributes="0"/>
                   <Group type="103" groupAlignment="3" attributes="0">
@@ -191,6 +196,13 @@
             </Property>
           </Properties>
         </Component>
+        <Component class="javax.swing.JCheckBox" name="jCheckBox1">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="cz/fidentis/analyst/distance/Bundle.properties" key="DistancePanel.jCheckBox1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+        </Component>
       </SubComponents>
     </Container>
     <Container class="javax.swing.JScrollPane" name="jScrollPane1">
diff --git a/GUI/src/main/java/cz/fidentis/analyst/distance/DistancePanel.java b/GUI/src/main/java/cz/fidentis/analyst/distance/DistancePanel.java
index 2a8f87c144e74d2a7f25c7d9ddefdcc065338db9..31b8980cd0f9402fe819384100761e2a2e882b85 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/distance/DistancePanel.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/distance/DistancePanel.java
@@ -47,6 +47,7 @@ public class DistancePanel extends ControlPanel {
     public static final String ACTION_COMMAND_FEATURE_POINT_SELECT_ALL = "select all feature points";
     public static final String ACTION_COMMAND_FEATURE_POINT_SELECT_NONE = "deselect all feature points";
     public static final String ACTION_COMMAND_FEATURE_POINT_SELECT_AUTO = "Select significant feature points";
+    public static final String ACTION_COMMAND_HD_AUTO_CROP = "Crop";
     
     /**
      * Constructor.
@@ -118,6 +119,8 @@ public class DistancePanel extends ControlPanel {
         jButton1.addActionListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_SELECT_ALL));
         jButton2.addActionListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_SELECT_NONE));
         jButton3.addActionListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_SELECT_AUTO));
+        
+        jCheckBox1.addActionListener(createListener(action, ACTION_COMMAND_HD_AUTO_CROP));
     }
     
     /**
@@ -170,6 +173,7 @@ public class DistancePanel extends ControlPanel {
         jLabel3 = new javax.swing.JLabel();
         jLabel4 = new javax.swing.JLabel();
         jLabel5 = new javax.swing.JLabel();
+        jCheckBox1 = new javax.swing.JCheckBox();
         jScrollPane1 = new javax.swing.JScrollPane();
         featurePointsPanel1 = new cz.fidentis.analyst.distance.FeaturePointsPanel();
         jPanel2 = new javax.swing.JPanel();
@@ -200,6 +204,8 @@ public class DistancePanel extends ControlPanel {
 
         org.openide.awt.Mnemonics.setLocalizedText(jLabel5, org.openide.util.NbBundle.getMessage(DistancePanel.class, "DistancePanel.jLabel5.text")); // NOI18N
 
+        org.openide.awt.Mnemonics.setLocalizedText(jCheckBox1, org.openide.util.NbBundle.getMessage(DistancePanel.class, "DistancePanel.jCheckBox1.text")); // NOI18N
+
         javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
         jPanel1.setLayout(jPanel1Layout);
         jPanel1Layout.setHorizontalGroup(
@@ -219,8 +225,11 @@ public class DistancePanel extends ControlPanel {
                         .addComponent(jLabel3)
                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                         .addComponent(jComboBox4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                    .addComponent(jComboBox3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                .addGap(102, 102, 102))
+                    .addGroup(jPanel1Layout.createSequentialGroup()
+                        .addComponent(jComboBox3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(jCheckBox1)))
+                .addContainerGap())
         );
         jPanel1Layout.setVerticalGroup(
             jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -229,7 +238,8 @@ public class DistancePanel extends ControlPanel {
                 .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                     .addComponent(jComboBox2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                     .addComponent(jComboBox3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                    .addComponent(jLabel5))
+                    .addComponent(jLabel5)
+                    .addComponent(jCheckBox1))
                 .addGap(15, 15, 15)
                 .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                     .addComponent(jLabel3)
@@ -345,6 +355,7 @@ public class DistancePanel extends ControlPanel {
     private javax.swing.JButton jButton1;
     private javax.swing.JButton jButton2;
     private javax.swing.JButton jButton3;
+    private javax.swing.JCheckBox jCheckBox1;
     private javax.swing.JComboBox<String> jComboBox1;
     private javax.swing.JComboBox<String> jComboBox2;
     private javax.swing.JComboBox<String> jComboBox3;
diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java
index a49c59f31809e815a58d54d55fe325f5f5c2048b..28307ee24f8efe10b4e8788a91704abed0e9e4e5 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java
@@ -1,6 +1,7 @@
 package cz.fidentis.analyst.scene;
 
 import com.jogamp.opengl.GL2;
+import cz.fidentis.analyst.Logger;
 import cz.fidentis.analyst.face.HumanFace;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
 import java.awt.Color;
diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/HeatmapRenderer.java b/GUI/src/main/java/cz/fidentis/analyst/scene/HeatmapRenderer.java
index 015692b2bf614d9ec54f6a6d660f0032f7cae946..e112a874237943050feeceaf31b3faf0d5e425f4 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/scene/HeatmapRenderer.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/scene/HeatmapRenderer.java
@@ -17,8 +17,8 @@ import javax.vecmath.Vector3d;
  */
 public class HeatmapRenderer {
     
-    private Color minColor = Color.RED;
-    private Color maxColor = Color.BLUE;
+    private Color minColor = Color.BLUE;
+    private Color maxColor = Color.RED;
 
     public void setMinColor(Color color) {
         minColor = color;
@@ -35,8 +35,11 @@ public class HeatmapRenderer {
      * @param distances Distances in mesh model vertices
      * @param saturation Saturation of colors at mesh model vertices
      */
-    public void drawMeshModel(GL2 gl, MeshModel model,
-            Map<MeshFacet, List<Double>> distances, Map<MeshFacet, List<Double>> saturation) {
+    public void drawMeshModel(
+            GL2 gl, MeshModel model,
+            Map<MeshFacet, List<Double>> distances, 
+            Map<MeshFacet, List<Double>> saturation) {
+        
         double minDistance = Double.POSITIVE_INFINITY;
         double maxDistance = Double.NEGATIVE_INFINITY;
         
@@ -47,6 +50,7 @@ public class HeatmapRenderer {
                         minDistance,
                         distanceList.parallelStream()
                                 .mapToDouble(Double::doubleValue)
+                                .filter(v -> Double.isFinite(v))
                                 .min()
                                 .getAsDouble()
                 );
@@ -54,6 +58,7 @@ public class HeatmapRenderer {
                         maxDistance,
                         distanceList.parallelStream()
                                 .mapToDouble(Double::doubleValue)
+                                .filter(v -> Double.isFinite(v))
                                 .max()
                                 .getAsDouble()
                 );
@@ -76,8 +81,14 @@ public class HeatmapRenderer {
      * @param minDistance Minimal distance threshold (smaller distances are cut-off)
      * @param maxDistance Maxim distance threshold (bigger distances are cut-off)
      */
-    public void renderMeshFacet(GL2 gl, MeshFacet facet, List<Double> distancesList,
-            List<Double> saturationList, double minDistance, double maxDistance) {
+    public void renderMeshFacet(
+            GL2 gl, 
+            MeshFacet facet, 
+            List<Double> distancesList,
+            List<Double> saturationList, 
+            double minDistance, 
+            double maxDistance) {
+        
         gl.glBegin(GL2.GL_TRIANGLES); //vertices are rendered as triangles
 
         // get the normal and tex coords indicies for face i
@@ -96,7 +107,9 @@ public class HeatmapRenderer {
             //get color of vertex
             double currentDistance = distancesList.get(vertexIndex);
             double currentSaturation = saturationList == null ? 1d : saturationList.get(vertexIndex);
-            Color c = getColor(currentDistance, currentSaturation, minDistance, maxDistance);
+            Color c = Double.isFinite(currentDistance) ? 
+                    getColor(currentDistance, currentSaturation, minDistance, maxDistance) : 
+                    Color.BLACK;
             gl.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE, c.getComponents(null), 0);
 
             gl.glVertex3d(vert.x, vert.y, vert.z);
diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java
index 81599d7b530d280517d00b9738667d2a1651909f..feb0d0d2d9aeecc2198f101d526ac483234f663d 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java
@@ -83,7 +83,7 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe
                 sPlane = 2;
                 recomputeFromFeaturePoints(0);
                 recomputeFromFeaturePoints(1);
-                break;     
+                break;
             default:
                 // do nothing
         }
@@ -187,7 +187,13 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe
         }
         
         face.computeKdTree(false);
-        HausdorffDistance visitor = new HausdorffDistance(face.getKdTree(), Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, true);
+        HausdorffDistance visitor = new HausdorffDistance(
+                face.getKdTree(), 
+                Strategy.POINT_TO_POINT_DISTANCE_ONLY, 
+                false, 
+                true,
+                false
+        );
         clone.compute(visitor);
         controlPanel.updateHausdorffDistanceStats(visitor.getStats(), getScene().getHumanFace(0) == face);
     }
diff --git a/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityApproxHausdorff.java b/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityApproxHausdorff.java
index 32b5bf5b44d4aadc275b7fedf2e06cb2ef093b1c..7cc8988b4220d79f87e5aed318cecbd92856f6d2 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityApproxHausdorff.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityApproxHausdorff.java
@@ -64,7 +64,7 @@ public class BatchSimilarityApproxHausdorff {
             IcpTransformer icp = new IcpTransformer(priFace.getMeshModel(), 50, true, 0.3, new NoUndersampling());
             secFace.getMeshModel().compute(icp, true);
             
-            HausdorffDistance hd = new HausdorffDistance(secFace.getKdTree(), HausdorffDistance.Strategy.POINT_TO_POINT, true, true);
+            HausdorffDistance hd = new HausdorffDistance(secFace.getKdTree(), HausdorffDistance.Strategy.POINT_TO_POINT, true, true, true);
             priFace.getMeshModel().compute(hd, true);
             
             //System.out.println(hd.getDistances().get(priFace.getMeshModel().getFacets().get(0)).size());
diff --git a/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruth.java b/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruth.java
index 85852531a2c7d4875628088ed84550b918393dd3..d6c43c62d5431ca7486f4d681f20e64a746154c9 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruth.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruth.java
@@ -12,18 +12,29 @@ import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.time.Duration;
-import java.util.DoubleSummaryStatistics;
 import java.util.List;
 import java.util.stream.Collectors;
 
 /**
  * A class for testing the efficiency of batch processing algorithms.
  * The goal of this tool is to create a "ground truth" measurements for other optimization techniques
- * 
- * - All pairs are take one by one from the collection
- * - First (primary) face is stored in k-d tree
- * - Secondary face is superimposed using ICP
- * - Hausdorff distance from the secondary to the primary face is computed using POINT_TO_POINT strategy and absolute distances.
+ * <ul>
+ * <li>All pairs of faces are taken one by one from the collection.</li>
+ * <li>ICP registration is performed for each pair, but only in one direction (the second to the first)</li>
+ * <li>Hausdorff distance for each pair is computed in both direction (A-B and B-A). HD uses POINT_TO_POINT strategy and absolute distances.</li>
+ * </ul>
+ * Stats for 100 faces WITH CROP:
+ * <pre>
+ * ICP computation time: 02:27:32,170
+ * HD computation time: 02:08:34,001
+ * Total computation time: 05:29:55,321
+ * </pre>
+ * Stats for 100 faces WITHOUT CROP:
+ * <pre>
+ * ICP computation time: 02:23:06,495
+ * HD computation time: 01:59:55,520
+ * Total computation time: 05:16:46,154
+ * </pre>
  * 
  * @author Radek Oslejsek
  */
@@ -32,6 +43,7 @@ public class BatchSimilarityGroundTruth {
     private static final String DATA_DIR = "../../analyst-data-antropologie/_ECA";
     private static final String OUTPUT_FILE = "../../SIMILARITY_GROUND_TRUTH.csv";
     private static final int MAX_SAMPLES = 100;
+    private static final boolean CROP_HD = false;
     
     /**
      * Main method 
@@ -45,48 +57,78 @@ public class BatchSimilarityGroundTruth {
                 .limit(MAX_SAMPLES)
                 .collect(Collectors.toList());
         
-        BufferedWriter bfw = new BufferedWriter(new FileWriter(OUTPUT_FILE));
-        bfw.write("SEC FACE;PRI FACE;MIN;MAX;AVG (from SEC to PRI);IPC time (mm:ss,mil);HD time (mm:ss,mil)");
-        bfw.newLine();
+        double[][] distances = new double[faces.size()][faces.size()];
+        String[] names = new String[faces.size()];
+        
+        long totalTime = System.currentTimeMillis();
+        long icpComputationTime = 0;
+        long hdComputationTime = 0;
         
-        int counter = 1;
+        int counter = 0;
         for (int i = 0; i < faces.size(); i++) {
             HumanFace priFace = new HumanFace(faces.get(i).toFile());
-            priFace.computeKdTree(false);
+            names[i] = priFace.getShortName().replaceAll("_01_ECA", "");
             
-            for (int j = 0; j < faces.size(); j++) {
-                if (i == j) {
-                    continue;
-                }
-                
+            for (int j = i; j < faces.size(); j++) { // starts with "i"!
                 HumanFace secFace = new HumanFace(faces.get(j).toFile());
                 
-                System.out.println(counter++ + "/" + (faces.size()*faces.size()-faces.size()) + " (" + priFace.getShortName() + "/" + secFace.getShortName() + ")");
+                System.out.println(++counter + ": " + priFace.getShortName() + " - " + secFace.getShortName());
                 
-                long icpTime = System.currentTimeMillis();
-                IcpTransformer icp = new IcpTransformer(priFace.getMeshModel(), 50, true, 0.3, new NoUndersampling());
-                secFace.getMeshModel().compute(icp, true);
-                Duration icpDuration = Duration.ofMillis(System.currentTimeMillis() - icpTime);
+                // transform secondary face
+                if (i != j) { // register only different faces
+                    long icpTime = System.currentTimeMillis();
+                    IcpTransformer icp = new IcpTransformer(priFace.getMeshModel(), 100, false, 0.3, new NoUndersampling());
+                    secFace.getMeshModel().compute(icp, true);
+                    icpComputationTime += System.currentTimeMillis() - icpTime;
+                }
                 
                 long hdTime = System.currentTimeMillis();
-                HausdorffDistance hd = new HausdorffDistance(priFace.getKdTree(), Strategy.POINT_TO_POINT, false, true);
+                // compute HD from secondary to primary:
+                priFace.computeKdTree(true);
+                HausdorffDistance hd = new HausdorffDistance(priFace.getKdTree(), Strategy.POINT_TO_POINT, false, true, CROP_HD);
                 secFace.getMeshModel().compute(hd, true);
-                DoubleSummaryStatistics stats = hd.getStats();
-                Duration hdDuration = Duration.ofMillis(System.currentTimeMillis() - hdTime);
-                
-                bfw.write(secFace.getShortName()+";");
-                bfw.write(priFace.getShortName()+";");
-                bfw.write(String.format("%.20f", stats.getMin())+";");
-                bfw.write(String.format("%.20f", stats.getMax())+";");
-                bfw.write(String.format("%.20f", stats.getAverage())+";");
-                bfw.write(String.format("%02d:%02d,%03d", icpDuration.toMinutesPart(), icpDuration.toSecondsPart(), icpDuration.toMillisPart())+";");
-                bfw.write(String.format("%02d:%02d,%03d", hdDuration.toMinutesPart(), hdDuration.toSecondsPart(), hdDuration.toMillisPart())+"");
-                bfw.newLine();
-                bfw.flush();
+                distances[j][i] = hd.getStats().getAverage();
+                // compute HD from primary to secondary:
+                secFace.computeKdTree(true);
+                hd = new HausdorffDistance(secFace.getKdTree(), Strategy.POINT_TO_POINT, false, true, CROP_HD);
+                priFace.getMeshModel().compute(hd, true);
+                distances[i][j] = hd.getStats().getAverage();
+                hdComputationTime += System.currentTimeMillis() - hdTime;
             }
         }
         
-        bfw.close();        
+        BufferedWriter w = new BufferedWriter(new FileWriter(OUTPUT_FILE));
+        w.write("PRI FACE;SEC FACE;AVG HD from PRI to SEC;AVG HD from SEC to PRI;AVG HD lower; AVG HD higher");
+        w.newLine();
+        for (int i = 0; i < faces.size(); i++) {
+            for (int j = i; j < faces.size(); j++) {
+                w.write(names[i] + ";");
+                w.write(names[j] + ";");
+                w.write(String.format("%.8f", distances[i][j]) + ";");
+                w.write(String.format("%.8f", distances[j][i]) + ";");
+                if (distances[i][j] > distances[j][i]) {
+                    w.write(String.format("%.8f", distances[i][j]) + ";");
+                    w.write(String.format("%.8f", distances[j][i]) + "");
+                } else {
+                    w.write(String.format("%.8f", distances[j][i]) + ";");
+                    w.write(String.format("%.8f", distances[i][j]) + "");
+                }
+                w.newLine();
+            }
+        }
+        w.close();
+        
+        System.out.println();
+        Duration duration = Duration.ofMillis(icpComputationTime);
+        System.out.println("ICP computation time: " + 
+                String.format("%02d:%02d:%02d,%03d", duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(), duration.toMillisPart()));
+        duration = Duration.ofMillis(hdComputationTime);
+        System.out.println("HD computation time: " + 
+                String.format("%02d:%02d:%02d,%03d", duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(), duration.toMillisPart()));
+        duration = Duration.ofMillis(System.currentTimeMillis() - totalTime);
+        System.out.println("Total computation time: " + 
+                String.format("%02d:%02d:%02d,%03d", duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(), duration.toMillisPart()));
+        
     }
 
 }
diff --git a/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruthOpt.java b/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruthOpt.java
new file mode 100644
index 0000000000000000000000000000000000000000..20afd4b44965ae9b6823d1656600eeea615a07db
--- /dev/null
+++ b/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruthOpt.java
@@ -0,0 +1,168 @@
+package cz.fidentis.analyst.tests;
+
+import cz.fidentis.analyst.face.AvgFaceConstructor;
+import cz.fidentis.analyst.face.HumanFace;
+import cz.fidentis.analyst.face.HumanFaceFactory;
+import cz.fidentis.analyst.icp.IcpTransformer;
+import cz.fidentis.analyst.icp.NoUndersampling;
+import cz.fidentis.analyst.mesh.io.MeshObjExporter;
+import cz.fidentis.analyst.visitors.mesh.HausdorffDistance;
+import cz.fidentis.analyst.visitors.mesh.HausdorffDistance.Strategy;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * A class for testing the efficiency of batch processing algorithms.
+ * It works in the same way as {@link BatchSimilarityGroundTruth} with the following changes:
+ * <ul>
+ * <li>All faces are transformed (ICP) to the very first face of the collection. They are not transformed mutually.
+ *   It enables us to compute ICP only once for every face, but with possible loss of precision.
+ *   Moreover, {@code HumanFaceFactory} is used to quickly load/swap faces when used multiple times.</li>
+ * <ul>
+ * Stats for 100 faces WITH CROP:
+ * <pre>
+ * Time of AVG face computation time: 00:00:19,529
+ * ICP computation time: 00:05:19,096
+ * HD computation time: 03:17:29,446
+ * Total computation time: 03:32:30,671
+ * </pre>
+ * Stats for 100 faces WITHOUT CROP:
+ * <pre>
+ * Time of AVG face computation time: 00:00:19,386
+ * ICP computation time: 00:05:39,318
+ * HD computation time: 03:11:49,226
+ * Total computation time: 03:25:50,957
+ * </pre>
+ * 
+ * @author Radek Oslejsek
+ */
+public class BatchSimilarityGroundTruthOpt {
+    
+    private static final String DATA_DIR = "../../analyst-data-antropologie/_ECA";
+    private static final String OUTPUT_FILE = "../../SIMILARITY_GROUND_TRUTH_OPT.csv";
+    private static final String TEMPLATE_FACE_PATH = "../../analyst-data-antropologie/template_face.obj";
+    private static final int MAX_SAMPLES = 100;
+    private static final boolean CROP_HD = false;
+    
+    /**
+     * Main method 
+     * @param args Input arguments 
+     * @throws IOException on IO error
+     */
+    public static void main(String[] args) throws IOException, ClassNotFoundException, Exception {
+        List<Path> faces = Files.list(new File(DATA_DIR).toPath())
+                .filter(f -> f.toString().endsWith(".obj"))
+                .sorted()
+                .limit(MAX_SAMPLES)
+                .collect(Collectors.toList());
+        
+        double[][] distances = new double[faces.size()][faces.size()];
+        String[] names = new String[faces.size()];
+        
+        long totalTime = System.currentTimeMillis();
+        long avgFaceComputationTime = 0;
+        long icpComputationTime = 0;
+        long hdComputationTime = 0;
+        
+        AvgFaceConstructor avgFaceConstructor = new AvgFaceConstructor(new HumanFace(faces.get(0).toFile()).getMeshModel());
+        
+        HumanFaceFactory.instance().setReuseDumpFile(true);
+        HumanFaceFactory.instance().setStrategy(HumanFaceFactory.Strategy.MRU);
+        
+        int counter = 0;
+        for (int i = 0; i < faces.size(); i++) {
+            String priFaceId = HumanFaceFactory.instance().loadFace(faces.get(i).toFile());
+            HumanFace priFace = HumanFaceFactory.instance().getFace(priFaceId);
+            names[i] = priFace.getShortName().replaceAll("_01_ECA", "");
+            
+            for (int j = i; j < faces.size(); j++) { // starts with "i"!
+                priFace = HumanFaceFactory.instance().getFace(priFaceId); // re-read if dumped, at leat update the access time
+                
+                String secFaceId = HumanFaceFactory.instance().loadFace(faces.get(j).toFile());
+                HumanFace secFace = HumanFaceFactory.instance().getFace(secFaceId);
+                
+                System.out.print(++counter + ": " + priFace.getShortName() + " - " + secFace.getShortName());
+                
+                // transform secondary face, but only once. Transformed faces are stored in the HumanFaceFactory! Don't transform the same face
+                if (i == 0 && i != j) { 
+                    System.out.print(", ICP");
+                    long icpTime = System.currentTimeMillis();
+                    IcpTransformer icp = new IcpTransformer(priFace.getMeshModel(), 100, true, 0.3, new NoUndersampling());
+                    secFace.getMeshModel().compute(icp, true);
+                    icpComputationTime += System.currentTimeMillis() - icpTime;
+                }
+                
+                long hdTime = System.currentTimeMillis();
+                // compute HD from secondary to primary:
+                priFace.computeKdTree(true);
+                HausdorffDistance hd = new HausdorffDistance(priFace.getKdTree(), Strategy.POINT_TO_POINT, false, true, CROP_HD);
+                secFace.getMeshModel().compute(hd, true);
+                distances[j][i] = hd.getStats().getAverage();
+                // compute HD from primary to secondary:
+                secFace.computeKdTree(true);
+                hd = new HausdorffDistance(secFace.getKdTree(), Strategy.POINT_TO_POINT, false, true, CROP_HD);
+                priFace.getMeshModel().compute(hd, true);
+                distances[i][j] = hd.getStats().getAverage();
+                hdComputationTime += System.currentTimeMillis() - hdTime;
+                
+                // Compute AVG face. Use each tranfromed face only once. Skip the very first face
+                if (i == 0 && j != 0) { 
+                    System.out.print(", AVG");
+                    long avgFaceTime = System.currentTimeMillis();
+                    priFace.getKdTree().accept(avgFaceConstructor);
+                    avgFaceComputationTime += System.currentTimeMillis() - avgFaceTime;
+                }
+                
+                System.out.println(", " + HumanFaceFactory.instance().toString());
+            }
+            
+            HumanFaceFactory.instance().removeFace(priFaceId); // the face is no longer needed
+        }
+        
+        MeshObjExporter exp = new MeshObjExporter(avgFaceConstructor.getAveragedMeshModel());
+        exp.exportModelToObj(new File(TEMPLATE_FACE_PATH));
+        
+        BufferedWriter w = new BufferedWriter(new FileWriter(OUTPUT_FILE));
+        w.write("PRI FACE;SEC FACE;AVG HD from PRI to SEC;AVG HD from SEC to PRI");
+        w.newLine();
+        for (int i = 0; i < faces.size(); i++) {
+            for (int j = i; j < faces.size(); j++) {
+                w.write(names[i] + ";");
+                w.write(names[j] + ";");
+                w.write(String.format("%.8f", distances[i][j]) + ";");
+                w.write(String.format("%.8f", distances[j][i]) + ";");
+                if (distances[i][j] > distances[j][i]) {
+                    w.write(String.format("%.8f", distances[i][j]) + ";");
+                    w.write(String.format("%.8f", distances[j][i]) + "");
+                } else {
+                    w.write(String.format("%.8f", distances[j][i]) + ";");
+                    w.write(String.format("%.8f", distances[i][j]) + "");
+                }
+                w.newLine();
+            }
+        }
+        w.close();
+        
+        System.out.println();
+        Duration duration = Duration.ofMillis(avgFaceComputationTime);
+        System.out.println("Time of AVG face computation time: " + 
+                String.format("%02d:%02d:%02d,%03d", duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(), duration.toMillisPart()));
+        duration = Duration.ofMillis(icpComputationTime);
+        System.out.println("ICP computation time: " + 
+                String.format("%02d:%02d:%02d,%03d", duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(), duration.toMillisPart()));
+        duration = Duration.ofMillis(hdComputationTime);
+        System.out.println("HD computation time: " + 
+                String.format("%02d:%02d:%02d,%03d", duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(), duration.toMillisPart()));
+        duration = Duration.ofMillis(System.currentTimeMillis() - totalTime);
+        System.out.println("Total computation time: " + 
+                String.format("%02d:%02d:%02d,%03d", duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart(), duration.toMillisPart()));
+    }
+
+}
diff --git a/GUI/src/main/java/cz/fidentis/analyst/tests/EfficiencyTests.java b/GUI/src/main/java/cz/fidentis/analyst/tests/EfficiencyTests.java
index 3d3fc9a06236ade1b8fc77c24f063a650fe6158c..1de0990da9bbac754363edc8dcae0e3b62aa528d 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/tests/EfficiencyTests.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/tests/EfficiencyTests.java
@@ -63,16 +63,16 @@ public class EfficiencyTests {
         
         System.out.println();
         System.out.println("Hausdorff distance sequentially:");
-        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false), printDetails);
-        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, false), printDetails);
-        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false), printDetails);
-        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, false), printDetails);
+        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false, false), printDetails);
+        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, false, false), printDetails);
+        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, false, false), printDetails);
+        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, false, false), printDetails);
 
         System.out.println();
         System.out.println("Hausdorff distance concurrently:");
-        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, true), printDetails);
-        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, true), printDetails);
-        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, true), printDetails);
+        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT_DISTANCE_ONLY, false, true, false), printDetails);
+        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_POINT, relativeDist, true, false), printDetails);
+        testAndPrint(face1, new HausdorffDistance(face2.getMeshModel(), Strategy.POINT_TO_TRIANGLE_APPROXIMATE, relativeDist, true, false), printDetails);
     }
     
     protected static void testAndPrint(HumanFace face, HausdorffDistance vis, boolean printDetails) {
diff --git a/GUI/src/main/resources/cz/fidentis/analyst/distance/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/distance/Bundle.properties
index ca75b48c570b1dec8dc57b115265bbd56de05fec..09e41c8dda811818341d9a4357e25a4bf223b952 100644
--- a/GUI/src/main/resources/cz/fidentis/analyst/distance/Bundle.properties
+++ b/GUI/src/main/resources/cz/fidentis/analyst/distance/Bundle.properties
@@ -12,3 +12,4 @@ DistancePanel.jLabel3.text=Spheres:
 DistancePanel.jPanel2.border.title=Measurements:
 DistancePanel.jLabel4.text=Show heatmap:
 DistancePanel.jLabel5.text=Algorithm options:
+DistancePanel.jCheckBox1.text=crop