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 3782e7dda2093cd855e0878b0635ccf574426bf5..60c2644b97cd410cbbe9c4a3b4657ddd1fba803f 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java
@@ -1,6 +1,8 @@
 package cz.fidentis.analyst.face;
 
 import com.google.common.eventbus.EventBus;
+import cz.fidentis.analyst.feature.FeaturePoint;
+import cz.fidentis.analyst.feature.services.FeaturePointImportExportService;
 import cz.fidentis.analyst.kdtree.KdTree;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
 import cz.fidentis.analyst.mesh.core.MeshModel;
@@ -8,6 +10,7 @@ import cz.fidentis.analyst.mesh.events.MeshEvent;
 import cz.fidentis.analyst.mesh.events.MeshListener;
 import cz.fidentis.analyst.mesh.io.MeshObjLoader;
 import cz.fidentis.analyst.symmetry.Plane;
+import cz.fidentis.analyst.visitors.face.HumanFaceVisitor;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 
@@ -18,6 +21,8 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
 import java.util.ArrayList;
 import java.util.Objects;
 //import org.nustaq.serialization.FSTConfiguration;
@@ -57,6 +62,8 @@ public class HumanFace implements MeshListener, Serializable {
 
     private MeshFacet cuttingPlane;
     
+    private List<FeaturePoint> featurePoints;
+    
     private final transient EventBus eventBus = new EventBus();
     
     private final String id;
@@ -134,7 +141,7 @@ public class HumanFace implements MeshListener, Serializable {
     /**
      *
      * @param plane The new symmetry plane
-     * @param paneFacet The symmetry plane mesh
+     * @param planeFacet The symmetry plane mesh
      */
     public void setSymmetryPlane(Plane plane, MeshFacet planeFacet) {
         this.symmetryPlane = plane;
@@ -169,6 +176,36 @@ public class HumanFace implements MeshListener, Serializable {
         return cuttingPlane;
     }
     
+    /**
+     * 
+     * @param points List of feature points
+     */
+    public void setFeaturePoints(List<FeaturePoint> points) {
+        featurePoints = points;
+    }
+    
+    /**
+     * Reads feature points from a file on the given path.
+     * 
+     * @param path Directory where the file is located
+     * @param fileName Name of the file
+     * @throws IOException on I/O failure
+     */
+    public void loadFeaturePoints(String path, String fileName) throws IOException {
+        featurePoints = FeaturePointImportExportService.importFeaturePoints(path, fileName);
+    }
+    
+    /**
+     * 
+     * @return The face's feature points.
+     */
+    public List<FeaturePoint> getFeaturePoints() {
+        if (featurePoints == null) {
+            return Collections.emptyList();
+        }
+        return Collections.unmodifiableList(featurePoints);
+    }
+    
     /**
      * Returns unique ID of the face.
      * 
@@ -267,6 +304,15 @@ public class HumanFace implements MeshListener, Serializable {
         }
         */
     }
+    
+    /**
+     * Visits this face.
+     * 
+     * @param visitor Visitor
+     */
+    public void accept(HumanFaceVisitor visitor) {
+        visitor.visitHumanFace(this);
+    }
 
     @Override
     public int hashCode() {
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java
new file mode 100644
index 0000000000000000000000000000000000000000..279a64d18280c4d47e298b22a24fe74cdd220e3a
--- /dev/null
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java
@@ -0,0 +1,242 @@
+package cz.fidentis.analyst.visitors.face;
+
+import cz.fidentis.analyst.face.HumanFace;
+import cz.fidentis.analyst.feature.FeaturePoint;
+import cz.fidentis.analyst.feature.FeaturePointType;
+import cz.fidentis.analyst.kdtree.KdTree;
+import cz.fidentis.analyst.mesh.core.MeshFacet;
+import cz.fidentis.analyst.mesh.core.MeshModel;
+import cz.fidentis.analyst.visitors.mesh.HausdorffDistance;
+import cz.fidentis.analyst.visitors.mesh.HausdorffDistance.Strategy;
+import cz.fidentis.analyst.visitors.mesh.PrioritySphere;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.vecmath.Point3d;
+
+/**
+ * Visitor for prioritized Hausdorff distance. 
+ * This visitor is instantiated with a single k-d tree (either given as the input
+ * parameter, or automatically created from the triangular mesh).
+ * When applied to other human faces, it computes prioritized Hausdorff distance from their mesh facets
+ * to the instantiated k-d tree.
+ * <p>
+ * This visitor is thread-safe, i.e., a single instance of the visitor can be used
+ * to inspect multiple human faces simultaneously.
+ * </p>
+ * <p>
+ * The distance is computed either as absolute or relative. Absolute
+ * represents Euclidean distance (all numbers are positive). On the contrary,
+ * relative distance considers orientation of the visited face's facets (determined by its normal vectors)
+ * and produces positive or negative distances depending on whether the primary
+ * mesh is "in front of" or "behind" the given vertex.
+ * </p>
+ * 
+ * @author Daniel Schramm
+ */
+public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
+    
+    private final HausdorffDistance distanceVisitor;
+    
+    private final FeaturePointType featurePointType;
+    
+    private final Map<MeshFacet, List<Double>> priorities = new HashMap<>();
+    
+    /**
+     * Constructor.
+     * 
+     * @param mainFacets Facets to which distance from the visited human face's facets is to be computed.
+     * Must not be {@code null}.
+     * @param featurePoint Feature point according to which the Hausdorff distances will be prioritized
+     * @param strategy Strategy of the computation of distance
+     * @param relativeDistance If {@code true}, then the visitor calculates the relative distances with respect 
+     * to the normal vectors of source facets (normal vectors have to present),
+     * i.e., we can get negative distances.
+     * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
+     * @throws IllegalArgumentException if some parameter is wrong
+     */
+    public HausdorffDistancePrioritized(Set<MeshFacet> mainFacets, FeaturePointType featurePoint, Strategy strategy, boolean relativeDistance, boolean parallel) {
+        distanceVisitor = new HausdorffDistance(mainFacets, strategy, relativeDistance, parallel);
+        featurePointType = featurePoint;
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param mainFacet Primary facet to which distance from the visited human face's facets is to be computed. Must not be {@code null}.
+     * @param featurePoint Feature point according to which the Hausdorff distances will be prioritized
+     * @param strategy Strategy of the computation of distance
+     * @param relativeDistance If {@code true}, then the visitor calculates the relative distances with respect 
+     * to the normal vectors of source facets (normal vectors have to present),
+     * i.e., we can get negative distances.
+     * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
+     * @throws IllegalArgumentException if some parameter is wrong
+     */
+    public HausdorffDistancePrioritized(MeshFacet mainFacet, FeaturePointType featurePoint, Strategy strategy, boolean relativeDistance, boolean parallel) {
+        this(new HashSet<>(Collections.singleton(mainFacet)), featurePoint, strategy, relativeDistance, parallel);
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param mainModel The mesh model with primary facets to which distance from the visited human face's facets is to be computed.
+     * Must not be {@code null} or empty.
+     * @param featurePoint Feature point according to which the Hausdorff distances will be prioritized
+     * @param strategy Strategy of the computation of distance
+     * @param relativeDistance If {@code true}, then the visitor calculates the relative distances with respect 
+     * to the normal vectors of source facets (normal vectors have to present),
+     * i.e., we can get negative distances.
+     * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
+     * @throws IllegalArgumentException if some parameter is wrong
+     */
+    public HausdorffDistancePrioritized(MeshModel mainModel, FeaturePointType featurePoint, Strategy strategy, boolean relativeDistance, boolean parallel) {
+        this(new HashSet<>(mainModel.getFacets()), featurePoint, strategy, relativeDistance, parallel);
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param face Human face to which distance from other human faces is to be computed. Must not be {@code null}.
+     * @param featurePoint Feature point according to which the Hausdorff distances will be prioritized
+     * @param strategy Strategy of the computation of distance
+     * @param relativeDistance If {@code true}, then the visitor calculates the relative distances with respect 
+     * to the normal vectors of source facets (normal vectors have to present),
+     * i.e., we can get negative distances.
+     * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
+     * @throws IllegalArgumentException if some parameter is wrong
+     */
+    public HausdorffDistancePrioritized(HumanFace face, FeaturePointType featurePoint, Strategy strategy, boolean relativeDistance, boolean parallel) {
+        this(face.getMeshModel(), featurePoint, strategy, relativeDistance, parallel);
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param mainKdTree The KD tree to which distance from the visited human face's facets is to be computed.
+     * Must not be {@code null}.
+     * @param featurePoint Feature point according to which the Hausdorff distances will be prioritized
+     * @param strategy Strategy of the computation of distance
+     * @param relativeDistance If {@code true}, then the visitor calculates the relative distances with respect 
+     * to the normal vectors of source facets (normal vectors have to present),
+     * i.e., we can get negative distances.
+     * @param parallel If {@code true}, then the algorithm runs concurrently utilizing all CPU cores
+     * @throws IllegalArgumentException if some parameter is wrong
+     */
+    public HausdorffDistancePrioritized(KdTree mainKdTree, FeaturePointType featurePoint, Strategy strategy, boolean relativeDistance, boolean parallel) {
+        distanceVisitor = new HausdorffDistance(mainKdTree, strategy, relativeDistance, parallel);
+        featurePointType = featurePoint;
+    }
+
+    public FeaturePointType getFeaturePointType() {
+        return featurePointType;
+    }
+    
+    /**
+     * Returns Hausdorff distance of the visited faces' mesh facets to the source mesh facets. 
+     * 
+     * Keys in the map contain mesh facets that were measured with the 
+     * source facets.
+     * For each facet of the visited human face, a list of distances to the source 
+     * facets is stored. The order of distances corresponds to the order of vertices
+     * in the measured facet, i.e., the i-th value is the distance of the i-th vertex
+     * of the visited face's facet.
+     * 
+     * @return Hausdorff distance for all points of all the visited human faces' facets
+     */
+    public Map<MeshFacet, List<Double>> getDistances() {
+        return distanceVisitor.getDistances();
+    }
+    
+    /**
+     * Returns the nearest points of the visited faces' mesh facets to the source mesh facets.
+     * 
+     * Keys in the map contain mesh facets that were measured with the
+     * source facets.
+     * For each facet of the visited human face, a list of the nearest points to the source
+     * facets is stored. The order of  points corresponds to the order of vertices
+     * in the measured facet, i.e., the i-th point is the nearest point of the i-th vertex
+     * of the visited face's facet.
+     *
+     * @return The nearest points for all points of all the visited human faces' facets
+     */
+    public Map<MeshFacet, List<Point3d>> getNearestPoints() {
+        return distanceVisitor.getNearestPoints();
+    }
+
+    /**
+     * Returns priorities of points of the visited faces' mesh facets. 
+     * 
+     * Keys in the map contain mesh facets of the visited human faces.
+     * For each facet of the visited human face, a list of priorities is stored.
+     * The order of priorities corresponds to the order of vertices
+     * in the visited facet, i.e., the i-th value is the priority of the i-th vertex
+     * of the visited face's facet.
+     * 
+     * @return Priorities of all points of all the visited human faces' facets
+     */
+    public Map<MeshFacet, List<Double>> getPriorities() {
+        return Collections.unmodifiableMap(priorities);
+    }
+    
+    /**
+     * Returns {@code true} if the distance was computed as relative 
+     * (with possibly negative values). 
+     * 
+     * @return {@code true} if the distance is computed as relative, {@code false} otherwise
+     */
+    public boolean relativeDistance() {
+        return distanceVisitor.relativeDistance();
+    }
+    
+    /**
+     * Returns the strategy of the computation of distance.
+     * 
+     * @return strategy of the computation of distance
+     */
+    public Strategy getStrategy() {
+        return distanceVisitor.getStrategy();
+    }
+    
+    /**
+     * Returns {@code true} if the distance computation is parallel.
+     * 
+     * @return {@code true} if the distance computation is parallel
+     */
+    public boolean inParallel() {
+        return distanceVisitor.inParallel();
+    }
+    
+    /**
+     * Return the KD tree to which distances are computed.
+     * 
+     * @return KD tree to which distances are computed
+     */
+    public KdTree getMainKdTree() {
+        return distanceVisitor.getMainKdTree();
+    }
+
+    @Override
+    public void visitHumanFace(HumanFace humanFace) {
+        humanFace.getMeshModel().compute(distanceVisitor, inParallel());
+        
+        final FeaturePoint featurePoint = humanFace.getFeaturePoints().get(featurePointType.getType());
+        final PrioritySphere priorityVisitor = new PrioritySphere(featurePoint.getPosition(), computeSphereRadius(humanFace));
+        humanFace.getMeshModel().compute(priorityVisitor, inParallel());
+        
+        synchronized(this) {
+            // Retrieve results from the visitor's internal structure
+            priorities.putAll(priorityVisitor.getPriorities());
+        }
+    }
+    
+    private double computeSphereRadius(HumanFace humanFace) {
+        // TODO TEMPORARY
+        // Sphere radius needs to be computed dynamically.
+        // The best way to compute the right radius should be thought out in more depth.
+        return 1;
+        // TODO TEMPORARY
+    }
+}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HumanFaceVisitor.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HumanFaceVisitor.java
new file mode 100644
index 0000000000000000000000000000000000000000..28789a339a97d4e091f62ceee06c2ad67f41e2a5
--- /dev/null
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HumanFaceVisitor.java
@@ -0,0 +1,42 @@
+package cz.fidentis.analyst.visitors.face;
+
+import cz.fidentis.analyst.face.HumanFace;
+
+/**
+ * A functor. When instantiated, it can be gradually applied to multiple faces.
+ * It inspects the state of the human face one by one, and (cumulatively) computes results.
+ * <p>
+ * Implement this interface whenever you want to define new algorithm over a human face.
+ * </p>
+ * <p>
+ * If the visitor is thread-safe, then a single instance of the visitor
+ * can visit concurrently (and asynchronously) multiple faces. Otherwise, 
+ * the parallel inspection is still possible, but a new instance of the visitor 
+ * has to be used for each human face.
+ * </p>
+ *
+ * @author Daniel Schramm
+ */
+public abstract class HumanFaceVisitor {
+    
+    /**
+     * Returns {@code true} if the implementation is thread-safe and then
+     * <b>a single visitor instance</b> can be applied to multiple faces simultaneously.
+     * <p>
+     * Thread-safe implementation means that any read or write from/to the visitor's 
+     * state is protected by {@code synchronized}.
+     * </p>
+     * 
+     * @return {@code true} if the implementation is thread-safe.
+     */
+    public boolean isThreadSafe() {
+        return true;
+    }
+    
+    /**
+     * The main inspection method to be implemented by specific visitors.
+     * 
+     * @param humanFace Face to be visited
+     */
+    public abstract void visitHumanFace(HumanFace humanFace);
+}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/package-info.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b05c0785db08f5f2926faad96809006428d5763
--- /dev/null
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Visitors used to explore the human face (i.e., implementing the 
+ * {@link cz.fidentis.analyst.visitors.face.HumanFaceVisitor}).
+ */
+package cz.fidentis.analyst.visitors.face;
\ No newline at end of file
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 dad64d39c857534ef702ac825ed04273858bb40c..a1539dd4b459dd1128ae7bf2e08498eac7754ff1 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
@@ -94,8 +94,8 @@ public class KdTreeApproxDistanceToTriangles extends KdTreeVisitor implements Di
                         distance = dist;
                         nearestPoints.clear();                    
                     }
-                    nearestPoints.putIfAbsent(facet, new ArrayList<>());
-                    nearestPoints.get(facet).add(projection);
+                    nearestPoints.computeIfAbsent(facet, meshFacet -> new ArrayList<>())
+                        .add(projection);
                 }
             }
         }
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 c421c48eaa9a6540f99838502fe361b2b317547a..2f8594cba8b8eac151483250077c634ddd230cdc 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
@@ -76,8 +76,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.putIfAbsent(facet, new ArrayList<>());
-                    nearestPoints.get(facet).add(point.getPosition());
+                    nearestPoints.computeIfAbsent(facet, meshFacet -> new ArrayList<>())
+                        .add(point.getPosition());
                 }
             }
         }
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 05f9e6b35370b9ecb6ab845c4cf5444b0dd2233f..7d21ada3ec9721aa9ce7798b0b62b246c0a89bad 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
@@ -39,7 +39,7 @@ import javax.vecmath.Point3d;
  * to inspect multiple meshes simultaneously. 
  * </p>
  * <p>
