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 449918875d4536aab0328752bfa9e4402b451dae..4ad68932a1073c8ac3c5490e7b35db98bb34e6f1 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/face/HumanFace.java
@@ -38,7 +38,7 @@ import java.util.Objects;
  * <p>
  * Changes in the human face and its data structures (e.g., mesh model, etc.)
  * can be monitored by listeners. Listeners have to implement the 
- * {@link cz.fidentis.analyst.face.HumanFaceListener} interface and they have to be
+ * {@link cz.fidentis.analyst.face.events.HumanFaceListener} interface and they have to be
  * registered using the {@link cz.fidentis.analyst.face.HumanFace#registerListener} method.
  * Then they are informed about changes in the human automatically via methods 
  * prescribed by the interface.
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/CrossSectionCurve.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/CrossSectionCurve.java
new file mode 100644
index 0000000000000000000000000000000000000000..dbc475e5e71a9d016618e81f7bc97f40485da289
--- /dev/null
+++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/CrossSectionCurve.java
@@ -0,0 +1,219 @@
+package cz.fidentis.analyst.symmetry;
+
+import cz.fidentis.analyst.mesh.core.MeshPoint;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.vecmath.Point3d;
+
+/**
+ * A 3D curve produced by a plane cutting a human face.
+ * 
+ * @author Radek Oslejsek
+ */
+public class CrossSectionCurve {
+    
+    private final List<List<Point3d>> segments = new ArrayList<>();
+    private final List<Point3d> fpProjections = new ArrayList<>();
+    
+    /**
+     * Add a new point to the beginning of the curve segment.
+     * 
+     * @param segment Index of the segment
+     * @param point A 3D point to be inserted
+     * @throws IllegalArgumentException if the segment does not exist or the point is {@code null}
+     */
+    public void addPointToSegmentStart(int segment, Point3d point) {
+        if (segment < 0 || segment > segments.size() - 1) {
+            throw new IllegalArgumentException("segment");
+        }
+        
+        if (point == null) {
+            throw new IllegalArgumentException("point");
+        }
+        
+        segments.get(segment).add(0, point);
+    }
+    
+    /**
+     * Add a new point to the end of the curve segment.
+     * 
+     * @param segment Index of the segment
+     * @param point A 3D point to be inserted
+     * @throws IllegalArgumentException if the segment does not exist or the point is {@code null}
+     */
+    public void addPointToSegmentEnd(int segment, Point3d point) {
+        if (segment < 0 || segment > segments.size() - 1) {
+            throw new IllegalArgumentException("segment");
+        }
+        
+        if (point == null) {
+            throw new IllegalArgumentException("point");
+        }
+        
+        segments.get(segment).add(point);
+    }
+    
+    /**
+     * Adds a new segment to the curve.
+     * 
+     * @return Index of the new segment
+     */
+    public int addNewSegment() {
+        segments.add(new ArrayList<>());
+        return segments.size() - 1;
+    }
+    
+    /**
+     * Returns number of segments.
+     * @return number of segments.
+     */
+    public int getNumSegments() {
+        return segments.size();
+    }
+    
+    /**
+     * Returns number of points in given segment.
+     * 
+     * @param segment Index of the segment
+     * @return number of points in given segment.
+     * @throws IllegalArgumentException if the segment does not exist
+     */
+    public int getSegmentSize(int segment) {
+        if (segment < 0 || segment > segments.size() - 1) {
+            throw new IllegalArgumentException("segment");
+        }
+        return segments.get(segment).size();
+    }
+    
+    /**
+     * Returns given segment.
+     * 
+     * @param segment Index of the segment
+     * @return Points of the segment
+     * @throws IllegalArgumentException if the segment does not exist
+     */
+    public List<Point3d> getSegment(int segment) {
+        if (segment < 0 || segment > segments.size() - 1) {
+            throw new IllegalArgumentException("segment");
+        }
+        return Collections.unmodifiableList(segments.get(segment));
+    }
+    
+    /**
+     * Returns all segments.
+     * @return Curve segments
+     */
+    public List<List<Point3d>> getSegments() {
+        return Collections.unmodifiableList(segments);
+    }
+    
+    /**
+     * Returns given point of in given segment.
+     * 
+     * @param segment Index of the segment
+     * @param point Index of the point in the segment
+     * @return Points of the curve
+     * @throws IndexOutOfBoundsException if the input arguments are out of range
+     */
+    public Point3d getPoint(int segment, int point) {
+        return segments.get(segment).get(point);
+    }
+    
+    /**
+     * Find the closest point of the curve to the given point.
+     * Returned list contains two indexes. The first one is the index of 
+     * curve segment, the second one is the index of the closest point in the segment.
+     * 
+     * @param point A 3D point
+     * @return The closest point of the curve or {@code null}
+     * @throws NullPointerException if the input argument is {@code null}
+     */
+    public Point3d getClosestPoint(Point3d point) {
+        Point3d ret = null;
+        double minDist = Double.POSITIVE_INFINITY;
+        
+        for (int i = 0; i < getNumSegments(); i++) {
+            for (int j = 0; j < getSegmentSize(i); j++) {
+                double dist = MeshPoint.distance(point, getPoint(i, j));
+                if (dist < minDist) {
+                    minDist = dist;
+                    ret = getPoint(i, j);
+                }
+            }
+        }
+        
+        return ret;
+    }
+    
+    /**
+     * Finds points of the curve that are the closest the the given list of 3D points
+     * (locations of feature points) and returns them.
+     * 
+     * @param candidatePoints (Feature) points to be projected to the curve
+     * @return projections, i.e., points of the curve that are the closest to the candidate points.
+     */
+    public List<Point3d> projectFeaturePointsToCurve(List<Point3d> candidatePoints) {
+        this.fpProjections.clear();
+        for (Point3d p: candidatePoints) {
+            this.fpProjections.add(getClosestPoint(p));
+        }
+        return Collections.unmodifiableList(fpProjections);
+    }
+    
+    /**
+     * Returns projected feature points.
+     * @return projected feature points.
+     */
+    public List<Point3d> getProjectedFeaturePoints() {
+        return Collections.unmodifiableList(fpProjections);
+    }
+    
+    /**
+     * Finds the closest point (see {@link #getClosestPoint(javax.vecmath.Point3d)}.
+     * If the point is inside of some segment, the segment is split. 
+     * 
+     * @param point A 3D point whose closeness is calculated
+     * @return The index of the original segment that has been split. -1 if no slitting has been made.
+     * @throws NullPointerException if the input argument is {@code null}
+     */
+    /*
+    public int splitByClosestPoint(Point3d point) {
+        List<Integer> closest = getClosestPoint(point);
+        int clSegment = closest.get(0);
+        int clPoint = closest.get(1);
+        
+        if (closest == null) {
+            return -1;
+        }
+        
+        if (clPoint == 0 || clPoint == getSegmentSize(clSegment)-1) { // bordery points
+            return -1;
+        }
+        
+        List<Point3d> newSegment = new ArrayList<>();
+        newSegment.add(new Point3d(getPoint(clSegment, clPoint)));
+        int i = clPoint + 1;
+        while (i < getSegmentSize(closest.get(0))) {
+            newSegment.add(segments.get(clSegment).remove(i));
+        }
+        segments.add(clSegment+1, newSegment);
+        
+        return clSegment;
+    }
+    */
+    
+    /**
+     * Joints neighboring segments. Independent segments (separated by a whole)
+     * remain separated.
+     */
+    public void joinSegments() {
+        for (int i = 0; i < getNumSegments() -1 ; i++) {
+            if (getPoint(i, getSegmentSize(i)-1).equals(getPoint(i+1, 0))) {
+                segments.get(i+1).remove(0);
+                segments.get(i).addAll(segments.remove(i+1));
+                i--;
+            }
+        }
+    }
+}
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java
index c7951081c7ccc80c47ed8a76dc2db42e87eb8bc2..6a46bc19962b9e0f6ca30397b137eea8390978e7 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/Plane.java
@@ -128,6 +128,17 @@ public class Plane implements Serializable {
     protected void setDistance(double dist) {
         this.distance = dist;
     }
+    
+    /**
+     * Returns distance of the point from the plane.
+     * @param point Point whose distance is to be computed
+     * @return Point's distance. If the point is on the opposite side with
+     * the respect to the plane's normal, the negative distance is returned
+     */
+    public double getPointDistance(Point3d point) {
+        return ((normal.x * point.x) + (normal.y * point.y) + (normal.z * point.z) + distance)
+                / Math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
+    }
 
     /**
      * Calculates an intersection of a plane and a line given by two points
@@ -137,12 +148,8 @@ public class Plane implements Serializable {
      * @return The point of intersection of null if no point found
      */
     public Point3d getIntersectionWithLine(Point3d p1, Point3d p2) {
-        double distance1 = ((normal.x * p1.x) + (normal.y * p1.y) + (normal.z * p1.z) + distance)
-                / Math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
-
-        double distance2 = ((normal.x * p2.x) + (normal.y * p2.y) + (normal.z * p2.z) + distance)
-                / Math.sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
-
+        double distance1 = getPointDistance(p1);
+        double distance2 = getPointDistance(p2);
         double t = distance1 / (distance1 - distance2);
 
         if (distance1 * distance2 > 0) {
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 6b905a2fbea01c8cc8c88dd4d3ce5a21d77ecf0b..38b451c5436318b2a1710a227e31d5cd14bfccc5 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/symmetry/SymmetryEstimator.java
@@ -1,10 +1,8 @@
 package cz.fidentis.analyst.symmetry;
 
 import cz.fidentis.analyst.mesh.MeshVisitor;
-import cz.fidentis.analyst.mesh.core.CornerTableRow;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
-import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
-import cz.fidentis.analyst.mesh.core.MeshPointImpl;
+import cz.fidentis.analyst.mesh.core.MeshRectangleFacet;
 import cz.fidentis.analyst.visitors.mesh.BoundingBox;
 import cz.fidentis.analyst.visitors.mesh.BoundingBox.BBox;
 import java.util.ArrayList;
@@ -141,7 +139,7 @@ public class SymmetryEstimator extends MeshVisitor {
         
         double scale = bbox.getMaxPoint().x - bbox.getMinPoint().x;
         
-        return createMeshFacet(midPointOnPlane, frontDir, upDir, symmetryPlane.getNormal(), scale);
+        return new MeshRectangleFacet(midPointOnPlane, frontDir, upDir, symmetryPlane.getNormal(), 2*scale, 2*scale);
     }
     
     /**
@@ -230,49 +228,6 @@ public class SymmetryEstimator extends MeshVisitor {
         setSymmetryPlane(planes);
     }
     
-    protected static MeshFacet createMeshFacet(Point3d centroid, Vector3d frontDir, Vector3d upDir, Vector3d normal, double scale) {
-        
-        Point3d[] points = new Point3d[4];
-        
-        Point3d aScaled = new Point3d(frontDir);
-        Point3d bScaled = new Point3d(upDir);
-        aScaled.scale(scale);
-        bScaled.scale(scale);
-        
-        points[0] = new Point3d(centroid);
-        points[0].sub(aScaled);
-        points[0].sub(bScaled);
-        
-        points[1] = new Point3d(centroid);
-        points[1].sub(aScaled);
-        points[1].add(bScaled);
-        
-        points[2] = new Point3d(centroid);
-        points[2].add(aScaled);
-        points[2].add(bScaled);
-        
-        points[3] = new Point3d(centroid);
-        points[3].add(aScaled);
-        points[3].sub(bScaled);
-        
-        MeshFacet facet = new MeshFacetImpl();
-        for (Point3d point : points) {
-            facet.addVertex(new MeshPointImpl(point, normal, null));
-        }
-
-        //Create a simple square cornerTable
-        facet.getCornerTable().addRow(new CornerTableRow(0, 5));
-        facet.getCornerTable().addRow(new CornerTableRow(1, -1));
-        facet.getCornerTable().addRow(new CornerTableRow(3, -1));
-        facet.getCornerTable().addRow(new CornerTableRow(3, -1));
-        facet.getCornerTable().addRow(new CornerTableRow(1, -1));
-        facet.getCornerTable().addRow(new CornerTableRow(2, 0));
-
-        //facet.calculateVertexNormals();
-        
-        return facet;
-    }
-
     protected int checkAndUpdatePlanes(List<Plane> planes, ApproxSymmetryPlane newPlane, int maxVotes) {
         if (newPlane.getVotes() > maxVotes) {
             planes.clear();
diff --git a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java
index db7ec0c0ae3c338a8212ce4e71d5d46e65ea7a42..5b815eef88514684f645aa39d0456fec6b01ddee 100644
--- a/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java
+++ b/Comparison/src/main/java/cz/fidentis/analyst/visitors/mesh/CrossSectionZigZag.java
@@ -1,14 +1,12 @@
 package cz.fidentis.analyst.visitors.mesh;
 
-import cz.fidentis.analyst.Logger;
 import cz.fidentis.analyst.mesh.MeshVisitor;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
 import cz.fidentis.analyst.mesh.core.MeshTriangle;
+import cz.fidentis.analyst.symmetry.CrossSectionCurve;
 import cz.fidentis.analyst.symmetry.Plane;
 
 import javax.vecmath.Point3d;
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -25,7 +23,7 @@ import java.util.stream.Collectors;
  * @author Radek Oslejsek
  */
 public class CrossSectionZigZag extends MeshVisitor {
-    private List<List<Point3d>> segments;
+    private CrossSectionCurve curve;
     private Set<Point3d> usedPoints;
     private Set<MeshTriangle> visited;
     private Set<MeshTriangle> toVisit;
@@ -40,7 +38,7 @@ public class CrossSectionZigZag extends MeshVisitor {
     public CrossSectionZigZag(Plane plane) {
         this.plane = plane;
         //this.points = new ArrayList<>();
-        this.segments = new ArrayList<>();
+        this.curve = new CrossSectionCurve();
         this.usedPoints = new HashSet<>();
     }
 
@@ -49,9 +47,9 @@ public class CrossSectionZigZag extends MeshVisitor {
             usedPoints.add(p);
 
             if (direction) {
-                segments.get(segment).add(p);
+                curve.addPointToSegmentEnd(segment, p);
             } else {
-                segments.get(segment).add(0, p);
+                curve.addPointToSegmentStart(segment, p);
             }
         }
     }
@@ -113,8 +111,7 @@ public class CrossSectionZigZag extends MeshVisitor {
             while (!toVisit.isEmpty()) {
                 MeshTriangle tri = toVisit.iterator().next();
                 toVisit.remove(tri);
-                segments.add(new ArrayList<>());
-                int segment = segments.size() - 1;
+                int segment = curve.addNewSegment();
                 
                 // Figure out which lines are intersected
                 Point3d intersection1 = plane.getIntersectionWithLine(tri.getVertex1(), tri.getVertex2());
@@ -156,7 +153,11 @@ public class CrossSectionZigZag extends MeshVisitor {
         //out.printDuration("Cross section with zigzag method");
     }
 
-    public List<List<Point3d>> getSegments() {
-        return Collections.unmodifiableList(segments);
+    /**
+     * Returns computed cross section curve.
+     * @return cross section curve
+     */
+    public CrossSectionCurve getCrossSectionCurve() {
+        return curve;
     }
 }
diff --git a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/CrossSectionTest.java b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/CrossSectionTest.java
index cc4bf5ed8eac8c73c7340690ffba5c17d5bddfad..217995700ed30ef5f8d6a3ea17a8459d8da82073 100644
--- a/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/CrossSectionTest.java
+++ b/Comparison/src/test/java/cz/fidentis/analyst/visitors/mesh/CrossSectionTest.java
@@ -8,6 +8,7 @@ import cz.fidentis.analyst.mesh.core.MeshFacet;
 import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
 import cz.fidentis.analyst.mesh.core.MeshModel;
 import cz.fidentis.analyst.mesh.core.MeshPointImpl;
+import cz.fidentis.analyst.symmetry.CrossSectionCurve;
 import cz.fidentis.analyst.symmetry.Plane;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -59,24 +60,24 @@ public class CrossSectionTest {
 
         CrossSectionZigZag cs = new CrossSectionZigZag(cuttingPlane);
         model.compute(cs);
-        List<List<Point3d>> points = cs.getSegments();
+        CrossSectionCurve curve = cs.getCrossSectionCurve();
 
         //They can be ordered two ways, check both
-        Assertions.assertEquals(points.get(0).size(), 6);
-        if (points.get(0).equals(new Point3d(0.5, 0, 0))) {
-            Assertions.assertEquals(points.get(0).get(0), new Point3d(0.5, 0, 0));
-            Assertions.assertEquals(points.get(0).get(1), new Point3d(0.5, 0.5, 0));
-            Assertions.assertEquals(points.get(0).get(2), new Point3d(0.5, 1, 0));
-            Assertions.assertEquals(points.get(0).get(3), new Point3d(0.5, 1.5, 0));
-            Assertions.assertEquals(points.get(0).get(4), new Point3d(0.5, 2, 0));
-            Assertions.assertEquals(points.get(0).get(5), new Point3d(0.5, 2.5, 0));
-        } else if (points.get(0).get(0).equals(new Point3d(0.5, 2.5, 0))) {
-            Assertions.assertEquals(points.get(0).get(0), new Point3d(0.5, 2.5, 0));
-            Assertions.assertEquals(points.get(0).get(1), new Point3d(0.5, 2, 0));
-            Assertions.assertEquals(points.get(0).get(2), new Point3d(0.5, 1.5, 0));
-            Assertions.assertEquals(points.get(0).get(3), new Point3d(0.5, 1, 0));
-            Assertions.assertEquals(points.get(0).get(4), new Point3d(0.5, 0.5, 0));
-            Assertions.assertEquals(points.get(0).get(5), new Point3d(0.5, 0, 0));
+        Assertions.assertEquals(curve.getSegmentSize(0), 6);
+        if (curve.getSegment(0).get(0).equals(new Point3d(0.5, 0, 0))) {
+            Assertions.assertEquals(curve.getSegment(0).get(0), new Point3d(0.5, 0, 0));
+            Assertions.assertEquals(curve.getSegment(0).get(1), new Point3d(0.5, 0.5, 0));
+            Assertions.assertEquals(curve.getSegment(0).get(2), new Point3d(0.5, 1, 0));
+            Assertions.assertEquals(curve.getSegment(0).get(3), new Point3d(0.5, 1.5, 0));
+            Assertions.assertEquals(curve.getSegment(0).get(4), new Point3d(0.5, 2, 0));
+            Assertions.assertEquals(curve.getSegment(0).get(5), new Point3d(0.5, 2.5, 0));
+        } else if (curve.getSegment(0).get(0).equals(new Point3d(0.5, 2.5, 0))) {
+            Assertions.assertEquals(curve.getSegment(0).get(0), new Point3d(0.5, 2.5, 0));
+            Assertions.assertEquals(curve.getSegment(0).get(1), new Point3d(0.5, 2, 0));
+            Assertions.assertEquals(curve.getSegment(0).get(2), new Point3d(0.5, 1.5, 0));
+            Assertions.assertEquals(curve.getSegment(0).get(3), new Point3d(0.5, 1, 0));
+            Assertions.assertEquals(curve.getSegment(0).get(4), new Point3d(0.5, 0.5, 0));
+            Assertions.assertEquals(curve.getSegment(0).get(5), new Point3d(0.5, 0, 0));
         } else {
             Assertions.fail();
         }
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 3c9c9583fa738ad549efd82f6d3f3f6e6c0db35b..605aa4c9e3b58e1b09379fa0b6a464ad15454708 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/distance/FeaturePointsPanel.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/distance/FeaturePointsPanel.java
@@ -118,11 +118,11 @@ public class FeaturePointsPanel extends JPanel {
     
     /**
      * Creates and returns action listener that can be connected with a low-level 
-     * GUI element (e.g., a button). Action event of the low-level element is then
-     * re-directed to the given {@code ControlPanelAction} as given command.
+     * GUI element (e.g., a button). Action event of triggered low-level element is
+     * redirected to the given {@code action}.
      * The listener may also carry additional data as a payload.
      * 
-     * @param action An instance of the {@link ControlPanelAction}
+     * @param action Action listener
      * @param command Control panel command
      * @param data Payload data of the action listener
      * @return Action listener
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 c6835ccc3859f6ffad670655a23d63369e916382..37aabc017b119a486992ebc92b1444de853f9c89 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/registration/RegistrationAction.java
@@ -273,9 +273,9 @@ public class RegistrationAction extends ControlPanelAction implements HumanFaceL
      * First it creates object of type ProcrusteAnalasys containing two face models converted
      * to ProcrustesAnalysisFaceModel objects which are required for next analysis step.
      * 
-     * In analysis step faces are superimposed and rotated.
+     * In the analysis step, faces are superimposed and rotated.
      * 
-     * If {@see this.procrustesScalingEnabled} is set to true then one of them is scaled as well.
+     * If the {@code procrustesScalingEnabled} attribute is set to true, then one of the faces is scaled as well.
      */
     protected void applyProcrustes() {
         Logger out = Logger.measureTime();
diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFeaturePoints.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFeaturePoints.java
index 10e7d7b6b350f57c5cfcd8a993c3489accd407c6..3018786eef6d85c0396ff3282523be0aaa92b11c 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFeaturePoints.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFeaturePoints.java
@@ -57,7 +57,7 @@ public class DrawableFeaturePoints extends Drawable {
      * 
      * @param featurePoints Feature points
      * @param defaultColor Default color
-     * @param defaultSize Default perimeter
+     * @param defaultPerimeter Default perimeter
      */
     public DrawableFeaturePoints(List<FeaturePoint> featurePoints, Color defaultColor, double defaultPerimeter) {
         this.featurePoints = new ArrayList<>(featurePoints);
diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFpWeights.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFpWeights.java
index 3667c0fa2a64f3bbeabdd0d79e0558f8595169f4..4ce2b39dc1d209e79f00e50cbc579ed09c3762c6 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFpWeights.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableFpWeights.java
@@ -30,7 +30,7 @@ public class DrawableFpWeights extends DrawableFeaturePoints {
      * 
      * @param featurePoints Feature points
      * @param defaultColor Default color
-     * @param defaultSize Default perimeter
+     * @param defaultPerimeter Default perimeter
      */
     public DrawableFpWeights(List<FeaturePoint> featurePoints, Color defaultColor, double defaultPerimeter) {
         super(featurePoints, FPW_DEFAULT_COLOR, FPW_DEFAULT_SIZE);
diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableMesh.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableMesh.java
index 3963550706e1b7e2de69a519c05ac0d3a6283a42..e153b6277f4b490eff9ea271cb7e0a8120f62069 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableMesh.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawableMesh.java
@@ -23,7 +23,7 @@ public class DrawableMesh extends Drawable {
     /**
      * Copy constructor. 
      * 
-     * @param model Mesh to be copied
+     * @param mesh Mesh to be copied
      * @throws NullPointException if the input argument is {@code null}
      */
     public DrawableMesh(DrawableMesh mesh) {
diff --git a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java
index 0ed46b150aca54541caab13c59941e70c40372c7..2c7aa70d56275da41e0428d266e2b018b3047506 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/scene/DrawablePlane.java
@@ -66,10 +66,7 @@ public class DrawablePlane extends DrawableMesh {
         
         // Move Drawable plane
         Vector3d move = this.plane.getNormal();
-        move.x *= value;
-        move.y *= value;
-        move.z *= value;
-        
+        move.scale(value);
         for (int i = 0; i < 4; ++i) {
             getFacets().get(0).getVertex(i).getPosition().sub(move);
         }
diff --git a/GUI/src/main/java/cz/fidentis/analyst/symmetry/CurveRenderingPanel.java b/GUI/src/main/java/cz/fidentis/analyst/symmetry/CurveRenderingPanel.java
index b403593dcb41d649bbd857df4fb887cca04f638e..7c30646625bc75f9fd37e0ae5790657a09222799 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/CurveRenderingPanel.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/CurveRenderingPanel.java
@@ -7,7 +7,9 @@ import java.awt.Dimension;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.Stroke;
+import java.awt.geom.Ellipse2D;
 import java.awt.geom.Line2D;
+import java.util.ArrayList;
 import java.util.List;
 import javax.swing.JPanel;
 import javax.vecmath.Point3d;
@@ -21,6 +23,7 @@ import javax.vecmath.Point3d;
  */
 public class CurveRenderingPanel extends JPanel {
     
+    private static final int SEGMENT_POINT_SIZE = 7;
     private static final int PREF_W = 400;
     private static final int PREF_H = 400;
     private static final int BORDER_GAP = 15;
@@ -36,12 +39,11 @@ public class CurveRenderingPanel extends JPanel {
     */
     private static final Stroke GRAPH_STROKE = new BasicStroke(3f);
     
-    private List<List<Point3d>> primarySegments;
-    private List<List<Point3d>> secondarySegments;
-    private List<List<Point3d>> primaryMirrorSegments;
-    private List<List<Point3d>> secondaryMirrorSegments;
+    private CrossSectionCurve primarySegments;
+    private CrossSectionCurve secondarySegments;
+    private CrossSectionCurve primaryMirrorSegments;
+    private CrossSectionCurve secondaryMirrorSegments;
     
-    private double primaryOffsetZ = 0;
     private boolean mirrorCuts = false;
     
     private double minZ = Double.POSITIVE_INFINITY;
@@ -60,11 +62,11 @@ public class CurveRenderingPanel extends JPanel {
     /**
      * Sets primary points and draws them in the panel
      *
-     * @param segments primary points
+     * @param curve Points of the primary curve
      */
-    public void setPrimarySegments(List<List<Point3d>> segments) {
-        this.primarySegments = segments;
-        updateMinMax(segments);
+    public void setPrimarySegments(CrossSectionCurve curve) {
+        this.primarySegments = curve;
+        updateMinMax(curve);
         repaint();
     }
 
@@ -72,22 +74,22 @@ public class CurveRenderingPanel extends JPanel {
      * Sets primary mirror points.
      * Only draws them if mirrorCuts is checked
      *
-     * @param segments primary mirror points
+     * @param curve Points of the primary mirror curve
      */
-    public void setPrimaryMirrorSegments(List<List<Point3d>> segments) {
-        this.primaryMirrorSegments = segments;
-        updateMinMax(segments);
+    public void setPrimaryMirrorSegments(CrossSectionCurve curve) {
+        this.primaryMirrorSegments = curve;
+        updateMinMax(curve);
         repaint();
     }
 
     /**
      * Sets secondary points and draws them in the panel
      *
-     * @param segments secondary points
+     * @param curve Points of the secondary curve
      */
-    public void setSecondarySegments(List<List<Point3d>> segments) {
-        this.secondarySegments = segments;
-        updateMinMax(segments);
+    public void setSecondarySegments(CrossSectionCurve curve) {
+        this.secondarySegments = curve;
+        updateMinMax(curve);
         repaint();
     }
 
@@ -95,11 +97,11 @@ public class CurveRenderingPanel extends JPanel {
      * Sets secondary mirror points.
      * Only draws them if mirrorCuts is checked
      *
-     * @param segments secondary mirror points
+     * @param curve Points of the secondary mirror curve
      */
-    public void setSecondaryMirrorSegments(List<List<Point3d>> segments) {
-        this.secondaryMirrorSegments = segments;
-        updateMinMax(segments);
+    public void setSecondaryMirrorSegments(CrossSectionCurve curve) {
+        this.secondaryMirrorSegments = curve;
+        updateMinMax(curve);
         repaint();
     }
 
@@ -122,85 +124,83 @@ public class CurveRenderingPanel extends JPanel {
     protected void paintComponent(Graphics g) {
         super.paintComponent(g);
         Graphics2D g2 = (Graphics2D)g;
-
-        if (mirrorCuts) {
-            drawFace(g2, primarySegments, PRIMARY_COLOR, true);
-            drawFace(g2, primaryMirrorSegments, PRIMARY_MIRROR_COLOR, true);
-
-            if (secondarySegments != null) {
-                drawFace(g2, secondarySegments, SECONDARY_COLOR, false);
-                drawFace(g2, secondaryMirrorSegments, SECONDARY_MIRROR_COLOR, false);
-            }
-        } else {
-            drawFace(g2, primarySegments, PRIMARY_COLOR, true);
-
-            if (secondarySegments != null) {
-                drawFace(g2, secondarySegments, SECONDARY_COLOR, false);
+        
+        if (mirrorCuts && secondarySegments != null) { // two faces with mirror cutting planes => double view
+            double offsetZ = (PREF_W * 0.5) - (maxZ * scale);
+            drawFace(g2, primarySegments, PRIMARY_COLOR, offsetZ);
+            drawFace(g2, secondarySegments, SECONDARY_COLOR, offsetZ);
+            offsetZ = (PREF_W * 1.0) - (maxZ * scale);
+            drawFace(g2, primaryMirrorSegments, PRIMARY_COLOR, offsetZ);
+            drawFace(g2, secondaryMirrorSegments, SECONDARY_COLOR, offsetZ);
+        } else { // either a single face or two faces without mirror cutting planes => single view
+            double offsetZ = (PREF_W * 0.7) - (maxZ * scale);
+            drawFace(g2, primarySegments, PRIMARY_COLOR, offsetZ);
+            if (mirrorCuts) { // single face with mirror cutting plane
+                drawFace(g2, primaryMirrorSegments, PRIMARY_COLOR.darker(), offsetZ);
+            } else { // two faces without mirror cutting plane
+                drawFace(g2, secondarySegments, SECONDARY_COLOR, offsetZ);
             }
         }
     }
 
-    protected void drawFace(Graphics2D g2, List<List<Point3d>> curve, Color faceColor, boolean isPrimary) {
+    protected void drawFace(Graphics2D g2, CrossSectionCurve curve, Color faceColor, double offsetZ) {
         if (curve == null) {
             return;
         }
         
-        for (int i = 0; i < curve.size(); i++) {
-            drawCurveSegment(g2, curve.get(i), faceColor, isPrimary, maxY, maxZ);
+        for (int i = 0; i < curve.getNumSegments(); i++) {
+            drawCurveSegment(g2, curve.getSegment(i), faceColor, offsetZ);
         }
+        drawProjectedFeaturePoints(g2, curve.getProjectedFeaturePoints(), faceColor, offsetZ);
     }
 
-    protected void drawCurveSegment(Graphics2D g2, List<Point3d> curveSegment, Color faceColor, boolean isPrimary, double maxY, double maxZ) {
-        double offsetZ = 0;
-
-        //Calculate the offsets
-        if (mirrorCuts) {
-            if (secondarySegments == null) {
-                //Mirror cuts with single face - center the face
-                this.primaryOffsetZ = (PREF_W * 0.7) - (maxZ * scale);
-                offsetZ = this.primaryOffsetZ;
-            } else {
-                //Mirror cuts with two faces - separate primary and secondary
-                offsetZ = isPrimary ? (PREF_W * 0.4) - (maxZ * scale) : (PREF_W * 0.9) - (maxZ * scale);
-            }
-        } else {
-            //No mirror cuts - center all faces
-            if (isPrimary) {
-                this.primaryOffsetZ = (PREF_W * 0.7) - (maxZ * scale);
-            }
-           //offsetZ = this.alignProfiles ? (PREF_W * 0.7) - (maxZ * scale) : this.primaryOffsetZ;
-           offsetZ = this.primaryOffsetZ;
-        }
-
+    protected void drawCurveSegment(Graphics2D g2, List<Point3d> curveSegment, Color faceColor, double offsetZ) {
         //Draw lines
+        List<Ellipse2D.Double> points = new ArrayList<>();
         g2.setColor(faceColor);
         g2.setStroke(GRAPH_STROKE);
         for (int i = 0; i < curveSegment.size() - 1; i++) {
-            double z1 = (curveSegment.get(i).z) * scale + BORDER_GAP + offsetZ;
+            double z1 = curveSegment.get(i).z * scale + BORDER_GAP + offsetZ;
             //double y1 = (maxY - curveSegment.get(i).y) * scale + BORDER_GAP;
-            double y1 = PREF_H / 2 - curveSegment.get(i).y * scale + 3 * BORDER_GAP;
+            double y1 = PREF_H / 2 - curveSegment.get(i).y * scale + 2 * BORDER_GAP;
             double z2 = (curveSegment.get(i + 1).z) * scale + BORDER_GAP + offsetZ;
             //double y2 = (maxY - curveSegment.get(i + 1).y) * scale + BORDER_GAP;
-            double y2 = PREF_H / 2 - curveSegment.get(i + 1).y * scale + 3 * BORDER_GAP;
+            double y2 = PREF_H / 2 - curveSegment.get(i + 1).y * scale + 2 * BORDER_GAP;
             
             g2.draw(new Line2D.Double(z1, y1, z2, y2));
         }
     }
+    
+    protected void drawProjectedFeaturePoints(Graphics2D g2, List<Point3d> fps, Color faceColor, double offsetZ) {
+        g2.setColor(faceColor);
+        for (int i = 0; i < fps.size(); i++) {
+            double z = fps.get(i).z * scale + BORDER_GAP + offsetZ;
+            double y = PREF_H / 2 - fps.get(i).y * scale + 2 * BORDER_GAP;
+            Ellipse2D.Double point = new Ellipse2D.Double(
+                    z - SEGMENT_POINT_SIZE / 2d, 
+                    y - SEGMENT_POINT_SIZE / 2d, 
+                    SEGMENT_POINT_SIZE, 
+                    SEGMENT_POINT_SIZE);
+            g2.fill(point);
+            g2.draw(point);
+        }
+    }
 
-    protected void updateMinMax(List<List<Point3d>> curve) {
+    protected void updateMinMax(CrossSectionCurve curve) {
         if (curve == null) {
             return;
         }
         
-        for (int i = 0; i < curve.size(); i++) {
-            for (int j = 0; j < curve.get(i).size(); j++) {
-                minZ = Math.min(minZ, curve.get(i).get(j).z);
-                maxZ = Math.max(maxZ, curve.get(i).get(j).z);
-                minY = Math.min(minY, curve.get(i).get(j).y);
-                maxY = Math.max(maxY, curve.get(i).get(j).y);
+        for (int i = 0; i < curve.getNumSegments(); i++) {
+            for (int j = 0; j < curve.getSegment(i).size(); j++) {
+                minZ = Math.min(minZ, curve.getSegment(i).get(j).z);
+                maxZ = Math.max(maxZ, curve.getSegment(i).get(j).z);
+                minY = Math.min(minY, curve.getSegment(i).get(j).y);
+                maxY = Math.max(maxY, curve.getSegment(i).get(j).y);
             }
         }
 
         scale = ((double) PREF_H - 2 * BORDER_GAP) / (maxY - minY);
+        scale *= 0.9;
     }
 }
\ No newline at end of file
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 5403b36c7c80040f62019d47b44ed30cd5568fea..a611171c9e3a7b6cfe292cbe8e4a64ed8214ff96 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesAction.java
@@ -3,12 +3,13 @@ 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.face.HumanFace;
 import cz.fidentis.analyst.face.events.HumanFaceEvent;
 import cz.fidentis.analyst.face.events.HumanFaceListener;
-import cz.fidentis.analyst.mesh.core.CornerTableRow;
+import cz.fidentis.analyst.face.events.SymmetryPlaneChangedEvent;
 import cz.fidentis.analyst.mesh.core.MeshFacet;
 import cz.fidentis.analyst.mesh.core.MeshFacetImpl;
-import cz.fidentis.analyst.mesh.core.MeshPointImpl;
+import cz.fidentis.analyst.mesh.core.MeshRectangleFacet;
 import cz.fidentis.analyst.scene.DrawablePlane;
 import cz.fidentis.analyst.visitors.mesh.BoundingBox;
 import cz.fidentis.analyst.visitors.mesh.CrossSectionZigZag;
@@ -22,6 +23,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.stream.Collectors;
 import javax.swing.JComboBox;
 import javax.swing.JFrame;
 import javax.swing.JOptionPane;
@@ -34,6 +36,8 @@ import javax.vecmath.Vector3d;
  * @author Radek Oslejsek
  */
 public class ProfilesAction extends ControlPanelAction implements HumanFaceListener {
+    
+    public static final double FEATUE_POINTS_CLOSENESS = 1.5;
 
     /*
      * GUI elements
@@ -44,12 +48,13 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
     /*
      * Calculated profiles
      */
-    private List<List<Point3d>> primarySegments;
-    private List<List<Point3d>> secondarySegments;
-    private List<List<Point3d>> primaryMirrorSegments;
-    private List<List<Point3d>> secondaryMirrorSegments;
+    private CrossSectionCurve primaryCurve;
+    private CrossSectionCurve secondaryCurve;
+    private CrossSectionCurve primaryMirrorCurve;
+    private CrossSectionCurve secondaryMirrorCurve;
 
     private double lastSliderValue = 0.5;
+    private boolean cuttingPlaneFromSymmetry;
 
     /**
      * Constructor.
@@ -67,12 +72,12 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
 
         if (getSecondaryDrawableFace() != null) {
             //recomputeSecondaryProfile();
-            controlPanel = new ProfilesPanel(this, this.primarySegments, this.secondarySegments);
-            controlPanel.getProfileRenderingPanel().setSecondaryMirrorSegments(this.secondaryMirrorSegments);
+            controlPanel = new ProfilesPanel(this, this.primaryCurve, this.secondaryCurve);
+            controlPanel.getProfileRenderingPanel().setSecondaryMirrorSegments(this.secondaryMirrorCurve);
         } else {
-            controlPanel = new ProfilesPanel(this, this.primarySegments);
+            controlPanel = new ProfilesPanel(this, this.primaryCurve);
         }
-        controlPanel.getProfileRenderingPanel().setPrimaryMirrorSegments(this.primaryMirrorSegments);
+        controlPanel.getProfileRenderingPanel().setPrimaryMirrorSegments(this.primaryMirrorCurve);
 
         // Place control panel to the topControlPanel
         this.topControlPanel.addTab(controlPanel.getName(), controlPanel.getIcon(), controlPanel);
@@ -107,15 +112,15 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
         
         switch (action) {
             case ProfilesPanel.ACTION_COMMAND_EXPORT:
-                exportProfile(this.primarySegments, "Export primary face profile to file");
+                exportProfile(this.primaryCurve, "Export primary face profile to file");
                 if (controlPanel.isMirrorCutsChecked()) {
-                    exportProfile(this.primaryMirrorSegments, "Export primary face mirror profile to file");
+                    exportProfile(this.primaryMirrorCurve, "Export primary face mirror profile to file");
                 }
 
                 if (getSecondaryDrawableFace() != null) {
-                    exportProfile(this.secondarySegments, "Export secondary face profile to file");
+                    exportProfile(this.secondaryCurve, "Export secondary face profile to file");
                     if (controlPanel.isMirrorCutsChecked()) {
-                        exportProfile(this.secondaryMirrorSegments, "Export secondary face mirror profile to file");
+                        exportProfile(this.secondaryMirrorCurve, "Export secondary face mirror profile to file");
                     }
                 }
                 break;
@@ -172,9 +177,17 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
     
     @Override
     public void acceptEvent(HumanFaceEvent event) {
+        if (event instanceof SymmetryPlaneChangedEvent && cuttingPlaneFromSymmetry) {
+            computeCuttingPlanesFromSymmetry(); // recompute cutting planes
+            if (controlPanel.isMirrorCutsChecked()) {
+                getCanvas().getScene().showMirrorPlanes();
+            } else {
+                getCanvas().getScene().hideMirrorPlanes();
+            }
+        }
     }
 
-    private void exportProfile(List<List<Point3d>> segments, String title) {
+    private void exportProfile(CrossSectionCurve curve, String title) {
         File file = new FileChooserBuilder(ProfilesAction.class)
                 .setTitle(title)
                 .setDefaultWorkingDirectory(new File(System.getProperty("user.home")))
@@ -200,9 +213,9 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
 
             PrintWriter writer = new PrintWriter(file.getAbsoluteFile(), "UTF-8");
             writer.println("N,X-Coordinate,Z-Coordinate");
-            for (int i = 0; i < segments.size(); i++) {
-                for (int j = 0; j < segments.get(i).size(); ++j) {
-                    writer.println(((i+1)*(j+1)) + "," + segments.get(i).get(j).x + "," + segments.get(i).get(j).z);
+            for (int i = 0; i < curve.getNumSegments(); i++) {
+                for (int j = 0; j < curve.getSegmentSize(i); ++j) {
+                    writer.println(((i+1)*(j+1)) + "," + curve.getSegment(j).get(j).x + "," + curve.getSegment(i).get(j).z);
                 }
             }
 
@@ -216,35 +229,51 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
         //Main profile
         CrossSectionZigZag cs = new CrossSectionZigZag(getScene().getDrawableCuttingPlane(0).getPlane());
         getPrimaryDrawableFace().getModel().compute(cs);
-        this.primarySegments = cs.getSegments();
+        this.primaryCurve = cs.getCrossSectionCurve();
 
         //Mirror profile
         CrossSectionZigZag mcs = new CrossSectionZigZag(getScene().getDrawableMirrorPlane(0).getPlane());
         getPrimaryDrawableFace().getModel().compute(mcs);
-        this.primaryMirrorSegments = mcs.getSegments();
+        this.primaryMirrorCurve = mcs.getCrossSectionCurve();
     }
 
     private void recomputeSecondaryProfile() {
         //Main profile
         CrossSectionZigZag cs = new CrossSectionZigZag(getScene().getDrawableCuttingPlane(1).getPlane());
         getSecondaryDrawableFace().getModel().compute(cs);
-        this.secondarySegments = cs.getSegments();
+        this.secondaryCurve = cs.getCrossSectionCurve();
 
         //Mirror profile
         CrossSectionZigZag mcs = new CrossSectionZigZag(getScene().getDrawableMirrorPlane(1).getPlane());
         getSecondaryDrawableFace().getModel().compute(mcs);
-        this.secondaryMirrorSegments = mcs.getSegments();
+        this.secondaryMirrorCurve = mcs.getCrossSectionCurve();
     }
 
     private void recomputeProfiles() {
         recomputePrimaryProfile();
-        controlPanel.getProfileRenderingPanel().setPrimarySegments(this.primarySegments);
-        controlPanel.getProfileRenderingPanel().setPrimaryMirrorSegments(this.primaryMirrorSegments);
+        projectCloseFeaturePoints(
+                primaryCurve, 
+                getCanvas().getScene().getHumanFace(0), 
+                getScene().getDrawableCuttingPlane(0).getPlane());
+        projectCloseFeaturePoints(
+                primaryMirrorCurve, 
+                getCanvas().getScene().getHumanFace(0), 
+                getScene().getDrawableCuttingPlane(0).getPlane());
+        controlPanel.getProfileRenderingPanel().setPrimarySegments(this.primaryCurve);
+        controlPanel.getProfileRenderingPanel().setPrimaryMirrorSegments(this.primaryMirrorCurve);
 
         if (getSecondaryDrawableFace() != null) {
             recomputeSecondaryProfile();
-            controlPanel.getProfileRenderingPanel().setSecondarySegments(this.secondarySegments);
-            controlPanel.getProfileRenderingPanel().setSecondaryMirrorSegments(this.secondaryMirrorSegments);
+            projectCloseFeaturePoints(
+                    secondaryCurve, 
+                    getCanvas().getScene().getHumanFace(1), 
+                    getScene().getDrawableCuttingPlane(1).getPlane());
+            projectCloseFeaturePoints(
+                    secondaryMirrorCurve, 
+                    getCanvas().getScene().getHumanFace(1), 
+                    getScene().getDrawableCuttingPlane(1).getPlane());
+            controlPanel.getProfileRenderingPanel().setSecondarySegments(this.secondaryCurve);
+            controlPanel.getProfileRenderingPanel().setSecondaryMirrorSegments(this.secondaryMirrorCurve);
         }
     }
 
@@ -274,9 +303,10 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
         getCanvas().getScene().setDrawableCuttingPlane(0, drPlane);
         getCanvas().getScene().setDrawableMirrorPlane(0, new DrawablePlane(drPlane));
         if (getSecondaryDrawableFace() != null) {
-            getCanvas().getScene().setDrawableCuttingPlane(1, drPlane);
+            getCanvas().getScene().setDrawableCuttingPlane(1, new DrawablePlane(drPlane));
             getCanvas().getScene().setDrawableMirrorPlane(1, new DrawablePlane(drPlane));
         }
+        cuttingPlaneFromSymmetry = false;
     }
     
     protected DrawablePlane getDrawableOrthogonalPlane(boolean horizontal) {
@@ -286,7 +316,7 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
             getSecondaryDrawableFace().getModel().compute(bbox);
         }
         
-        Vector3d normal = (horizontal) ? new Vector3d(0,1,0) : new Vector3d(1,0,0);
+        Vector3d normal = (horizontal) ? new Vector3d(0,1,0) : new Vector3d(-1,0,0);
         Point3d midPoint = bbox.getBoundingBox().getMidPoint();
         Point3d minPoint = bbox.getBoundingBox().getMinPoint();
         Point3d maxPoint = bbox.getBoundingBox().getMaxPoint();
@@ -301,24 +331,24 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
         
         Plane plane = new Plane(normal, (horizontal) ? midPoint.y : midPoint.x);
         
-        MeshFacet planeMesh = new MeshFacetImpl();
+        MeshRectangleFacet planeMesh;
         if (horizontal) {
-            planeMesh.addVertex(new MeshPointImpl(new Point3d(minPoint.x, midPoint.y, maxPoint.z), normal, null));
-            planeMesh.addVertex(new MeshPointImpl(new Point3d(minPoint.x, midPoint.y, minPoint.z), normal, null));
-            planeMesh.addVertex(new MeshPointImpl(new Point3d(maxPoint.x, midPoint.y, minPoint.z), normal, null));
-            planeMesh.addVertex(new MeshPointImpl(new Point3d(maxPoint.x, midPoint.y, maxPoint.z), normal, null));
+            planeMesh = new MeshRectangleFacet(
+                    midPoint,
+                    new Vector3d(1, 0, 0),
+                    new Vector3d(0, 0, 1),
+                    maxPoint.x - minPoint.x,
+                    maxPoint.z - minPoint.z
+            );
         } else {
-            planeMesh.addVertex(new MeshPointImpl(new Point3d(midPoint.x, minPoint.y, minPoint.z), normal, null));
-            planeMesh.addVertex(new MeshPointImpl(new Point3d(midPoint.x, maxPoint.y, minPoint.z), normal, null));
-            planeMesh.addVertex(new MeshPointImpl(new Point3d(midPoint.x, maxPoint.y, maxPoint.z), normal, null));
-            planeMesh.addVertex(new MeshPointImpl(new Point3d(midPoint.x, minPoint.y, maxPoint.z), normal, null));
+            planeMesh = new MeshRectangleFacet(
+                    midPoint,
+                    new Vector3d(0, 0, 1),
+                    new Vector3d(0, 1, 0),
+                    maxPoint.z - minPoint.z,
+                    maxPoint.y - minPoint.y
+            );
         }
-        planeMesh.getCornerTable().addRow(new CornerTableRow(0, 5));
-        planeMesh.getCornerTable().addRow(new CornerTableRow(1, -1));
-        planeMesh.getCornerTable().addRow(new CornerTableRow(3, -1));
-        planeMesh.getCornerTable().addRow(new CornerTableRow(3, -1));
-        planeMesh.getCornerTable().addRow(new CornerTableRow(1, -1));
-        planeMesh.getCornerTable().addRow(new CornerTableRow(2, 0));
 
         DrawablePlane cuttingPlane = new DrawablePlane(planeMesh, plane);
         cuttingPlane.setTransparency(0.5f);
@@ -354,6 +384,18 @@ public class ProfilesAction extends ControlPanelAction implements HumanFaceListe
             getCanvas().getScene().setDrawableMirrorPlane(1, new DrawablePlane(cuttingPlane));
             recomputeSecondaryProfile();
         }
+        cuttingPlaneFromSymmetry = true;
     }
 
+    protected void projectCloseFeaturePoints(CrossSectionCurve curve, HumanFace face, Plane cuttingPlane) {
+        if (!face.hasFeaturePoints()) {
+            return;
+        }
+        List<Point3d> close = face.getFeaturePoints()
+                .stream()
+                .filter(fp -> Math.abs(cuttingPlane.getPointDistance(fp.getPosition())) < FEATUE_POINTS_CLOSENESS)
+                .map(fp -> fp.getPosition())
+                .collect(Collectors.toList());
+        curve.projectFeaturePointsToCurve(close);
+    }
 }
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 b3dd5b1b72f6ab0df8df7f986d6ce39bedca6b29..e9ac0b558c5e1574433758cc35be0720d0325f52 100644
--- a/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java
+++ b/GUI/src/main/java/cz/fidentis/analyst/symmetry/ProfilesPanel.java
@@ -7,9 +7,7 @@ package cz.fidentis.analyst.symmetry;
 
 import cz.fidentis.analyst.core.ControlPanel;
 import java.awt.event.ActionListener;
-import java.util.List;
 import javax.swing.ImageIcon;
-import javax.vecmath.Point3d;
 
 /**
  * Control panel for face profiles.
@@ -37,21 +35,28 @@ public class ProfilesPanel extends ControlPanel {
     public static final String NAME = "Profiles";
     
     /**
-     * Constructor for one face
+     * Constructor for one face.
+     * 
+     * @param action Action listener
+     * @param curve Cross section curve
      */
-    public ProfilesPanel(ActionListener action, List<List<Point3d>> values) {
-        this(action, values, null);
+    public ProfilesPanel(ActionListener action, CrossSectionCurve curve) {
+        this(action, curve, null);
     }
     
     /**
      * Constructor for two faces
+     * 
+     * @param action Action listener
+     * @param primaryCurve Primary cross section curve
+     * @param secondaryCurve Secondary cross section curve
      */
-    public ProfilesPanel(ActionListener action, List<List<Point3d>> primary, List<List<Point3d>> secondary) {
+    public ProfilesPanel(ActionListener action, CrossSectionCurve primaryCurve, CrossSectionCurve secondaryCurve) {
         setName(NAME);
         initComponents();
         
-        polylinePanel1.setPrimarySegments(primary);
-        polylinePanel1.setSecondarySegments(secondary);
+        polylinePanel1.setPrimarySegments(primaryCurve);
+        polylinePanel1.setSecondarySegments(secondaryCurve);
         
         comboSliderDouble1.setRange(0, 1, 2);
         comboSliderDouble1.setValue(0.5);
diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java
index 9d7047415f29a5785e50ed0da51b581f52ac9ef1..f046f5f948ffc74e693bd2f0b5de662298fdd059 100644
--- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java
+++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshModel.java
@@ -58,8 +58,6 @@ public class MeshModel implements Serializable {
 
     /**
      * Adds a new mesh facet to the model. 
-     * Fires {@link cz.fidentis.analyst.mesh.events.FacetAddedEvent} to
-     * registered listeners.
      *
      * @param facet new MeshFacet
      */
@@ -69,8 +67,6 @@ public class MeshModel implements Serializable {
     
     /**
      * Adds a new mesh facets to the model. 
-     * Fires {@link cz.fidentis.analyst.mesh.events.FacetAddedEvent} to
-     * registered listeners.
      *
      * @param newFacets collection of new new facets
      */
diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPoint.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPoint.java
index 67cd4703631ed78e2f667e6a3d5512b2c5827c95..ab67c9edef169daf9335fff21339c4f0315947f1 100644
--- a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPoint.java
+++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshPoint.java
@@ -21,7 +21,7 @@ public interface MeshPoint extends IPosition, Serializable {
      * @return distance
      * @throws NullPointerException if some input parameter is missing.
      */
-    static double distance(Vector3d v1, Vector3d v2) {
+    static double distance(Point3d v1, Point3d v2) {
         return Math.sqrt(Math.pow(v1.x - v2.x, 2)
                 + Math.pow(v1.y - v2.y, 2)
                 + Math.pow(v1.z - v2.z, 2));
diff --git a/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshRectangleFacet.java b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshRectangleFacet.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c54facb7d3580a09106e84b14bb044706bfdc58
--- /dev/null
+++ b/MeshModel/src/main/java/cz/fidentis/analyst/mesh/core/MeshRectangleFacet.java
@@ -0,0 +1,83 @@
+package cz.fidentis.analyst.mesh.core;
+
+import javax.vecmath.Point3d;
+import javax.vecmath.Vector3d;
+
+/**
+ * A rectangular mesh facet consisted of two triangles.
+ * 
+ * @author Radek Oslejsek
+ */
+public class MeshRectangleFacet extends MeshFacetImpl {
+    
+    /**
+     * Constructor.
+     * 
+     * @param center Central point of the rectangle
+     * @param hDir Normalized horizontal direction of the rectangle
+     * @param vDir Normalized vertical direction of the rectangle
+     * @param w Width
+     * @param h Height
+     */
+    public MeshRectangleFacet(Point3d center, Vector3d hDir, Vector3d vDir, double w, double h) {
+        this(center, hDir, vDir, null, w, h);
+    }
+    
+    /**
+     * Constructor.
+     * 
+     * @param center Central point of the rectangle
+     * @param hDir Normalized horizontal direction of the rectangle
+     * @param vDir Normalized vertical direction of the rectangle
+     * @param normal Optional normalized normal direction perpendicular to {@code hDir} and {@code vDir}. Can be {@code null}.
+     * @param w Width
+     * @param h Height
+     */
+    public MeshRectangleFacet(Point3d center, Vector3d hDir, Vector3d vDir, Vector3d normal, double w, double h) {
+        initRectangle(center, hDir, vDir, null, w, h);
+    }
+    
+    protected final void initRectangle(Point3d center, Vector3d hDir, Vector3d vDir, Vector3d normal, double w, double h) {
+        Vector3d myNormal;
+        
+        if (normal == null) {
+            myNormal = new Vector3d();
+            myNormal.cross(hDir, vDir);
+        } else {
+            myNormal = new Vector3d(normal);
+        }
+        
+        Point3d corner = new Point3d(center);
+        Vector3d aScaled = new Vector3d(hDir);
+        Vector3d bScaled = new Vector3d(vDir);
+        aScaled.scale(w/2f);
+        bScaled.scale(h/2f);
+        corner.sub(aScaled);
+        corner.sub(bScaled);
+        
+        Point3d p = new Point3d(corner);
+        addVertex(new MeshPointImpl(p, myNormal, null));
+        
+        Vector3d dir = new Vector3d(vDir);
+        dir.scale(h);
+        p = new Point3d(corner);
+        p.add(dir);
+        addVertex(new MeshPointImpl(p, myNormal, null));
+        
+        dir = new Vector3d(hDir);
+        dir.scale(w);
+        p.add(dir);
+        addVertex(new MeshPointImpl(p, myNormal, null));
+        
+        p = new Point3d(corner);
+        p.add(dir);
+        addVertex(new MeshPointImpl(p, myNormal, null));
+        
+        getCornerTable().addRow(new CornerTableRow(0,  5));
+        getCornerTable().addRow(new CornerTableRow(1, -1));
+        getCornerTable().addRow(new CornerTableRow(3, -1));
+        getCornerTable().addRow(new CornerTableRow(3, -1));
+        getCornerTable().addRow(new CornerTableRow(1, -1));
+        getCornerTable().addRow(new CornerTableRow(2,  0));
+    }
+}