]> gerrit.simantics Code Review - simantics/3d.git/commitdiff
Revised update logic for inline components 61/3661/1
authorMarko Luukkainen <marko.luukkainen@semantum.fi>
Tue, 3 Dec 2019 11:49:36 +0000 (13:49 +0200)
committerMarko Luukkainen <marko.luukkainen@semantum.fi>
Tue, 3 Dec 2019 11:53:58 +0000 (11:53 +0000)
* PipingRules no longer have separate code for path legs with offsets
* Overlapping fixed length components are forced to be separate, as long
as there is available space
* Using more stable calculation for turn component orientations (reusing
path leg direction vector)
* Error messages of overlapping inline components

gitlab #14
gitlab #59

Change-Id: I85e754ffb6dab37ca7b7ae8e5f28842fbf095a11
(cherry picked from commit aca223a1616159645710d7c9ee67ed1a6bd47b99)

org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipeControlPoint.java
org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipingRules.java

index 8616e70352815a50256a1b16e32afdbe1c52c787..abd899ac23ae0af43f07b3020894fa29b9b0bfc9 100644 (file)
@@ -529,26 +529,34 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                        return getControlPointOrientationQuat(dir, turnAxis, angle);
                }
        }
+       
+       public Quat4d getControlPointOrientationQuat(Vector3d dir, double angle, boolean reversed) {
+           if (turnAxis == null) {
+            if (dir.lengthSquared() > MathTools.NEAR_ZERO)
+                dir.normalize();
+            Quat4d q =  getControlPointOrientationQuat(dir, angle);
+            if (reversed) {
+                Quat4d q2 = new Quat4d();
+                q2.set(new AxisAngle4d(MathTools.Y_AXIS, Math.PI));
+                q.mulInverse(q2);
+            }
+            return q;
+        } else {
+            if (dir.lengthSquared() > MathTools.NEAR_ZERO)
+                dir.normalize();
+            return getControlPointOrientationQuat(dir, turnAxis, angle);
+        }
+       }
 
        public Quat4d getControlPointOrientationQuat(double angle, boolean reversed) {
 
                if (turnAxis == null) {
                        Vector3d dir = getPathLegDirection(Direction.NEXT);
-                       if (dir.lengthSquared() > MathTools.NEAR_ZERO)
-                               dir.normalize();
-                       Quat4d q =  getControlPointOrientationQuat(dir, angle);
-                       if (reversed) {
-                               Quat4d q2 = new Quat4d();
-                               q2.set(new AxisAngle4d(MathTools.Y_AXIS, Math.PI));
-                               q.mulInverse(q2);
-                       }
-                       return q;
+                       return getControlPointOrientationQuat(dir, angle, reversed);
                } else {
                        Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
                        dir.negate();
-                       if (dir.lengthSquared() > MathTools.NEAR_ZERO)
-                               dir.normalize();
-                       return getControlPointOrientationQuat(dir, turnAxis, angle);
+                       return getControlPointOrientationQuat(dir, angle, reversed);
                }
        }
 
index 688e50f9f6485824a6f51efb4abfcf86e85eaf80..b64a9e93cce04a303747931499753e76d002645b 100644 (file)
@@ -16,6 +16,7 @@ import org.simantics.plant3d.scenegraph.PipelineComponent;
 import org.simantics.plant3d.scenegraph.TurnComponent;
 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction;
 import org.simantics.plant3d.utils.ComponentUtils;