- * The distance is computer either as absolute or relative. Absolute
+ * The distance is computed either as absolute or relative. Absolute
  * represents Euclidean distance (all numbers are positive). On the contrary, 
  * relative distance considers orientation of the visited mesh (determined by its normal vectors)
  * and produces positive or negative distances depending on whether the primary
@@ -247,6 +247,7 @@ public class HausdorffDistance extends MeshVisitor  {
     public Map<MeshFacet, List<Point3d>> getNearestPoints() {
         return Collections.unmodifiableMap(nearestPoints);
     }
+
     /**
      * Returns {@code true} if the distance was computed as relative 
      * (with possibly negative values). 
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshApproxDistanceToTriangles.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshApproxDistanceToTriangles.java
index 343d9d5dc2fbe419dbaf082725e555f37627f615..6d7fa32e8e536b873bc32a5681fa9bb1c36e8352 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshApproxDistanceToTriangles.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshApproxDistanceToTriangles.java
@@ -101,8 +101,8 @@ public class MeshApproxDistanceToTriangles extends MeshVisitor implements Distan
                     distance = dist;
                     nearestPoints.clear();                    
                 }
-                nearestPoints.putIfAbsent(facet, new ArrayList<>());
-                nearestPoints.get(facet).add(projection);
+                nearestPoints.computeIfAbsent(facet, meshFacet -> new ArrayList<>())
+                        .add(projection);
             }
         }
     }
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshDistanceToTriangles.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshDistanceToTriangles.java
index e806c2980e863f2b6343fc4b605290b4432a6686..fa54f1db8df8de39ea4a916f89c2aa18dbf4beff 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshDistanceToTriangles.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshDistanceToTriangles.java
@@ -65,8 +65,8 @@ public class MeshDistanceToTriangles extends MeshVisitor implements DistanceWith
                     distance = dist;
                     nearestPoints.clear();                    
                 }
-                nearestPoints.putIfAbsent(facet, new ArrayList<>());
-                nearestPoints.get(facet).add(projection);
+                nearestPoints.computeIfAbsent(facet, meshFacet -> new ArrayList<>())
+                        .add(projection);
             }
         }
     }
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshDistanceToVertices.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshDistanceToVertices.java
index 9520075a811a8b26f7bb4fdede76473d8f5d1fa8..cb4f174b0c25709398b8331439ce0e6aa82d2237 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshDistanceToVertices.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/MeshDistanceToVertices.java
@@ -67,8 +67,8 @@ public class MeshDistanceToVertices extends MeshVisitor implements DistanceWithN
                     distance = dist;
                     nearestPoints.clear();                    
                 }
-                nearestPoints.putIfAbsent(facet, new ArrayList<>());
-                nearestPoints.get(facet).add(pointOnSurface);
+                nearestPoints.computeIfAbsent(facet, meshFacet -> new ArrayList<>())
+                        .add(pointOnSurface);
             }
         }
     }
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/PrioritySphere.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/PrioritySphere.java
new file mode 100644
index 0000000000000000000000000000000000000000..533016dbe80513c410bb959404f4cf33d8b1abb4
--- /dev/null
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/PrioritySphere.java
@@ -0,0 +1,85 @@
+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.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.vecmath.Point3d;
+
+/**
+ * This visitor computes priorities of vertices of a mesh facet according to their absolute distance from
+ * the given {@link PrioritySphere#sphereCenterPosition} point.
+ * <ul>
+ *   <li>1 is maximal priority; 0 is minimal priority</li>
+ *   <li>The closer a vertex is to {@link PrioritySphere#sphereCenterPosition}, the higher its priority is.</li>
+ *   <li>Vertices that are further than {@link PrioritySphere#sphereRadius} from {@link PrioritySphere#sphereCenterPosition}
+ *       have priority equal to 0.</li>
+ * </ul>
+ * <p>
+ *   The visitor returns all mesh facets together with priorities of all their vertices
+ * </p>
+ * <p>
+ *   This visitor is thread-safe.
+ * </p>
+ * 
+ * @author Daniel Schramm
+ */
+public class PrioritySphere extends MeshVisitor  {
+    
+    private final Point3d sphereCenterPosition;
+    private final double sphereRadius;
+    
+    private final Map<MeshFacet, List<Double>> priorities = new HashMap<>();
+
+    /**
+     * Constructor.
+     * 
+     * @param sphereCenterPosition Position of the center of the sphere in which
+     *                             the priorities will be calculated
+     * @param sphereRadius Radius of the sphere
+     */
+    public PrioritySphere(Point3d sphereCenterPosition, double sphereRadius) {
+        if (sphereCenterPosition == null) {
+            throw new IllegalArgumentException("sphereCenterPosition");
+        }
+        this.sphereCenterPosition = sphereCenterPosition;
+        this.sphereRadius = sphereRadius;
+    }
+
+    /**
+     * Returns map of visited mesh facets together with a list of priorities of their vertices.
+     * 
+     * The order of priorities corresponds to the order of vertices
+     * in the visited facet, i.e., the i-th priority is a priority of the i-th vertex
+     * of the visited facet.
+     * 
+     * @return map of visited mesh facets and priorities of their vertices
+     */
+    public Map<MeshFacet, List<Double>> getPriorities() {
+        return Collections.unmodifiableMap(priorities);
+    }
+
+    @Override
+    public void visitMeshFacet(MeshFacet facet) {
+        final List<MeshPoint> vertices = facet.getVertices();
+        
+        for (int i = 0; i < vertices.size(); i++) {
+            final double distance = sphereCenterPosition.distance(vertices.get(i).getPosition());
+            final double priority;
+            if (distance > sphereRadius) {
+                priority = 0;
+            } else {
+                priority = 1 - distance / sphereRadius;
+            }
+            
+            synchronized(this) {
+                priorities.computeIfAbsent(facet, meshFacet -> new ArrayList<>())
+                        .add(priority);
+            }
+        }
+    }
+}
diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/PrioritySphereTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/PrioritySphereTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ecf0d7f24a10d35ef8797e2720996fa00f0973e
--- /dev/null
+++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/PrioritySphereTest.java
@@ -0,0 +1,123 @@
+package cz.fidentis.analyst.visitors.mesh;
+
+import cz.fidentis.analyst.mesh.core.CornerTableRow;
+import cz.fidentis.analyst.mesh.core.MeshFacet;
+import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
+import cz.fidentis.analyst.mesh.core.MeshModel;
+import cz.fidentis.analyst.mesh.core.MeshPointImpl;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.DoubleStream;
+import javax.vecmath.Point3d;
+import javax.vecmath.Vector3d;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.junit.jupiter.api.Test;
+
+public class PrioritySphereTest {
+    
+    private static final double DELTA = 1e-15;
+    
+    protected MeshFacet getTrivialFacet(int count, double xSpace, double ySpace, double zSpace, int offset) {
+        final MeshFacet facet = new MeshFacetImpl();
+        for (int i = offset; i < count + offset; i++) {
+            facet.addVertex(new MeshPointImpl(new Point3d(xSpace * i, ySpace * i, zSpace * i),
+                    new Vector3d(0, 0, 1),
+                    new Vector3d()));
+            facet.getCornerTable().addRow(new CornerTableRow(i - offset, -1));
+        }
+
+        return facet;
+    }
+    
+    protected void performTest(Map<MeshFacet, List<Double>> facetsWithExpectedPriorities,
+            PrioritySphere visitor,
+            boolean concurrently) {
+
+        final Set<MeshFacet> facets = facetsWithExpectedPriorities.keySet();
+        
+        MeshModel model = new MeshModel();
+//        model.addFacets(new HashSet<>(facets));
+        for (final MeshFacet facet: facets) {
+            model.addFacet(facet);
+        }
+        
+        model.compute(visitor, concurrently);
+        
+        final Map<MeshFacet, List<Double>> priorities = visitor.getPriorities();
+        assertEquals(facetsWithExpectedPriorities.size(), priorities.size());
+        
+        for (final MeshFacet facet: facets) {
+            assertTrue(priorities.containsKey(facet));
+            
+            final List<Double> expectedPriorities = facetsWithExpectedPriorities.get(facet);
+            final List<Double> actualPriorities = priorities.get(facet);
+            assertEquals(expectedPriorities.size(), actualPriorities.size());
+            
+            for (int i = 0; i < expectedPriorities.size(); i++) {
+                assertEquals(expectedPriorities.get(i), actualPriorities.get(i), DELTA);
+            }
+        }
+    }
+    
+    @Test
+    public void variousPrioritiesTest() {
+        final List<Double> priorities = List.of(1d, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0d, 0d);
+        final Map<MeshFacet, List<Double>> expected = new HashMap<>();
+        expected.put(getTrivialFacet(12, 1, 0, 0, 0), priorities);
+        expected.put(getTrivialFacet(12, -1, 0, 0, 0), priorities);
+        expected.put(getTrivialFacet(12, 0, 1, 0, 0), priorities);
+        expected.put(getTrivialFacet(12, 0, -1, 0, 0), priorities);
+        expected.put(getTrivialFacet(12, 0, 0, 1, 0), priorities);
+        expected.put(getTrivialFacet(12, 0, 0, -1, 0), priorities);
+        
+        final PrioritySphere visitor = new PrioritySphere(new Point3d(0, 0, 0), 10);
+        
+        performTest(expected, visitor, false);
+    }
+    
+    @Test
+    public void allZeroPrioritiesTest() {
+        final Map<MeshFacet, List<Double>> expected = new HashMap<>();
+        expected.put(getTrivialFacet(1, 3, 0, 0, 1), List.of(0d));
+        expected.put(getTrivialFacet(5, 0, 3, 0, 1), List.of(0d, 0d, 0d, 0d, 0d));
+        expected.put(getTrivialFacet(3, -3, -3, -3, 1), List.of(0d, 0d, 0d));
+        
+        final PrioritySphere visitor = new PrioritySphere(new Point3d(0, 0, 0), 3);
+        
+        performTest(expected, visitor, false);
+    }
+    
+    @Test
+    public void noZeroPriorityTest() {
+        final Map<MeshFacet, List<Double>> expected = new HashMap<>();
+        expected.put(getTrivialFacet(1, 10, 0, 0, 1), List.of(0.9));
+        expected.put(getTrivialFacet(5, 0, -20, 0, 0), List.of(1d, 0.8, 0.6, 0.4, 0.2));
+        expected.put(getTrivialFacet(100, 0, 0, -1, 0), DoubleStream.iterate(1, d -> d > 0d, d -> d - 0.01)
+                .boxed()
+                .collect(Collectors.toList()));
+        
+        final PrioritySphere visitor = new PrioritySphere(new Point3d(0, 0, 0), 100);
+        
+        performTest(expected, visitor, false);
+    }
+    
+    @Test
+    public void concurrencyTest() {
+        final List<Double> priorities = List.of(1d, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0d, 0d);
+        final Map<MeshFacet, List<Double>> expected = new HashMap<>();
+        expected.put(getTrivialFacet(12, 1, 0, 0, 0), priorities);
+        expected.put(getTrivialFacet(12, -1, 0, 0, 0), priorities);
+        expected.put(getTrivialFacet(12, 0, 1, 0, 0), priorities);
+        expected.put(getTrivialFacet(12, 0, -1, 0, 0), priorities);
+        expected.put(getTrivialFacet(12, 0, 0, 1, 0), priorities);
+        expected.put(getTrivialFacet(12, 0, 0, -1, 0), priorities);
+        
+        final PrioritySphere visitor = new PrioritySphere(new Point3d(0, 0, 0), 10);
+        
+        performTest(expected, visitor, true);
+    }
+}
diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/feature/FeaturePoint.java b/MeshModel/src/main/java/cz/fidentis/analyst/feature/FeaturePoint.java
index 1b4a6a4c557a650cd041c7a2392afcdb24e56cac..f0c65b632d034e46ee9d2b63b9da458a280cf831 100644
--- a/MeshModel/src/main/java/cz/fidentis/analyst/feature/FeaturePoint.java
+++ b/MeshModel/src/main/java/cz/fidentis/analyst/feature/FeaturePoint.java
@@ -1,38 +1,36 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
 package cz.fidentis.analyst.feature;
 
+import java.io.Serializable;
+import javax.vecmath.Point3d;
+
 /**
  *
  * @author kubok
  */
-public class FeaturePoint {
+public class FeaturePoint implements Serializable {
 
-    private final double X;
-    private final double Y;
-    private final double Z;
+    private final Point3d position;
     private final FeaturePointType FEATURE_POINT_TYPE;
 
     public FeaturePoint(double x, double y, double z, FeaturePointType featurePointType) {
-        this.X = x;
-        this.Y = y;
-        this.Z = z;
+        this.position = new Point3d(x, y, z);
         this.FEATURE_POINT_TYPE = featurePointType;
     }
+    
+    public Point3d getPosition() {
+        return position;
+    }
 
     public double getX() {
-        return X;
+        return position.x;
     }
 
     public double getY() {
-        return Y;
+        return position.y;
     }
 
     public double getZ() {
-        return Z;
+        return position.z;
     }
 
     public FeaturePointType getFeaturePointType() {
diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/feature/FeaturePointType.java b/MeshModel/src/main/java/cz/fidentis/analyst/feature/FeaturePointType.java
index 1c3b71a30c9e030056f02b90e700fc18d11c5fc2..0d4910be79e18499ae9200ea4f5ee86ab35eec35 100644
--- a/MeshModel/src/main/java/cz/fidentis/analyst/feature/FeaturePointType.java
+++ b/MeshModel/src/main/java/cz/fidentis/analyst/feature/FeaturePointType.java
@@ -1,16 +1,15 @@
 package cz.fidentis.analyst.feature;
 
-public class FeaturePointType {
+import java.io.Serializable;
+
+public class FeaturePointType implements Serializable {
+    
     private final int type;
     private final String name;
     private final String info;
     private final String code;
 
-    public FeaturePointType(
-            int type,
-             String name,
-             String info,
-             String code) {
+    public FeaturePointType(int type, String name, String info, String code) {
         this.type = type;
         this.name = name;
         this.info = info;
diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/feature/exception/FeaturePointException.java b/MeshModel/src/main/java/cz/fidentis/analyst/feature/exception/FeaturePointException.java
deleted file mode 100644
index a7330475150a8b3e73349fada2f812ba1c717852..0000000000000000000000000000000000000000
--- a/MeshModel/src/main/java/cz/fidentis/analyst/feature/exception/FeaturePointException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package cz.fidentis.analyst.feature.exception;
-
-/**
- *
- * @author kubok
- */
-public class FeaturePointException extends RuntimeException {
-    
-    public FeaturePointException(String message) {
-        super(message);
-    }
-    
-}
diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/feature/services/FeaturePointImportExportService.java b/MeshModel/src/main/java/cz/fidentis/analyst/feature/services/FeaturePointImportExportService.java
index 8452e8c0966bb81024f955d3ed9206f2e397686d..7fd77ee84e312651047ee1d2310a5639a3ac2757 100644
--- a/MeshModel/src/main/java/cz/fidentis/analyst/feature/services/FeaturePointImportExportService.java
+++ b/MeshModel/src/main/java/cz/fidentis/analyst/feature/services/FeaturePointImportExportService.java
@@ -6,7 +6,6 @@
 package cz.fidentis.analyst.feature.services;
 
 import cz.fidentis.analyst.feature.FeaturePoint;
-import cz.fidentis.analyst.feature.exception.FeaturePointException;
 import cz.fidentis.analyst.feature.provider.FeaturePointTypeProvider;
 import cz.fidentis.analyst.feature.utils.FileResourcesUtils;
 import java.io.BufferedReader;
@@ -15,14 +14,11 @@ import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Optional;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Stream;
@@ -36,7 +32,7 @@ public class FeaturePointImportExportService {
     private static final String COLUMN_DELIMETER = ",";
     private static final String CODE_PREFIX_DELIMETER = " ";
 
-    public Optional<List<FeaturePoint>> importFeaturePoints(String path, String fileName) {
+    public static List<FeaturePoint> importFeaturePoints(String path, String fileName) throws FileNotFoundException, IOException {
         FileResourcesUtils app = new FileResourcesUtils();
         try (InputStreamReader streamReader
                 = new InputStreamReader(app.getFileAsStream(path, fileName), StandardCharsets.UTF_8);
@@ -52,7 +48,7 @@ public class FeaturePointImportExportService {
 
             if (linesList.size() != 2
                     || linesList.get(0).size() != linesList.get(1).size()) {
-                throw new FeaturePointException(String.format("Feature point import file '%s' has wrong format", fileName));
+                throw new IOException(String.format("Feature point import file '%s' has wrong format", fileName));
             }
 
             List<FeaturePoint> points = new ArrayList<>();
@@ -66,20 +62,18 @@ public class FeaturePointImportExportService {
                 );
                 points.add(point);
             }
-            return Optional.of(points);
+            return points;
 
-        } catch (IOException e) {
-            throw new FeaturePointException(String.format("Feature point cannot open file", fileName));
         } catch (NumberFormatException e1) {
-            throw new FeaturePointException(e1.getMessage());
+            throw new IOException(e1);
         }
     }
 
-    private String getCode(String str) {
+    private static String getCode(String str) {
         return str.substring(0, str.indexOf(CODE_PREFIX_DELIMETER));
     }
 
-    public void exportFeaturePoints(List<FeaturePoint> featurePointList, String objectName) throws FileNotFoundException, IOException {
+    public static void exportFeaturePoints(List<FeaturePoint> featurePointList, String objectName) throws FileNotFoundException, IOException {
         File csvOutputFile = new File(String.format("%s_landmarks.csv", objectName));
         // CSV is a normal text file, need a writer
         try (BufferedWriter bw = new BufferedWriter(new FileWriter(csvOutputFile))) {
diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/kdtree/KdTree.java b/MeshModel/src/main/java/cz/fidentis/analyst/kdtree/KdTree.java
index 3de3a67302e7aeebe9e15db3d22d40259f64b20a..cadc0548bfe59b72a6157081163e53e2f00924db 100644
--- a/MeshModel/src/main/java/cz/fidentis/analyst/kdtree/KdTree.java
+++ b/MeshModel/src/main/java/cz/fidentis/analyst/kdtree/KdTree.java
@@ -180,7 +180,7 @@ public class KdTree implements Serializable {
     }
     
     /**
-     * Visits this facet.
+     * Visits this tree.
      * 
      * @param visitor Visitor
      */
diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/kdtree/KdTreeVisitor.java b/MeshModel/src/main/java/cz/fidentis/analyst/kdtree/KdTreeVisitor.java
index ae02da318b51906609b9c054cfcaef8c5fcb7cd6..c478f67a44ff75c5f9c2a9730d50a3ec88347aa4 100644
--- a/MeshModel/src/main/java/cz/fidentis/analyst/kdtree/KdTreeVisitor.java
+++ b/MeshModel/src/main/java/cz/fidentis/analyst/kdtree/KdTreeVisitor.java
@@ -32,7 +32,7 @@ public abstract class KdTreeVisitor {
     }
     
     /**
-     * The main inspection methodto be implemented by specific visitors.
+     * The main inspection method to be implemented by specific visitors.
      * 
      * @param kdTree K-d tree to be visited
      */
diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java
index ab3b39f3c4e3607d4892dc6b9545c6efedf5012e..27694b38cf2debea774354777d07d48b5850942b 100644
--- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java
+++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java
@@ -1,173 +1,173 @@
-package cz.fidentis.analyst.mesh.core;
-
-import com.google.common.eventbus.EventBus;
-import java.util.ArrayList;
-import java.util.List;
-import cz.fidentis.analyst.mesh.MeshVisitor;
-import cz.fidentis.analyst.mesh.events.FacetAddedEvent;
-import java.util.Collections;
-import cz.fidentis.analyst.mesh.events.MeshListener;
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * The main object for triangular meshes. Each mesh model consists  
- * of one or more mesh facets.
- * <p>
- * This class implements the publish-subscribe notifications to changes. 
- * However, the {@code HumanFace} class provides similar
- * functionality at the higher level of abstraction. Therefore, we recommend
- * to monitor this class when you want to be informed about changes in concrete human face.
- * <p>
- * Events fired by the class:
- * <ul>
- * <li>{@link cz.fidentis.analyst.mesh.events.FacetAddedEvent} when new facet is added.</li>
- * </ul>
- * </p>
- * 
- * @author Matej Lukes
- * @author Radek Oslejsek
- */
-public class MeshModel implements Serializable {
-    
-    private final List<MeshFacet> facets = new ArrayList<>();
-    
-    private final transient EventBus eventBus = new EventBus();
-    
-    /**
-     * Constructor of MeshModel
-     */
-    public MeshModel() {
-
-    }
-
-    /**
-     * Copy constructor of MeshModel
-     *
-     * @param meshModel copied MeshModel
-     */
-    public MeshModel(MeshModel meshModel) {
-        for (MeshFacet facet: meshModel.facets) {
-            facets.add(new MeshFacetImpl(facet));
-        }
-    }
-
-    /**
-     * returns list of MeshFacets
-     *
-     * @return list of MeshFacets
-     */
-    public List<MeshFacet> getFacets() {
-        return Collections.unmodifiableList(facets);
-    }
-
-    /**
-     * Adds a new mesh facet to the model. 
-     * Fires {@link cz.fidentis.analyst.mesh.events.FacetAddedEvent} to
-     * registered listeners.
-     *
-     * @param facet new MeshFacet
-     */
-    public void addFacet(MeshFacet facet) {
-        facets.add(facet);
-        eventBus.post(new FacetAddedEvent(facet));
-    }
-    
-    /**
-     * Adds a new mesh facets to the model. 
-     * Fires {@link cz.fidentis.analyst.mesh.events.FacetAddedEvent} to
-     * registered listeners.
-     *
-     * @param facet new MeshFacet
-     */
-    public void addFacets(Collection<MeshFacet> facets) {
-        facets.addAll(facets);
-        for (MeshFacet f: facets) {
-            eventBus.post(new FacetAddedEvent(f));
-        }
-    }
-    
-    /**
-     * Applies the visitor to all mesh facets. If the visitor is thread-safe
-     * and the {@code concurrently} is {@code true}, then the visitor is
-     * applied to all mesh facet concurrently using all CPU cores.
-     * 
-     * @param visitor Visitor to be applied for the computation
-     * @param concurrently Parallel computation
-     * @throws NullPointerException if the visitor is {@code null}
-     */
-    public void compute(MeshVisitor visitor, boolean concurrently) {
-        if (concurrently && visitor.isThreadSafe()) {
-            ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
-            
-            for (MeshFacet f: this.facets) {
-                Runnable worker = new Runnable() {
-                    @Override
-                    public void run() {
-                        f.accept(visitor);
-                    }
-                };
-                executor.execute(worker);
-            }
-            
-            // Wait until all symmetry planes are computed:
-            executor.shutdown();
-            while (!executor.isTerminated()){}
-        } else {
-            for (MeshFacet f: this.facets) {
-                f.accept(visitor);
-            }
-        }
-    }
-    
-    /**
-     * Applies the visitor to all mesh facets sequentially.
-     * 
-     * @param visitor Visitor to be applied for the computation
-     * @throws NullPointerException if the visitor is {@code null}
-     */
-    public void compute(MeshVisitor visitor) {
-        compute(visitor, false);
-    }
-    
-    /**
-     * Registers listeners (objects concerned in the mesh model changes) to receive events.
-     * If listener is {@code null}, no exception is thrown and no action is taken.
-     * 
-     * @param listener Listener concerned in the mesh model changes.
-     */
-    public void registerListener(MeshListener listener) {
-        eventBus.register(listener);
-    }
-    
-    /**
-     * Unregisters listeners from receiving events.
-     * 
-     * @param listener Registered listener
-     */
-    public void unregisterListener(MeshListener listener) {
-        eventBus.unregister(listener);
-    }
-    
-    /**
-     * Removes duplicate vertices that differ only in normal vectors or texture coordinates.
-     * Multiple normals are replaced with the average normal. If the texture coordinate 
-     * differ then randomly selected one is used.
-     */
-    public void simplifyModel() {
-        for (MeshFacet f : this.facets) {
-            f.simplify();
-        }
-    }
-    
-    @Override
-    public String toString() {
-        int verts = 0;
-        for (MeshFacet f: facets) {
-            verts += f.getNumberOfVertices();
-        }
-        return facets.size() + " facets with " + verts + " vertices";
-    }
-}
+package cz.fidentis.analyst.mesh.core;
+
+import com.google.common.eventbus.EventBus;
+import java.util.ArrayList;
+import java.util.List;
+import cz.fidentis.analyst.mesh.MeshVisitor;
+import cz.fidentis.analyst.mesh.events.FacetAddedEvent;
+import java.util.Collections;
+import cz.fidentis.analyst.mesh.events.MeshListener;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * The main object for triangular meshes. Each mesh model consists  
+ * of one or more mesh facets.
+ * <p>
+ * This class implements the publish-subscribe notifications to changes. 
+ * However, the {@code HumanFace} class provides similar
+ * functionality at the higher level of abstraction. Therefore, we recommend
+ * to monitor this class when you want to be informed about changes in concrete human face.
+ * <p>
+ * Events fired by the class:
+ * <ul>
+ * <li>{@link cz.fidentis.analyst.mesh.events.FacetAddedEvent} when new facet is added.</li>
+ * </ul>
+ * </p>
+ * 
+ * @author Matej Lukes
+ * @author Radek Oslejsek
+ */
+public class MeshModel implements Serializable {
+    
+    private final List<MeshFacet> facets = new ArrayList<>();
+    
+    private final transient EventBus eventBus = new EventBus();
+    
+    /**
+     * Constructor of MeshModel
+     */
+    public MeshModel() {
+
+    }
+
+    /**
+     * Copy constructor of MeshModel
+     *
+     * @param meshModel copied MeshModel
+     */
+    public MeshModel(MeshModel meshModel) {
+        for (MeshFacet facet: meshModel.facets) {
+            facets.add(new MeshFacetImpl(facet));
+        }
+    }
+
+    /**
+     * returns list of MeshFacets
+     *
+     * @return list of MeshFacets
+     */
+    public List<MeshFacet> getFacets() {
+        return Collections.unmodifiableList(facets);
+    }
+
+    /**
+     * Adds a new mesh facet to the model. 
+     * Fires {@link cz.fidentis.analyst.mesh.events.FacetAddedEvent} to
+     * registered listeners.
+     *
+     * @param facet new MeshFacet
+     */
+    public void addFacet(MeshFacet facet) {
+        facets.add(facet);
+        eventBus.post(new FacetAddedEvent(facet));
+    }
+    
+    /**
+     * Adds a new mesh facets to the model. 
+     * Fires {@link cz.fidentis.analyst.mesh.events.FacetAddedEvent} to
+     * registered listeners.
+     *
+     * @param newFacets collection of new new facets
+     */
+    public void addFacets(Collection<MeshFacet> newFacets) {
+        facets.addAll(newFacets);
+        for (MeshFacet f: newFacets) {
+            eventBus.post(new FacetAddedEvent(f));
+        }
+    }
+    
+    /**
+     * Applies the visitor to all mesh facets. If the visitor is thread-safe
+     * and the {@code concurrently} is {@code true}, then the visitor is
+     * applied to all mesh facet concurrently using all CPU cores.
+     * 
+     * @param visitor Visitor to be applied for the computation
+     * @param concurrently Parallel computation
+     * @throws NullPointerException if the visitor is {@code null}
+     */
+    public void compute(MeshVisitor visitor, boolean concurrently) {
+        if (concurrently && visitor.isThreadSafe()) {
+            ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+            
+            for (MeshFacet f: this.facets) {
+                Runnable worker = new Runnable() {
+                    @Override
+                    public void run() {
+                        f.accept(visitor);
+                    }
+                };
+                executor.execute(worker);
+            }
+            
+            // Wait until all symmetry planes are computed:
+            executor.shutdown();
+            while (!executor.isTerminated()){}
+        } else {
+            for (MeshFacet f: this.facets) {
+                f.accept(visitor);
+            }
+        }
+    }
+    
+    /**
+     * Applies the visitor to all mesh facets sequentially.
+     * 
+     * @param visitor Visitor to be applied for the computation
+     * @throws NullPointerException if the visitor is {@code null}
+     */
+    public void compute(MeshVisitor visitor) {
+        compute(visitor, false);
+    }
+    
+    /**
+     * Registers listeners (objects concerned in the mesh model changes) to receive events.
+     * If listener is {@code null}, no exception is thrown and no action is taken.
+     * 
+     * @param listener Listener concerned in the mesh model changes.
+     */
+    public void registerListener(MeshListener listener) {
+        eventBus.register(listener);
+    }
+    
+    /**
+     * Unregisters listeners from receiving events.
+     * 
+     * @param listener Registered listener
+     */
+    public void unregisterListener(MeshListener listener) {
+        eventBus.unregister(listener);
+    }
+    
+    /**
+     * Removes duplicate vertices that differ only in normal vectors or texture coordinates.
+     * Multiple normals are replaced with the average normal. If the texture coordinate 
+     * differ then randomly selected one is used.
+     */
+    public void simplifyModel() {
+        for (MeshFacet f : this.facets) {
+            f.simplify();
+        }
+    }
+    
+    @Override
+    public String toString() {
+        int verts = 0;
+        for (MeshFacet f: facets) {
+            verts += f.getNumberOfVertices();
+        }
+        return facets.size() + " facets with " + verts + " vertices";
+    }
+}
diff --git a/MeshModel/src/test/java/cz/fidentis/analyst/feature/services/FeaturePointImportExportServiceTest.java b/MeshModel/src/test/java/cz/fidentis/analyst/feature/services/FeaturePointImportExportServiceTest.java
index 5cc52b9af135739f8a61eba0aeda60c7af29bd46..7368ce54abad2f7f1e5a4444385a61bd5bcaa44b 100644
--- a/MeshModel/src/test/java/cz/fidentis/analyst/feature/services/FeaturePointImportExportServiceTest.java
+++ b/MeshModel/src/test/java/cz/fidentis/analyst/feature/services/FeaturePointImportExportServiceTest.java
@@ -1,20 +1,12 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
 package cz.fidentis.analyst.feature.services;
 
 import cz.fidentis.analyst.feature.FeaturePoint;
 import cz.fidentis.analyst.feature.provider.FeaturePointTypeProvider;
-import cz.fidentis.analyst.mesh.io.MeshObjLoader;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.List;
-import java.util.Optional;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.DisplayName;
@@ -37,14 +29,14 @@ public class FeaturePointImportExportServiceTest {
     
     @DisplayName("Test loading a CSV file")
     @Test
-    void importFeaturePointsTest() {
-        Optional<List<FeaturePoint>> featurePoints = loadFeaturePoints();
-        assertTrue(featurePoints.isPresent());
-        assertTrue(featurePoints.get().size() > 0);
-        assertTrue(featurePoints.get().get(0).getX() == -45.3298
-                && featurePoints.get().get(0).getY() == 37.1466
-                && featurePoints.get().get(0).getZ() == -40.5415
-                && featurePoints.get().get(0).getFeaturePointType().equals(
+    void importFeaturePointsTest() throws IOException {
+        List<FeaturePoint> featurePoints = loadFeaturePoints();
+        assertTrue(featurePoints != null);
+        assertTrue(featurePoints.size() > 0);
+        assertTrue(featurePoints.get(0).getX() == -45.3298
+                && featurePoints.get(0).getY() == 37.1466
+                && featurePoints.get(0).getZ() == -40.5415
+                && featurePoints.get(0).getFeaturePointType().equals(
                         FeaturePointTypeProvider.getInstance().getFeaturePointTypeByCode("EX_R"))
         );
     }
@@ -52,15 +44,12 @@ public class FeaturePointImportExportServiceTest {
    @DisplayName("Test writing a CSV file")
     @Test
     void exportFeaturePointsTest() throws IOException {
-        FeaturePointImportExportService service = new FeaturePointImportExportService();
-        Optional<List<FeaturePoint>> featurePoints = loadFeaturePoints();
-        service.exportFeaturePoints(featurePoints.get(), "test_file");
+        List<FeaturePoint> featurePoints = loadFeaturePoints();
+        FeaturePointImportExportService.exportFeaturePoints(featurePoints, "test_file");
     }
 
-    private Optional<List<FeaturePoint>> loadFeaturePoints() {
-        FeaturePointImportExportService service = new FeaturePointImportExportService();
-        Optional<List<FeaturePoint>> featurePoints = service.importFeaturePoints(testFileDirectory.toString(), TEST_CSV_FILE);
+    private List<FeaturePoint> loadFeaturePoints() throws IOException {
+        List<FeaturePoint> featurePoints = FeaturePointImportExportService.importFeaturePoints(testFileDirectory.toString(), TEST_CSV_FILE);
         return featurePoints;
-
     }
 }