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 302d52a76af9d079aee0572b74fda3803c87ae4d..5ea502d615f0670151b61b810d9073f5c4ce6f47 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java
@@ -13,6 +13,7 @@ import cz.fidentis.analyst.symmetry.Plane;
 import cz.fidentis.analyst.visitors.face.HumanFaceVisitor;
 import cz.fidentis.analyst.visitors.mesh.BoundingBox;
 import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox;
+import cz.fidentis.analyst.visitors.mesh.Curvature;
 import java.awt.image.BufferedImage;
 
 import java.io.File;
@@ -63,6 +64,8 @@ public class HumanFace implements Serializable {
     
     private transient BufferedImage preview;
     
+    private transient Curvature curvature;
+    
     /**
      * Fast (de)serialization handler
      */
@@ -254,6 +257,19 @@ public class HumanFace implements Serializable {
     public BBox getBoundingBox() {
         return bbox;
     }
+    
+    /**
+     * Computes and returns curvature.
+     * 
+     * @return curvature
+     */
+    public Curvature getCurvature() {
+        if (this.curvature == null) {
+            this.curvature = new Curvature();
+            getMeshModel().compute(curvature);
+        }
+        return this.curvature;
+    }
 
     /**
      * 
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceUtils.java b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceUtils.java
index 700fff542062a5103b1e6dd93f22b2c15f1c95e8..bb51d592eb23831313ed582424d27de414a5df0d 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceUtils.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFaceUtils.java
@@ -3,11 +3,10 @@ package cz.fidentis.analyst.face;
 import cz.fidentis.analyst.Logger;
 import cz.fidentis.analyst.feature.FeaturePoint;
 import cz.fidentis.analyst.icp.IcpTransformer;
-import cz.fidentis.analyst.icp.NoUndersampling;
 import cz.fidentis.analyst.icp.Quaternion;
-import cz.fidentis.analyst.icp.RandomStrategy;
 import cz.fidentis.analyst.procrustes.ProcrustesAnalysis;
 import cz.fidentis.analyst.symmetry.Plane;
+import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling;
 import java.util.zip.DataFormatException;
 import javax.vecmath.Matrix3d;
 import javax.vecmath.Matrix4d;
@@ -33,14 +32,15 @@ public class HumanFaceUtils {
      * Reasonable number seems to be 10.
      * @param scale Whether to scale face as well
      * @param error Acceptable error (a number bigger than or equal to zero).
-     * @param undersampling 100 = no undersampling, 10 = undersampling into 10% of vertices.
+     * @param samplingStrategy Downsampling strategy
      * @param recomputeKdTree If {@code true} and the k-d tree of the {@code transformedFace} exists, 
      *        the it automatically re-computed. Otherwise, it is simply removed.
      * @return ICP visitor that holds the transformations performed on the {@code transformedFace}.
      */
     public static IcpTransformer alignMeshes(
             HumanFace staticFace, HumanFace transformedFace,
-            int maxIterations, boolean scale, double error, int undersampling,
+            int maxIterations, boolean scale, double error, 
+            PointSampling samplingStrategy,
             boolean recomputeKdTree) {
         
         // transform mesh:
@@ -49,7 +49,7 @@ public class HumanFaceUtils {
                 maxIterations, 
                 scale, 
                 error, 
-                (undersampling == 100) ? new NoUndersampling() : new RandomStrategy(undersampling)
+                samplingStrategy
         );
         transformedFace.getMeshModel().compute(icp, true); // superimpose face towards the static face
 
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 bd9431a4b9eabbcbe79896e11feecbc51f5e9d82..f58f58107cec3f4e03588b0acd2989b06f3e4708 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/IcpTransformer.java
@@ -7,6 +7,8 @@ import cz.fidentis.analyst.mesh.core.MeshFacet;
 import cz.fidentis.analyst.mesh.core.MeshModel;
 import cz.fidentis.analyst.mesh.core.MeshPoint;
 import cz.fidentis.analyst.visitors.mesh.HausdorffDistance;
+import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling;
+import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -77,7 +79,7 @@ public class IcpTransformer extends MeshVisitor   {
      */
     private final KdTree primaryKdTree;
     
-    private final UndersamplingStrategy reductionStrategy;
+    private final PointSampling samplingStrategy;
     
     /**
      * Constructor.
@@ -93,7 +95,7 @@ public class IcpTransformer extends MeshVisitor   {
      * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used.
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public IcpTransformer(MeshFacet mainFacet, int maxIteration, boolean scale, double error, UndersamplingStrategy strategy) {
+    public IcpTransformer(MeshFacet mainFacet, int maxIteration, boolean scale, double error, PointSampling strategy) {
         this(new HashSet<>(Collections.singleton(mainFacet)), maxIteration, scale, error, strategy);
         if (mainFacet == null) {
             throw new IllegalArgumentException("mainFacet");
@@ -114,7 +116,7 @@ public class IcpTransformer extends MeshVisitor   {
      * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used.
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public IcpTransformer(Set<MeshFacet> mainFacets, int maxIteration, boolean scale, double error, UndersamplingStrategy strategy) {
+    public IcpTransformer(Set<MeshFacet> mainFacets, int maxIteration, boolean scale, double error, PointSampling strategy) {
         if (mainFacets == null) {
             throw new IllegalArgumentException("mainFacets");
         }
@@ -128,7 +130,7 @@ public class IcpTransformer extends MeshVisitor   {
         this.error = error;
         this.maxIteration = maxIteration;
         this.scale = scale;
-        this.reductionStrategy = (strategy == null) ? new NoUndersampling() : strategy;
+        this.samplingStrategy = (strategy == null) ? new NoSampling() : strategy;
     }
 
     /**
@@ -145,7 +147,7 @@ public class IcpTransformer extends MeshVisitor   {
      * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used.
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public IcpTransformer(MeshModel mainModel, int maxIteration, boolean scale, double error, UndersamplingStrategy strategy) {
+    public IcpTransformer(MeshModel mainModel, int maxIteration, boolean scale, double error, PointSampling strategy) {
         this(new HashSet<>(mainModel.getFacets()), maxIteration, scale, error, strategy);
         if (mainModel.getFacets().isEmpty()) {
             throw new IllegalArgumentException("mainModel");
@@ -165,7 +167,7 @@ public class IcpTransformer extends MeshVisitor   {
      * @param strategy One of the reduction strategies. If {@code null}, then {@link NoUndersampling} is used.
      * @throws IllegalArgumentException if some parameter is wrong
      */
-    public IcpTransformer(KdTree primaryKdTree, int maxIteration, boolean scale, double error, UndersamplingStrategy strategy) {
+    public IcpTransformer(KdTree primaryKdTree, int maxIteration, boolean scale, double error, PointSampling strategy) {
         if (primaryKdTree == null) {
             throw new IllegalArgumentException("primaryKdTree");
         }
@@ -179,7 +181,7 @@ public class IcpTransformer extends MeshVisitor   {
         this.error = error;
         this.maxIteration = maxIteration;
         this.scale = scale;
-        this.reductionStrategy = (strategy == null) ? new NoUndersampling() : strategy;
+        this.samplingStrategy = (strategy == null) ? new NoSampling() : strategy;
     }
     
     /**
@@ -257,7 +259,7 @@ public class IcpTransformer extends MeshVisitor   {
                 false  // auto cut
         );
         
-        MeshFacet reducedFacet = new UndersampledMeshFacet(transformedFacet, reductionStrategy);
+        MeshFacet reducedFacet = new UndersampledMeshFacet(transformedFacet, samplingStrategy);
 
         int currentIteration = 0;
         IcpTransformation transformation = null;
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/NoUndersampling.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/NoUndersampling.java
deleted file mode 100644
index c32c1109bc3c5e427ba912f61b40105ab1c89556..0000000000000000000000000000000000000000
--- a/Comparison/src/main/java/cz/fidentis/analyst/icp/NoUndersampling.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package cz.fidentis.analyst.icp;
-
-import cz.fidentis.analyst.mesh.core.MeshPoint;
-import java.util.List;
-
-/**
- * No undersampling. The triangular mesh keeps unchanged.
- * 
- * @author Radek Oslejsek
- */
-public class NoUndersampling extends UndersamplingStrategy {
-    
-    @Override
-    public List<MeshPoint> reduceMeshVertices(List<MeshPoint> meshPoints) {
-        return meshPoints;
-    }
- 
-    @Override
-    public String toString() {
-        return "no undersampling";
-    }
-}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/RandomStrategy.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/RandomStrategy.java
deleted file mode 100644
index 8e7899ad7fa03967c97a68cad331bc96fec8b802..0000000000000000000000000000000000000000
--- a/Comparison/src/main/java/cz/fidentis/analyst/icp/RandomStrategy.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package cz.fidentis.analyst.icp;
-
-import cz.fidentis.analyst.mesh.core.MeshPoint;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-/**
- * Random undersampling when the mesh vertices to be removed are selected randomly.
- * 
- * @author Radek Oslejsek
- */
-public class RandomStrategy extends UndersamplingStrategy {
-    
-    /**
-     * Constructor for PERCENTAGE undersampling type.
-     * 
-     * @param perc Percentage - a value in (0.0, 1.0&gt;
-     * @throws IllegalArgumentException if the input parameter is wrong
-     */
-    public RandomStrategy(double perc) {
-        super(perc);
-    }
-    
-    /**
-     * Constructor for PERCENTAGE undersampling type.
-     * 
-     * @param max Maximal number of vertices. Must be bigger than zero
-     * @throws IllegalArgumentException if the input parameter is wrong
-     */
-    public RandomStrategy(int max) {
-        super(max);
-    }
-
-    @Override
-    public List<MeshPoint> reduceMeshVertices(List<MeshPoint> meshPoints) {
-        if (meshPoints == null) {
-            return null;
-        }
-        
-        int numVertices = getNumUndersampledVertices(meshPoints.size());
-        
-        if (meshPoints.size() == numVertices) {
-            return meshPoints;
-        }
-        
-        // generate randomly ordered indexes:
-        List<Integer> range = IntStream.range(0, meshPoints.size()).boxed().collect(Collectors.toCollection(ArrayList::new));
-        Collections.shuffle(range);
-        
-        if (numVertices < meshPoints.size() / 2) { // copy indices
-            MeshPoint[] array = new MeshPoint[meshPoints.size()];
-            range.stream().limit(numVertices).forEach(
-                    i -> array[i] = meshPoints.get(i)
-            );
-            return Arrays.stream(array).filter(
-                    p -> p != null
-            ).collect(Collectors.<MeshPoint>toList());
-        } else { // remove indices
-            List<MeshPoint> copy = new ArrayList<>(meshPoints);
-            range.stream().limit(meshPoints.size() - numVertices).forEach(
-                    i -> copy.set(i, null)
-            );
-            return copy.parallelStream().filter(
-                    p -> p != null
-            ).collect(Collectors.<MeshPoint>toList());
-        }
-    }
-    
-    @Override
-    public String toString() {
-        return "random " + super.toString();
-    }
-}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersampledMeshFacet.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersampledMeshFacet.java
index c7b947ec8e9f404582c80094a096ccf09d88348f..588c5ef6b0d7a84d602a7d58ee2ea802c8fd246a 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersampledMeshFacet.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersampledMeshFacet.java
@@ -5,6 +5,7 @@ import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
 import cz.fidentis.analyst.mesh.core.MeshPoint;
 import cz.fidentis.analyst.mesh.core.MeshTriangle;
 import cz.fidentis.analyst.mesh.core.TriangleFan;
+import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -27,7 +28,7 @@ public class UndersampledMeshFacet extends MeshFacetImpl {
      * @param origFacet Original facet
      * @param strategy Undersampling strategy
      */
-    public UndersampledMeshFacet(MeshFacet origFacet, UndersamplingStrategy strategy) {
+    public UndersampledMeshFacet(MeshFacet origFacet, PointSampling strategy) {
         if (origFacet == null) {
             throw new IllegalArgumentException("origFacet");
         }
@@ -36,7 +37,8 @@ public class UndersampledMeshFacet extends MeshFacetImpl {
             throw new IllegalArgumentException("strategy");
         }
         
-        this.reducedVertices = strategy.reduceMeshVertices(origFacet.getVertices());
+        strategy.visitMeshFacet(origFacet);
+        this.reducedVertices = strategy.getSamples();
     }
     
     @Override
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersamplingStrategy.java b/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersamplingStrategy.java
deleted file mode 100644
index ed812252691a242071e65aefbaf28fa7bdd66b07..0000000000000000000000000000000000000000
--- a/Comparison/src/main/java/cz/fidentis/analyst/icp/UndersamplingStrategy.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package cz.fidentis.analyst.icp;
-
-import cz.fidentis.analyst.mesh.core.MeshPoint;
-import java.util.List;
-
-/**
- * Undersampling strategies used to reduce the size of triangle meshes and then 
- * accelerate ICP computation.
- * 
- * @author Radek Oslejsek
- */
-public abstract class UndersamplingStrategy {
-    
-    /**
-     * If undersamoling is used, then this configuration parameter 
-     * encodes whether the reduction of mesh size is expressed as the percentage 
-     * of the original size (number of vertices) or as the maximal number of vertices
-     * to be used.
-     * 
-     * @author Radek Oslejsek
-     */
-    public enum UndersamplingType {
-        PERCENTAGE,
-        MAX_VERTICES
-    };
-    
-    private final UndersamplingType undersamplingType;
-    private final double undersamplingLimit;
-    
-    /** 
-     * Constructor for no undersampling.
-     */
-    public UndersamplingStrategy() {
-        this.undersamplingType = null;
-        this.undersamplingLimit = 0.0;
-    }
-    
-    /**
-     * Constructor for PERCENTAGE undersampling type.
-     * 
-     * @param perc Percentage - a value in (0.0, 1.0&gt;
-     * @throws IllegalArgumentException if the input parameter is wrong
-     */
-    public UndersamplingStrategy(double perc) {
-        if (perc <= 0.0 || perc > 1) {
-            throw new IllegalArgumentException("perc");
-        }
-        this.undersamplingType = UndersamplingType.PERCENTAGE;
-        this.undersamplingLimit = perc;
-    }
-    
-    /**
-     * Constructor for PERCENTAGE undersampling type.
-     * 
-     * @param max Maximal number of vertices. Must be bigger than zero
-     * @throws IllegalArgumentException if the input parameter is wrong
-     */
-    public UndersamplingStrategy(int max) {
-        if (max <= 0) {
-            throw new IllegalArgumentException("max");
-        }
-        this.undersamplingType = UndersamplingType.MAX_VERTICES;
-        this.undersamplingLimit = max;
-    }
-    
-    /**
-     * Reduces mesh points. Returned {@code MeshPoints} are backed by 
-     * original mesh points.
-     * 
-     * @param meshPoints Original list of mesh points
-     * @return Reduced list of mesh points or {@code null}.
-     */
-    public abstract List<MeshPoint> reduceMeshVertices(List<MeshPoint> meshPoints);
-    
-    @Override
-    public String toString() {
-        if (this.undersamplingType == UndersamplingType.PERCENTAGE) {
-            return "undersampling to " + (undersamplingLimit * 100) + "%";
-        } else {
-            return "undersampling to " + undersamplingLimit + " vertices";
-        }
-    }
-    
-    /**
-     * Returns number of vertices to be returned after undersampling.
-     * 
-     * @param origVertices Original number of vertices
-     * @return number of vertices to be returned after undersampling.
-     */
-    protected int getNumUndersampledVertices(int origVertices) {
-        switch (this.undersamplingType) {
-            case PERCENTAGE:
-                return (int) (origVertices * this.undersamplingLimit);
-            case MAX_VERTICES:
-                //nt limit = (int) this.undersamplingLimit;
-                //return (limit <= origVertices) ? limit : origVertices;
-                return Math.min((int) this.undersamplingLimit, origVertices);
-            default:
-                return 0;
-        }
-    }
-}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/ApproxSymmetryPlane.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/ApproxSymmetryPlane.java
index 48228ce7c7f06b0495946cb1ce94882685034250..9a14fccc528c8de416eb087abce6646ef6e00bc0 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/ApproxSymmetryPlane.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/ApproxSymmetryPlane.java
@@ -1,7 +1,7 @@
 package cz.fidentis.analyst.symmetry;
 
-import cz.fidentis.analyst.mesh.core.MeshFacet;
 import cz.fidentis.analyst.mesh.core.MeshPoint;
+import java.util.List;
 import javax.vecmath.Vector3d;
 
 /**
@@ -17,19 +17,20 @@ public class ApproxSymmetryPlane extends Plane implements Comparable<ApproxSymme
     /**
      * Constructor.
      * 
-     * @param sigPoints Mesh vertices with the most significant curvature
+     * @param vertices Mesh vertices 
+     * @param cache Precomputed values form mesh vertices
      * @param config Symmetry plane configuration
      * @param i index of the first significant point used for the plane construction
      * @param j index of the second significant point used for the plane construction
      * @param maxDist Distance limit
      * @throws UnsupportedOperationException if the symmetry plane cannot be created
      */
-    public ApproxSymmetryPlane(SignificantPoints sigPoints, SymmetryConfig config, int i, int j, double maxDist) throws UnsupportedOperationException {
+    public ApproxSymmetryPlane(List<MeshPoint> vertices, SymmetryCache cache, SymmetryConfig config, int i, int j, double maxDist) throws UnsupportedOperationException {
         if (i == j) {
             throw new UnsupportedOperationException();
         }
-        setNormAndDist(sigPoints, config, i, j);
-        computeVotes(sigPoints, config, maxDist);
+        setNormAndDist(vertices, cache, config, i, j);
+        computeVotes(vertices, cache, config, maxDist);
     }
 
     /**
@@ -41,19 +42,15 @@ public class ApproxSymmetryPlane extends Plane implements Comparable<ApproxSymme
         return votes;
     }
     
-    private void setNormAndDist(SignificantPoints sigPoints, SymmetryConfig config, int i, int j) {
-        MeshFacet facetI = sigPoints.getMeshFacet(i);
-        MeshFacet facetJ = sigPoints.getMeshFacet(j);
-        int sigPointI = sigPoints.getVertexIndex(i);
-        int sigPointJ = sigPoints.getVertexIndex(j);
-        MeshPoint meshPointI = facetI.getVertex(sigPointI);
-        MeshPoint meshPointJ = facetJ.getVertex(sigPointJ);
+    private void setNormAndDist(List<MeshPoint> vertices, SymmetryCache cache, SymmetryConfig config, int i, int j) {
+        MeshPoint meshPointI = vertices.get(i);
+        MeshPoint meshPointJ = vertices.get(j);
                 
         // accpet only point pairs with significantly different curvatures (WTF?)
         double minRatio = config.getMinCurvRatio();
         double maxRatio = 1.0 / minRatio;
-        double ratioIJ = sigPoints.getCachedCurRatio(i, j);
-        if (ratioIJ < minRatio || ratioIJ > maxRatio) {
+        double ratioIJ = cache.getCurRatio(i, j);
+        if (Double.isFinite(ratioIJ) && (ratioIJ < minRatio || ratioIJ > maxRatio)) {
             throw new UnsupportedOperationException();
         }
                 
@@ -64,12 +61,12 @@ public class ApproxSymmetryPlane extends Plane implements Comparable<ApproxSymme
         normal.normalize(); 
         
         // accpect only point pair with oposite normals along with the plane normal:
-        double normCos = sigPoints.getCachedNormCosVec(i, j).dot(normal);
+        double normCos = cache.getNormCosVec(i, j).dot(normal);
         if (Math.abs(normCos) < config.getMinNormAngleCos()) {
             throw new UnsupportedOperationException();
         }
         
-        setDistance(-normal.dot(sigPoints.getCachedAvgPos(i, j))); 
+        setDistance(-normal.dot(cache.getAvgPos(i, j))); 
         setNormal(normal);
     }
 
@@ -80,41 +77,38 @@ public class ApproxSymmetryPlane extends Plane implements Comparable<ApproxSymme
      * @param config Symmetry plane configuration
      * @param maxDist Distance limit
      */
-    private void computeVotes(SignificantPoints sigPoints, SymmetryConfig config, double maxDist) {
+    private void computeVotes(List<MeshPoint> vertices, SymmetryCache cache, SymmetryConfig config, double maxDist) {
         normalize();
         Vector3d normal = getNormal();
         double d = getDistance();
         double maxCurvRatio = 1.0 / config.getMinCurvRatio();
         
-        for (int i = 0; i < sigPoints.size(); i++) {
-            for (int j = 0; j < sigPoints.size(); j++) {
+        for (int i = 0; i < vertices.size(); i++) {
+            for (int j = 0; j < vertices.size(); j++) {
                 if (i == j) {
                     continue;
                 }
                 
-                MeshFacet facetI = sigPoints.getMeshFacet(i);
-                MeshFacet facetJ = sigPoints.getMeshFacet(j);
-                
-                double ratioIJ = sigPoints.getCachedCurRatio(i, j);
-                if (ratioIJ < config.getMinCurvRatio() || ratioIJ > maxCurvRatio) {
+                double ratioIJ = cache.getCurRatio(i, j);
+                if (Double.isFinite(ratioIJ) && (ratioIJ < config.getMinCurvRatio() || ratioIJ > maxCurvRatio)) {
                     continue;
                 }
                 
-                double normCos = sigPoints.getCachedNormCosVec(i, j).dot(normal);
+                double normCos = cache.getNormCosVec(i, j).dot(normal);
                 if (Math.abs(normCos) < config.getMinNormAngleCos()) {
                     continue;
                 }
                 
                 // Caching this part doesn't improve efficiency anymore:
-                Vector3d vec = new Vector3d(facetI.getVertex(sigPoints.getVertexIndex(i)).getPosition());
-                vec.sub(facetJ.getVertex(sigPoints.getVertexIndex(j)).getPosition());
+                Vector3d vec = new Vector3d(vertices.get(i).getPosition());
+                vec.sub(vertices.get(j).getPosition());
                 vec.normalize();
                 double cos = vec.dot(normal);
                 if (Math.abs(cos) < config.getMinAngleCos()) {
                     continue;
                 }
                 
-                Vector3d avg = sigPoints.getCachedAvgPos(i, j);
+                Vector3d avg = cache.getAvgPos(i, j);
                 double dist = Math.abs(normal.dot(avg) + d);
                 if (dist <= maxDist) {                 
                     votes++;  
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/CurvatureAlg.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/CurvatureAlg.java
deleted file mode 100644
index 64eabced06338550c08014ce6b4ca0dae8b862a3..0000000000000000000000000000000000000000
--- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/CurvatureAlg.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package cz.fidentis.analyst.symmetry;
-
-/**
- * Curvature algorithm used for the selection of the top X significant points.
- * 
- * @author Radek Oslejsek
- */
-public enum CurvatureAlg {
-    MEAN,
-    GAUSSIAN,
-    MAX,
-    MIN
-}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SignificantPoints.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SignificantPoints.java
deleted file mode 100644
index 1aa8bceb697b75e943b69606835622c254c809b5..0000000000000000000000000000000000000000
--- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SignificantPoints.java
+++ /dev/null
@@ -1,309 +0,0 @@
-package cz.fidentis.analyst.symmetry;
-
-import cz.fidentis.analyst.mesh.MeshVisitor;
-import cz.fidentis.analyst.mesh.core.MeshFacet;
-import cz.fidentis.analyst.mesh.core.MeshPoint;
-import cz.fidentis.analyst.visitors.mesh.Curvature;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.PriorityQueue;
-import javax.vecmath.Vector3d;
-
-/**
- * Visitor selecting X most significant vertices of inspected mesh facets. 
- * The most significant vertices are those having the highest curvature.
- * <p>
- * This visitor <b>is not thread-safe</b> for the efficiency reasons, i.e., 
- * a single instance of the visitor cannot be used to inspect multiple meshes 
- * simultaneously (sequential inspection is okay).
- * </p>
- * 
- * @author Radek Oslejsek
- * @author Natalia Bebjakova
- */
-public class SignificantPoints extends MeshVisitor {
-    
-    private final int maxPoints;
-    
-    private final Curvature curvatureVisitor;
-    
-    private final CurvatureAlg curvatureAlg;
-    
-    private List<VertCurvature> significantPoints;
-    
-    private List<List<Vector3d>> normCosVecCache;
-    
-    private List<List<Vector3d>> avgPosCache;
-    
-    private List<List<Double>> curRatioCache;
-    
-    /**
-     * Constructor.
-     * 
-     * @param cAlg Curvature strategy to be used for the selection of significant points.
-     * @param max Maximal number of significant points
-     * @throws IllegalArgumentException if the {@code curbatureVisitor} is missing
-     */
-    public SignificantPoints(CurvatureAlg cAlg, int max) {
-        this(new Curvature(), cAlg, max);
-    }
-    
-    /**
-     * Constructor.
-     * 
-     * @param curvatureVisitor Curvature. If the object has
-     * pre-filled curvatures of meshes, then they are used also used for the computation
-     * of significant points. In this way, it is possible to reuse already computed
-     * curvatures and skip calling the {@link #visitMeshFacet} inspection method.
-     * @param cAlg Curvature strategy to be used for the selection of significant points.
-     * @param max Maximal number of significant points
-     * @throws IllegalArgumentException if the {@code curbatureVisitor} is missing
-     */
-    public SignificantPoints(Curvature curvatureVisitor, CurvatureAlg cAlg, int max) {
-        if (curvatureVisitor == null) {
-            throw new IllegalArgumentException("curvatureVisitor");
-        }
-        this.maxPoints = max;
-        this.curvatureVisitor = curvatureVisitor;
-        this.curvatureAlg = cAlg;
-    }
-    
-    @Override
-    public boolean isThreadSafe() {
-        return false;
-    }
-    
-    @Override
-    public void visitMeshFacet(MeshFacet facet) {
-        significantPoints = null; // clear previous results
-        curvatureVisitor.visitMeshFacet(facet); // compute curvature for new inspected facet
-    }
-    
-    /**
-     * Returns cached normal vector for cos.
-     * @param i index of the i-th significant curvature point
-     * @param j index of the j-th significant curvature point
-     * @return cached value or null
-     */
-    public Vector3d getCachedNormCosVec(int i, int j) {
-        checkAndComputeSignificantPoints();
-        if (normCosVecCache == null || i >= normCosVecCache.size() || i >= normCosVecCache.size() || i < 0 || j < 0) {
-            return null;
-        }
-        return normCosVecCache.get(i).get(j);
-    }
-    
-    /**
-     * Returns cached vector for the computation average normal.
-     * @param i index of the i-th significant curvature point
-     * @param j index of the j-th significant curvature point
-     * @return cached value or null
-     */
-    public Vector3d getCachedAvgPos(int i, int j) {
-        checkAndComputeSignificantPoints();
-        if (avgPosCache == null || i >= avgPosCache.size() || i >= avgPosCache.size() || i < 0 || j < 0) {
-            return null;
-        }
-        return avgPosCache.get(i).get(j);
-    }
-    
-    /**
-     * Returns cached curvature ratio.
-     * @param i index of the i-th significant curvature point
-     * @param j index of the j-th significant curvature point
-     * @return cached value or {@code Double.NaN}
-     */
-    public double getCachedCurRatio(int i, int j) {
-        checkAndComputeSignificantPoints();
-        if (curRatioCache == null || i >= curRatioCache.size() || i >= curRatioCache.size() || i < 0 || j < 0) {
-            return Double.NaN;
-        }
-        return curRatioCache.get(i).get(j);
-    }
-    
-    /**
-     * Returns index of the i-th most significant mesh vertex.
-     * 
-     * @param i The "i" (index of the ordering)
-     * @return index of the i-th most significant mesh vertex.
-     * @throws IllegalArgumentException if the {@code i} parameter is out of rage
-     */
-    public int getVertexIndex(int i) {
-        checkAndComputeSignificantPoints();
-        if (i < 0 || i >= significantPoints.size()) {
-            throw new IllegalArgumentException("i");
-        }
-        return significantPoints.get(i).vertIndex;
-    }
-    
-    /**
-     * Returns curvature of the i-th most significant mesh vertex.
-     * 
-     * @param i The "i" (index of the ordering)
-     * @return curvature of the i-th most significant mesh vertex.
-     * @throws IllegalArgumentException if the {@code i} parameter is out of rage
-     */
-    public double getCurvature(int i) {
-        checkAndComputeSignificantPoints();
-        if (i < 0 || i >= significantPoints.size()) {
-            throw new IllegalArgumentException("i");
-        }
-        return significantPoints.get(i).curvature;
-    }
-    
-    /**
-     * Returns mesh facet of the i-th most significant mesh vertex.
-     * 
-     * @param i The "i" (index of the ordering)
-     * @return mesh facet of the i-th most significant mesh vertex.
-     * @throws IllegalArgumentException if the {@code i} parameter is out of rage
-     */
-    public MeshFacet getMeshFacet(int i) {
-        checkAndComputeSignificantPoints();
-        if (i < 0 || i >= significantPoints.size()) {
-            throw new IllegalArgumentException("i");
-        }
-        return significantPoints.get(i).facet;
-    }
-    
-    /**
-     * Returns number of the most significant points.
-     * @return number of the most significant points.
-     */
-    public int size() {
-        checkAndComputeSignificantPoints();
-        return significantPoints.size();
-    }
-    
-    protected void checkAndComputeSignificantPoints() {
-        if (significantPoints != null) {
-            return; // already computed
-        }
-        
-        // fill the priority queue
-        final PriorityQueue<VertCurvature> priorityQueue = new PriorityQueue<>();
-        
-        Map<MeshFacet, List<Double>> curvMap = null;
-        switch (curvatureAlg) {
-            case MEAN: 
-                curvMap = curvatureVisitor.getMeanCurvatures();
-                break;
-            case GAUSSIAN: 
-                curvMap = curvatureVisitor.getGaussianCurvatures();
-                break;
-            case MAX: 
-                curvMap = curvatureVisitor.getMaxPrincipalCurvatures();
-                break;
-            case MIN: 
-                curvMap = curvatureVisitor.getMinPrincipalCurvatures();
-                break;
-            default:
-                throw new IllegalArgumentException("curvatureAlg");
-        }
-
-        for (MeshFacet facet: curvMap.keySet()) {
-            List<Double> curvs = curvMap.get(facet);
-            for (int i = 0; i < curvs.size(); i++) { 
-                // store helper objects, replace NaN with 0.0:
-                double c = curvs.get(i);
-                priorityQueue.add(new VertCurvature(facet, i, (Double.isNaN(c) ? 0.0 : c)));
-            }  
-        }
-        
-        // select top significant points
-        significantPoints = new ArrayList<>(maxPoints);
-        for (int i = 0; i < maxPoints; i++) {
-            VertCurvature vc = priorityQueue.poll();
-            if (vc == null) {
-                break; // no more points available
-            } else {
-                significantPoints.add(vc);
-            }
-        }
-        
-        // finally, precompute and cache data of the significant points
-        cacheData();
-    }
-    
-    /**
-     * Pre-computes and caches some values related to the computation of
-     * asymmetric plane from curvature.
-     */
-    protected void cacheData() {
-        normCosVecCache = new ArrayList<>(significantPoints.size());
-        avgPosCache = new ArrayList<>(significantPoints.size());
-        curRatioCache = new ArrayList<>(significantPoints.size());
-        
-        for (int i = 0; i < significantPoints.size(); i++) {
-            List<Vector3d> cosArray = new ArrayList<>(significantPoints.size());
-            normCosVecCache.add(cosArray);
-            
-            List<Vector3d> posArray = new ArrayList<>(significantPoints.size());
-            avgPosCache.add(posArray);
-            
-            List<Double> curArray = new ArrayList<>(significantPoints.size());
-            curRatioCache.add(curArray);
-            
-            for (int j = 0; j < significantPoints.size(); j++) {
-                VertCurvature vcI = significantPoints.get(i);
-                VertCurvature vcJ = significantPoints.get(j);
-                MeshPoint meshPointI = vcI.facet.getVertex(vcI.vertIndex);
-                MeshPoint meshPointJ = vcJ.facet.getVertex(vcJ.vertIndex);
-                
-                Vector3d ni = new Vector3d(meshPointI.getNormal());
-                Vector3d nj = new Vector3d(meshPointJ.getNormal());
-                ni.normalize();
-                nj.normalize();
-                ni.sub(nj);
-                ni.normalize();
-                cosArray.add(ni);
-                
-                Vector3d avrg = new Vector3d(meshPointI.getPosition());
-                Vector3d aux = new Vector3d(meshPointJ.getPosition());
-                avrg.add(aux);
-                avrg.scale(0.5);
-                posArray.add(avrg);
-                curArray.add(vcI.curvature / vcJ.curvature);
-            }
-        }
-    }
-        
-    /**
-     * Helper class for sorting points with respect to their curvature.
-     * @author Radek Oslejsek
-     */
-    private class VertCurvature implements Comparable<VertCurvature> {
-        
-        private final int vertIndex;
-        private final double curvature;
-        private final MeshFacet facet;
-        
-        VertCurvature(MeshFacet facet, int vertIndex, double curvature) {
-            this.vertIndex = vertIndex;
-            this.curvature = curvature;
-            this.facet = facet;
-        }
-
-        /**
-         * Compares this object with the specified object for order. 
-         * Curvature is taken into consideration as the primary value (descendant ordering).
-         * If the curvature equals, then the objects are sorted randomly (1 is always returned).
-         * This approach preserves all points in sorted collections.
-         * 
-         * @param arg the object to be compared.
-         * @return a negative integer, zero, or a positive integer as this object 
-         * is less than, equal to, or greater than the specified object.
-         */
-        @Override
-        public int compareTo(VertCurvature arg) {
-            int comp = Double.compare(arg.curvature, this.curvature);
-            return (comp == 0) ? 1 : comp;
-        }
-        
-        public String toString() {
-            return ""+curvature;
-        }
-    }
-    
-}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryCache.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..54ba355815ea4cd7fc6371ce9a8c06fca9bc406b
--- /dev/null
+++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryCache.java
@@ -0,0 +1,110 @@
+package cz.fidentis.analyst.symmetry;
+
+import cz.fidentis.analyst.mesh.core.MeshPoint;
+import java.util.ArrayList;
+import java.util.List;
+import javax.vecmath.Vector3d;
+
+/**
+ * Precomputed values for the symmetry plane estimation.
+ * 
+ * @author Radek Oslejsek
+ */
+public class SymmetryCache {
+    
+    private List<List<Vector3d>> normCosVecCache;
+    private List<List<Vector3d>> avgPosCache;
+    private List<List<Double>> curRatioCache;
+    
+    /**
+     * Constructor.
+     * 
+     * @param vertices Mesh vertices 
+     * @param curvatures Curvatures in the vertices
+     */
+    public SymmetryCache(List<MeshPoint> vertices, List<Double> curvatures) {
+        if (vertices == null || vertices.isEmpty()) {
+            throw new IllegalArgumentException("points");
+        }
+        
+        normCosVecCache = new ArrayList<>(vertices.size());
+        avgPosCache = new ArrayList<>(vertices.size());
+        curRatioCache = (curvatures == null) ? null : new ArrayList<>(vertices.size());
+        
+        for (int i = 0; i < vertices.size(); i++) {
+            List<Vector3d> cosArray = new ArrayList<>(vertices.size());
+            normCosVecCache.add(cosArray);
+            
+            List<Vector3d> posArray = new ArrayList<>(vertices.size());
+            avgPosCache.add(posArray);
+            
+            List<Double> curArray = null;
+            if (curvatures != null) {
+                curArray = new ArrayList<>(vertices.size());
+                curRatioCache.add(curArray);
+            }
+            
+            for (int j = 0; j < vertices.size(); j++) {
+                MeshPoint meshPointI = vertices.get(i);
+                MeshPoint meshPointJ = vertices.get(j);
+                
+                Vector3d ni = new Vector3d(meshPointI.getNormal());
+                Vector3d nj = new Vector3d(meshPointJ.getNormal());
+                ni.normalize();
+                nj.normalize();
+                ni.sub(nj);
+                ni.normalize();
+                cosArray.add(ni);
+                
+                Vector3d avrg = new Vector3d(meshPointI.getPosition());
+                Vector3d aux = new Vector3d(meshPointJ.getPosition());
+                avrg.add(aux);
+                avrg.scale(0.5);
+                posArray.add(avrg);
+                if (curvatures != null) {
+                    curArray.add(curvatures.get(i) / curvatures.get(j));
+                }
+            }
+        }
+    }
+    
+    /**
+     * Returns cached normal vector for cos.
+     * @param i index of the i-th significant curvature point
+     * @param j index of the j-th significant curvature point
+     * @return cached value or null
+     */
+    public Vector3d getNormCosVec(int i, int j) {
+        if (normCosVecCache == null || i >= normCosVecCache.size() || i >= normCosVecCache.size() || i < 0 || j < 0) {
+            return null;
+        }
+        return normCosVecCache.get(i).get(j);
+    }
+    
+    /**
+     * Returns cached vector for the computation average normal.
+     * @param i index of the i-th significant curvature point
+     * @param j index of the j-th significant curvature point
+     * @return cached value or null
+     */
+    public Vector3d getAvgPos(int i, int j) {
+        if (avgPosCache == null || i >= avgPosCache.size() || i >= avgPosCache.size() || i < 0 || j < 0) {
+            return null;
+        }
+        return avgPosCache.get(i).get(j);
+    }
+    
+    /**
+     * Returns cached curvature ratio.
+     * @param i index of the i-th significant curvature point
+     * @param j index of the j-th significant curvature point
+     * @return cached value or {@code Double.NaN}
+     */
+    public double getCurRatio(int i, int j) {
+        if (curRatioCache == null || i >= curRatioCache.size() || i >= curRatioCache.size() || i < 0 || j < 0) {
+            return Double.NaN;
+        }
+        return curRatioCache.get(i).get(j);
+    }
+    
+}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryConfig.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryConfig.java
index 1b730fda3682f6657c14c03e297d1fb25b405000..827ff9817774aafe2478118fdf1e507908bc74f9 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryConfig.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryConfig.java
@@ -1,5 +1,7 @@
 package cz.fidentis.analyst.symmetry;
 
+import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling;
+
 /**
  * Representation of configuration for symmetry estimate.
  * Default numbers are given due to the best results on tested data.
@@ -16,7 +18,7 @@ public class SymmetryConfig {
     private static final double DEFAULT_MAX_REL_DISTANCE = 1.0 / 100.0;
     private static final int DEFAULT_SIGNIFICANT_POINT_COUNT = 200;
     private static final boolean DEFAULT_AVERAGING = true;
-    private static final CurvatureAlg DEFAULT_CURVATURE_ALGORITHM = CurvatureAlg.GAUSSIAN;
+    private static final CurvatureSampling.CurvatureAlg DEFAULT_CURVATURE_ALGORITHM = CurvatureSampling.CurvatureAlg.GAUSSIAN;
     
     private double minCurvRatio;
     private double minAngleCos;
@@ -24,7 +26,7 @@ public class SymmetryConfig {
     private double maxRelDistance;
     private int significantPointCount;
     private boolean averaging;
-    private CurvatureAlg curvatureAlg;
+    private CurvatureSampling.CurvatureAlg curvatureAlg;
     
     /**
      * Creates configuration with default values 
@@ -179,7 +181,7 @@ public class SymmetryConfig {
      * Returns curvature algorithm.
      * @return curvature algorithm 
      */
-    public CurvatureAlg getCurvatureAlg() {
+    public CurvatureSampling.CurvatureAlg getCurvatureAlg() {
         return this.curvatureAlg;
     }
     
@@ -187,7 +189,7 @@ public class SymmetryConfig {
      * Sets  curvature algorithm.
      * @param alg curvature algorithm
      */
-    public void setCurvatureAlg(CurvatureAlg alg) {
+    public void setCurvatureAlg(CurvatureSampling.CurvatureAlg alg) {
         this.curvatureAlg = alg;
     }
 
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java
index 7431560852e8ee3f643b0df08a16a4484cefc801..7988d530adc807bd1dbd37f0859b23fa897c4466 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java
@@ -2,8 +2,11 @@ package cz.fidentis.analyst.symmetry;
 
 import cz.fidentis.analyst.mesh.MeshVisitor;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
+import cz.fidentis.analyst.mesh.core.MeshPoint;
 import cz.fidentis.analyst.visitors.mesh.BoundingBox;
 import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox;
+import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling;
+import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Callable;
@@ -33,7 +36,7 @@ import java.util.logging.Level;
 public class SymmetryEstimator extends MeshVisitor {
     
     private final SymmetryConfig config;
-    private final SignificantPoints sigPointsVisitor;
+    private final PointSampling samplingStrategy;
     private final BoundingBox bbVisitor = new BoundingBox();
     
     private Plane symmetryPlane;
@@ -42,14 +45,18 @@ public class SymmetryEstimator extends MeshVisitor {
      * Constructor.
      * 
      * @param config Algorithm options
+     * @param samplingStrategy Downsampling strategy. Must not be {@code null}
      * @throws IllegalArgumentException if some input parameter is missing
      */
-    public SymmetryEstimator(SymmetryConfig config) {
+    public SymmetryEstimator(SymmetryConfig config, PointSampling samplingStrategy) {
         if (config == null) {
             throw new IllegalArgumentException("config");
         }
+        if (samplingStrategy == null) {
+            throw new IllegalArgumentException("samplingStrategy");
+        }
         this.config = config;
-        this.sigPointsVisitor = new SignificantPoints(config.getCurvatureAlg(), config.getSignificantPointCount());
+        this.samplingStrategy = samplingStrategy;
     }
     
     @Override
@@ -65,7 +72,7 @@ public class SymmetryEstimator extends MeshVisitor {
                 facet.calculateVertexNormals();
             }
         }
-        sigPointsVisitor.visitMeshFacet(facet);
+        samplingStrategy.visitMeshFacet(facet);
         bbVisitor.visitMeshFacet(facet);
     }
     
@@ -109,16 +116,24 @@ public class SymmetryEstimator extends MeshVisitor {
         final List<Plane> planes = new ArrayList<>();
         final double maxDistance = bbVisitor.getBoundingBox().getDiagonalLength() * config.getMaxRelDistance();
         
+        List<MeshPoint> sigVertices = samplingStrategy.getSamples();
+        SymmetryCache cache = new SymmetryCache(
+                sigVertices, 
+                (samplingStrategy.getClass() == CurvatureSampling.class) 
+                        ? ((CurvatureSampling)samplingStrategy).getSampledCurvatures()
+                        : null
+        );
+        
         /*
          * Sequential computation
          */
         if (!concurrently) {
             int maxVotes = 0;
-            for (int i = 0; i < sigPointsVisitor.size(); i++) {
-                for (int j = 0; j < sigPointsVisitor.size(); j++) {
+            for (int i = 0; i < sigVertices.size(); i++) {
+                for (int j = 0; j < sigVertices.size(); j++) {
                     ApproxSymmetryPlane newPlane;
                     try {
-                        newPlane = new ApproxSymmetryPlane(sigPointsVisitor, config, i, j, maxDistance);
+                        newPlane = new ApproxSymmetryPlane(sigVertices, cache, config, i, j, maxDistance);
                     } catch(UnsupportedOperationException ex) {
                         continue;
                     }
@@ -135,11 +150,11 @@ public class SymmetryEstimator extends MeshVisitor {
         
         // Initiate structure for concurrent computation:
         ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
-        final List<Future<ApproxSymmetryPlane>> results = new ArrayList<>(sigPointsVisitor.size()*sigPointsVisitor.size());
+        final List<Future<ApproxSymmetryPlane>> results = new ArrayList<>(sigVertices.size() * sigVertices.size());
         
         // Compute candidate planes concurrently:
-        for (int i = 0; i < sigPointsVisitor.size(); i++) {
-            for (int j = 0; j < sigPointsVisitor.size(); j++) {
+        for (int i = 0; i < sigVertices.size(); i++) {
+            for (int j = 0; j < sigVertices.size(); j++) {
                 int finalI = i;
                 int finalJ = j;
                 
@@ -147,7 +162,7 @@ public class SymmetryEstimator extends MeshVisitor {
                     @Override
                     public ApproxSymmetryPlane call() throws Exception {
                         try {
-                            return new ApproxSymmetryPlane(sigPointsVisitor, config, finalI, finalJ, maxDistance);
+                            return new ApproxSymmetryPlane(sigVertices, cache, config, finalI, finalJ, maxDistance);
                         } catch(UnsupportedOperationException ex) {
                             return null;
                         }
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java
index 98850ff527713a1112961f2c3b45664fc8934fd3..722e0917bdc4a7f9e9c6f14692951b05b493bcb8 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/HausdorffDistancePrioritized.java
@@ -234,7 +234,7 @@ public class HausdorffDistancePrioritized extends HumanFaceVisitor  {
     
     @Override
     public void visitHumanFace(HumanFace humanFace) {
-        // Compute the Hasudorff distance using the 'distanceVisitor', but only once
+        // Compute the (standard) Hasudorff distance using the 'distanceVisitor', but only once
         if (!distanceVisitor.getDistances().keySet().containsAll(humanFace.getMeshModel().getFacets())) {
             humanFace.getMeshModel().compute(distanceVisitor, inParallel());
         }
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/WeightedAverageCollector.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/WeightedAverageCollector.java
index 1ad08cd93a043d476c5e6f6101f8cca9d4c2317c..046912fc7a325e296448a941d3c112acfad7629b 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/WeightedAverageCollector.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/face/WeightedAverageCollector.java
@@ -143,10 +143,14 @@ class IntemediateResults {
     }
 
     public void addWeightedValSum(double weightedValSum) {
-        this.weightedValSum += weightedValSum;
+        if (Double.isFinite(weightedValSum)) {
+            this.weightedValSum += weightedValSum;
+        }
     }
 
     public void addWeightSum(double weightSum) {
-        this.weightSum += weightSum;
+        if (Double.isFinite(weightSum)) {
+            this.weightSum += weightSum;
+        }
     }
 }
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/CurvatureSampling.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/CurvatureSampling.java
new file mode 100644
index 0000000000000000000000000000000000000000..b66c4133af250e7ef54c2e534feae4ceced64f27
--- /dev/null
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/CurvatureSampling.java
@@ -0,0 +1,231 @@
+package cz.fidentis.analyst.visitors.mesh.sampling;
+
+import cz.fidentis.analyst.mesh.core.MeshFacet;
+import cz.fidentis.analyst.mesh.core.MeshPoint;
+import cz.fidentis.analyst.visitors.mesh.Curvature;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.stream.Collectors;
+
+/**
+ * A relevance-based point sampling using highest curvature.
+ * {@see https://graphics.stanford.edu/papers/zipper/zipper.pdf}
+ * 
+ * <p>
+ * This visitor <b>is not thread-safe</b> for the efficiency reasons, i.e., 
+ * a single instance of the visitor cannot be used to inspect multiple meshes 
+ * simultaneously (sequential inspection is okay).
+ * </p>
+ * 
+ * @author Radek Oslejsek
+ * @author Natalia Bebjakova
+ */
+public class CurvatureSampling extends PointSampling {
+    
+    /**
+     * Curvature algorithm used for the selection of the top X significant points.
+     * 
+     * @author Radek Oslejsek
+     */
+    public enum CurvatureAlg {
+        MEAN,
+        GAUSSIAN,
+        MAX,
+        MIN
+    }
+
+    //private final int maxPoints;
+    
+    private final Curvature curvatureVisitor;
+    
+    private final CurvatureAlg curvatureAlg;
+    
+    private List<VertCurvature> significantPoints;
+    
+    /**
+     * Constructor.
+     * 
+     * @param cAlg Curvature strategy to be used for the selection of significant points.
+     * @param max Maximal number of significant points
+     * @throws IllegalArgumentException if the {@code curbatureVisitor} is missing
+     */
+    public CurvatureSampling(CurvatureAlg cAlg, int max) {
+        this(new Curvature(), cAlg, max);
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param cAlg Curvature strategy to be used for the selection of significant points.
+     * @param perc Maximal number of significant points as percents of the original size
+     */
+    public CurvatureSampling(CurvatureAlg cAlg, double perc) {
+        this(new Curvature(), cAlg, perc);
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param curvatureVisitor Curvature. If the object has
+     * pre-filled curvatures of meshes, then they are also used for the computation
+     * of significant points. In this way, it is possible to reuse already computed
+     * curvatures and skip calling the {@link #visitMeshFacet} inspection method.
+     * @param cAlg Curvature strategy to be used for the selection of significant points.
+     * @param max Maximal number of significant points
+     * @throws IllegalArgumentException if the {@code curbatureVisitor} is missing
+     */
+    public CurvatureSampling(Curvature curvatureVisitor, CurvatureAlg cAlg, int max) {
+        super(max);
+        
+        if (curvatureVisitor == null) {
+            throw new IllegalArgumentException("curvatureVisitor");
+        }
+
+        this.curvatureVisitor = curvatureVisitor;
+        this.curvatureAlg = cAlg;
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param curvatureVisitor Curvature. If the object has
+     * pre-filled curvatures of meshes, then they are also used for the computation
+     * of significant points. In this way, it is possible to reuse already computed
+     * curvatures and skip calling the {@link #visitMeshFacet} inspection method.
+     * @param cAlg Curvature strategy to be used for the selection of significant points.
+     * @param perc Maximal number of significant points as percents of the original size
+     */
+    public CurvatureSampling(Curvature curvatureVisitor, CurvatureAlg cAlg, double perc) {
+        super(perc);
+        
+        if (curvatureVisitor == null) {
+            throw new IllegalArgumentException("curvatureVisitor");
+        }
+
+        this.curvatureVisitor = curvatureVisitor;
+        this.curvatureAlg = cAlg;
+    }
+    
+    @Override
+    public boolean isThreadSafe() {
+        return false;
+    }
+    
+    @Override
+    public void visitMeshFacet(MeshFacet facet) {
+        significantPoints = null; // clear previous results
+        curvatureVisitor.visitMeshFacet(facet); // compute curvature for new inspected facet
+    }
+    
+    @Override
+    public List<MeshPoint> getSamples() {
+        checkAndComputeSignificantPoints();
+        return significantPoints.stream().map(vc -> vc.point).collect(Collectors.toList());
+    }
+    
+    public CurvatureAlg getCurvatureAlg() {
+        return this.curvatureAlg;
+    }
+    
+    /**
+     * Returns curvatures of selected samples
+     * 
+     * @return curvatures of selected samples
+     */
+    public List<Double> getSampledCurvatures() {
+        checkAndComputeSignificantPoints();
+        return significantPoints.stream().map(vc -> vc.curvature).collect(Collectors.toList());
+    }
+    
+    @Override
+    public String toString() {
+        return this.curvatureAlg + " curvature " + super.toString();
+    }
+
+    protected void checkAndComputeSignificantPoints() {
+        if (significantPoints != null) {
+            return; // already computed
+        }
+        
+        // fill the priority queue
+        final PriorityQueue<VertCurvature> priorityQueue = new PriorityQueue<>();
+        
+        Map<MeshFacet, List<Double>> curvMap = null;
+        switch (curvatureAlg) {
+            case MEAN: 
+                curvMap = curvatureVisitor.getMeanCurvatures();
+                break;
+            case GAUSSIAN: 
+                curvMap = curvatureVisitor.getGaussianCurvatures();
+                break;
+            case MAX: 
+                curvMap = curvatureVisitor.getMaxPrincipalCurvatures();
+                break;
+            case MIN: 
+                curvMap = curvatureVisitor.getMinPrincipalCurvatures();
+                break;
+            default:
+                throw new IllegalArgumentException("curvatureAlg");
+        }
+
+        for (MeshFacet facet: curvMap.keySet()) {
+            List<Double> curvs = curvMap.get(facet);
+            for (int i = 0; i < curvs.size(); i++) { 
+                // store helper objects, replace NaN with 0.0:
+                double c = curvs.get(i);
+                priorityQueue.add(new VertCurvature(facet.getVertex(i), (Double.isNaN(c) ? 0.0 : c)));
+            }  
+        }
+        
+        // select top significant points
+        int maxPoints = getNumDownsampledPoints(priorityQueue.size());
+        significantPoints = new ArrayList<>(maxPoints);
+        for (int i = 0; i < maxPoints; i++) {
+            VertCurvature vc = priorityQueue.poll();
+            if (vc == null) {
+                break; // no more points available
+            } else {
+                significantPoints.add(vc);
+            }
+        }
+    }
+    
+    /**
+     * Helper class for sorting points with respect to their curvature.
+     * @author Radek Oslejsek
+     */
+    private class VertCurvature implements Comparable<VertCurvature> {
+        
+        private final double curvature;
+        private final MeshPoint point;
+        
+        VertCurvature(MeshPoint point, double curvature) {
+            this.curvature = curvature;
+            this.point = point;
+        }
+
+        /**
+         * Compares this object with the specified object for order. 
+         * Curvature is taken into consideration as the primary value (descendant ordering).
+         * If the curvature equals, then the objects are sorted randomly (1 is always returned).
+         * This approach preserves all points in sorted collections.
+         * 
+         * @param arg the object to be compared.
+         * @return a negative integer, zero, or a positive integer as this object 
+         * is less than, equal to, or greater than the specified object.
+         */
+        @Override
+        public int compareTo(VertCurvature arg) {
+            int comp = Double.compare(arg.curvature, this.curvature);
+            return (comp == 0) ? 1 : comp;
+        }
+        
+        @Override
+        public String toString() {
+            return ""+curvature;
+        }
+    }
+    
+}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/NoSampling.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/NoSampling.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac3cc7162b39341dc49829e70bf18c6673d04d23
--- /dev/null
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/NoSampling.java
@@ -0,0 +1,36 @@
+package cz.fidentis.analyst.visitors.mesh.sampling;
+
+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.List;
+
+/**
+ * No point sampling. All mesh points are returned.
+ * 
+ * @author Radek Oslejsek
+ */
+public class NoSampling extends PointSampling {
+    
+    private List<MeshPoint> allVertices = new ArrayList<>();
+    
+    @Override
+    public void visitMeshFacet(MeshFacet facet) {
+        if (facet != null) {
+            allVertices.addAll(facet.getVertices());
+        }
+    }
+
+    @Override
+    public List<MeshPoint> getSamples() {
+        return Collections.unmodifiableList(allVertices);
+    }
+    
+    @Override
+    public String toString() {
+        return "none";
+    }
+
+    
+}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/PointSampling.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/PointSampling.java
new file mode 100644
index 0000000000000000000000000000000000000000..132b683bca045eaacbd2402baf1f69624d68c809
--- /dev/null
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/PointSampling.java
@@ -0,0 +1,106 @@
+package cz.fidentis.analyst.visitors.mesh.sampling;
+
+import cz.fidentis.analyst.mesh.MeshVisitor;
+import cz.fidentis.analyst.mesh.core.MeshFacet;
+import cz.fidentis.analyst.mesh.core.MeshPoint;
+import java.util.List;
+
+/**
+ * Point sampling strategies used to reduce the size of meshes.
+ * 
+ * @author Radek Oslejsek
+ */
+public abstract class PointSampling extends MeshVisitor {
+
+    /**
+     * If point sampling is used, then this configuration parameter 
+     * encodes whether the reduction of mesh size is expressed as the percentage 
+     * of the original size (number of vertices) or as the maximal number of vertices
+     * to be used.
+     * 
+     * @author Radek Oslejsek
+     */
+    public enum PointSamplingType {
+        PERCENTAGE,
+        MAX_VERTICES
+    };
+    
+    private final PointSamplingType samplingType;
+    private final double samplingLimit;
+    
+    /** 
+     * Constructor for no point sampling.
+     */
+    public PointSampling() {
+        this.samplingType = null;
+        this.samplingLimit = 0.0;
+    }
+    
+    /**
+     * Constructor for PERCENTAGE point sampling type.
+     * 
+     * @param perc Percentage - a value in (0.0, 1.0&gt;
+     * @throws IllegalArgumentException if the input parameter is wrong
+     */
+    public PointSampling(double perc) {
+        if (perc <= 0.0 || perc > 1) {
+            throw new IllegalArgumentException("perc");
+        }
+        this.samplingType = PointSamplingType.PERCENTAGE;
+        this.samplingLimit = perc;
+    }
+    
+    /**
+     * Constructor for MAX_VERTICES point sampling type.
+     * 
+     * @param max Maximal number of vertices. Must be bigger than zero
+     * @throws IllegalArgumentException if the input parameter is wrong
+     */
+    public PointSampling(int max) {
+        if (max <= 0) {
+            throw new IllegalArgumentException("max");
+        }
+        this.samplingType = PointSamplingType.MAX_VERTICES;
+        this.samplingLimit = max;
+    }
+    
+    @Override
+    public abstract void visitMeshFacet(MeshFacet facet);    
+    
+    /**
+     * Returns a list of vertices reduced according to the strategy. 
+     * The returned mesh points are backed by original points.
+     * 
+     * @return selected vertices of inspected meshes
+     */
+    public abstract List<MeshPoint> getSamples();
+    
+    @Override
+    public String toString() {
+        if (this.samplingType == PointSamplingType.PERCENTAGE) {
+            return "sampling to " + (int) (samplingLimit * 100) + "%";
+        } else {
+            return "sampling of " + (int) samplingLimit + " points";
+        }
+    }
+    
+    /**
+     * Returns number of points to be returned after downsampling.
+     * 
+     * @param origPoints Original number of vertices
+     * @return number of points to be returned after downsampling.
+     */
+    protected int getNumDownsampledPoints(int origPoints) {
+        switch (this.samplingType) {
+            case PERCENTAGE:
+                return (int) (origPoints * this.samplingLimit);
+            case MAX_VERTICES:
+                //nt limit = (int) this.undersamplingLimit;
+                //return (limit <= origVertices) ? limit : origVertices;
+                return Math.min((int) this.samplingLimit, origPoints);
+            default:
+                return 0;
+        }
+    }
+    
+}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/RandomSampling.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/RandomSampling.java
new file mode 100644
index 0000000000000000000000000000000000000000..87800504e32b19fc2cc5cadcd3ae958d5c96e966
--- /dev/null
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/sampling/RandomSampling.java
@@ -0,0 +1,80 @@
+package cz.fidentis.analyst.visitors.mesh.sampling;
+
+import cz.fidentis.analyst.mesh.core.MeshFacet;
+import cz.fidentis.analyst.mesh.core.MeshPoint;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * Random sampling when vertices are selected randomly.
+ * 
+ * @author Radek Oslejsek
+ */
+public class RandomSampling extends PointSampling {
+    
+    private List<MeshPoint> allVertices = new ArrayList<>();
+
+    /**
+     * Constructor for PERCENTAGE point sampling type.
+     * 
+     * @param perc Percentage - a value in (0.0, 1.0&gt;
+     * @throws IllegalArgumentException if the input parameter is wrong
+     */
+    public RandomSampling(double perc) {
+        super(perc);
+    }
+    
+    /**
+     * Constructor for MAX_VERTICES point sampling type.
+     * 
+     * @param max Maximal number of vertices. Must be bigger than zero
+     * @throws IllegalArgumentException if the input parameter is wrong
+     */
+    public RandomSampling(int max) {
+        super(max);
+    }
+     
+    @Override
+    public void visitMeshFacet(MeshFacet facet) {
+        if (facet != null) {
+            allVertices.addAll(facet.getVertices());
+        }
+    }
+    
+    @Override
+    public List<MeshPoint> getSamples() {
+        int numReducedVertices = getNumDownsampledPoints(allVertices.size());
+        
+        if (allVertices.size() == numReducedVertices) {
+            return Collections.unmodifiableList(allVertices);
+        }
+        
+        // generate randomly ordered indexes:
+        List<Integer> range = IntStream.range(0, allVertices.size()).boxed().collect(Collectors.toCollection(ArrayList::new));
+        Collections.shuffle(range);
+        
+        if (numReducedVertices < allVertices.size() / 2) { // copy indices
+            MeshPoint[] array = new MeshPoint[allVertices.size()];
+            range.stream().limit(numReducedVertices).forEach(
+                    i -> array[i] = allVertices.get(i)
+            );
+            return Arrays.stream(array).filter(p -> p != null).collect(Collectors.<MeshPoint>toList());
+        } else { // remove indices
+            List<MeshPoint> copy = new ArrayList<>(allVertices);
+            range.stream().limit(allVertices.size() - numReducedVertices).forEach(
+                    i -> copy.set(i, null)
+            );
+            return copy.parallelStream().filter(p -> p != null).collect(Collectors.<MeshPoint>toList());
+        }        
+    }
+    
+    @Override
+    public String toString() {
+        return "random " + super.toString();
+    }
+    
+}
diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form
index 831516dd0d672de61e1f03b6ecf324a3731b9c58..a3f2757eb1431749c6705183e983d9c8032c80f1 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form
+++ b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.form
@@ -41,7 +41,7 @@
               <Component id="jPanel1" min="-2" max="-2" attributes="0"/>
               <EmptySpace max="-2" attributes="0"/>
               <Component id="jPanel3" min="-2" max="-2" attributes="0"/>
-              <EmptySpace max="32767" attributes="0"/>
+              <EmptySpace pref="99" max="32767" attributes="0"/>
           </Group>
       </Group>
     </DimensionLayout>
@@ -73,16 +73,14 @@
                           <EmptySpace max="-2" attributes="0"/>
                           <Component id="jButton5" min="-2" max="-2" attributes="0"/>
                       </Group>
-                      <Group type="102" attributes="0">
-                          <Group type="103" groupAlignment="0" attributes="0">
-                              <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
-                              <Component id="jCheckBox4" alignment="0" min="-2" max="-2" attributes="0"/>
-                          </Group>
+                      <Group type="102" alignment="0" attributes="0">
+                          <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
                           <EmptySpace max="-2" attributes="0"/>
-                          <Component id="comboSliderInteger1" min="-2" max="-2" attributes="0"/>
+                          <Component id="spinSlider1" min="-2" max="-2" attributes="0"/>
                       </Group>
+                      <Component id="jCheckBox4" alignment="0" min="-2" max="-2" attributes="0"/>
                   </Group>
-                  <EmptySpace pref="22" max="32767" attributes="0"/>
+                  <EmptySpace pref="96" max="32767" attributes="0"/>
               </Group>
           </Group>
         </DimensionLayout>
@@ -90,15 +88,18 @@
           <Group type="103" groupAlignment="0" attributes="0">
               <Group type="102" attributes="0">
                   <Group type="103" groupAlignment="0" attributes="0">
+                      <Group type="102" attributes="0">
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="spinSlider1" min="-2" max="-2" attributes="0"/>
+                      </Group>
                       <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
+                          <EmptySpace min="-2" pref="25" max="-2" attributes="0"/>
                           <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace type="unrelated" max="-2" attributes="0"/>
-                          <Component id="jCheckBox4" min="-2" max="-2" attributes="0"/>
                       </Group>
-                      <Component id="comboSliderInteger1" alignment="0" min="-2" max="-2" attributes="0"/>
                   </Group>
-                  <EmptySpace type="separate" max="-2" attributes="0"/>
+                  <EmptySpace pref="14" max="32767" attributes="0"/>
+                  <Component id="jCheckBox4" min="-2" max="-2" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
                   <Component id="jCheckBox2" min="-2" max="-2" attributes="0"/>
                   <EmptySpace max="-2" attributes="0"/>
                   <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/>
@@ -109,7 +110,7 @@
                       <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/>
                       <Component id="jButton5" alignment="3" min="-2" max="-2" attributes="0"/>
                   </Group>
-                  <EmptySpace max="32767" attributes="0"/>
+                  <EmptySpace max="-2" attributes="0"/>
               </Group>
           </Group>
         </DimensionLayout>
@@ -132,8 +133,6 @@
             </Property>
           </Properties>
         </Component>
-        <Component class="cz.fidentis.analyst.core.ComboSliderInteger" name="comboSliderInteger1">
-        </Component>
         <Component class="javax.swing.JCheckBox" name="jCheckBox1">
           <Properties>
             <Property name="selected" type="boolean" value="true"/>
@@ -179,6 +178,8 @@
             <Property name="enabled" type="boolean" value="false"/>
           </Properties>
         </Component>
+        <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider1">
+        </Component>
       </SubComponents>
     </Container>
     <Container class="javax.swing.JPanel" name="jPanel2">
diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java
index 1be2375c85b26cafe3b9270085628018caa59e0a..37fd2e6e1360d037e6e47ad386834cd5b24794c3 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/batch/BatchPanel.java
@@ -72,11 +72,9 @@ public class BatchPanel extends ControlPanel {
         
         jButton1.addActionListener(createListener(action, ACTION_COMMAND_COMPUTE_ICP));
         
-        comboSliderInteger1.setSliderEast();
-        comboSliderInteger1.setRange(10, 100);
-        comboSliderInteger1.setValue(this.undersampling);
-        comboSliderInteger1.addInputFieldListener((ActionEvent ae) -> { // update slider when the input field changed
-            this.undersampling = comboSliderInteger1.getValue();
+        spinSlider1.initPercentage(this.undersampling);
+        spinSlider1.addSpinnerListener((ActionEvent ae) -> { // update slider when the input field changed
+            this.undersampling = (Integer) spinSlider1.getValue();
         });
         
         jComboBox2.addItem(SIMILARITY_APPROX_HD);
@@ -324,12 +322,12 @@ public class BatchPanel extends ControlPanel {
         jPanel1 = new javax.swing.JPanel();
         jButton1 = new javax.swing.JButton();
         jLabel2 = new javax.swing.JLabel();
-        comboSliderInteger1 = new cz.fidentis.analyst.core.ComboSliderInteger();
         jCheckBox1 = new javax.swing.JCheckBox();
         jCheckBox2 = new javax.swing.JCheckBox();
         jCheckBox3 = new javax.swing.JCheckBox();
         jCheckBox4 = new javax.swing.JCheckBox();
         jButton5 = new javax.swing.JButton();
+        spinSlider1 = new cz.fidentis.analyst.core.SpinSlider();
         jPanel2 = new javax.swing.JPanel();
         jLabel1 = new javax.swing.JLabel();
         jComboBox1 = new javax.swing.JComboBox<>();
@@ -393,24 +391,25 @@ public class BatchPanel extends ControlPanel {
                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                         .addComponent(jButton5))
                     .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                            .addComponent(jLabel2)
-                            .addComponent(jCheckBox4))
+                        .addComponent(jLabel2)
                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
-                .addContainerGap(22, Short.MAX_VALUE))
+                        .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                    .addComponent(jCheckBox4))
+                .addContainerGap(96, Short.MAX_VALUE))
         );
         jPanel1Layout.setVerticalGroup(
             jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
             .addGroup(jPanel1Layout.createSequentialGroup()
                 .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                     .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addGap(20, 20, 20)
-                        .addComponent(jLabel2)
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
-                        .addComponent(jCheckBox4))
-                    .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                .addGap(18, 18, 18)
+                        .addContainerGap()
+                        .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                    .addGroup(jPanel1Layout.createSequentialGroup()
+                        .addGap(25, 25, 25)
+                        .addComponent(jLabel2)))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 14, Short.MAX_VALUE)
+                .addComponent(jCheckBox4)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(jCheckBox2)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(jCheckBox1)
@@ -420,7 +419,7 @@ public class BatchPanel extends ControlPanel {
                 .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                     .addComponent(jButton1)
                     .addComponent(jButton5))
-                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                .addContainerGap())
         );
 
         jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(BatchPanel.class, "BatchPanel.jPanel2.border.title_1"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 12))); // NOI18N
@@ -537,7 +536,7 @@ public class BatchPanel extends ControlPanel {
                 .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                .addContainerGap(99, Short.MAX_VALUE))
         );
     }// </editor-fold>//GEN-END:initComponents
 
@@ -555,7 +554,6 @@ public class BatchPanel extends ControlPanel {
 
 
     // Variables declaration - do not modify//GEN-BEGIN:variables
-    private cz.fidentis.analyst.core.ComboSliderInteger comboSliderInteger1;
     private javax.swing.JButton jButton1;
     private javax.swing.JButton jButton4;
     private javax.swing.JButton jButton5;
@@ -574,6 +572,7 @@ public class BatchPanel extends ControlPanel {
     private javax.swing.JPanel jPanel1;
     private javax.swing.JPanel jPanel2;
     private javax.swing.JPanel jPanel3;
+    private cz.fidentis.analyst.core.SpinSlider spinSlider1;
     // End of variables declaration//GEN-END:variables
 
 }
diff --git a/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java b/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java
index ecd13150a8c63f47646dd126b46c960b3caef3e8..f62bf7e5f15183b1515a78423f14592b708ad4b6 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/batch/IcpTask.java
@@ -10,6 +10,7 @@ import cz.fidentis.analyst.mesh.core.MeshModel;
 import cz.fidentis.analyst.mesh.io.MeshObjExporter;
 import cz.fidentis.analyst.scene.DrawableFace;
 import cz.fidentis.analyst.visitors.kdtree.AvgFaceConstructor;
+import cz.fidentis.analyst.visitors.mesh.sampling.RandomSampling;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Path;
@@ -120,7 +121,7 @@ public class IcpTask extends SwingWorker<MeshModel, HumanFace> {
                             100,  // max iterations
                             controlPanel.scaleIcp(),
                             0.3,  // error
-                            undersampling,
+                            new RandomSampling(undersampling),
                             false // drop k-d tree, if exists
                     );
                     icpComputationTime.stop();
diff --git a/GUI/src/main/java/cz/fidentis/analyst/canvas/toolbar/RenderingModeToolbox.java b/GUI/src/main/java/cz/fidentis/analyst/canvas/toolbar/RenderingModeToolbox.java
index 5aa3e880df3afd89a0d66bc08eefa064cfeac253..8ae8cd93716df9c13839f5ba38561bafac378a42 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/canvas/toolbar/RenderingModeToolbox.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/canvas/toolbar/RenderingModeToolbox.java
@@ -2,6 +2,7 @@ package cz.fidentis.analyst.canvas.toolbar;
 
 import com.jogamp.opengl.GL2;
 import cz.fidentis.analyst.canvas.Canvas;
+import cz.fidentis.analyst.scene.DrawableFace;
 import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
 import javax.swing.AbstractAction;
@@ -63,9 +64,68 @@ public class RenderingModeToolbox extends JPopupMenu {
             }
         });
         
+        JMenuItem menuItem4 = new JMenuItem(new AbstractAction() { // Random sampling
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                canvas.getScene().getFaceSlots().forEach(i -> {
+                   canvas.getScene().getDrawableFace(i).setRenderMode(DrawableFace.RANDOM_SAMPLING);
+                });
+                canvas.renderScene();
+            }
+        });
+        
+        JMenuItem menuItem5 = new JMenuItem(new AbstractAction() { // Curvature sampling
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                canvas.getScene().getFaceSlots().forEach(i -> {
+                   canvas.getScene().getDrawableFace(i).setRenderMode(DrawableFace.CURVATURE_SAMPLING_MIN);
+                });
+                canvas.renderScene();
+            }
+        });
+        
+        JMenuItem menuItem6 = new JMenuItem(new AbstractAction() { // Curvature sampling
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                canvas.getScene().getFaceSlots().forEach(i -> {
+                   canvas.getScene().getDrawableFace(i).setRenderMode(DrawableFace.CURVATURE_SAMPLING_MAX);
+                });
+                canvas.renderScene();
+            }
+        });
+        
+        JMenuItem menuItem7 = new JMenuItem(new AbstractAction() { // Curvature sampling
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                canvas.getScene().getFaceSlots().forEach(i -> {
+                   canvas.getScene().getDrawableFace(i).setRenderMode(DrawableFace.CURVATURE_SAMPLING_MEAN);
+                });
+                canvas.renderScene();
+            }
+        });
+        
+        JMenuItem menuItem8 = new JMenuItem(new AbstractAction() { // Curvature sampling
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                canvas.getScene().getFaceSlots().forEach(i -> {
+                   canvas.getScene().getDrawableFace(i).setRenderMode(DrawableFace.CURVATURE_SAMPLING_GAUSSIAN);
+                });
+                canvas.renderScene();
+            }
+        });
+        
         menuItem1.setIcon(new ImageIcon(getClass().getResource("/" + RenderingModeToolbox.SMOOT_BUTTON_ICON)));
         menuItem2.setIcon(new ImageIcon(getClass().getResource("/" + RenderingModeToolbox.WIREFRAME_BUTTON_ICON)));
         menuItem3.setIcon(new ImageIcon(getClass().getResource("/" + RenderingModeToolbox.POINTS_BUTTON_ICON)));
+        
+        menuItem1.setText("Shading");
+        menuItem2.setText("Wire-frame");
+        menuItem3.setText("Points Cloud");
+        menuItem4.setText("Random Sampling");
+        menuItem5.setText("Min. Curvature");
+        menuItem6.setText("Max. Curvature");
+        menuItem7.setText("Mean Curvature");
+        menuItem8.setText("Gaussian Curvature");
 
         menuItem1.setToolTipText(NbBundle.getMessage(RenderingModeToolbox.class, "RenderingModeToolbox.smooth.text"));
         menuItem2.setToolTipText(NbBundle.getMessage(RenderingModeToolbox.class, "RenderingModeToolbox.wireframe.text"));
@@ -74,7 +134,14 @@ public class RenderingModeToolbox extends JPopupMenu {
         add(menuItem1);
         add(menuItem2);
         add(menuItem3);
+        addSeparator();
+        add(menuItem4);
+        add(menuItem5);
+        add(menuItem6);
+        add(menuItem7);
+        add(menuItem8);
         
-        setLayout(new GridLayout(1,0));        
+        //setLayout(new GridLayout(1,0));        
+        setLayout(new GridLayout(0,1));        
     }
 }
diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSlider.java b/GUI/src/main/java/cz/fidentis/analyst/core/ComboSlider.java
deleted file mode 100644
index 26e30149ee219feb0fcc317a180d28ef951222cd..0000000000000000000000000000000000000000
--- a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSlider.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package cz.fidentis.analyst.core;
-
-import java.awt.Dimension;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import javax.swing.JFormattedTextField;
-import javax.swing.JPanel;
-import javax.swing.JSlider;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-
-/**
- * ComboSlider implements a combination of horizontal slider and input text field.
- * The slider and input field are synchronized automatically.
- * 
- * @author Radek Oslejsek
- */
-public abstract class ComboSlider extends JPanel {
- 
-    private final JSlider slider = new JSlider();
-    private final JFormattedTextField inputField = new JFormattedTextField();
-    private boolean continousSync;
-
-    public static final int DEFAULT_TEXT_FIELD_WIDTH = 70;
-    public static final int DEFAULT_TEXT_FIELD_HEIGHT = 40;
-    
-    private ChangeListener changeListener = new ChangeListener() {
-        @Override
-        public void stateChanged(ChangeEvent e) {
-            setValueFromSlider();
-        }
-    };
-    
-    private MouseListener mouseListener = new MouseAdapter() {
-        @Override
-        public void mouseReleased(MouseEvent e) {
-            setValueFromSlider();
-        }
-    };
-    
-    /**
-     * Constructor.
-     */
-    public ComboSlider() {
-        initComponents();
-        
-        setContinousSync(true); // update input field on slider's change and inform external listeners
-        
-        inputField.addActionListener((ActionEvent ae) -> { // update slider when the input field changed
-            setValueFromInputField();
-        });
-        
-    }
-    
-    /**
-     * Connects the specified listener with the input field.
-     * The listener is invoked on the input field change, which is affected
-     * by the {@link #setContinousSync(boolean)}.
-     * Event's source is set to {@code JFormattedTextField}
-     *
-     * @param listener the action listener to be added
-     */
-    public synchronized void addInputFieldListener(ActionListener listener) {
-        inputField.addActionListener(listener);
-    }
-    
-    /**
-     * Connects the specified listener with the slider.
-     * Event's source is set to {@code JSlider}
-     *
-     * @param listener the action listener to be added
-     */
-    public synchronized void addSliderListener(ActionListener listener) {
-        slider.addChangeListener((ChangeEvent e) -> {
-            listener.actionPerformed(new ActionEvent(slider, ActionEvent.ACTION_PERFORMED, null));
-        });
-    }
-    
-    /**
-     * The slider is on the left, followed by ti input field, by default.
-     * This method switches the order.
-     */
-    public void setSliderEast() {
-        remove(slider);
-        add(slider);
-    }
-    
-    /**
-     * If {@code true}, then the input field is updated during the slider move.
-     * Otherwise, the input field is updated only on mouse key release.
-     * 
-     * @param continousSync Whether to update input field continuously.
-     */
-    public final void setContinousSync(boolean continousSync) {
-        this.continousSync = continousSync;
-        if (this.continousSync) {
-            slider.removeMouseListener(mouseListener);
-            slider.addChangeListener(changeListener);
-        } else {
-            slider.addMouseListener(mouseListener);
-            slider.removeChangeListener(changeListener);
-        }
-    }
-
-    /**
-     * If {@code true}, then the input field is updated during the slider move.
-     * Otherwise, the input field is updated only on mouse key release.
-     * 
-     * @return {@code true} if the slider and input field are synchronized continuously. 
-     */
-    public boolean isContinousSync() {
-        return continousSync;
-    }
-    
-    public JSlider getSlider() {
-        return slider;
-    }
-    
-    public JFormattedTextField getInputField() {
-        return inputField;
-    }
-    
-    /**
-     * Reads the value of the slider, scales it into the range of the input field, 
-     * updates the input field and triggers input field change event.
-     */
-    protected abstract void setValueFromSlider();
-    
-    /**
-     * Reads the value of the input field, scales it into the range of the slider, 
-     * and updates the slider. No change event is triggered.
-     */
-    protected abstract void setValueFromInputField();
-    
-    protected final void initComponents() {
-        inputField.setPreferredSize(new Dimension(DEFAULT_TEXT_FIELD_WIDTH, DEFAULT_TEXT_FIELD_HEIGHT));
-        add(slider);
-        add(inputField);
-    }
-    
-}
diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderDouble.java b/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderDouble.java
deleted file mode 100644
index f5f7a1b36db70b9689c5be7750672466a111891a..0000000000000000000000000000000000000000
--- a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderDouble.java
+++ /dev/null
@@ -1,166 +0,0 @@
-package cz.fidentis.analyst.core;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.math.RoundingMode;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.util.Locale;
-import javax.swing.text.DefaultFormatterFactory;
-import javax.swing.text.NumberFormatter;
-
-/**
- * ComboSlider with real numbers.
- * The input field handles real number in given precision (number of fraction digits).
- * The slider shows real numbers on an automatic integer scale.
- * 
- * @author Radek Oslejsek
- */
-public class ComboSliderDouble extends ComboSlider {
-    
-    private double min = 0.0;
-    private double max = 1.0;
-    private int fractionDigits = 2;
-    
-    /**
-     * Constructor. The default scale and precision is from 0.00 to 1.00.
-     */
-    public ComboSliderDouble() {
-        setRange(0.0, 1.0, 2);
-    }
-    
-    /**
-     * Sets the slider and input field range.
-     * 
-     * @param min Minimum value
-     * @param max Maximum value. If less than {@code min}, then {@code min} is used instead.
-     * @param fractionDigits precision, i.e., the number of allowed digits in the fraction part of real numbers
-     */
-    public final void setRange(double min, double max, int fractionDigits) {
-        this.min = min;
-        this.max = (max < min) ? min : max;
-        this.fractionDigits = fractionDigits;
-        
-        NumberFormat format = DecimalFormat.getInstance(Locale.getDefault());
-        format.setMinimumFractionDigits(1);
-        format.setMaximumFractionDigits(fractionDigits);
-        format.setRoundingMode(RoundingMode.HALF_UP);
-        
-        NumberFormatter formatter = new NumberFormatter(format);
-        formatter.setValueClass(Double.class);
-        formatter.setMinimum(this.min);
-        formatter.setMaximum(this.max);
-        formatter.setAllowsInvalid(false);
-        
-        getInputField().setFormatterFactory(new DefaultFormatterFactory(formatter));
-        getInputField().setText(TextFieldUtils.doubleToStringLocale(this.min, fractionDigits));
-        
-        getSlider().setMinimum((int) (this.min * getRecomputationFactor()));
-        getSlider().setMaximum((int) (this.max * getRecomputationFactor()));
-        
-        setValue(this.min);
-    }
-    
-    /**
-     * Connects the specified listener witch the input field.
-     * The listener is invoked on the input field change, which is affected
-     * by the {@link #setContinousSync(boolean)}.
-     * In contrast to the {@link #addInputFieldListener(java.awt.event.ActionListener)},
-     * the event's source is set to {@code ComboSliderDouble}. Therefore, 
-     * the {@link #getValue()} method can be used directly.
-     *
-     * @param listener the action listener to be added
-     */
-    public synchronized void addListener(ActionListener listener) {
-        getInputField().addActionListener((ActionEvent e) -> {
-            listener.actionPerformed(new ActionEvent(this, e.getID(), e.getActionCommand()));
-        });
-    }
-
-    
-
-    /**
-     * Returns the value.
-     * 
-     * @return the value.
-     */
-    public double getValue() {
-        return TextFieldUtils.parseLocaleDouble(getInputField());
-    }
-
-    /**
-     * Sets the value of the slider and input field. 
-     * If the value is outside of the range then nothing happens.
-     * 
-     * @param value a new value
-     */
-    public void setValue(double value) {
-        if (value >= getMinimum() && value <= getMaximum()) {
-            getInputField().setText(TextFieldUtils.doubleToStringLocale(value, fractionDigits));
-            getInputField().postActionEvent(); // invoke textField action listener
-        }
-    }
-    
-    /**
-     * Sets the value of the slider and input field. 
-     * In contrast to the {@link #setValue(double)} method, 
-     * this call does not trigger change event.
-     * 
-     * @param value a new value
-     */
-    public void setValueSilently(double value) {
-        if (value >= getMinimum() && value <= getMaximum()) {
-            getInputField().setText(TextFieldUtils.doubleToStringLocale(value, fractionDigits));
-            setValueFromInputField();
-        }
-    }
-    
-    /**
-     * Returns the lower limit of the range.
-     * 
-     * @return the lower limit
-    */
-    public double getMinimum() {
-        return this.min;
-    }
-
-    /**
-     * Returns the upper limit of the range.
-     * 
-     * @return the upper limit
-     */
-    public double getMaximum() {
-        return this.max;
-    }
-    
-    /**
-     * Returns the number of allowed digits in the fraction part of real numbers.
-     * 
-     * @return the number of allowed digits in the fraction part of real numbers
-     */
-    public int getFractionDigits() {
-        return this.fractionDigits;
-    }
-    
-    @Override
-    protected void setValueFromSlider() {
-        setValue(getSlider().getValue() / getRecomputationFactor()); // also triggers the event
-    }
-    
-    @Override
-    protected void setValueFromInputField() {
-        getSlider().setValue((int) (TextFieldUtils.parseLocaleDouble(getInputField()) * getRecomputationFactor()));
-    }
-    
-    /**
-     * Returns 1, 10, 100, etc., based on the fraction digits.
-     * @return 1, 10, 100, etc., based on the fraction digits.
-     */
-    public double getRecomputationFactor() {
-        int scale = 1;
-        for (int i = 0; i < fractionDigits; i++) {
-            scale *= 10;
-        }
-        return scale;
-    }
-}
diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderInteger.java b/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderInteger.java
deleted file mode 100644
index cc626be175614601e49389c9e3322e00d7e4ea74..0000000000000000000000000000000000000000
--- a/GUI/src/main/java/cz/fidentis/analyst/core/ComboSliderInteger.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package cz.fidentis.analyst.core;
-
-import java.text.NumberFormat;
-import javax.swing.text.DefaultFormatterFactory;
-import javax.swing.text.NumberFormatter;
-
-/**
- * ComboSlider with integer numbers.
- * 
- * @author Radek Oslejsek
- */
-public class ComboSliderInteger extends ComboSlider {
-    
-    private int min = 0;
-    private int max = 100;
-    
-    /**
-     * Constructor. The default scale is from 0 to 100.
-     */
-    public ComboSliderInteger() {
-        setRange(0, 100);
-    }
-    
-    /**
-     * Sets the slider and input field range.
-     * 
-     * @param min Minimum value
-     * @param max Maximum value. If less than {@code min}, then {@code min} is used instead.
-     */
-    public final void setRange(int min, int max) {
-        this.min = min;
-        this.max = (max < min) ? min : max;
-        
-        NumberFormatter formatter = new NumberFormatter(NumberFormat.getInstance());
-        formatter.setValueClass(Integer.class);
-        formatter.setMinimum(this.min);
-        formatter.setMaximum(this.max);
-        formatter.setAllowsInvalid(false);
-        
-        getInputField().setFormatterFactory(new DefaultFormatterFactory(formatter));
-        getInputField().setText(Integer.toString(this.min));
-        
-        getSlider().setMinimum(this.min);
-        getSlider().setMaximum(this.max);
-        
-        setValue(this.min);
-        getInputField().postActionEvent(); // invoke textField action listener
-    }
-    
-    /**
-     * Returns the value.
-     * 
-     * @return the value.
-     */
-    public int getValue() {
-        return TextFieldUtils.parseLocaleInt(getInputField());
-    }
-
-    /**
-     * Sets the value of the slider and input field. 
-     * If the value is outside of the range then nothing happens.
-     * 
-     * @param value a new value
-     */
-    public void setValue(int value) {
-        if (value >= getMinimum() && value <= getMaximum()) {
-            getInputField().setText(TextFieldUtils.intToStringLocale(value));
-            getInputField().postActionEvent(); // invoke textField action listener
-        }
-    }
-    
-    /**
-     * Returns the lower limit of the range.
-     * 
-     * @return the lower limit
-    */
-    public int getMinimum() {
-        return this.min;
-    }
-
-    /**
-     * Returns the upper limit of the range.
-     * 
-     * @return the upper limit
-     */
-    public int getMaximum() {
-        return this.max;
-    }
-    
-    @Override
-    protected void setValueFromSlider() {
-        setValue(getSlider().getValue()); // also triggers the event
-    }
-    
-    @Override
-    protected void setValueFromInputField() {
-        getSlider().setValue(TextFieldUtils.parseLocaleInt(getInputField()));
-    }
-    
-}
diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/DoubleSpinner.java b/GUI/src/main/java/cz/fidentis/analyst/core/DoubleSpinner.java
new file mode 100644
index 0000000000000000000000000000000000000000..c7f9c43ca0313931a54eab278a50b9346ede1a3f
--- /dev/null
+++ b/GUI/src/main/java/cz/fidentis/analyst/core/DoubleSpinner.java
@@ -0,0 +1,71 @@
+package cz.fidentis.analyst.core;
+
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * A single line input field that lets the user show and select real number
+ * from an ordered sequence. 
+ * 
+ * @author Radek Oslejsek
+ */
+public class DoubleSpinner extends JSpinner {
+    
+    private int fractionDigits = 2;
+
+    private static final double STEP_RATIO = 0.1;
+
+    private SpinnerNumberModel model;
+
+    /**
+     * Constructor.
+     * 
+     * @param fractionDigits precision of flouting numbers, 
+     * i.e., the number of digits allowed after the floating dot
+     */
+    public DoubleSpinner(int fractionDigits) {
+        this.fractionDigits = fractionDigits;
+
+        model = new SpinnerNumberModel(0.0, -getRecomputationFactor(), getRecomputationFactor(), 0.1);
+        this.setModel(model);
+        //model.setMinimum(min);
+        //smodel.setMinimum(max);
+        
+        // Step recalculation
+        this.addChangeListener(new ChangeListener() {
+            @Override
+            public void stateChanged(ChangeEvent e) {
+                Double value = getDouble();
+                // Steps are sensitive to the current magnitude of the value
+                long magnitude = Math.round(Math.log10(value));
+                double stepSize = STEP_RATIO * Math.pow(10, magnitude);
+                if (stepSize == 0.0) {
+                    stepSize = 0.1;
+                }
+                model.setStepSize(stepSize);
+            }
+        });
+        
+    }
+
+    /**
+     * Returns the current value as a Double
+     */
+    public Double getDouble() {
+        return (Double) getValue();
+    }
+
+    /**
+     * Returns 1, 10, 100, etc., based on the fraction digits.
+     * @return 1, 10, 100, etc., based on the fraction digits.
+     */
+    public double getRecomputationFactor() {
+        int scale = 1;
+        for (int i = 0; i < fractionDigits; i++) {
+            scale *= 10;
+        }
+        return scale;
+    }
+}
\ No newline at end of file
diff --git a/GUI/src/main/java/cz/fidentis/analyst/core/SpinSlider.java b/GUI/src/main/java/cz/fidentis/analyst/core/SpinSlider.java
new file mode 100644
index 0000000000000000000000000000000000000000..211289a4b2ead39e56b0d34484b915b7ce8141b3
--- /dev/null
+++ b/GUI/src/main/java/cz/fidentis/analyst/core/SpinSlider.java
@@ -0,0 +1,292 @@
+package cz.fidentis.analyst.core;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * SpinSlider implements a combination of horizontal slider and input text field (Spinner).
+ * The slider and the spinner are synchronized automatically.
+ * 
+ * @author Radek Oslejsek
+ */
+public class SpinSlider  extends JPanel {
+    
+    /**
+     * SpinSlider type
+     * 
+     * @author Radek Oslejsek
+     */
+    public enum ValueType {
+        INTEGER,
+        DOUBLE,
+        PERCENTAGE
+    };
+    
+    private final JSlider slider = new JSlider();
+    private JSpinner spinner;
+    private ValueType type;
+    
+    private boolean continousSync;
+    
+    /**
+     * Listener for continuous synchronization of the slider and the spinner.
+     * The spinner value is updates whenever the slider is moved.
+     */
+    private ChangeListener changeListener = new ChangeListener() {
+        @Override
+        public void stateChanged(ChangeEvent e) {
+            JSlider s = (JSlider) e.getSource();
+            if (spinner.getClass() == DoubleSpinner.class) {
+                Double val = Double.valueOf((Integer) s.getValue());
+                val /= ((DoubleSpinner) spinner).getRecomputationFactor();
+                spinner.setValue(val);
+            } else {
+                spinner.setValue(s.getValue());
+            }
+        }
+    };
+    
+    /**
+     * Listener for postponed synchronization of the slider and the spinner.
+     * The spinner value remains unchanged until the mouse button is released.
+     */
+    private MouseListener mouseListener = new MouseAdapter() {
+        @Override
+        public void mouseReleased(MouseEvent e) {
+            JSlider s = (JSlider) e.getSource();
+            if (spinner.getClass() == DoubleSpinner.class) {
+                Double val = Double.valueOf((Integer) s.getValue());
+                val /= ((DoubleSpinner) spinner).getRecomputationFactor();
+                spinner.setValue(val);
+            } else {
+                spinner.setValue(s.getValue());
+            }
+        }
+    };
+    
+    
+    /**
+     * Constructor that creates percentage spin-slider.
+     * Call {@link #initDouble(double, double, double, int)} 
+     * or {@link #initInteger(int, int, int, int)} to change it.
+     */
+    public SpinSlider() {
+        initPercentage(100);
+        initComponents();
+        setContinousSync(true); // update input field on slider's change and inform external listeners
+        slider.setFont(new java.awt.Font("Dialog", 1, 0)); // hide numbers
+        
+        setBackground(Color.LIGHT_GRAY);
+    }
+    
+    /**
+     * Returns current value reflecting the slider's position.
+     * Even if the postponed synchronization is turned on (see {@link #setContinousSync(boolean)}),
+     * the correct value is computed and returned anyway.
+     * <p>
+     * Based on the type, the return value is
+     * <ul>
+     * <li>{@code INTEGER}: An {@code Integer} from set range.</li>
+     * <li>{@code DOUBLE}: An {@code Double} from set range.</li>
+     * <li>{@code PERCENTAGE}: An {@code Integer} between 0 and 100.</li>
+     * </ul>
+     * </p>
+     * <p>
+     * Usage:
+     * <ul>
+     * <li>After {@code initDouble()}: {@code double var = (Double) spinSlider.getValue()}.</li>
+     * <li>After {@code initInteger()}: {@code double var = (Integer) spinSlider.getValue()}.</li>
+     * <li>After {@code initPercentage()}: {@code double var = (Integer) spinSlider.getValue()}.</li>
+     * </ul>
+     * </p>
+     * 
+     * @return Current value
+     */
+    public Number getValue() {
+        if (this.type == ValueType.DOUBLE) {
+            DoubleSpinner ds = (DoubleSpinner) spinner;
+            return slider.getValue() / ds.getRecomputationFactor();
+        } else {
+            return (Number) spinner.getValue();
+        }
+    }
+    
+    /**
+     * Sets the value. 
+     * 
+     * @param value A new value
+     */
+    public void setValue(Number value) {
+        spinner.setValue(value);
+    }
+    
+    /**
+     * Initializes this spin-slider to integers.
+     * 
+     * @param value Initial value
+     * @param minimum Minimum value
+     * @param maximum Maximum value
+     * @param stepSize Spin step size
+     */
+    public void initInteger(int value, int min, int max, int stepSize) {
+        remove(slider);
+        if (spinner != null) {
+            remove(spinner);
+        }
+        
+        this.type = ValueType.INTEGER;
+        spinner = new JSpinner();
+        spinner.setModel(new SpinnerNumberModel(value, min, max, stepSize));
+        spinner.setEditor(new JSpinner.NumberEditor(spinner));
+        slider.setMinimum(min);
+        slider.setMaximum(max);
+        
+        initComponents();
+        setValue(value);
+    }
+
+    /**
+     * Initializes this spin-slider to doubles.
+     * 
+     * @param value Initial value
+     * @param minimum Minimum value
+     * @param maximum Maximum value
+     * @param fractionDigits precision of flouting numbers, i.e., 
+     *          the number of digits allowed after the floating dot
+     */
+    public void initDouble(double value, double min, double max, int fractionDigits) {
+        remove(slider);
+        if (spinner != null) {
+            remove(spinner);
+        }
+        
+        this.type = ValueType.DOUBLE;
+        DoubleSpinner ds = new DoubleSpinner(fractionDigits);
+        spinner = ds;
+        slider.setMinimum((int) (min * ds.getRecomputationFactor()));
+        slider.setMaximum((int) (max * ds.getRecomputationFactor()));
+        
+        initComponents();
+        setValue(value);
+    }
+    
+    /**
+     * Initializes this spin-slider to percents.
+     * 
+     * @param value Initial value between 0 and 100.
+     */
+    public void initPercentage(int value) {
+        remove(slider);
+        if (spinner != null) {
+            remove(spinner);
+        }
+        
+        this.type = ValueType.PERCENTAGE;
+        spinner = new JSpinner();
+        spinner.setModel(new SpinnerNumberModel(value, 0, 100, 1));
+        spinner.setEditor(new JSpinner.NumberEditor(spinner, "0'%'"));
+        slider.setMinimum(0);
+        slider.setMaximum(100);
+        slider.setValue(value);
+        
+        initComponents();
+        setValue(value);
+    }
+    
+    /**
+     * The slider is on the left, followed by ti input field, by default.
+     * This method switches the order.
+     */
+    //public void setSliderEast() {
+    //    remove(slider);
+    //    add(slider);
+    //}
+    
+    /**
+     * If {@code true}, then the spinner is updated continuously during the slider move.
+     * Otherwise, the spinner remains unchanged until the mouse key releases.
+     * 
+     * @param continousSync Whether to update the spinner continuously.
+     */
+    public final void setContinousSync(boolean continousSync) {
+        this.continousSync = continousSync;
+        if (this.continousSync) {
+            slider.removeMouseListener(mouseListener);
+            slider.addChangeListener(changeListener);
+        } else {
+            slider.addMouseListener(mouseListener);
+            slider.removeChangeListener(changeListener);
+        }
+    }
+    
+    /**
+     * If {@code true}, then the spinner is updated continuously during the slider move.
+     * Otherwise, the spinner remains unchanged until the mouse key releases.
+     * 
+     * @return {@code true} if the slider and the spinner are synchronized continuously. 
+     */
+    public boolean isContinousSync() {
+        return continousSync;
+    }
+    
+    /**
+     * Be informed when the spinner's value changes.
+     * See also {@link #setContinousSync(boolean)}.
+     * Event's source is set to {@code JSpinner}. 
+     * For the double type of the spin-slider, it is {@code DoubleSpinner}
+     *
+     * @param listener the action listener to be added
+     */
+    public synchronized void addSpinnerListener(ActionListener listener) {
+        spinner.addChangeListener((ChangeEvent e) -> {
+            listener.actionPerformed(new ActionEvent(spinner, ActionEvent.ACTION_PERFORMED, null));
+        });
+    }
+    
+    /**
+     * Be informed when the slider changes.
+     * See also {@link #setContinousSync(boolean)}.
+     * Event's source is set to {@code JSlider}
+     *
+     * @param listener the action listener to be added
+     */
+    public synchronized void addSliderListener(ActionListener listener) {
+        slider.addChangeListener((ChangeEvent e) -> {
+            listener.actionPerformed(new ActionEvent(slider, ActionEvent.ACTION_PERFORMED, null));
+        });
+    }
+    
+    protected final void initComponents() {
+        //this.setLayout(new FlowLayout());
+        this.setLayout(new BorderLayout());
+        
+        add(slider, BorderLayout.LINE_START);
+        add(spinner, BorderLayout.LINE_END);
+        
+        spinner.addChangeListener(new ChangeListener() {
+            @Override
+            public void stateChanged(ChangeEvent e) {
+                JSpinner s = (JSpinner) e.getSource();
+                if (spinner.getClass() == DoubleSpinner.class) {
+                    Double val = (Double) s.getValue();
+                    val *= ((DoubleSpinner) spinner).getRecomputationFactor();
+                    slider.setValue(val.intValue());
+                    //slider.setValue(((Double) s.getValue()).intValue());
+                } else {
+                    slider.setValue((Integer) s.getValue());
+                }
+            }
+        });
+    }    
+}
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 28798ab389df4daee6ae1fef6d76c0c000980706..a2ffebfbebb4ea513901d110f02e0a68fa4f131d 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/distance/DistanceAction.java
@@ -193,7 +193,7 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe
                 resizeFeaturePoint((LoadedActionEvent) ae);
                 break;
             case FeaturePointsPanel.ACTION_COMMAND_DISTANCE_RECOMPUTE:
-                computeAndUpdateHausdorffDistance(true);
+                computeAndUpdateHausdorffDistance(false);
                 break;
             default:
                 // to nothing
@@ -319,17 +319,18 @@ public class DistanceAction extends ControlPanelAction implements HumanFaceListe
         controlPanel.getFeaturePointsListPanel().updateFeaturePointWeights(
                 whdVisitor.getFeaturePointWeights()
                         .get(getSecondaryDrawableFace().getHumanFace()) // Get FP weights for the secondary face
-                        .entrySet()
+                        .entrySet() // Entry of Map<FeaturePointType, Map<MeshFacet, Double>>
                         .stream()
-                        .collect(
-                                Collectors.toMap(
-                                        Map.Entry::getKey,            // For each FP type at the secondary face...
-                                        weights -> weights.getValue() // ... compute average FP weight over all its facets
-                                                .values()
-                                                .stream()
-                                                .mapToDouble(Double::doubleValue)
-                                                .average()
-                                                .orElse(Double.NaN))));
+                        .collect(Collectors.toMap(
+                                Map.Entry::getKey,            // For each FP type at the secondary face...
+                                weights -> weights.getValue() // ... compute average FP weight over all its facets
+                                        .values() // Collection<Double>
+                                        .stream()
+                                        .mapToDouble(Double::doubleValue)
+                                        .average()
+                                        .orElse(Double.NaN))
+                        )
+        );
     }
 
     private void selectFeaturePoint(LoadedActionEvent actionEvent) {
diff --git a/GUI/src/main/java/cz/fidentis/analyst/distance/FeaturePointsPanel.java b/GUI/src/main/java/cz/fidentis/analyst/distance/FeaturePointsPanel.java
index 605aa4c9e3b58e1b09379fa0b6a464ad15454708..a0d7565ccc6dbc22d48f20b5607b0e6a588deb21 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/distance/FeaturePointsPanel.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/distance/FeaturePointsPanel.java
@@ -1,7 +1,7 @@
 package cz.fidentis.analyst.distance;
 
-import cz.fidentis.analyst.core.ComboSliderDouble;
 import cz.fidentis.analyst.core.LoadedActionEvent;
+import cz.fidentis.analyst.core.SpinSlider;
 import cz.fidentis.analyst.feature.FeaturePoint;
 import cz.fidentis.analyst.feature.FeaturePointType;
 import cz.fidentis.analyst.scene.DrawableFpWeights;
@@ -37,7 +37,7 @@ public class FeaturePointsPanel extends JPanel {
     
     private Map<FeaturePointType, JLabel> featurePointStats;
     private List<JCheckBox> featurePointCheckBoxes;
-    private List<ComboSliderDouble> comboSliders;
+    private List<SpinSlider> comboSliders;
     
     /**
      * Constructor.
@@ -112,8 +112,8 @@ public class FeaturePointsPanel extends JPanel {
         if (index < 0 || index >= comboSliders.size()) {
             return Double.NaN;
         }
-        ComboSliderDouble sl = comboSliders.get(index);
-        return sl.getSlider().getValue() / sl.getRecomputationFactor();
+        SpinSlider sl = comboSliders.get(index);
+        return (Double) sl.getValue();
     }
     
     /**
@@ -190,26 +190,23 @@ public class FeaturePointsPanel extends JPanel {
         c2.fill = GridBagConstraints.NONE;
         c2.insets = new Insets(0, 0, 0, 0);
 
-        final ComboSliderDouble slider = new ComboSliderDouble(); // 0.0 .. 1.0 by default
+        final SpinSlider slider = new SpinSlider(); // 0.0 .. 1.0 by default
+        slider.initDouble(DrawableFpWeights.FPW_DEFAULT_SIZE, 0.0, 100.0, 2);
         slider.setContinousSync(false);
-        slider.setRange(0, 100, 2);
-        slider.setValue(DrawableFpWeights.FPW_DEFAULT_SIZE);
         
         //slider.addInputFieldListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_RESIZE, row));
         slider.addSliderListener(createListener(action, ACTION_COMMAND_FEATURE_POINT_RESIZE, row));
         
-        slider.addInputFieldListener(
-                (ActionEvent e) -> {
-                    if (!checkBox.isSelected()) {
-                        return; // Recompute only if the feature point is selected
-                    }
-                    action.actionPerformed(new ActionEvent(
-                            e.getSource(),
-                            ActionEvent.ACTION_PERFORMED,
-                            ACTION_COMMAND_DISTANCE_RECOMPUTE
-                    ));
-                }
-        );
+        slider.addSpinnerListener((ActionEvent e) -> {
+            if (!checkBox.isSelected()) {
+                return; // Recompute only if the feature point is selected
+            }
+            action.actionPerformed(new ActionEvent(
+                    e.getSource(),
+                    ActionEvent.ACTION_PERFORMED,
+                    ACTION_COMMAND_DISTANCE_RECOMPUTE
+            ));
+        });
         add(slider, c2);
         comboSliders.add(slider);
         
diff --git a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java
index b8ceea78b48a0ea4b7010e594102f553b84cd638..3769d9fe416cf04c9240181bfcedcbf571b28091 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java
@@ -9,6 +9,11 @@ import cz.fidentis.analyst.face.events.HumanFaceEvent;
 import cz.fidentis.analyst.face.events.HumanFaceListener;
 import cz.fidentis.analyst.face.events.HumanFaceTransformedEvent;
 import cz.fidentis.analyst.face.events.SymmetryPlaneChangedEvent;
+import cz.fidentis.analyst.visitors.mesh.Curvature;
+import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling;
+import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling;
+import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling;
+import cz.fidentis.analyst.visitors.mesh.sampling.RandomSampling;
 import java.awt.Color;
 import java.awt.event.ActionEvent;
 import javax.swing.JFormattedTextField;
@@ -214,6 +219,7 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL
     
     protected void applyICP() {
         Logger out = Logger.measureTime();
+        PointSampling samplingStrategy = getSamplingStrategy(getCanvas().getSecondaryFace().getCurvature());
         
         HumanFaceUtils.alignMeshes(
                 getCanvas().getPrimaryFace(),
@@ -221,7 +227,7 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL
                 controlPanel.getMaxIcpIterParam(),
                 controlPanel.getScaleParam(),
                 controlPanel.getMinIcpErrorParam(),
-                controlPanel.getIcpUndersampling(),
+                samplingStrategy,
                 false // drop k-d tree, if exists
         );
         
@@ -229,7 +235,8 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL
                 + getCanvas().getPrimaryFace().getMeshModel().getNumVertices()
                 + "/"
                 + getCanvas().getSecondaryFace().getMeshModel().getNumVertices()
-                + " vertices"
+                + " vertices. Downsampling of the secondary face: "
+                + samplingStrategy
         );
     }
     
@@ -278,4 +285,26 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL
                 true   // forbid rotation around normal
         );
     }
+    
+    private PointSampling getSamplingStrategy(Curvature samplingCurvature) {
+        String st = controlPanel.getIcpUdersamplingStrategy();
+        double strength = controlPanel.getIcpUndersamplingStrength() / 100.0;
+        
+        if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[0])) {
+            return new NoSampling();
+        } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[1])) {
+            return new RandomSampling(strength);
+        } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[2])) {
+            return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MEAN, strength);
+        } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[3])) {
+            return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.GAUSSIAN, strength);
+        } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[4])) {
+            return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MAX, strength);
+        } else if (st.equals(RegistrationPanel.POINT_SAMPLING_STRATEGIES[5])) {
+            return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MIN, strength);
+        } else {
+            return null;
+        }
+    }
+    
 }
diff --git a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form
index d7774fda01f026a5626269f363185cb29cd662c7..3f0d2ad93b51e0bae609084bab9e6d169383a9d9 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form
+++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.form
@@ -77,7 +77,7 @@
               <Component id="jPanel4" min="-2" max="-2" attributes="0"/>
               <EmptySpace max="-2" attributes="0"/>
               <Component id="jPanel2" min="-2" max="-2" attributes="0"/>
-              <EmptySpace min="-2" pref="451" max="-2" attributes="0"/>
+              <EmptySpace min="-2" pref="481" max="-2" attributes="0"/>
               <Component id="jSeparator11" min="-2" pref="10" max="-2" attributes="0"/>
               <EmptySpace min="-2" pref="221" max="-2" attributes="0"/>
               <Component id="jSeparator2" min="-2" pref="10" max="-2" attributes="0"/>
@@ -899,34 +899,53 @@
               <Group type="102" attributes="0">
                   <EmptySpace max="-2" attributes="0"/>
                   <Group type="103" groupAlignment="0" attributes="0">
+                      <Group type="102" attributes="0">
+                          <Group type="103" groupAlignment="0" attributes="0">
+                              <Group type="102" attributes="0">
+                                  <Group type="103" groupAlignment="0" attributes="0">
+                                      <Component id="jLabel9" alignment="0" min="-2" max="-2" attributes="0"/>
+                                      <Component id="jLabel5" alignment="0" min="-2" max="-2" attributes="0"/>
+                                      <Component id="jLabel6" alignment="0" min="-2" max="-2" attributes="0"/>
+                                  </Group>
+                                  <EmptySpace min="-2" pref="15" max="-2" attributes="0"/>
+                              </Group>
+                              <Group type="102" alignment="1" attributes="0">
+                                  <Component id="jLabel8" min="-2" max="-2" attributes="0"/>
+                                  <EmptySpace max="-2" attributes="0"/>
+                              </Group>
+                          </Group>
+                          <Group type="103" groupAlignment="0" attributes="0">
+                              <Group type="102" attributes="0">
+                                  <Group type="103" groupAlignment="0" attributes="0">
+                                      <Component id="jFormattedTextField2" min="-2" pref="53" max="-2" attributes="0"/>
+                                      <Component id="jFormattedTextField1" min="-2" pref="49" max="-2" attributes="0"/>
+                                      <Component id="jCheckBox1" alignment="0" min="-2" max="-2" attributes="0"/>
+                                      <Component id="spinSlider1" min="-2" max="-2" attributes="0"/>
+                                  </Group>
+                                  <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
+                              </Group>
+                              <Group type="102" alignment="0" attributes="0">
+                                  <Component id="jComboBox1" min="-2" max="-2" attributes="0"/>
+                                  <EmptySpace max="32767" attributes="0"/>
+                              </Group>
+                          </Group>
+                      </Group>
                       <Group type="102" alignment="0" attributes="0">
-                          <Component id="jButton1" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="jButton2" min="-2" max="-2" attributes="0"/>
+                          <Group type="103" groupAlignment="1" attributes="0">
+                              <Component id="jLabel3" alignment="0" min="-2" max="-2" attributes="0"/>
+                              <Group type="102" attributes="0">
+                                  <Component id="jButton1" min="-2" max="-2" attributes="0"/>
+                                  <EmptySpace max="-2" attributes="0"/>
+                                  <Component id="jButton2" min="-2" max="-2" attributes="0"/>
+                              </Group>
+                          </Group>
                           <EmptySpace max="-2" attributes="0"/>
                           <Component id="jButton3" min="-2" max="-2" attributes="0"/>
                           <EmptySpace type="unrelated" max="-2" attributes="0"/>
                           <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/>
-                      </Group>
-                      <Group type="102" alignment="0" attributes="0">
-                          <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace type="separate" max="-2" attributes="0"/>
-                          <Component id="jLabel5" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="jFormattedTextField1" min="-2" pref="49" max="-2" attributes="0"/>
-                          <EmptySpace type="separate" max="-2" attributes="0"/>
-                          <Component id="jLabel6" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="jFormattedTextField2" min="-2" pref="53" max="-2" attributes="0"/>
+                          <EmptySpace max="32767" attributes="0"/>
                       </Group>
                   </Group>
-                  <EmptySpace max="32767" attributes="0"/>
-              </Group>
-              <Group type="102" alignment="0" attributes="0">
-                  <Component id="comboSliderInteger1" min="-2" max="-2" attributes="0"/>
-                  <EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
-                  <Component id="jLabel8" min="-2" max="-2" attributes="0"/>
-                  <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
               </Group>
           </Group>
         </DimensionLayout>
@@ -942,22 +961,37 @@
                       </Group>
                       <Component id="jButtonInfo1" pref="0" max="32767" attributes="0"/>
                   </Group>
-                  <EmptySpace pref="16" max="32767" attributes="0"/>
-                  <Group type="103" groupAlignment="3" attributes="0">
-                      <Component id="jCheckBox1" alignment="3" min="-2" max="-2" attributes="0"/>
-                      <Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/>
-                      <Component id="jFormattedTextField1" alignment="3" min="-2" pref="22" max="-2" attributes="0"/>
-                      <Component id="jLabel6" alignment="3" min="-2" max="-2" attributes="0"/>
-                      <Component id="jFormattedTextField2" alignment="3" min="-2" pref="20" max="-2" attributes="0"/>
+                  <EmptySpace type="separate" max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
                   </Group>
-                  <EmptySpace max="-2" attributes="0"/>
+                  <EmptySpace type="separate" max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="jFormattedTextField1" min="-2" pref="22" max="-2" attributes="0"/>
+                      <Component id="jLabel5" alignment="0" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace type="unrelated" max="-2" attributes="0"/>
                   <Group type="103" groupAlignment="0" attributes="0">
-                      <Component id="comboSliderInteger1" alignment="0" min="-2" max="-2" attributes="0"/>
+                      <Component id="jFormattedTextField2" min="-2" pref="20" max="-2" attributes="0"/>
+                      <Component id="jLabel6" min="-2" pref="16" max="-2" attributes="0"/>
+                  </Group>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Group type="102" attributes="0">
+                          <EmptySpace type="unrelated" max="-2" attributes="0"/>
+                          <Component id="spinSlider1" min="-2" max="-2" attributes="0"/>
+                      </Group>
                       <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
+                          <EmptySpace type="separate" min="-2" max="-2" attributes="0"/>
                           <Component id="jLabel8" min="-2" max="-2" attributes="0"/>
                       </Group>
                   </Group>
+                  <EmptySpace type="unrelated" max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="jComboBox1" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="jLabel9" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="32767" attributes="0"/>
               </Group>
           </Group>
         </DimensionLayout>
@@ -1015,8 +1049,6 @@
             </Property>
           </Properties>
         </Component>
-        <Component class="cz.fidentis.analyst.core.ComboSliderInteger" name="comboSliderInteger1">
-        </Component>
         <Component class="javax.swing.JButton" name="jButton1">
           <Properties>
             <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
@@ -1079,6 +1111,32 @@
             <Property name="rolloverEnabled" type="boolean" value="false"/>
           </Properties>
         </Component>
+        <Component class="javax.swing.JLabel" name="jLabel9">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jLabel9.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JComboBox" name="jComboBox1">
+          <Properties>
+            <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+              <StringArray count="0"/>
+            </Property>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+          </AuxValues>
+        </Component>
+        <Component class="javax.swing.JLabel" name="jLabel3">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="cz/fidentis/analyst/registration/Bundle.properties" key="RegistrationPanel.jLabel3.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+        </Component>
+        <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider1">
+        </Component>
       </SubComponents>
     </Container>
     <Container class="javax.swing.JPanel" name="jPanel4">
diff --git a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java
index bc2214834b7ef705e82c155c344b62e5fa83bbeb..475df698353347b4fbd8274a6c0cee9da3e05206 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationPanel.java
@@ -4,6 +4,7 @@ import cz.fidentis.analyst.canvas.Direction;
 import cz.fidentis.analyst.core.ControlPanel;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.util.Arrays;
 import java.util.DoubleSummaryStatistics;
 import javax.swing.ImageIcon;
 import javax.swing.JOptionPane;
@@ -37,12 +38,24 @@ public class RegistrationPanel extends ControlPanel {
     
     public static final String ACTION_COMMAND_ALIGN_SYMMETRY_PLANES = "align symmetry planes";
     
+    public static final String ACTION_COMMAND_POINT_SAMPLING_STRATEGY = "Point sampling strategy";
+    
     /*
      * Configuration of panel-specific GUI elements
      */
     public static final String STRATEGY_POINT_TO_POINT = "Point to point";
     public static final String STRATEGY_POINT_TO_TRIANGLE = "Point to triangle";
     
+    public static final String[] POINT_SAMPLING_STRATEGIES = new String[] {
+        "None",
+        "Random Sampling",
+        "Mean Curvature",
+        "Gaussian Curvature",
+        "Max Curvature",
+        "Min Curvature",
+    };
+    
+    
     /**
      * Animator which animates transformations
      */
@@ -53,7 +66,7 @@ public class RegistrationPanel extends ControlPanel {
     /**
      * ICP undersampling. 100 = none
      */
-    private int undersampling = 100;
+    private int undersamplingStrength = 50;
     
     private Vector3d manualRotation = new Vector3d();
     private Vector3d manualTranslation = new Vector3d();
@@ -78,11 +91,9 @@ public class RegistrationPanel extends ControlPanel {
         thersholdFTF.setValue(5.0);
         thersholdFTF.addActionListener(createListener(action, ACTION_COMMAND_FP_CLOSENESS_THRESHOLD));
 
-        //comboSliderInteger1.setSliderEast();
-        comboSliderInteger1.setRange(10, 100);
-        comboSliderInteger1.setValue(this.undersampling);
-        comboSliderInteger1.addInputFieldListener((ActionEvent ae) -> { // update slider when the input field changed
-            this.undersampling = comboSliderInteger1.getValue();
+        spinSlider1.initPercentage(this.undersamplingStrength);
+        spinSlider1.addSpinnerListener((ActionEvent e) -> { // update slider when the input field changed
+            this.undersamplingStrength = (Integer) spinSlider1.getValue();
         });
         
         jButtonInfo1.addActionListener((ActionEvent e) -> { 
@@ -91,6 +102,10 @@ public class RegistrationPanel extends ControlPanel {
         
         jFormattedTextField1.setValue(Double.valueOf(0.3));
         jFormattedTextField2.setValue(Integer.valueOf(50));
+        
+        Arrays.stream(POINT_SAMPLING_STRATEGIES).forEach(v -> jComboBox1.addItem(v));
+        jComboBox1.setSelectedIndex(0);
+        jComboBox1.addActionListener(createListener(action, ACTION_COMMAND_POINT_SAMPLING_STRATEGY));
     }
     
     /**
@@ -127,8 +142,16 @@ public class RegistrationPanel extends ControlPanel {
      * Returns ICP undersampling parameter
      * @return ICP undersampling parameter
      */
-    public int getIcpUndersampling() {
-        return undersampling;
+    public int getIcpUndersamplingStrength() {
+        return undersamplingStrength;
+    }
+    
+    /**
+     * Return selected point sampling strategy
+     * @return selected point sampling strategy
+     */
+    public String getIcpUdersamplingStrategy() {
+        return POINT_SAMPLING_STRATEGIES[jComboBox1.getSelectedIndex()];
     }
     
     /**
@@ -318,11 +341,14 @@ public class RegistrationPanel extends ControlPanel {
         jLabel6 = new javax.swing.JLabel();
         jFormattedTextField2 = new javax.swing.JFormattedTextField();
         jLabel8 = new javax.swing.JLabel();
-        comboSliderInteger1 = new cz.fidentis.analyst.core.ComboSliderInteger();
         jButton1 = new javax.swing.JButton();
         jButton2 = new javax.swing.JButton();
         jButton3 = new javax.swing.JButton();
         jButtonInfo1 = new javax.swing.JButton();
+        jLabel9 = new javax.swing.JLabel();
+        jComboBox1 = new javax.swing.JComboBox<>();
+        jLabel3 = new javax.swing.JLabel();
+        spinSlider1 = new cz.fidentis.analyst.core.SpinSlider();
         jPanel4 = new javax.swing.JPanel();
         featurePointsLabel = new javax.swing.JLabel();
         thersholdFTF = new javax.swing.JFormattedTextField();
@@ -820,6 +846,10 @@ public class RegistrationPanel extends ControlPanel {
         jButtonInfo1.setRequestFocusEnabled(false);
         jButtonInfo1.setRolloverEnabled(false);
 
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel9, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jLabel9.text")); // NOI18N
+
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jLabel3.text")); // NOI18N
+
         javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
         jPanel1.setLayout(jPanel1Layout);
         jPanel1Layout.setHorizontalGroup(
@@ -828,29 +858,39 @@ public class RegistrationPanel extends ControlPanel {
                 .addContainerGap()
                 .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                     .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addComponent(jButton1)
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(jButton2)
+                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addGroup(jPanel1Layout.createSequentialGroup()
+                                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                                    .addComponent(jLabel9)
+                                    .addComponent(jLabel5)
+                                    .addComponent(jLabel6))
+                                .addGap(15, 15, 15))
+                            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
+                                .addComponent(jLabel8)
+                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)))
+                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addGroup(jPanel1Layout.createSequentialGroup()
+                                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                                    .addComponent(jFormattedTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                    .addComponent(jFormattedTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                    .addComponent(jCheckBox1)
+                                    .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                                .addGap(0, 0, Short.MAX_VALUE))
+                            .addGroup(jPanel1Layout.createSequentialGroup()
+                                .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
+                    .addGroup(jPanel1Layout.createSequentialGroup()
+                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+                            .addComponent(jLabel3, javax.swing.GroupLayout.Alignment.LEADING)
+                            .addGroup(jPanel1Layout.createSequentialGroup()
+                                .addComponent(jButton1)
+                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                                .addComponent(jButton2)))
                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                         .addComponent(jButton3)
                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
-                        .addComponent(jButtonInfo1))
-                    .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addComponent(jCheckBox1)
-                        .addGap(18, 18, 18)
-                        .addComponent(jLabel5)
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(jFormattedTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE)
-                        .addGap(18, 18, 18)
-                        .addComponent(jLabel6)
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(jFormattedTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)))
-                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
-            .addGroup(jPanel1Layout.createSequentialGroup()
-                .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                .addGap(12, 12, 12)
-                .addComponent(jLabel8)
-                .addGap(0, 0, Short.MAX_VALUE))
+                        .addComponent(jButtonInfo1)
+                        .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
         );
         jPanel1Layout.setVerticalGroup(
             jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -862,19 +902,30 @@ public class RegistrationPanel extends ControlPanel {
                         .addComponent(jButton2)
                         .addComponent(jButton3))
                     .addComponent(jButtonInfo1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
-                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 16, Short.MAX_VALUE)
-                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                .addGap(18, 18, 18)
+                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                     .addComponent(jCheckBox1)
-                    .addComponent(jLabel5)
+                    .addComponent(jLabel3))
+                .addGap(18, 18, 18)
+                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                     .addComponent(jFormattedTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
-                    .addComponent(jLabel6)
-                    .addComponent(jFormattedTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE))
-                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                    .addComponent(jLabel5))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                 .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                    .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(jFormattedTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(jPanel1Layout.createSequentialGroup()
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                        .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                     .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addGap(21, 21, 21)
-                        .addComponent(jLabel8))))
+                        .addGap(18, 18, 18)
+                        .addComponent(jLabel8)))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(jLabel9))
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
         );
 
         jPanel4.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(RegistrationPanel.class, "RegistrationPanel.jPanel4.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 14))); // NOI18N
@@ -1020,7 +1071,7 @@ public class RegistrationPanel extends ControlPanel {
                 .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                .addGap(451, 451, 451)
+                .addGap(481, 481, 481)
                 .addComponent(jSeparator11, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addGap(221, 221, 221)
                 .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
@@ -1236,20 +1287,22 @@ public class RegistrationPanel extends ControlPanel {
 
 
     // Variables declaration - do not modify//GEN-BEGIN:variables
-    private cz.fidentis.analyst.core.ComboSliderInteger comboSliderInteger1;
     private javax.swing.JLabel featurePointsLabel;
     private javax.swing.JButton jButton1;
     private javax.swing.JButton jButton2;
     private javax.swing.JButton jButton3;
     private javax.swing.JButton jButtonInfo1;
     private javax.swing.JCheckBox jCheckBox1;
+    private javax.swing.JComboBox<String> jComboBox1;
     private javax.swing.JFormattedTextField jFormattedTextField1;
     private javax.swing.JFormattedTextField jFormattedTextField2;
     private javax.swing.JLabel jLabel1;
     private javax.swing.JLabel jLabel2;
+    private javax.swing.JLabel jLabel3;
     private javax.swing.JLabel jLabel5;
     private javax.swing.JLabel jLabel6;
     private javax.swing.JLabel jLabel8;
+    private javax.swing.JLabel jLabel9;
     private javax.swing.JPanel jPanel1;
     private javax.swing.JPanel jPanel2;
     private javax.swing.JPanel jPanel4;
@@ -1282,6 +1335,7 @@ public class RegistrationPanel extends ControlPanel {
     private javax.swing.JButton scalePlusButton;
     private javax.swing.ButtonGroup secondaryRenerModeGroup;
     private javax.swing.JPanel shiftPanel;
+    private cz.fidentis.analyst.core.SpinSlider spinSlider1;
     private javax.swing.JFormattedTextField thersholdFTF;
     private javax.swing.JButton thersholdUpButton;
     private javax.swing.JButton thresholdDownButton;
diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/Drawable.java b/GUI/src/main/java/cz/fidentis/analyst/scene/Drawable.java
index 3b60dee7000f46b2a9f5ffadbe1070d89c01979c..1c31da3dac59bb042871e4d5e28ba27a893445b5 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/scene/Drawable.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/scene/Drawable.java
@@ -163,11 +163,13 @@ public abstract class Drawable {
      * @throws IllegalArgumentException if renderMode isn't {@code GL_FILL}, {@code GL_LINE} or {@code GL_POINT}
      */
     public void setRenderMode(int renderMode) {
+        /*
         if (renderMode != GL2.GL_FILL && 
                 renderMode != GL2.GL_LINE &&
                 renderMode != GL2.GL_POINT) {
             throw new IllegalArgumentException("invalid mode");
         }
+        */
         this.renderMode = renderMode;
     }
 
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 8537e830702bbda21e1a727eebace42461601579..1e2ee2277eabecdb25d8dcc14db799650550626e 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFace.java
@@ -1,8 +1,14 @@
 package cz.fidentis.analyst.scene;
 
 import com.jogamp.opengl.GL2;
+import com.jogamp.opengl.glu.GLU;
+import com.jogamp.opengl.glu.GLUquadric;
 import cz.fidentis.analyst.face.HumanFace;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
+import cz.fidentis.analyst.mesh.core.MeshPoint;
+import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling;
+import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling;
+import cz.fidentis.analyst.visitors.mesh.sampling.RandomSampling;
 import java.awt.Color;
 import java.util.Collections;
 import java.util.HashMap;
@@ -16,6 +22,14 @@ import java.util.Map;
  * @author Daniel Schramm
  */
 public class DrawableFace extends DrawableMesh {
+
+    public static final int RANDOM_SAMPLING = 100000;
+    public static final int CURVATURE_SAMPLING_MIN = 100001;
+    public static final int CURVATURE_SAMPLING_MAX = 100002;
+    public static final int CURVATURE_SAMPLING_MEAN = 100003;
+    public static final int CURVATURE_SAMPLING_GAUSSIAN = 100004;
+    
+    private PointSampling sampling = null;
     
     public static final Color SKIN_COLOR_PRIMARY = new Color(224, 172, 105);
     public static final Color SKIN_COLOR_SECONDARY = new Color(242, 214, 208); //new Color(236, 188, 180);
@@ -130,8 +144,70 @@ public class DrawableFace extends DrawableMesh {
         if (isHeatmapRendered()) {
             new HeatmapRenderer().drawMeshModel(gl, getModel(), heatmap, heatmapSaturation, this.getTransparency());
         } else {
-            super.renderObject(gl);
+            switch (getRenderMode()) {
+                case RANDOM_SAMPLING:
+                    if (sampling == null || sampling.getClass() != RandomSampling.class) {
+                        sampling = new RandomSampling(500);
+                        getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet));
+                    }
+                    break;
+                case CURVATURE_SAMPLING_MIN:
+                    if (sampling == null 
+                            || sampling.getClass() != CurvatureSampling.class 
+                            || ((CurvatureSampling)sampling).getCurvatureAlg() != CurvatureSampling.CurvatureAlg.MIN) {
+                        sampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.MIN, 500);
+                        getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet));
+                    }
+                    break;
+                case CURVATURE_SAMPLING_MAX:
+                    if (sampling == null 
+                            || sampling.getClass() != CurvatureSampling.class 
+                            || ((CurvatureSampling)sampling).getCurvatureAlg() != CurvatureSampling.CurvatureAlg.MAX) {
+                    sampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.MAX, 500);
+                        getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet));
+                    }
+                    break;
+                case CURVATURE_SAMPLING_MEAN:
+                    if (sampling == null 
+                            || sampling.getClass() != CurvatureSampling.class 
+                            || ((CurvatureSampling)sampling).getCurvatureAlg() != CurvatureSampling.CurvatureAlg.MEAN) {
+                    sampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.MEAN, 500);
+                        getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet));
+                    }
+                    break;
+                case CURVATURE_SAMPLING_GAUSSIAN:
+                    if (sampling == null 
+                            || sampling.getClass() != CurvatureSampling.class 
+                            || ((CurvatureSampling)sampling).getCurvatureAlg() != CurvatureSampling.CurvatureAlg.GAUSSIAN) {
+                    sampling = new CurvatureSampling(CurvatureSampling.CurvatureAlg.GAUSSIAN, 500);
+                        getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet));
+                    }
+                    break;
+                default:
+                    super.renderObject(gl);
+                    return;
+            }
+            
+            getFacets().stream().forEach(facet -> sampling.visitMeshFacet(facet));
+            renderPoints(gl, sampling.getSamples());
         }
     }
     
+    protected void renderPoints(GL2 gl, List<MeshPoint> points) {
+        GLU gluContext = new GLU();
+        
+        gl.glBegin(GL2.GL_POINTS); //vertices are rendered as triangles
+        points.stream().forEach(mp -> {
+            gl.glPushMatrix(); 
+            gl.glTranslated(mp.getX(), mp.getY(), mp.getZ());
+            GLUquadric center = gluContext.gluNewQuadric();
+            gluContext.gluQuadricDrawStyle(center, GLU.GLU_FILL);
+            gluContext.gluQuadricNormals(center, GLU.GLU_FLAT);
+            gluContext.gluQuadricOrientation(center, GLU.GLU_OUTSIDE);
+            gluContext.gluSphere(center, 0.7, 16, 16);
+            gluContext.gluDeleteQuadric(center);
+            gl.glPopMatrix();    
+        });
+        gl.glEnd();
+    }
 }
diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePointCloud.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePointCloud.java
new file mode 100644
index 0000000000000000000000000000000000000000..07b20c2c0142e1de7cd4d027f661519145bc5169
--- /dev/null
+++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePointCloud.java
@@ -0,0 +1,60 @@
+package cz.fidentis.analyst.scene;
+
+import com.jogamp.opengl.GL2;
+import com.jogamp.opengl.glu.GLU;
+import com.jogamp.opengl.glu.GLUquadric;
+import cz.fidentis.analyst.mesh.core.MeshPoint;
+import java.awt.Color;
+import java.util.List;
+
+/**
+ * Points rendered as small spheres.
+ * 
+ * @author Radek Oslejsek
+ */
+public class DrawablePointCloud extends Drawable {
+    
+    public static final Color DEFAULT_COLOR = Color.RED.brighter();
+    public static final double FP_DEFAULT_SIZE = .7f;
+    
+    private static final GLU GLU_CONTEXT = new GLU();
+    
+    private final List<MeshPoint> points;
+    
+    /**
+     * Constructor.
+     * 
+     * @param featurePoints Feature points
+     * @param defaultColor Default color
+     * @param defaultPerimeter Default perimeter
+     */
+    public DrawablePointCloud(List<MeshPoint> points) {
+        this.points = points;
+        setColor(DEFAULT_COLOR);
+    }
+    
+    @Override
+    protected void renderObject(GL2 gl) {
+        float[] rgba = {
+            getColor().getRed() / 255f, 
+            getColor().getGreen() / 255f, 
+            getColor().getBlue() / 255f, 
+            getTransparency()
+        };
+        gl.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_DIFFUSE, rgba, 0); // set default color
+        
+        gl.glBegin(GL2.GL_POINTS); //vertices are rendered as triangles
+        points.stream().forEach(mp -> {
+            gl.glPushMatrix(); 
+            gl.glTranslated(mp.getX(), mp.getY(), mp.getZ());
+            GLUquadric center = GLU_CONTEXT.gluNewQuadric();
+            GLU_CONTEXT.gluQuadricDrawStyle(center, GLU.GLU_FILL);
+            GLU_CONTEXT.gluQuadricNormals(center, GLU.GLU_FLAT);
+            GLU_CONTEXT.gluQuadricOrientation(center, GLU.GLU_OUTSIDE);
+            GLU_CONTEXT.gluSphere(center, 0.7, 16, 16);
+            GLU_CONTEXT.gluDeleteQuadric(center);
+            gl.glPopMatrix();    
+        });
+        gl.glEnd();
+    }
+}
diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java
index 3534229e31312cadf1650786c9e1870036c26f54..224e0ce48c7cb9d30468314d21ff6c0fa9badb68 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java
@@ -1,8 +1,8 @@
 package cz.fidentis.analyst.symmetry;
 
 import cz.fidentis.analyst.canvas.Canvas;
-import cz.fidentis.analyst.core.ComboSliderDouble;
 import cz.fidentis.analyst.core.ControlPanelAction;
+import cz.fidentis.analyst.core.DoubleSpinner;
 import cz.fidentis.analyst.face.HumanFace;
 import cz.fidentis.analyst.face.events.HumanFaceEvent;
 import cz.fidentis.analyst.face.events.HumanFaceListener;
@@ -118,7 +118,7 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
                 }
                 break;
             case ProfilesPanel.ACTION_SHIFT_CUTTING_PLANE:
-                double sliderVal = ((ComboSliderDouble) ae.getSource()).getValue();
+                double sliderVal = (Double) ((DoubleSpinner) ae.getSource()).getValue();
                 double xExtent = maxExtentX();
                 double shift = (sliderVal >= 0.5) 
                         ? ( (sliderVal - 0.5) / 0.5) * xExtent  // move right
@@ -143,10 +143,10 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
                        controlPanel.setDefaultPlaneSelection();
                        return;
                     }
-                    controlPanel.resetSliderSilently();
+                    controlPanel.resetSlider();
                     computeCuttingPlanesFromSymmetry();
                 } else if (option.equals(ProfilesPanel.OPTION_VERTICAL_PLANE)) {
-                    controlPanel.resetSliderSilently();
+                    controlPanel.resetSlider();
                     computeVerticalCuttingPlanes();
                 }
                 
@@ -167,7 +167,8 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
     public void acceptEvent(HumanFaceEvent event) {
         // If some human face is transformed, then cutting planes have to updated.
         if (event instanceof HumanFaceTransformedEvent) {
-            controlPanel.resetSliderSilently();
+            //controlPanel.resetSliderSilently();
+            controlPanel.resetSlider();
             if (cuttingPlaneFromSymmetry) {
                 computeCuttingPlanesFromSymmetry(); // recompute cutting planes
                 //getCanvas().getScene().showCuttingPlanes(false, controlPanel.isMirrorCutsChecked());
diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.form b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.form
index 652258b3a0b315ab88eb05e8c1852e0c7dbcb788..d7bc5be13d3ce00508a0780adc5b49e9a4a0614c 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.form
+++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.form
@@ -61,9 +61,9 @@
                           <EmptySpace type="separate" max="-2" attributes="0"/>
                           <Component id="jButton1" min="-2" max="-2" attributes="0"/>
                       </Group>
-                      <Component id="comboSliderDouble1" alignment="0" min="-2" max="-2" attributes="0"/>
+                      <Component id="spinSlider1" alignment="0" min="-2" max="-2" attributes="0"/>
                   </Group>
-                  <EmptySpace pref="197" max="32767" attributes="0"/>
+                  <EmptySpace pref="174" max="32767" attributes="0"/>
               </Group>
           </Group>
         </DimensionLayout>
@@ -71,21 +71,19 @@
           <Group type="103" groupAlignment="0" attributes="0">
               <Group type="102" attributes="0">
                   <EmptySpace max="-2" attributes="0"/>
-                  <Component id="comboSliderDouble1" min="-2" max="-2" attributes="0"/>
+                  <Component id="spinSlider1" min="-2" max="-2" attributes="0"/>
                   <EmptySpace max="-2" attributes="0"/>
                   <Group type="103" groupAlignment="3" attributes="0">
                       <Component id="jComboBox1" alignment="3" min="-2" max="-2" attributes="0"/>
                       <Component id="jCheckBox1" alignment="3" min="-2" max="-2" attributes="0"/>
                       <Component id="jButton1" alignment="3" min="-2" max="-2" attributes="0"/>
                   </Group>
-                  <EmptySpace max="32767" attributes="0"/>
+                  <EmptySpace pref="25" max="32767" attributes="0"/>
               </Group>
           </Group>
         </DimensionLayout>
       </Layout>
       <SubComponents>
-        <Component class="cz.fidentis.analyst.core.ComboSliderDouble" name="comboSliderDouble1">
-        </Component>
         <Component class="javax.swing.JCheckBox" name="jCheckBox1">
           <Properties>
             <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
@@ -113,6 +111,8 @@
             <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
           </AuxValues>
         </Component>
+        <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider1">
+        </Component>
       </SubComponents>
     </Container>
     <Container class="cz.fidentis.analyst.symmetry.CurveRenderingPanel" name="polylinePanel1">
diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java
index 347c46eaba2516164c99e4919de0baa7b976029d..f87eaaa3607da04334c8c9b003c667bc76031963 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java
@@ -34,6 +34,8 @@ public class ProfilesPanel extends ControlPanel {
     public static final String ICON = "profiles28x28.png";
     public static final String NAME = "Profiles";
     
+    private final ActionListener action;
+    
     /**
      * Constructor for one face.
      * 
@@ -55,12 +57,13 @@ public class ProfilesPanel extends ControlPanel {
         setName(NAME);
         initComponents();
         
+        this.action = action;
+        
         polylinePanel1.setPrimarySegments(primaryCurve);
         polylinePanel1.setSecondarySegments(secondaryCurve);
         
-        comboSliderDouble1.setRange(0, 1, 2);
-        comboSliderDouble1.setValue(0.5);
-        comboSliderDouble1.addListener(createListener(action, ACTION_SHIFT_CUTTING_PLANE));
+        this.resetSlider();
+        
         jCheckBox1.addActionListener(createListener(action, ACTION_MIRROR_CUTS));
         jButton1.addActionListener(createListener(action, ACTION_COMMAND_EXPORT));
         
@@ -115,10 +118,11 @@ public class ProfilesPanel extends ControlPanel {
     }
     
     /**
-     * Resets the slider without triggering action event.
+     * Resets the slider into initial values
      */
-    public void resetSliderSilently() {
-        comboSliderDouble1.setValueSilently(0.5);
+    public void resetSlider() {
+        spinSlider1.initDouble(0.5, 0.0, 1.0, 2);
+        spinSlider1.addSpinnerListener(createListener(this.action, ACTION_SHIFT_CUTTING_PLANE));
     }
     
     /**
@@ -138,10 +142,10 @@ public class ProfilesPanel extends ControlPanel {
     private void initComponents() {
 
         jPanel1 = new javax.swing.JPanel();
-        comboSliderDouble1 = new cz.fidentis.analyst.core.ComboSliderDouble();
         jCheckBox1 = new javax.swing.JCheckBox();
         jButton1 = new javax.swing.JButton();
         jComboBox1 = new javax.swing.JComboBox<>();
+        spinSlider1 = new cz.fidentis.analyst.core.SpinSlider();
         polylinePanel1 = new cz.fidentis.analyst.symmetry.CurveRenderingPanel();
 
         jPanel1.setBorder(javax.swing.BorderFactory.createEtchedBorder());
@@ -168,20 +172,20 @@ public class ProfilesPanel extends ControlPanel {
                         .addComponent(jCheckBox1)
                         .addGap(18, 18, 18)
                         .addComponent(jButton1))
-                    .addComponent(comboSliderDouble1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                .addContainerGap(197, Short.MAX_VALUE))
+                    .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addContainerGap(174, Short.MAX_VALUE))
         );
         jPanel1Layout.setVerticalGroup(
             jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
             .addGroup(jPanel1Layout.createSequentialGroup()
                 .addContainerGap()
-                .addComponent(comboSliderDouble1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                     .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                     .addComponent(jCheckBox1)
                     .addComponent(jButton1))
-                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                .addContainerGap(25, Short.MAX_VALUE))
         );
 
         polylinePanel1.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
@@ -194,7 +198,7 @@ public class ProfilesPanel extends ControlPanel {
         );
         polylinePanel1Layout.setVerticalGroup(
             polylinePanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addGap(0, 498, Short.MAX_VALUE)
+            .addGap(0, 398, Short.MAX_VALUE)
         );
 
         javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
@@ -205,7 +209,7 @@ public class ProfilesPanel extends ControlPanel {
                 .addContainerGap()
                 .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                     .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                    .addComponent(polylinePanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                    .addComponent(polylinePanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE))
                 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
         );
         layout.setVerticalGroup(
@@ -225,11 +229,11 @@ public class ProfilesPanel extends ControlPanel {
 
 
     // Variables declaration - do not modify//GEN-BEGIN:variables
-    private cz.fidentis.analyst.core.ComboSliderDouble comboSliderDouble1;
     private javax.swing.JButton jButton1;
     private javax.swing.JCheckBox jCheckBox1;
     private javax.swing.JComboBox<String> jComboBox1;
     private javax.swing.JPanel jPanel1;
     private cz.fidentis.analyst.symmetry.CurveRenderingPanel polylinePanel1;
+    private cz.fidentis.analyst.core.SpinSlider spinSlider1;
     // End of variables declaration//GEN-END:variables
 }
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 c6a46ba130367314824d83d8196a13326796ea20..a433f83ecf3c7de601073888109e6cb3a214dd98 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryAction.java
@@ -3,6 +3,7 @@ package cz.fidentis.analyst.symmetry;
 import cz.fidentis.analyst.Logger;
 import cz.fidentis.analyst.canvas.Canvas;
 import cz.fidentis.analyst.core.ControlPanelAction;
+import cz.fidentis.analyst.core.SpinSlider;
 import cz.fidentis.analyst.face.HumanFace;
 import cz.fidentis.analyst.face.events.HumanFaceEvent;
 import cz.fidentis.analyst.face.events.HumanFaceListener;
@@ -11,8 +12,14 @@ import cz.fidentis.analyst.feature.FeaturePoint;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
 import cz.fidentis.analyst.mesh.core.MeshModel;
 import cz.fidentis.analyst.scene.DrawableFace;
+import cz.fidentis.analyst.scene.DrawablePointCloud;
+import cz.fidentis.analyst.visitors.mesh.Curvature;
 import cz.fidentis.analyst.visitors.mesh.HausdorffDistance;
 import cz.fidentis.analyst.visitors.mesh.HausdorffDistance.Strategy;
+import cz.fidentis.analyst.visitors.mesh.sampling.CurvatureSampling;
+import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling;
+import cz.fidentis.analyst.visitors.mesh.sampling.PointSampling;
+import cz.fidentis.analyst.visitors.mesh.sampling.RandomSampling;
 
 import java.awt.event.ActionEvent;
 import java.util.ArrayList;
@@ -33,6 +40,9 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe
     private final SymmetryPanel controlPanel;
     private final JTabbedPane topControlPanel;
     private int sPlane = 0; // 0 = none, 1 = from mesh, 2 = from FPs
+    
+    private int primCloudSlot = -1;
+    //private int secCloudSlot = -1;
 
     /**
      * Constructor.
@@ -53,19 +63,30 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe
 
         // Place control panel to the topControlPanel
         this.topControlPanel.addTab(controlPanel.getName(), controlPanel.getIcon(), controlPanel);
-        /*
         this.topControlPanel.addChangeListener(e -> {
             // If the symmetry panel is focused...
             if (((JTabbedPane) e.getSource()).getSelectedComponent() instanceof SymmetryPanel) {
-                getCanvas().getScene().setDefaultColors();
-                getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getPrimaryFaceSlot(), true);
-                getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getSecondaryFaceSlot(), true);
-            } else {
-                getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getPrimaryFaceSlot(), false);
-                getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getSecondaryFaceSlot(), false);
+                // show point cloud:
+                if (getCanvas().getSecondaryFace() == null) { // single face analysis
+                    primCloudSlot = drawPointSamples(
+                            getCanvas().getScene().getPrimaryFaceSlot(), 
+                            primCloudSlot, 
+                            controlPanel.getPointSamplingStrength()
+                    );
+                }
+                //getCanvas().getScene().setDefaultColors();
+                //getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getPrimaryFaceSlot(), true);
+                //getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getSecondaryFaceSlot(), true);
+            } else { 
+                // hide point cloud:
+                getScene().setOtherDrawable(primCloudSlot, null);
+                //getScene().setOtherDrawable(secCloudSlot, null);
+                primCloudSlot = -1;
+                //secCloudSlot = -1;
+                //getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getPrimaryFaceSlot(), false);
+                //getCanvas().getScene().showSymmetryPlane(getCanvas().getScene().getSecondaryFaceSlot(), false);
             }
         });
-        */
 
         // Be informed about changes in faces perfomed by other GUI elements
         getPrimaryDrawableFace().getHumanFace().registerListener(this);
@@ -89,6 +110,19 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe
                 recomputeFromFeaturePoints(getCanvas().getScene().getPrimaryFaceSlot());
                 recomputeFromFeaturePoints(getCanvas().getScene().getSecondaryFaceSlot());
                 break;
+            case SymmetryPanel.ACTION_COMMAND_POINT_SAMPLING_STRENGTH:
+                int numSamples = (Integer) ((SpinSlider) ae.getSource()).getValue();
+                if (getCanvas().getSecondaryFace() == null) { // single face analysis
+                    primCloudSlot = drawPointSamples(getCanvas().getScene().getPrimaryFaceSlot(), primCloudSlot, numSamples);
+                }
+                //secCloudSlot = drawPointSamples(getCanvas().getScene().getSecondaryFaceSlot(), secCloudSlot, numSamples);
+                break;
+            case SymmetryPanel.ACTION_COMMAND_POINT_SAMPLING_STRATEGY:
+                if (getCanvas().getSecondaryFace() == null) { // single face analysis
+                    primCloudSlot = drawPointSamples(getCanvas().getScene().getPrimaryFaceSlot(), primCloudSlot, controlPanel.getPointSamplingStrength());
+                }
+                //secCloudSlot = drawPointSamples(getCanvas().getScene().getSecondaryFaceSlot(), secCloudSlot, controlPanel.getPointSamplingStrength());
+                break;
             default:
                 // do nothing
         }
@@ -121,15 +155,18 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe
         }
     }
     
-    protected void recomputeFromMesh(int index) {
-        HumanFace face = getCanvas().getHumanFace(index);
+    protected void recomputeFromMesh(int faceSlot) {
+        HumanFace face = getCanvas().getHumanFace(faceSlot);
         if (face != null) {
             Logger log = Logger.measureTime();
-            SymmetryEstimator estimator = new SymmetryEstimator(controlPanel.getSymmetryConfig());        
+            SymmetryEstimator estimator = new SymmetryEstimator(
+                    controlPanel.getSymmetryConfig(), 
+                    getSamplingStrategy(controlPanel.getPointSamplingStrength(), face.getCurvature())
+            );
             face.getMeshModel().compute(estimator);
             face.setSymmetryPlane(estimator.getSymmetryPlane());
             log.printDuration("Symmetry plane estimation from mesh for " + face.getShortName());
-            setDrawablePlane(face, index);
+            setDrawablePlane(face, faceSlot);
             face.announceEvent(new SymmetryPlaneChangedEvent(face, "", this));
         }
     }
@@ -208,4 +245,46 @@ public class SymmetryAction extends ControlPanelAction implements HumanFaceListe
         clone.compute(visitor);
         controlPanel.updateHausdorffDistanceStats(visitor.getStats(), getCanvas().getHumanFace(0) == face);
     }
+
+    private int drawPointSamples(int faceSlot, int cloudSlot, int numSamples) {
+        HumanFace face = getCanvas().getHumanFace(faceSlot);
+        if (face == null) {
+            return -1;
+        }                
+        
+        PointSampling sampling = getSamplingStrategy(numSamples, face.getCurvature());
+        face.getMeshModel().compute(sampling);
+        
+        if (sampling.getClass() == NoSampling.class) { // don't show
+            if (cloudSlot != -1) {
+                getScene().setOtherDrawable(cloudSlot, null);
+            }
+            return -1;
+        } else {
+            if (cloudSlot == -1) {
+                cloudSlot = getCanvas().getScene().getFreeSlot();
+            }
+            getScene().setOtherDrawable(cloudSlot, new DrawablePointCloud(sampling.getSamples()));
+            return cloudSlot;
+        }
+    }
+    
+    private PointSampling getSamplingStrategy(int numSamples, Curvature samplingCurvature) {
+        String st = controlPanel.getPointSamplingStrategy();
+        
+        if (st.equals(SymmetryPanel.POINT_SAMPLING_STRATEGIES[0])) {
+            return new RandomSampling(numSamples);
+        } else if (st.equals(SymmetryPanel.POINT_SAMPLING_STRATEGIES[1])) {
+            return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MEAN, numSamples);
+        } else if (st.equals(SymmetryPanel.POINT_SAMPLING_STRATEGIES[2])) {
+            return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.GAUSSIAN, numSamples);
+        } else if (st.equals(SymmetryPanel.POINT_SAMPLING_STRATEGIES[3])) {
+            return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MAX, numSamples);
+        } else if (st.equals(SymmetryPanel.POINT_SAMPLING_STRATEGIES[4])) {
+            return new CurvatureSampling(samplingCurvature, CurvatureSampling.CurvatureAlg.MIN, numSamples);
+        } else {
+            return null;
+        }
+    }
+    
 }
diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form
index 4147dbe6ef263e9f35b8634dc24ec2d4df902d3c..f3c452cce00c99bc0a4717e7c4d68fd1992f3942 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form
+++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.form
@@ -16,16 +16,12 @@
   <Layout>
     <DimensionLayout dim="0">
       <Group type="103" groupAlignment="0" attributes="0">
-          <Group type="102" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
               <EmptySpace max="-2" attributes="0"/>
-              <Group type="103" groupAlignment="0" attributes="0">
-                  <Component id="jPanel1" min="-2" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="1" max="-2" attributes="0">
+                  <Component id="jPanel3" max="32767" attributes="0"/>
+                  <Component id="jPanel1" alignment="1" max="32767" attributes="0"/>
                   <Component id="jPanel2" alignment="0" min="-2" max="-2" attributes="0"/>
-                  <Group type="102" alignment="0" attributes="0">
-                      <Component id="jComboBox1" min="-2" max="-2" attributes="0"/>
-                      <EmptySpace type="separate" max="-2" attributes="0"/>
-                      <Component id="jButton2" min="-2" max="-2" attributes="0"/>
-                  </Group>
               </Group>
               <EmptySpace max="32767" attributes="0"/>
           </Group>
@@ -35,15 +31,12 @@
       <Group type="103" groupAlignment="0" attributes="0">
           <Group type="102" alignment="0" attributes="0">
               <EmptySpace max="-2" attributes="0"/>
-              <Component id="jPanel1" min="-2" max="-2" attributes="0"/>
+              <Component id="jPanel3" min="-2" max="-2" attributes="0"/>
               <EmptySpace max="-2" attributes="0"/>
-              <Group type="103" groupAlignment="3" attributes="0">
-                  <Component id="jComboBox1" alignment="3" min="-2" max="-2" attributes="0"/>
-                  <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/>
-              </Group>
+              <Component id="jPanel1" min="-2" max="-2" attributes="0"/>
               <EmptySpace max="-2" attributes="0"/>
               <Component id="jPanel2" min="-2" max="-2" attributes="0"/>
-              <EmptySpace pref="82" max="32767" attributes="0"/>
+              <EmptySpace pref="98" max="32767" attributes="0"/>
           </Group>
       </Group>
     </DimensionLayout>
@@ -67,50 +60,48 @@
               <Group type="102" attributes="0">
                   <EmptySpace max="-2" attributes="0"/>
                   <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="jLabel9" max="32767" attributes="0"/>
                       <Group type="102" attributes="0">
-                          <Group type="103" groupAlignment="0" max="-2" attributes="0">
-                              <Component id="jLabel5" pref="143" max="32767" attributes="0"/>
-                              <Component id="jLabel6" max="32767" attributes="0"/>
+                          <Group type="103" groupAlignment="0" attributes="0">
+                              <Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0">
+                                  <Component id="jLabel4" max="32767" attributes="0"/>
+                                  <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
+                              </Group>
+                              <Component id="jLabel2" alignment="0" min="-2" pref="165" max="-2" attributes="0"/>
+                              <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
+                              <Component id="jLabel5" min="-2" pref="143" max="-2" attributes="0"/>
+                              <Component id="jLabel6" alignment="0" min="-2" pref="143" max="-2" attributes="0"/>
                           </Group>
-                          <EmptySpace type="separate" max="-2" attributes="0"/>
-                          <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace min="0" pref="12" max="32767" attributes="0"/>
                       </Group>
-                      <Group type="102" attributes="0">
+                  </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="jComboBox2" min="-2" max="-2" attributes="0"/>
+                      <Group type="102" alignment="0" attributes="0">
                           <Group type="103" groupAlignment="1" max="-2" attributes="0">
-                              <Component id="jLabel4" max="32767" attributes="0"/>
-                              <Component id="jLabel3" max="32767" attributes="0"/>
-                              <Component id="jLabel1" max="32767" attributes="0"/>
-                              <Component id="jLabel2" pref="143" max="32767" attributes="0"/>
-                          </Group>
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Group type="103" groupAlignment="0" attributes="0">
-                              <Group type="102" alignment="0" attributes="0">
-                                  <Group type="103" groupAlignment="0" attributes="0">
-                                      <Group type="103" alignment="0" groupAlignment="0" attributes="0">
-                                          <Group type="103" alignment="0" groupAlignment="0" attributes="0">
-                                              <Component id="comboSliderDouble1" alignment="0" min="-2" max="-2" attributes="0"/>
-                                              <Component id="comboSliderInteger1" alignment="1" min="-2" max="-2" attributes="0"/>
-                                          </Group>
-                                          <Component id="comboSliderDouble2" alignment="1" min="-2" max="-2" attributes="0"/>
-                                      </Group>
-                                      <Component id="comboSliderDouble3" alignment="1" min="-2" max="-2" attributes="0"/>
-                                  </Group>
-                                  <EmptySpace max="-2" attributes="0"/>
-                                  <Group type="103" groupAlignment="0" attributes="0">
-                                      <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/>
-                                      <Component id="jButtonInfo2" min="-2" max="-2" attributes="0"/>
-                                      <Component id="jButtonInfo4" min="-2" max="-2" attributes="0"/>
-                                      <Component id="jButtonInfo3" min="-2" max="-2" attributes="0"/>
-                                  </Group>
+                              <Group type="102" attributes="0">
+                                  <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/>
+                                  <EmptySpace max="32767" attributes="0"/>
+                                  <Component id="jButton1" min="-2" max="-2" attributes="0"/>
                               </Group>
-                              <Group type="103" alignment="0" groupAlignment="1" attributes="0">
-                                  <Group type="102" attributes="0">
-                                      <Component id="jButton1" min="-2" max="-2" attributes="0"/>
-                                      <EmptySpace min="-2" pref="81" max="-2" attributes="0"/>
+                              <Group type="103" groupAlignment="0" attributes="0">
+                                  <Group type="103" alignment="0" groupAlignment="0" attributes="0">
+                                      <Component id="spinSlider2" min="-2" max="-2" attributes="0"/>
+                                      <Component id="spinSlider3" alignment="1" min="-2" max="-2" attributes="0"/>
                                   </Group>
-                                  <Component id="comboSliderDouble4" min="-2" max="-2" attributes="0"/>
+                                  <Component id="spinSlider4" min="-2" max="-2" attributes="0"/>
+                                  <Component id="spinSlider5" alignment="0" min="-2" max="-2" attributes="0"/>
+                                  <Component id="spinSlider1" alignment="0" min="-2" max="-2" attributes="0"/>
                               </Group>
                           </Group>
+                          <EmptySpace min="-2" pref="18" max="-2" attributes="0"/>
+                          <Group type="103" groupAlignment="0" attributes="0">
+                              <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/>
+                              <Component id="jButtonInfo2" min="-2" max="-2" attributes="0"/>
+                              <Component id="jButtonInfo4" min="-2" max="-2" attributes="0"/>
+                              <Component id="jButtonInfo3" min="-2" max="-2" attributes="0"/>
+                          </Group>
                       </Group>
                   </Group>
                   <EmptySpace max="32767" attributes="0"/>
@@ -120,70 +111,61 @@
         <DimensionLayout dim="1">
           <Group type="103" groupAlignment="0" attributes="0">
               <Group type="102" alignment="0" attributes="0">
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
-                          <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
-                      </Group>
-                      <Component id="comboSliderInteger1" alignment="0" min="-2" max="-2" attributes="0"/>
-                      <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/>
-                      </Group>
-                  </Group>
-                  <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
-                          <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
-                      </Group>
-                      <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="comboSliderDouble1" min="-2" max="-2" attributes="0"/>
-                      </Group>
-                      <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace min="-2" pref="17" max="-2" attributes="0"/>
-                          <Component id="jButtonInfo2" min="-2" max="-2" attributes="0"/>
-                      </Group>
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="jLabel9" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="jComboBox2" alignment="3" min="-2" max="-2" attributes="0"/>
                   </Group>
+                  <EmptySpace type="separate" max="-2" attributes="0"/>
                   <Group type="103" groupAlignment="0" attributes="0">
-                      <Group type="102" attributes="0">
-                          <EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
-                          <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
-                      </Group>
-                      <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace max="-2" attributes="0"/>
-                          <Component id="comboSliderDouble2" min="-2" max="-2" attributes="0"/>
-                      </Group>
+                      <Component id="jButtonInfo1" min="-2" max="-2" attributes="0"/>
+                      <Component id="spinSlider1" min="-2" max="-2" attributes="0"/>
                       <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace min="-2" pref="16" max="-2" attributes="0"/>
-                          <Component id="jButtonInfo3" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
+                          <Component id="jLabel1" min="-2" max="-2" attributes="0"/>
                       </Group>
                   </Group>
                   <EmptySpace max="-2" attributes="0"/>
                   <Group type="103" groupAlignment="0" attributes="0">
                       <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
+                          <EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
+                          <Component id="jLabel2" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
+                          <Component id="jLabel3" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
                           <Component id="jLabel4" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace min="-2" pref="47" max="-2" attributes="0"/>
+                          <EmptySpace min="-2" pref="28" max="-2" attributes="0"/>
                           <Component id="jLabel5" min="-2" max="-2" attributes="0"/>
                       </Group>
                       <Group type="102" alignment="0" attributes="0">
-                          <Component id="comboSliderDouble3" min="-2" max="-2" attributes="0"/>
+                          <Group type="103" groupAlignment="0" attributes="0">
+                              <Component id="spinSlider2" alignment="0" min="-2" max="-2" attributes="0"/>
+                              <Component id="jButtonInfo2" alignment="0" min="-2" max="-2" attributes="0"/>
+                          </Group>
                           <EmptySpace max="-2" attributes="0"/>
-                          <Component id="comboSliderDouble4" min="-2" max="-2" attributes="0"/>
-                      </Group>
-                      <Group type="102" alignment="0" attributes="0">
-                          <EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
-                          <Component id="jButtonInfo4" min="-2" max="-2" attributes="0"/>
+                          <Group type="103" groupAlignment="0" attributes="0">
+                              <Component id="spinSlider3" alignment="0" min="-2" max="-2" attributes="0"/>
+                              <Component id="jButtonInfo3" alignment="0" min="-2" max="-2" attributes="0"/>
+                          </Group>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Group type="103" groupAlignment="0" attributes="0">
+                              <Component id="jButtonInfo4" min="-2" max="-2" attributes="0"/>
+                              <Component id="spinSlider4" min="-2" max="-2" attributes="0"/>
+                          </Group>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="spinSlider5" min="-2" max="-2" attributes="0"/>
                       </Group>
                   </Group>
                   <EmptySpace min="-2" pref="24" max="-2" attributes="0"/>
-                  <Group type="103" groupAlignment="1" attributes="0">
-                      <Component id="jLabel6" min="-2" max="-2" attributes="0"/>
-                      <Component id="jCheckBox1" min="-2" max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
                       <Component id="jButton1" min="-2" max="-2" attributes="0"/>
+                      <Group type="102" alignment="0" attributes="0">
+                          <EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
+                          <Component id="jLabel6" min="-2" max="-2" attributes="0"/>
+                      </Group>
+                      <Component id="jCheckBox1" alignment="1" min="-2" max="-2" attributes="0"/>
                   </Group>
-                  <EmptySpace max="32767" attributes="0"/>
+                  <EmptySpace pref="26" max="32767" attributes="0"/>
               </Group>
           </Group>
         </DimensionLayout>
@@ -239,16 +221,6 @@
             </Property>
           </Properties>
         </Component>
-        <Component class="cz.fidentis.analyst.core.ComboSliderInteger" name="comboSliderInteger1">
-        </Component>
-        <Component class="cz.fidentis.analyst.core.ComboSliderDouble" name="comboSliderDouble4">
-        </Component>
-        <Component class="cz.fidentis.analyst.core.ComboSliderDouble" name="comboSliderDouble1">
-        </Component>
-        <Component class="cz.fidentis.analyst.core.ComboSliderDouble" name="comboSliderDouble2">
-        </Component>
-        <Component class="cz.fidentis.analyst.core.ComboSliderDouble" name="comboSliderDouble3">
-        </Component>
         <Component class="javax.swing.JButton" name="jButtonInfo1">
           <Properties>
             <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
@@ -292,6 +264,9 @@
             </Property>
             <Property name="borderPainted" type="boolean" value="false"/>
           </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButtonInfo3ActionPerformed"/>
+          </Events>
         </Component>
         <Component class="javax.swing.JButton" name="jButton1">
           <Properties>
@@ -300,6 +275,33 @@
             </Property>
           </Properties>
         </Component>
+        <Component class="javax.swing.JLabel" name="jLabel9">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="cz/fidentis/analyst/symmetry/Bundle.properties" key="SymmetryPanel.jLabel9.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JComboBox" name="jComboBox2">
+          <Properties>
+            <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+              <StringArray count="0"/>
+            </Property>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+          </AuxValues>
+        </Component>
+        <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider1">
+        </Component>
+        <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider2">
+        </Component>
+        <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider3">
+        </Component>
+        <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider4">
+        </Component>
+        <Component class="cz.fidentis.analyst.core.SpinSlider" name="spinSlider5">
+        </Component>
       </SubComponents>
     </Container>
     <Container class="javax.swing.JPanel" name="jPanel2">
@@ -376,25 +378,68 @@
         </Component>
       </SubComponents>
     </Container>
-    <Component class="javax.swing.JComboBox" name="jComboBox1">
-      <Properties>
-        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
-          <StringArray count="0"/>
-        </Property>
-      </Properties>
-      <AuxValues>
-        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
-      </AuxValues>
-    </Component>
-    <Component class="javax.swing.JButton" name="jButton2">
+    <Container class="javax.swing.JPanel" name="jPanel3">
       <Properties>
-        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-          <ResourceString bundle="cz/fidentis/analyst/symmetry/Bundle.properties" key="SymmetryPanel.jButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+          <Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
+            <TitledBorder title="Compute from">
+              <ResourceString PropertyName="titleX" bundle="cz/fidentis/analyst/symmetry/Bundle.properties" key="SymmetryPanel.jPanel3.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+              <Font PropertyName="font" name="Dialog" size="12" style="1"/>
+            </TitledBorder>
+          </Border>
         </Property>
       </Properties>
-      <Events>
-        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/>
-      </Events>
-    </Component>
+
+      <Layout>
+        <DimensionLayout dim="0">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Component id="jComboBox1" min="-2" max="-2" attributes="0"/>
+                  <EmptySpace type="separate" max="-2" attributes="0"/>
+                  <Component id="jButton2" min="-2" max="-2" attributes="0"/>
+                  <EmptySpace max="32767" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+        <DimensionLayout dim="1">
+          <Group type="103" groupAlignment="0" attributes="0">
+              <Group type="102" alignment="0" attributes="0">
+                  <EmptySpace max="-2" attributes="0"/>
+                  <Group type="103" groupAlignment="3" attributes="0">
+                      <Component id="jComboBox1" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="jButton2" alignment="3" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <EmptySpace max="32767" attributes="0"/>
+              </Group>
+          </Group>
+        </DimensionLayout>
+      </Layout>
+      <SubComponents>
+        <Component class="javax.swing.JComboBox" name="jComboBox1">
+          <Properties>
+            <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+              <StringArray count="0"/>
+            </Property>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+          </AuxValues>
+        </Component>
+        <Component class="javax.swing.JButton" name="jButton2">
+          <Properties>
+            <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
+              <Font name="Ubuntu" size="15" style="1"/>
+            </Property>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="cz/fidentis/analyst/symmetry/Bundle.properties" key="SymmetryPanel.jButton2.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jButton2ActionPerformed"/>
+          </Events>
+        </Component>
+      </SubComponents>
+    </Container>
   </SubComponents>
 </Form>
diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java
index 683ebf52cdedce7dd0ac0d96582860537ae8e700..ea8901e9c62c9db801de1ff3e227c827a82eb08c 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/SymmetryPanel.java
@@ -3,6 +3,7 @@ package cz.fidentis.analyst.symmetry;
 import cz.fidentis.analyst.core.ControlPanel;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.util.Arrays;
 import java.util.DoubleSummaryStatistics;
 import javax.swing.ImageIcon;
 import javax.swing.JOptionPane;
@@ -18,8 +19,18 @@ public class SymmetryPanel extends ControlPanel  {
     /*
      * Handled actions
      */
-    public static final String ACTION_COMMAND_RECOMPUTE_FROM_MESH = "From triangular mesh";
-    public static final String ACTION_COMMAND_COMPUTE_FROM_FPS = "From feature points";
+    public static final String ACTION_COMMAND_RECOMPUTE_FROM_MESH = "Mesh vertices";
+    public static final String ACTION_COMMAND_COMPUTE_FROM_FPS = "Feature points";
+    public static final String ACTION_COMMAND_POINT_SAMPLING_STRENGTH = "Point sampling strength";
+    public static final String ACTION_COMMAND_POINT_SAMPLING_STRATEGY = "Point sampling strategy";
+    
+    public static final String[] POINT_SAMPLING_STRATEGIES = new String[] {
+        "Random Sampling",
+        "Mean Curvature",
+        "Gaussian Curvature",
+        "Max Curvature",
+        "Min Curvature",
+    };
     
     /*
      * Mandatory design elements
@@ -44,95 +55,73 @@ public class SymmetryPanel extends ControlPanel  {
         this.setName(NAME);
         initComponents();
         
-        comboSliderInteger1.setRange(0, MAX_SIGNIFICANT_POINTS);
-        comboSliderInteger1.setValue(config.getSignificantPointCount());
-        comboSliderInteger1.setSliderEast();
-        comboSliderInteger1.addInputFieldListener(
-                (ActionEvent e) -> { 
-                    config.setSignificantPointCount(comboSliderInteger1.getValue());
-                }
-        );
+        Arrays.stream(POINT_SAMPLING_STRATEGIES).forEach(v -> jComboBox2.addItem(v));
+        jComboBox2.setSelectedIndex(0);
+        jComboBox2.addActionListener(createListener(action, ACTION_COMMAND_POINT_SAMPLING_STRATEGY));
         
-        comboSliderDouble1.setRange(0, 1, 3);
-        comboSliderDouble1.setSliderEast();
-        comboSliderDouble1.setValue(config.getMinCurvRatio());
-        comboSliderDouble1.addInputFieldListener(
-                (ActionEvent e) -> { 
-                    config.setMinCurvRatio(comboSliderDouble1.getValue());
-                }
-        );
+        spinSlider1.initInteger(config.getSignificantPointCount(), 10, MAX_SIGNIFICANT_POINTS, 1);
+        spinSlider1.addSpinnerListener((ActionEvent e) -> { 
+            config.setSignificantPointCount((Integer) spinSlider1.getValue());
+            action.actionPerformed(new ActionEvent(
+                    spinSlider1, 
+                    ActionEvent.ACTION_PERFORMED, 
+                    ACTION_COMMAND_POINT_SAMPLING_STRENGTH)
+            ); 
+        });
         
-        comboSliderDouble2.setRange(0, 1, 3);
-        comboSliderDouble2.setSliderEast();
-        comboSliderDouble2.setValue(config.getMinAngleCos());
-        comboSliderDouble2.addInputFieldListener(
-                (ActionEvent e) -> { 
-                    config.setMinAngleCos(comboSliderDouble2.getValue());
-                }
-        );
+        spinSlider2.initDouble(config.getMinCurvRatio(), 0.0, 1.0, 3);
+        spinSlider2.addSpinnerListener((ActionEvent e) -> { 
+            config.setMinCurvRatio((Double) spinSlider2.getValue());
+        });
         
-        comboSliderDouble3.setRange(0, 1, 3);
-        comboSliderDouble3.setSliderEast();
-        comboSliderDouble3.setValue(config.getMinNormAngleCos());
-        comboSliderDouble3.addInputFieldListener(
-                (ActionEvent e) -> { 
-                    config.setMinNormAngleCos(comboSliderDouble3.getValue());
-                }
-        );
+        spinSlider3.initDouble(config.getMinAngleCos(), 0.0, 1.0, 3);
+        spinSlider3.addSpinnerListener((ActionEvent e) -> { 
+            config.setMinAngleCos((Double) spinSlider3.getValue());
+        });
         
-        comboSliderDouble4.setRange(0, 1, 2);
-        comboSliderDouble4.setSliderEast();
-        comboSliderDouble4.setValue(config.getMaxRelDistance());
-        comboSliderDouble4.addInputFieldListener(
-                (ActionEvent e) -> { 
-                    config.setMaxRelDistance(comboSliderDouble4.getValue());
-                }
-        );
+        spinSlider4.initDouble(config.getMinNormAngleCos(), 0.0, 1.0, 3);
+        spinSlider4.addSpinnerListener((ActionEvent e) -> { 
+            config.setMinNormAngleCos((Double) spinSlider4.getValue());
+        });
         
-        jButton1.addActionListener(
-                (ActionEvent e) -> {
-                    config = new SymmetryConfig();
-                    comboSliderInteger1.setValue(config.getSignificantPointCount());
-                    comboSliderDouble1.setValue(config.getMinCurvRatio());
-                    comboSliderDouble2.setValue(config.getMinAngleCos());
-                    comboSliderDouble3.setValue(config.getMinNormAngleCos());
-                    comboSliderDouble4.setValue(config.getMaxRelDistance());
-                    //setConfig(new SymmetryConfig());
-                }
-        );
+        spinSlider5.initDouble(config.getMaxRelDistance(), 0.0, 1.0, 2);
+        spinSlider5.addSpinnerListener((ActionEvent e) -> { 
+            config.setMaxRelDistance((Double) spinSlider5.getValue());
+        });
+
+        jButton1.addActionListener((ActionEvent e) -> {
+            config = new SymmetryConfig();
+            spinSlider1.setValue(config.getSignificantPointCount());
+            spinSlider2.setValue(config.getMinCurvRatio());
+            spinSlider3.setValue(config.getMinAngleCos());
+            spinSlider4.setValue(config.getMinNormAngleCos());
+            spinSlider5.setValue(config.getMaxRelDistance());
+            //setConfig(new SymmetryConfig());
+        });
         
         jButton2.addActionListener((ActionEvent e) -> { 
-                    action.actionPerformed(new ActionEvent( // recompute
-                            e.getSource(), 
-                            ActionEvent.ACTION_PERFORMED,
-                            jComboBox1.getSelectedItem().toString()
-                    )); 
-                }
-        );
+            action.actionPerformed(new ActionEvent( // recompute
+                    e.getSource(), 
+                    ActionEvent.ACTION_PERFORMED,
+                    jComboBox1.getSelectedItem().toString()
+            )); 
+        });
         
-        jButtonInfo1.addActionListener(
-                (ActionEvent e) -> { 
-                    this.showSignPointsHelp();
-                }
-        );
+        jButtonInfo1.addActionListener((ActionEvent e) -> { 
+            this.showSignPointsHelp();
+        });
         
-        jButtonInfo2.addActionListener(
-                (ActionEvent e) -> { 
-                    this.showMinCurvHelp();
-                }
-        );
+        jButtonInfo2.addActionListener((ActionEvent e) -> { 
+            this.showMinCurvHelp();
+        });
         
-        jButtonInfo3.addActionListener(
-                (ActionEvent e) -> { 
-                    this.showMinAngleCosHelp();
-                }
-        );
+        jButtonInfo3.addActionListener((ActionEvent e) -> { 
+            this.showMinAngleCosHelp();
+        });
         
-        jButtonInfo4.addActionListener(
-                (ActionEvent e) -> { 
-                    this.showNormalAngleHelp();
-                }
-        );
+        jButtonInfo4.addActionListener((ActionEvent e) -> { 
+            this.showNormalAngleHelp();
+        });
         
         jComboBox1.addItem(SymmetryPanel.ACTION_COMMAND_RECOMPUTE_FROM_MESH);
         //jComboBox1.addItem(SymmetryPanel.ACTION_COMMAND_COMPUTE_FROM_FPS);
@@ -144,6 +133,22 @@ public class SymmetryPanel extends ControlPanel  {
         return getStaticIcon();
     }
     
+    /**
+     * Return the number of point samples
+     * @return the number of point samples
+     */
+    public int getPointSamplingStrength() {
+        return (Integer) spinSlider1.getValue();
+    }
+    
+    /**
+     * Return selected point sampling strategy
+     * @return selected point sampling strategy
+     */
+    public String getPointSamplingStrategy() {
+        return POINT_SAMPLING_STRATEGIES[jComboBox2.getSelectedIndex()];
+    }
+    
     /**
      * Returns symmetry plane configuration
      * @return symmetry plane configuration
@@ -273,21 +278,24 @@ public class SymmetryPanel extends ControlPanel  {
         jLabel5 = new javax.swing.JLabel();
         jLabel6 = new javax.swing.JLabel();
         jCheckBox1 = new javax.swing.JCheckBox();
-        comboSliderInteger1 = new cz.fidentis.analyst.core.ComboSliderInteger();
-        comboSliderDouble4 = new cz.fidentis.analyst.core.ComboSliderDouble();
-        comboSliderDouble1 = new cz.fidentis.analyst.core.ComboSliderDouble();
-        comboSliderDouble2 = new cz.fidentis.analyst.core.ComboSliderDouble();
-        comboSliderDouble3 = new cz.fidentis.analyst.core.ComboSliderDouble();
         jButtonInfo1 = new javax.swing.JButton();
         jButtonInfo2 = new javax.swing.JButton();
         jButtonInfo4 = new javax.swing.JButton();
         jButtonInfo3 = new javax.swing.JButton();
         jButton1 = new javax.swing.JButton();
+        jLabel9 = new javax.swing.JLabel();
+        jComboBox2 = new javax.swing.JComboBox<>();
+        spinSlider1 = new cz.fidentis.analyst.core.SpinSlider();
+        spinSlider2 = new cz.fidentis.analyst.core.SpinSlider();
+        spinSlider3 = new cz.fidentis.analyst.core.SpinSlider();
+        spinSlider4 = new cz.fidentis.analyst.core.SpinSlider();
+        spinSlider5 = new cz.fidentis.analyst.core.SpinSlider();
         jPanel2 = new javax.swing.JPanel();
         jTextField1 = new javax.swing.JTextField();
         jLabel7 = new javax.swing.JLabel();
         jLabel8 = new javax.swing.JLabel();
         jTextField2 = new javax.swing.JTextField();
+        jPanel3 = new javax.swing.JPanel();
         jComboBox1 = new javax.swing.JComboBox<>();
         jButton2 = new javax.swing.JButton();
 
@@ -323,9 +331,16 @@ public class SymmetryPanel extends ControlPanel  {
         jButtonInfo3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/info.png"))); // NOI18N
         org.openide.awt.Mnemonics.setLocalizedText(jButtonInfo3, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jButtonInfo3.text")); // NOI18N
         jButtonInfo3.setBorderPainted(false);
+        jButtonInfo3.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                jButtonInfo3ActionPerformed(evt);
+            }
+        });
 
         org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jButton1.text")); // NOI18N
 
+        org.openide.awt.Mnemonics.setLocalizedText(jLabel9, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jLabel9.text")); // NOI18N
+
         javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
         jPanel1.setLayout(jPanel1Layout);
         jPanel1Layout.setHorizontalGroup(
@@ -333,92 +348,88 @@ public class SymmetryPanel extends ControlPanel  {
             .addGroup(jPanel1Layout.createSequentialGroup()
                 .addContainerGap()
                 .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(jLabel9, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                     .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
-                            .addComponent(jLabel5, javax.swing.GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE)
-                            .addComponent(jLabel6, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
-                        .addGap(18, 18, 18)
-                        .addComponent(jCheckBox1))
+                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+                                .addComponent(jLabel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(jLabel3))
+                            .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 165, javax.swing.GroupLayout.PREFERRED_SIZE)
+                            .addComponent(jLabel1)
+                            .addComponent(jLabel5, javax.swing.GroupLayout.PREFERRED_SIZE, 143, javax.swing.GroupLayout.PREFERRED_SIZE)
+                            .addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, 143, javax.swing.GroupLayout.PREFERRED_SIZE))
+                        .addGap(0, 12, Short.MAX_VALUE)))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(jComboBox2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                     .addGroup(jPanel1Layout.createSequentialGroup()
                         .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
-                            .addComponent(jLabel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                            .addComponent(jLabel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                            .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                            .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE))
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                             .addGroup(jPanel1Layout.createSequentialGroup()
+                                .addComponent(jCheckBox1)
+                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                                .addComponent(jButton1))
+                            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                 .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                                    .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                                            .addComponent(comboSliderDouble1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                                            .addComponent(comboSliderInteger1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                                        .addComponent(comboSliderDouble2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                                    .addComponent(comboSliderDouble3, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                                    .addComponent(jButtonInfo1)
-                                    .addComponent(jButtonInfo2)
-                                    .addComponent(jButtonInfo4)
-                                    .addComponent(jButtonInfo3)))
-                            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
-                                .addGroup(jPanel1Layout.createSequentialGroup()
-                                    .addComponent(jButton1)
-                                    .addGap(81, 81, 81))
-                                .addComponent(comboSliderDouble4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))))
+                                    .addComponent(spinSlider2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                    .addComponent(spinSlider3, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                                .addComponent(spinSlider4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                .addComponent(spinSlider5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                                .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
+                        .addGap(18, 18, 18)
+                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addComponent(jButtonInfo1)
+                            .addComponent(jButtonInfo2)
+                            .addComponent(jButtonInfo4)
+                            .addComponent(jButtonInfo3))))
                 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
         );
         jPanel1Layout.setVerticalGroup(
             jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
             .addGroup(jPanel1Layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jLabel9)
+                    .addComponent(jComboBox2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addGap(18, 18, 18)
                 .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(jButtonInfo1)
+                    .addComponent(spinSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                     .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addGap(20, 20, 20)
-                        .addComponent(jLabel1))
-                    .addComponent(comboSliderInteger1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                    .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addContainerGap()
-                        .addComponent(jButtonInfo1)))
+                        .addGap(8, 8, 8)
+                        .addComponent(jLabel1)))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                     .addGroup(jPanel1Layout.createSequentialGroup()
+                        .addGap(8, 8, 8)
+                        .addComponent(jLabel2)
                         .addGap(26, 26, 26)
-                        .addComponent(jLabel2))
-                    .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(comboSliderDouble1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                    .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addGap(17, 17, 17)
-                        .addComponent(jButtonInfo2)))
-                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                    .addGroup(jPanel1Layout.createSequentialGroup()
+                        .addComponent(jLabel3)
                         .addGap(26, 26, 26)
-                        .addComponent(jLabel3))
-                    .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(comboSliderDouble2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                    .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addGap(16, 16, 16)
-                        .addComponent(jButtonInfo3)))
-                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                    .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addGap(20, 20, 20)
                         .addComponent(jLabel4)
-                        .addGap(47, 47, 47)
+                        .addGap(28, 28, 28)
                         .addComponent(jLabel5))
                     .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addComponent(comboSliderDouble3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addComponent(spinSlider2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                            .addComponent(jButtonInfo2))
                         .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                        .addComponent(comboSliderDouble4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
-                    .addGroup(jPanel1Layout.createSequentialGroup()
-                        .addGap(11, 11, 11)
-                        .addComponent(jButtonInfo4)))
+                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addComponent(spinSlider3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                            .addComponent(jButtonInfo3))
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                            .addComponent(jButtonInfo4)
+                            .addComponent(spinSlider4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(spinSlider5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                 .addGap(24, 24, 24)
-                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
-                    .addComponent(jLabel6)
-                    .addComponent(jCheckBox1)
-                    .addComponent(jButton1))
-                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(jButton1)
+                    .addGroup(jPanel1Layout.createSequentialGroup()
+                        .addGap(5, 5, 5)
+                        .addComponent(jLabel6))
+                    .addComponent(jCheckBox1, javax.swing.GroupLayout.Alignment.TRAILING))
+                .addContainerGap(26, Short.MAX_VALUE))
         );
 
         jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jPanel2.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 12))); // NOI18N
@@ -458,6 +469,9 @@ public class SymmetryPanel extends ControlPanel  {
                 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
         );
 
+        jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jPanel3.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Dialog", 1, 12))); // NOI18N
+
+        jButton2.setFont(new java.awt.Font("Ubuntu", 1, 15)); // NOI18N
         org.openide.awt.Mnemonics.setLocalizedText(jButton2, org.openide.util.NbBundle.getMessage(SymmetryPanel.class, "SymmetryPanel.jButton2.text")); // NOI18N
         jButton2.addActionListener(new java.awt.event.ActionListener() {
             public void actionPerformed(java.awt.event.ActionEvent evt) {
@@ -465,33 +479,49 @@ public class SymmetryPanel extends ControlPanel  {
             }
         });
 
+        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
+        jPanel3.setLayout(jPanel3Layout);
+        jPanel3Layout.setHorizontalGroup(
+            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(jPanel3Layout.createSequentialGroup()
+                .addContainerGap()
+                .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addGap(18, 18, 18)
+                .addComponent(jButton2)
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+        );
+        jPanel3Layout.setVerticalGroup(
+            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(jPanel3Layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(jButton2))
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+        );
+
         javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
         this.setLayout(layout);
         layout.setHorizontalGroup(
             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
             .addGroup(layout.createSequentialGroup()
                 .addContainerGap()
-                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                    .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                    .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                    .addGroup(layout.createSequentialGroup()
-                        .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                        .addGap(18, 18, 18)
-                        .addComponent(jButton2)))
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+                    .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                    .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                    .addComponent(jPanel2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                 .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
         );
         layout.setVerticalGroup(
             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
             .addGroup(layout.createSequentialGroup()
                 .addContainerGap()
-                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
-                    .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                    .addComponent(jButton2))
+                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
-                .addContainerGap(82, Short.MAX_VALUE))
+                .addContainerGap(98, Short.MAX_VALUE))
         );
     }// </editor-fold>//GEN-END:initComponents
 
@@ -499,13 +529,12 @@ public class SymmetryPanel extends ControlPanel  {
         // TODO add your handling code here:
     }//GEN-LAST:event_jButton2ActionPerformed
 
+    private void jButtonInfo3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonInfo3ActionPerformed
+        // TODO add your handling code here:
+    }//GEN-LAST:event_jButtonInfo3ActionPerformed
+
 
     // Variables declaration - do not modify//GEN-BEGIN:variables
-    private cz.fidentis.analyst.core.ComboSliderDouble comboSliderDouble1;
-    private cz.fidentis.analyst.core.ComboSliderDouble comboSliderDouble2;
-    private cz.fidentis.analyst.core.ComboSliderDouble comboSliderDouble3;
-    private cz.fidentis.analyst.core.ComboSliderDouble comboSliderDouble4;
-    private cz.fidentis.analyst.core.ComboSliderInteger comboSliderInteger1;
     private javax.swing.JButton jButton1;
     private javax.swing.JButton jButton2;
     private javax.swing.JButton jButtonInfo1;
@@ -514,6 +543,7 @@ public class SymmetryPanel extends ControlPanel  {
     private javax.swing.JButton jButtonInfo4;
     private javax.swing.JCheckBox jCheckBox1;
     private javax.swing.JComboBox<String> jComboBox1;
+    private javax.swing.JComboBox<String> jComboBox2;
     private javax.swing.JLabel jLabel1;
     private javax.swing.JLabel jLabel2;
     private javax.swing.JLabel jLabel3;
@@ -522,9 +552,16 @@ public class SymmetryPanel extends ControlPanel  {
     private javax.swing.JLabel jLabel6;
     private javax.swing.JLabel jLabel7;
     private javax.swing.JLabel jLabel8;
+    private javax.swing.JLabel jLabel9;
     private javax.swing.JPanel jPanel1;
     private javax.swing.JPanel jPanel2;
+    private javax.swing.JPanel jPanel3;
     private javax.swing.JTextField jTextField1;
     private javax.swing.JTextField jTextField2;
+    private cz.fidentis.analyst.core.SpinSlider spinSlider1;
+    private cz.fidentis.analyst.core.SpinSlider spinSlider2;
+    private cz.fidentis.analyst.core.SpinSlider spinSlider3;
+    private cz.fidentis.analyst.core.SpinSlider spinSlider4;
+    private cz.fidentis.analyst.core.SpinSlider spinSlider5;
     // End of variables declaration//GEN-END:variables
 }
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 f8dd484f18afa4150e29d19b38f4e45efe3f5376..3945cf584e53ed748528a8b4f8f2b8af7ae03122 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruth.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/tests/BatchSimilarityGroundTruth.java
@@ -3,6 +3,7 @@ package cz.fidentis.analyst.tests;
 import cz.fidentis.analyst.face.HumanFace;
 import cz.fidentis.analyst.face.HumanFaceUtils;
 import cz.fidentis.analyst.visitors.mesh.HausdorffDistance;
+import cz.fidentis.analyst.visitors.mesh.sampling.NoSampling;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileWriter;
@@ -81,7 +82,7 @@ public class BatchSimilarityGroundTruth {
                             100,  // max iterations
                             false,// scale
                             0.3,  // error
-                            100,  // no undersampling
+                            new NoSampling(),  // no undersampling
                             false // drop k-d tree, if exists
                     );
                     icpComputationTime += System.currentTimeMillis() - icpTime;
diff --git a/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties
index 4c560d531bf6448cfb43b5aa05ad6c22751822d3..1d136de071aad207a972bc9db6984fb79eca211c 100644
--- a/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties
+++ b/GUI/src/main/resources/cz/fidentis/analyst/batch/Bundle.properties
@@ -8,7 +8,7 @@ BatchPanel.jCheckBox4.text=ICP scaling
 BatchPanel.jCheckBox3.text=Show ICP transformations in the 3D preview
 BatchPanel.jCheckBox2.text=Transform faces towards the selected one using ICP
 BatchPanel.jCheckBox1.text_1=Compute an average face from the selected  one
-BatchPanel.jLabel2.text=ICP undersampling (100% = none):
+BatchPanel.jLabel2.text=ICP undersampling :
 BatchPanel.jButton1.text=Compute
 BatchPanel.jButtonInfo1.text=
 BatchPanel.jButton6.text=Export results
diff --git a/GUI/src/main/resources/cz/fidentis/analyst/core/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/core/Bundle.properties
index fb98808fdf280afd4593d85f9b9ffe4464aa6b4f..900e53446e3aececf9cf2f808764db76da1c6f24 100644
--- a/GUI/src/main/resources/cz/fidentis/analyst/core/Bundle.properties
+++ b/GUI/src/main/resources/cz/fidentis/analyst/core/Bundle.properties
@@ -41,3 +41,4 @@ ProjectTopComp.selectAllButton.text=Select all
 ProjectTopComp.deselectAllButton.text=Deselect all
 ProjectTopComp.inflateButton.text=Inflate
 ProjectTopComp.oneOnOneButton.text=Open 1:1
+TestPanel.jFormattedTextField1.text=jFormattedTextField1
diff --git a/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties
index 4d2dcd52e7983cb9e610f330eb44122e2075696f..15a33f9b70ec84ac7586897bc4bc97b6c06bdb79 100644
--- a/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties
+++ b/GUI/src/main/resources/cz/fidentis/analyst/registration/Bundle.properties
@@ -1,10 +1,10 @@
 RegistrationPanel.jButton1.text=Mesh
-RegistrationPanel.jCheckBox1.text=scale
+RegistrationPanel.jCheckBox1.text=
 RegistrationPanel.jFormattedTextField1.text=0,3
-RegistrationPanel.jLabel5.text=min ICP error:
+RegistrationPanel.jLabel5.text=ICP min error:
 RegistrationPanel.jFormattedTextField2.text=50
 RegistrationPanel.jLabel6.text=ICP iterations:
-RegistrationPanel.jLabel8.text=ICP undersampling (100% = none)
+RegistrationPanel.jLabel8.text=ICP downsampling strength
 RegistrationPanel.thersholdUpButton.text=
 RegistrationPanel.thresholdDownButton.text=
 RegistrationPanel.featurePointsLabel.text=Highlight feature point pairs closer than:
@@ -51,3 +51,5 @@ RegistrationPanel.rightTranslationXButton.text=
 RegistrationPanel.translationPanel.border.title=translation
 RegistrationPanel.transformationPanel.border.title=Manual alignment:
 RegistrationPanel.jButtonInfo1.text=
+RegistrationPanel.jLabel9.text=ICP downsampling strategy
+RegistrationPanel.jLabel3.text=scale:
diff --git a/GUI/src/main/resources/cz/fidentis/analyst/sampling/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/sampling/Bundle.properties
new file mode 100644
index 0000000000000000000000000000000000000000..c32a418c046144327c90fac5d0694357377356a7
--- /dev/null
+++ b/GUI/src/main/resources/cz/fidentis/analyst/sampling/Bundle.properties
@@ -0,0 +1,3 @@
+PointSamplingPanel.jLabel1.text=Strength:
+PointSamplingPanel.jLabel2.text=Strategy:
+PointSamplingPanel.jLabel3.text=100% = no downsampling
diff --git a/GUI/src/main/resources/cz/fidentis/analyst/symmetry/Bundle.properties b/GUI/src/main/resources/cz/fidentis/analyst/symmetry/Bundle.properties
index b0657e6a629bdbafa58121bc1f1165cb4e5abe66..24d640400e81ce289086aee02e25532dc4b38c59 100644
--- a/GUI/src/main/resources/cz/fidentis/analyst/symmetry/Bundle.properties
+++ b/GUI/src/main/resources/cz/fidentis/analyst/symmetry/Bundle.properties
@@ -20,20 +20,7 @@ SymmetryPanelNew.minAngleCosTF.text=
 SymmetryPanelNew.minNormAngleCosTF.text=
 SymmetryPanelNew.maxRelDistTF.text=
 SymmetryPanelNew.averaging.text=
-SymmetryPanel.jLabel4.text_1=Normal angle
-SymmetryPanel.jButtonInfo2.text=
-SymmetryPanel.jButtonInfo1.text=
-SymmetryPanel.jLabel3.text_1=Min. angle cosine
-SymmetryPanel.jLabel2.text_1=Min. curvature ratio
-SymmetryPanel.jLabel1.text_1=Undersampling
-SymmetryPanel.jPanel1.border.title_1=Symmetry from mesh
 SymmetryPanel.jButton2.text=Compute
-SymmetryPanel.jButton1.text=Reset to defaults
-SymmetryPanel.jCheckBox1.text=
-SymmetryPanel.jLabel6.text_1=Averaging
-SymmetryPanel.jButtonInfo3.text=
-SymmetryPanel.jLabel5.text_1=Relative distance
-SymmetryPanel.jButtonInfo4.text=
 ProfilesPanel.jCheckBox1.text=Mirror cuts
 ProfilesPanel.jButton1.text=Export
 SymmetryPanel.jPanel2.border.title=Precision (0 = best fit): 
@@ -41,3 +28,18 @@ SymmetryPanel.jTextField1.text=
 SymmetryPanel.jLabel7.text=Face 1:
 SymmetryPanel.jLabel8.text=Face 2:
 SymmetryPanel.jTextField2.text=
+SymmetryPanel.jPanel3.border.title=Compute from
+SymmetryPanel.jLabel9.text=Point sampling strategy
+SymmetryPanel.jButton1.text=Reset to defaults
+SymmetryPanel.jButtonInfo3.text=
+SymmetryPanel.jButtonInfo4.text=
+SymmetryPanel.jButtonInfo2.text=
+SymmetryPanel.jButtonInfo1.text=
+SymmetryPanel.jCheckBox1.text=
+SymmetryPanel.jLabel6.text_1=Averaging
+SymmetryPanel.jLabel5.text_1=Relative distance
+SymmetryPanel.jLabel4.text_1=Normal angle
+SymmetryPanel.jLabel3.text_1=Min. angle cosine
+SymmetryPanel.jLabel2.text_1=Min. curvature ratio
+SymmetryPanel.jLabel1.text_1=Point sampling strength
+SymmetryPanel.jPanel1.border.title_1=Symmetry from mesh