+import org.simantics.utils.datastructures.Pair;
 import org.simantics.utils.ui.ErrorLogger;
 
 public class PipingRules {
@@ -24,7 +25,7 @@ public class PipingRules {
 
        private static double MIN_TURN_ANGLE = 0.001;    // Threshold for removing turn components.
        private static double ALLOWED_OFFSET = 0.001;    // Allowed offset for directed path legs
-       private static double MIN_INLINE_LENGTH = 0.001; // Minimum length of inline components, when component removal is not allowed. 
+       private static double MIN_INLINE_LENGTH = 0.0005; // Minimum length of inline components, when component removal is not allowed. 
 
        private static final int REMOVE_NONE = 0;
        private static final int REMOVE_START = 1;
@@ -477,11 +478,11 @@ public class PipingRules {
                if (asDirected(u.end, Direction.PREVIOUS))
                        directed++;
                if (rs)
-                   u.start.getPipelineComponent().setError(null);
+                   setErrorForce(u.start, null);
                if (re)
-                   u.end.getPipelineComponent().setError(null);
+                   setErrorForce(u.end, null);
         for (PipeControlPoint pcp : u.list)
-            pcp.getPipelineComponent().setError(null);
+            setErrorForce(pcp, null);
                switch (directed) {
                case 0:
                        updateFreePathLeg(u, lengthChange);
@@ -520,149 +521,96 @@ public class PipingRules {
                }
 
                boolean recalcline = false;
-               if (!u.hasOffsets) {
-                       
-                       
-                       for (PipeControlPoint icp : u.list) {
-                               updateInlineControlPoint(icp, start, end, u.dir);
-                               
-                               if (icp.isOffset()) {
-                                       // TODO : offset vector is already calculated and should be cached
-                                       Vector3d off = icp.getSizeChangeOffsetVector(u.dir);
-                                       updateOffsetPoint(icp, off);
-                               }
-                       }
-                       if (!checkSizes)
-                               return;                 
 
-                       ArrayList<PipeControlPoint> pathLegPoints = new ArrayList<PipeControlPoint>();
-                       ArrayList<PipeControlPoint> fixedLengthPoints = new ArrayList<PipeControlPoint>();
-                       pathLegPoints.add(u.start);
-                       
-                       for (PipeControlPoint icp : u.list) {
-                               // updateInlineControlPoint(icp, u.startPoint,
-                               // u.endPoint,u.dir);
-                               updateBranchControlPointBranches(icp);
-                               pathLegPoints.add(icp);
-                               if (!icp.isVariableLength())
-                                   fixedLengthPoints.add(icp);
+               Vector3d sp = new Vector3d(start);
+               Vector3d ep = new Vector3d(end);
+               ep.sub(u.offset);
+               
+       
+               for (PipeControlPoint icp : u.list) {
+                       updateInlineControlPoint(icp, sp, ep, u.dir);
+                       if (icp.isOffset()) {
+                               // TODO : offset vector is already calculated and should be cached
+                               Vector3d  offset = icp.getSizeChangeOffsetVector(u.dir);
+                               updateOffsetPoint(icp, offset);
+                               sp.add(offset);
+                               ep.add(offset);
                        }
-                       pathLegPoints.add(u.end);
+               }
+               
+               if (!checkSizes)
+                       return; 
+
+               // Collect all path leg points for updating variable length components. This list will also contain leg ends (usually turns)
+        ArrayList<PipeControlPoint> pathLegPoints = new ArrayList<>();
+        // Collect all fixed length components with their offsets. 
+        ArrayList<Pair<PipeControlPoint,Vector3d>> fixedLengthPoints = new ArrayList<>();
+
+        pathLegPoints.add(u.start);
+        fixedLengthPoints.add(new Pair<PipeControlPoint, Vector3d>(u.start, new Vector3d()));
+        Vector3d off = new Vector3d();
+        for (PipeControlPoint icp : u.list) {
+            pathLegPoints.add(icp);
+            updateBranchControlPointBranches(icp);
+            if (icp.isOffset()) {
+                fixedLengthPoints.add(new Pair<PipeControlPoint, Vector3d>(icp, new Vector3d(off)));
+                Vector3d  offset = icp.getSizeChangeOffsetVector(u.dir);
+                off.add(offset);
+            } else if (!icp.isVariableLength()) {
+                fixedLengthPoints.add(new Pair<PipeControlPoint, Vector3d>(icp, new Vector3d(off)));
+            }
+        }
+        pathLegPoints.add(u.end);
+        fixedLengthPoints.add(new Pair<PipeControlPoint, Vector3d>(u.end, new Vector3d(off)));
+        
+               sp = new Vector3d(start);
+               ep = new Vector3d(end);
+               ep.sub(u.offset);
+               
+               updateFixedLengths(fixedLengthPoints, sp, ep, u.dir);
+               
+               for (int i = 0; i < pathLegPoints.size(); i++) {
+                       PipeControlPoint icp = pathLegPoints.get(i);
 
-                       // updateInlineControlPoint keeps components between path leg ends, but does not ensure that fixed length components do no overlap each other
+                       PipeControlPoint prev = i > 0 ? pathLegPoints.get(i - 1) : null;
+                       PipeControlPoint next = i < pathLegPoints.size() - 1 ? pathLegPoints.get(i + 1) : null;
                        
-                       for (int i = 0; i < fixedLengthPoints.size(); i++) {
-                           PipeControlPoint prev = i == 0 ? null : fixedLengthPoints.get(i-1);
-                           PipeControlPoint curr = fixedLengthPoints.get(i);
-                           PipeControlPoint next = i == fixedLengthPoints.size() -1 ? null : fixedLengthPoints.get(i+1);
-                           updateFixedLength(curr, prev, next, start,end, u.dir);
-                       }
-
-                       for (int i = 0; i < pathLegPoints.size(); i++) {
-                               PipeControlPoint icp = pathLegPoints.get(i);
-
-                               PipeControlPoint prev = i > 0 ? pathLegPoints.get(i - 1) : null;
-                               PipeControlPoint next = i < pathLegPoints.size() - 1 ? pathLegPoints.get(i + 1) : null;
+                       if (prev != null && prev.isDualInline())
+                               prev = prev.getDualSub();               
 
-                               if (icp.isVariableLength()) {
-                                       if (prev != null && next != null) {
-                                               
-                                               recalcline = recalcline | updateVariableLength(icp,  prev, next);
-
-                                       } else {
-                                               // this is variable length component at the end of the
-                                               // piperun.
-                                               // the problem is that we want to keep unconnected end
-                                               // of the component in the same
-                                               // place, but center of the component must be moved.
-                                               updateVariableLengthEnd(icp, prev != null ? prev : next);
-                                       }
-
-
-                               } else if (prev != null && !prev.isVariableLength()) {
-                                       // If this and previous control point are not variable
-                                       // length pcps, we'll have to check if there is no empty
-                                       // space between them.
-                                       // I there is, we'll have to create new variable length
-                                       // component between them.
-                                       recalcline = recalcline | possibleVaribleLengthInsert(icp, prev);
-                               }
-                       }
-               } else { // with offset
-                       Vector3d sp = new Vector3d(start);
-                       Vector3d ep = new Vector3d(end);
-                       ep.sub(u.offset);
-                       
-                       ArrayList<PipeControlPoint> pathLegPoints = new ArrayList<PipeControlPoint>();
-                       pathLegPoints.add(u.start);
+                       if (icp.isVariableLength()) {
+                               if (prev != null && next != null) {
+                                       recalcline = recalcline | updateVariableLength(icp,  prev, next);
 
-                       for (PipeControlPoint icp : u.list) {
-                               updateInlineControlPoint(icp, sp, ep, u.dir);
-                               updateBranchControlPointBranches(icp);
-                               pathLegPoints.add(icp);
-                               if (icp.isOffset()) {
-                                       // TODO : offset vector is already calculated and should be
-                                       // cached
-                                       Vector3d  offset = icp.getSizeChangeOffsetVector(u.dir);
-                                       updateOffsetPoint(icp, offset);
-                                       sp.add(offset);
-                                       ep.add(offset);
+                               } else {
+                                   // this is variable length component at the end of the piperun.
+                    // the problem is that we want to keep unconnected end of the component in the same
+                    // place, but center of the component must be moved.
+                                       updateVariableLengthEnd(icp, prev != null ? prev : next);
                                }
+                       } else if (prev != null && !prev.isVariableLength()) {
+                           // If this and previous control point are not variable length pcps, 
+                // we'll have to check if there is no empty space between them.
+                // I there is, we'll have to create new variable length component between them.
+                               recalcline = recalcline | possibleVaribleLengthInsert(icp, prev);
                        }
-                       pathLegPoints.add(u.end);
-                       
-                       if (!checkSizes)
-                               return; 
-                       
-                       sp = new Vector3d(u.startPoint);
-                       ep = new Vector3d(u.endPoint);
-                       ep.sub(u.offset);
-                       
-                       for (int i = 0; i < pathLegPoints.size(); i++) {
-                               PipeControlPoint icp = pathLegPoints.get(i);
-
-                               PipeControlPoint prev = i > 0 ? pathLegPoints.get(i - 1) : null;
-                               PipeControlPoint next = i < pathLegPoints.size() - 1 ? pathLegPoints.get(i + 1) : null;
-                               
-                               if (prev != null && prev.isDualInline())
-                                       prev = prev.getDualSub();
-                               
-
-                               if (icp.isVariableLength()) {
-                                       if (prev != null && next != null) {
-                                               recalcline = recalcline | updateVariableLength(icp,  prev, next);
-
-                                       } else {
-                                               // this is variable length component at the end of the
-                                               // piperun.
-                                               // the problem is that we want to keep unconnected end
-                                               // of the component in the same
-                                               // place, but center of the component must be moved.
-                                               updateVariableLengthEnd(icp, prev != null ? prev : next);
-                                       }
-                               } else if (prev != null && !prev.isVariableLength()) {
-                                       // If this and previous control point are not variable
-                                       // length pcps, we'll have to check if there is no empty
-                                       // space between them.
-                                       // I there is, we'll have to create new variable length
-                                       // component between them.
-                                       recalcline = recalcline | possibleVaribleLengthInsert(icp, prev);
-                               }
-                               if (icp.isOffset()) {
-                                       // TODO : offset vector is already calculated and should be
-                                       // cached
-                                       Vector3d  offset = icp.getSizeChangeOffsetVector(u.dir);
-                                       sp.add(offset);
-                                       ep.add(offset);
-                               }
+                       if (icp.isOffset()) {
+                               // TODO : offset vector is already calculated and should be cached
+                               Vector3d  offset = icp.getSizeChangeOffsetVector(u.dir);
+                               sp.add(offset);
+                               ep.add(offset);
                        }
                }
+               
                if (recalcline) {
                        u.list.clear();
                        u.start.findNextEnd(u.list);
                } 
                if (checkSizes) {
-                   double pathLegLength = MathTools.distance(u.startPoint, u.endPoint);
+                   sp = new Vector3d(u.startPoint);
+               ep = new Vector3d(u.endPoint);
+               ep.sub(u.offset);
+                   double pathLegLength = MathTools.distance(sp, ep);
             double availableLength = pathLegLength;
             if (u.start.isTurn())
                 availableLength -= u.start.getInlineLength();
@@ -673,32 +621,186 @@ public class PipingRules {
                     availableLength-= pcp.getLength();
                    }
             if (availableLength < 0.0) {
-                u.start.getPipelineComponent().setError("Not enough available space");
-                u.end.getPipelineComponent().setError("Not enough available space");
+                setError(u.start, "Not enough available space");
+                setError(u.end, "Not enough available space");
                 for (PipeControlPoint pcp : u.list)
-                    pcp.getPipelineComponent().setError("Not enough available space");
+                    setError(pcp, "Not enough available space");
             }
 //            System.out.println(u.start.getPipelineComponent().toString() + " " + pathLegLength + " " + availableLength + " " + u.end.getPipelineComponent().toString() + " " + u.start.getInlineLength() + " " + u.end.getInlineLength());
                }
        }
        
-       private static void updateFixedLength(PipeControlPoint icp, PipeControlPoint prev,  PipeControlPoint next, Vector3d s, Vector3d e, Vector3d dir) {
+       private enum Gap{ATTACHED,OVERLAP,SPACE};
+       
+       private static class GapObj {
+           Gap gap;
+           double d;
+           
+           Pair<PipeControlPoint,Vector3d> pcp1;
+           Pair<PipeControlPoint,Vector3d> pcp2;
+       }
+       
+       private static void updateFixedLengths(List<Pair<PipeControlPoint,Vector3d>> fixedLengthPoints, Vector3d s, Vector3d e, Vector3d dir) {
+           double totalLength = MathTools.distance(s, e);
+           double reservedLength = 0.0;
+           List<Double> distances = new ArrayList<>(fixedLengthPoints.size());
+           distances.add(0.0);
+           for (int i = 1; i < fixedLengthPoints.size()-1; i++) {
+               Pair<PipeControlPoint,Vector3d> pcp = fixedLengthPoints.get(i);
+               reservedLength += pcp.first.getLength();
+               Vector3d p = pcp.first.getWorldPosition();
+               p.sub(pcp.second);
+               double d= MathTools.distance(s, p);
+               distances.add(d);
+           }
+           distances.add(totalLength);
+           
+           if (totalLength >= reservedLength) {
+               // There is enough space for all fixed length components.
+               List<GapObj> gaps = new ArrayList<>(fixedLengthPoints.size()-1);
+               int overlaps = 0;
+               // Analyze gaps between components
+               for (int i = 0; i < fixedLengthPoints.size()-1; i++) {
+                   Pair<PipeControlPoint,Vector3d> pcp1 = fixedLengthPoints.get(i);
+                   Pair<PipeControlPoint,Vector3d> pcp2 = fixedLengthPoints.get(i+1);
+                   double d1 = distances.get(i);
+                   double d2 = distances.get(i+1);
+                   double ld1 = i == 0 ? 0.0 :pcp1.first.getInlineLength();
+                   double ld2 = i == fixedLengthPoints.size()-2 ? 0.0 : pcp2.first.getInlineLength();
+                   
+                   double e1 = d1 + ld1; // End of comp1
+                   double s2 = d2 - ld2; // Start of comp2
+                   double diff =s2 - e1;
+                   GapObj obj = new GapObj();
+                   obj.pcp1 = pcp1;
+                   obj.pcp2 = pcp2;
+                   obj.d = diff;
+                   if (diff < -MIN_INLINE_LENGTH) {
+                       obj.gap = Gap.OVERLAP;
+                       overlaps++;
+                   } else if (diff > MIN_INLINE_LENGTH) {
+                       obj.gap = Gap.SPACE;
+                   } else {
+                       obj.gap = Gap.ATTACHED;
+                   }
+                   gaps.add(obj);
+               }
+               // If there are no overlaps, there is nothing to do.
+               if (overlaps == 0)
+                   return;
+               // Get rid of overlapping components by using closest available free spaces.
+               for (int i = 0; i < gaps.size(); i++) {
+                   GapObj gapObj = gaps.get(i);
+                   if (gapObj.gap != Gap.OVERLAP)
+                       continue;
+                   double curr = gapObj.d;
+                   int d = 1;
+                   while (curr < -MIN_INLINE_LENGTH) {
+                       GapObj next = i+d >= 0 ? gaps.get(i+d) : null;
+                    GapObj prev = i-d >= 0 ? gaps.get(i-d) : null;
+                       if (next != null && next.gap == Gap.SPACE) {
+                           double move = Math.min(-curr, next.d);
+                           curr+= move;
+                           next.d -= move;
+                           if (next.d < MIN_INLINE_LENGTH)
+                               next.gap = Gap.ATTACHED;
+                           Vector3d mv = new Vector3d(dir);
+                           mv.normalize();
+                           mv.scale(move);
+                           for (int j = i ; j < i+d; j++) {
+                               Pair<PipeControlPoint,Vector3d> pcp = gaps.get(j).pcp2;
+                               Vector3d p = new Vector3d(pcp.first.getWorldPosition());
+                               p.add(mv);
+                               pcp.first.setWorldPosition(p);
+                           }
+                       }
+                       if (curr < -MIN_INLINE_LENGTH && prev != null && prev.gap == Gap.SPACE) {
+                           double move = Math.min(-curr, prev.d);
+                        curr+= move;
+                        next.d -= move;
+                        if (next.d < MIN_INLINE_LENGTH)
+                            next.gap = Gap.ATTACHED;
+                        Vector3d mv = new Vector3d(dir);
+                        mv.normalize();
+                        mv.scale(-move);
+                        for (int j = i ; j > i-d; j--) {
+                            Pair<PipeControlPoint,Vector3d> pcp = gaps.get(j).pcp1;
+                            Vector3d p = new Vector3d(pcp.first.getWorldPosition());
+                            p.add(mv);
+                            pcp.first.setWorldPosition(p);
+                        }
+                       }
+                   }
+               }
+           } else {
+            for (int i = 1; i < fixedLengthPoints.size()-1; i++) {
+               Pair<PipeControlPoint,Vector3d> prev = i == 0 ? null : fixedLengthPoints.get(i-1);
+               Pair<PipeControlPoint,Vector3d> curr = fixedLengthPoints.get(i);
+               Pair<PipeControlPoint,Vector3d> next = i == fixedLengthPoints.size() -1 ? null : fixedLengthPoints.get(i+1);
+                updateFixedLength(curr, prev, next, s,e, dir);
+            }
+           }
+       }
+       
+       private static void updateFixedLength(Pair<PipeControlPoint,Vector3d> icp, Pair<PipeControlPoint,Vector3d> prev,  Pair<PipeControlPoint,Vector3d> next, Vector3d s, Vector3d e, Vector3d dir) {
            if (prev != null) {
-              checkOverlap(icp, prev);
+              checkOverlap(prev, icp, dir,true);
            }
            if (next != null)
-               checkOverlap(icp, next);
+               checkOverlap(icp, next, dir,true);
        }
        
-       private static void checkOverlap(PipeControlPoint icp, PipeControlPoint prev) {
-           double d = MathTools.distance(prev.getWorldPosition(), icp.getWorldPosition());
-        double r = icp.getInlineLength() + prev.getInlineLength();
-        if (d < r) {
-            if (icp.getPipelineComponent().getError() == null)
-                icp.getPipelineComponent().setError("Overlapping");
-            if (prev.getPipelineComponent().getError() == null)
-                prev.getPipelineComponent().setError("Overlapping");
+       private static boolean checkOverlap(Pair<PipeControlPoint,Vector3d> icp, Pair<PipeControlPoint,Vector3d> icp2, Vector3d dir, boolean se) {
+           Vector3d p1 = icp.first.getWorldPosition();
+           Vector3d p2 = icp2.first.getWorldPosition();
+           p1.add(icp.second);
+           p2.add(icp2.second);
+           double u[] = new double[1];
+           MathTools.closestPointOnStraight(p2, p1, dir, u);
+           if (u[0] < 0.0) {
+               p2.set(p1);
+               p2.sub(icp.second);
+               p2.add(icp2.second);
+               MathTools.mad(p2, dir, MIN_INLINE_LENGTH);
+               icp2.first.setWorldPosition(p2);
+           }
+           double d = MathTools.distance(p1, p2);
+        double r = icp.first.getInlineLength() + icp2.first.getInlineLength();
+        
+           if ((d-r) < - MIN_INLINE_LENGTH) {
+            if (se) {
+                setError(icp.first, "Overlapping");
+                setError(icp2.first, "Overlapping");
+            }
+            return true;
         }
+        return false;
+       }
+       
+       /**
+        * Overrides current error of a component
+        * @param pcp
+        * @param error
+        */
+       private static void setErrorForce(PipeControlPoint pcp, String error) {
+           PipelineComponent comp = pcp.getPipelineComponent();
+        if (comp == null)
+            return;
+        comp.setError(error);
+       }
+       
+       /**
+        * Sets error for a component, if there is no existing error.
+        * @param pcp
+        * @param error
+        */
+       private static void setError(PipeControlPoint pcp, String error) {
+           PipelineComponent comp = pcp.getPipelineComponent();
+           if (comp == null)
+               return;
+           if (comp.getError() != null)
+               return;
+           comp.setError(error);
        }
        
        private static boolean updateVariableLength(PipeControlPoint icp, PipeControlPoint prev,  PipeControlPoint next) {
@@ -725,7 +827,7 @@ public class PipingRules {
                        if (icp.isDeletable()) {
                            if (!allowInsertRemove) {
                                icp.setLength(MIN_INLINE_LENGTH);
-                               icp.getPipelineComponent().setError("Not enough available space");
+                               setError(icp, "Not enough available space");
                                triedIR = true;
                                return false;
                            }
@@ -1376,26 +1478,26 @@ public class PipingRules {
                                updateTurnControlPointTurn(u.start, null, null);
 //                             updatePathLegPrev(u.start, u.start, PathLegUpdateType.NONE);
                        } else if (u.start.isEnd()) {
-                               updateEndComponentControlPoint(u.start, u.startPoint, u.endPoint);
+                               updateEndComponentControlPoint(u.start, u.dir);
                        } else if (u.start.isInline()) {
-                               updateControlPointOrientation(u.start);
+                               updateControlPointOrientation(u.start, u.dir);
                        }
                        if (u.end.isTurn()) {
                                //updateTurnControlPointTurn(u.end, u.end.getPrevious(), u.end.getNext());
                                updateTurnControlPointTurn(u.end, null, null);
 //                             updatePathLegNext(u.end, u.end, PathLegUpdateType.NONE);
                        } else if (u.end.isEnd()) {
-                               updateEndComponentControlPoint(u.end, u.startPoint, u.endPoint);
+                               updateEndComponentControlPoint(u.end, u.dir);
                        } else if (u.end.isInline()) {
-                               updateControlPointOrientation(u.end);
+                               updateControlPointOrientation(u.end, u.dir);
                        }
 
                } else {
                        if (u.start.isEnd()) {
-                               updateEndComponentControlPoint(u.start, u.startPoint, u.endPoint);
+                               updateEndComponentControlPoint(u.start, u.dir);
                        }
                        if (u.end.isEnd()) {
-                               updateEndComponentControlPoint(u.end, u.startPoint, u.endPoint);
+                               updateEndComponentControlPoint(u.end, u.dir);
                        }
                }
                if (updateInline)
@@ -1516,7 +1618,7 @@ public class PipingRules {
                        System.out.println(" " + newInlinePoint);
 
                icp.setWorldPosition(newInlinePoint);
-               updateControlPointOrientation(icp);
+               updateControlPointOrientation(icp, dir);
        }
 
        /**
@@ -1528,12 +1630,12 @@ public class PipingRules {
         * @param nextPoint
         * @param prevPoint
         */
-       private static void updateEndComponentControlPoint(PipeControlPoint ecp, Vector3d start, Vector3d end) throws Exception {
+       private static void updateEndComponentControlPoint(PipeControlPoint ecp, Vector3d dir) throws Exception {
                if (DEBUG)
                        System.out.println("PipingRules.updateEndComponentControlPoint() " + ecp);
                //FIXME : end control point cannot be fixed!
                //if (!ecp.isFixed())
-               updateControlPointOrientation(ecp);
+               updateControlPointOrientation(ecp, dir);
 
                for (PipeControlPoint pcp : ecp.getChildPoints()) {
                        // TODO update position
@@ -1541,21 +1643,18 @@ public class PipingRules {
                }
        }
 
-       private static void updateControlPointOrientation(PipeControlPoint pcp) {
-               // FIXME : hack to bypass variable length components orientation
-//             if (pcp.getAtMostOneRelatedObject(ProcessResource.g3dResource.HasWorldOrientation) == null)
-//                     return;
-//             if (pcp.rotationAngle == null)
-//                     return;
+       private static void updateControlPointOrientation(PipeControlPoint pcp, Vector3d dir) {
                Double angleO = pcp.getRotationAngle();
                double angle = 0.0;
                if (angleO != null)
                        angle = angleO;
-               Boolean reversedO = pcp.getReversed();
-               boolean reversed = false;
-               if (reversedO != null)
-                       reversed = reversedO;
-               Quat4d q = pcp.getControlPointOrientationQuat(angle, reversed);
+               boolean reversed = pcp._getReversed();
+               Quat4d q = null;
+               if (dir != null) {
+                   q = pcp.getControlPointOrientationQuat(dir, angle, reversed);
+               } else {
+                   q = pcp.getControlPointOrientationQuat(angle, reversed);
+               }
                pcp.setWorldOrientation(q);
        }
 
@@ -1624,7 +1723,7 @@ public class PipingRules {
                                tcp.setTurnAxis(new Vector3d(MathTools.Y_AXIS));
                        }
                        
-                       updateControlPointOrientation(tcp);
+                       updateControlPointOrientation(tcp,prev);
                        
                        if (DEBUG)
                                System.out.println("PipingTools.updateTurnControlPointTurn " + prev + " " + next + " " + turnAngle + " " + turnAxis);