]> gerrit.simantics Code Review - simantics/3d.git/blobdiff - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipingRules.java
Compiler warning elimination
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / scenegraph / controlpoint / PipingRules.java
index b096266f7af72901d2718812b9dd77d3570d3689..f5c6f876a98d507eca60c31bcd809dd8f25bb37d 100644 (file)
@@ -44,29 +44,46 @@ public class PipingRules {
        private static boolean triedIR = false;
 
        
-       private static List<PipeControlPoint> updates = new ArrayList<PipeControlPoint>();
+       private static List<PipeControlPoint> requestUpdates = new ArrayList<PipeControlPoint>();
+       private static List<PipeControlPoint> currentUpdates = new ArrayList<PipeControlPoint>();
        
-       private static Object mutex = new Object();
+       private static Object updateMutex = new Object();
+       private static Object ruleMutex = new Object();
        
        public static void requestUpdate(PipeControlPoint pcp) {
+           if (!PipingRules.enabled)
+               return;
                if (DEBUG) System.out.println("PipingRules request " + pcp);
-               synchronized (mutex) {
-               if (!updates.contains(pcp))
-                       updates.add(pcp);
+               synchronized (updateMutex) {
+                       if (!requestUpdates.contains(pcp))
+                               requestUpdates.add(pcp);
                }
        }
        
-       public static synchronized boolean update() throws Exception {
-               if (updates.size() == 0)
+       public static boolean update() throws Exception {
+           if (!PipingRules.enabled)
+               return false;
+           
+               if (requestUpdates.size() == 0)
                        return false;
-               List<PipeControlPoint> temp = new ArrayList<PipeControlPoint>(updates.size());
-               synchronized(mutex) {
-                       temp.addAll(updates);
-                       updates.clear();
+               
+               List<PipeControlPoint> temp = new ArrayList<PipeControlPoint>(requestUpdates.size());
+               synchronized(updateMutex) {
+                       temp.addAll(requestUpdates);
+                       requestUpdates.clear();
+               }
+               synchronized (ruleMutex) {
+                       currentUpdates.clear();
+                       currentUpdates.addAll(temp);
+                       // TODO : we should remove already processed control points from currentUpdates after each _positionUpdate call.
+                       for (PipeControlPoint pcp : currentUpdates)
+                               _positionUpdate(pcp, true);
+                       currentUpdates.clear();
+               }
+               synchronized(updateMutex) {
+                       requestUpdates.removeAll(temp);
                }
                
-               for (PipeControlPoint pcp : temp)
-                       positionUpdate(pcp);
                return true;
        }
        
@@ -76,6 +93,16 @@ public class PipingRules {
        }
        
        public static boolean positionUpdate(PipeControlPoint pcp, boolean allowIR) throws Exception {
+               synchronized (ruleMutex) {
+                       currentUpdates.add(pcp);
+                       boolean b = _positionUpdate(pcp, allowIR);
+                       currentUpdates.clear();
+                       return b;
+               }
+               
+       }
+       
+       private static boolean _positionUpdate(PipeControlPoint pcp, boolean allowIR) throws Exception {
                if (updating || !enabled)
                        return true;
                if (pcp.getPipeRun() == null)
@@ -106,7 +133,7 @@ public class PipingRules {
        public static void setEnabled(boolean enabled) {
                PipingRules.enabled = enabled;
                if(!enabled)
-                       updates.clear();
+                       currentUpdates.clear();
        }
        
        public static boolean isEnabled() {
@@ -274,41 +301,35 @@ public class PipingRules {
        }
 
        private static void updatePathLegNext(PipeControlPoint start, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
-               ArrayList<PipeControlPoint> list = new ArrayList<PipeControlPoint>();
-               PipeControlPoint end = start.findNextEnd(list);
-               // this is for inline cp that is also path leg end
+
+               UpdateStruct2 us = createUS(start, Direction.NEXT, 0, new ArrayList<ExpandIterInfo>(), updated);
                if (lengthChange == PathLegUpdateType.NONE) {
                        if (start.equals(updated))
                                lengthChange = PathLegUpdateType.NEXT;
-                       else if (end.equals(updated))
+                       else if (us.end.equals(updated))
                                lengthChange = PathLegUpdateType.PREV;
                }
-               updatePathLegNext(start, list, end, updated, lengthChange);
-       }
-
-       private static void updatePathLegNext(PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
-               updatePathLeg(start, list, end, false, 0, new ArrayList<ExpandIterInfo>(), updated, lengthChange);
+               if (us == null) {
+                   System.out.println("Null update struct " + start);
+                   return; 
+               }
+               updatePathLeg(us, lengthChange);
        }
        
        private static void updatePathLegPrev(PipeControlPoint start, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
-               ArrayList<PipeControlPoint> list = new ArrayList<PipeControlPoint>();
-               PipeControlPoint end = start.findPreviousEnd(list);
                // TODO: this method is not symmetric with updatePathLegNext, which may alter lengthChange parameter?
-               updatePathLegPrev(start, list, end, updated, lengthChange);
-       }
-
-       private static void updatePathLegPrev(PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
-               // reverses the list
-               ArrayList<PipeControlPoint> nextList = new ArrayList<PipeControlPoint>();
-               for (PipeControlPoint icp : list) {
-                       if (icp.isDualSub()) {
-                               nextList.add(0, icp.getParentPoint());
-                       } else {
-                               nextList.add(0, icp);
-                       }
-               }
-               updatePathLeg(end, nextList, start, true, 0, new ArrayList<ExpandIterInfo>(), updated, lengthChange);
-
+               UpdateStruct2 us = createUS(start, Direction.PREVIOUS, 0, new ArrayList<ExpandIterInfo>(), updated);
+//        if (lengthChange == PathLegUpdateType.NONE) {
+//            if (start.equals(updated))
+//                lengthChange = PathLegUpdateType.NEXT;
+//            else if (us.end.equals(updated))
+//                lengthChange = PathLegUpdateType.PREV;
+//        }
+               if (us == null) {
+            System.out.println("Null update struct " + start);
+            return; 
+        }
+               updatePathLeg(us, lengthChange);
        }
 
        private static class UpdateStruct2 {
@@ -354,6 +375,7 @@ public class PipingRules {
 
        }
 
+       @SuppressWarnings("unused")
        private static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, ArrayList<PipeControlPoint> list, Vector3d dir, Vector3d offset) {
                boolean hasOffsets = false;
                List<PipeControlPoint> offsets = new ArrayList<PipeControlPoint>(list.size());
@@ -403,60 +425,54 @@ public class PipingRules {
                        hasOffsets = true;
                }
                
-//             for (PipeControlPoint icp : list) {
-//                     if (icp.isOffset()) {
-//                             icp.setOffset(((InlineComponent)icp.getPipelineComponent()).getOffset());
-//                             hasOffsets = true;
-//                             Vector3d v = icp.getSizeChangeOffsetVector(dir);
-//                             offset.add(v);
-//                     } else if (icp.isDualSub())
-//                             ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
-//             }
                if (DEBUG && hasOffsets)
                        System.out.println("calcOffset s:"+ startPoint + " e:" + endPoint + " d:" + dir + " o:"+offset) ;
                return hasOffsets;
        }
-
-       /**
-        * @param start
-        *            starting point of the pipe run
-        * @param list
-        *            list of inline control points in the pipe run
-        * @param end
-        *            ending point of the pipe run
-        * @param reversed
-        *            boolean flag indicating wether start or end control point was
-        *            modified (if true then end point was modified)
-        * @throws TransactionException
-        */
-       private static void updatePathLeg(PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, boolean reversed, int iter, ArrayList<ExpandIterInfo> toRemove, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
+       
+       private static UpdateStruct2 createUS(PipeControlPoint start, Direction direction, int iter, ArrayList<ExpandIterInfo> toRemove, PipeControlPoint updated) {
+               ArrayList<PipeControlPoint> list = new ArrayList<PipeControlPoint>();
+               PipeControlPoint end = null;
+               if (direction == Direction.NEXT) {
+                       end = start.findNextEnd(list);
+               } else {
+                       ArrayList<PipeControlPoint> prevList = new ArrayList<PipeControlPoint>();
+                       PipeControlPoint tend = start.findPreviousEnd(prevList);
+                       for (PipeControlPoint icp : prevList) {
+                               if (icp.isDualSub()) {
+                                       list.add(0, icp.getParentPoint());
+                               } else {
+                                       list.add(0, icp);
+                               }
+                       }  
+                       end = start;
+                       start = tend;
+               }
                if (start == end)
-                       return;
-               // FIXME: direction is calculated wrong way!
+                       return null;
                boolean hasOffsets = false;
                Vector3d offset = new Vector3d();
                Vector3d startPoint = start.getWorldPosition();
                Vector3d endPoint = end.getWorldPosition();
                Vector3d dir = new Vector3d();
                hasOffsets = calculateOffset(startPoint, endPoint, list, dir, offset);
-               updatePathLeg(new UpdateStruct2(start, startPoint, list, end, endPoint, dir, offset, hasOffsets, iter, reversed, toRemove, updated), lengthChange);
-
+               return new UpdateStruct2(start, startPoint, list, end, endPoint, dir, offset, hasOffsets, iter, direction == Direction.PREVIOUS, toRemove, updated);
        }
        
        private static boolean asDirected(PipeControlPoint pcp, Direction direction) {
-           if (pcp.isDirected())
-               return true;
-           if (pcp.isTurn() && pcp.isFixed()) {
-              if (!pcp._getReversed())
-                  return direction == Direction.NEXT;
-              else
-                  return direction == Direction.PREVIOUS;
-           }
-           return false;
+               if (pcp.isDirected())
+                       return true;
+               if (pcp.asFixedAngle()) {
+                       if (!pcp._getReversed())
+                               return direction == Direction.NEXT;
+                       else
+                               return direction == Direction.PREVIOUS;
+               }
+               return false;
        }
        
        private static Vector3d direction(PipeControlPoint pcp, Direction direction) {
-           return pcp.getDirection(direction);
+               return pcp.getDirection(direction);
        }
  
        private static void updatePathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
@@ -483,7 +499,7 @@ public class PipingRules {
                if (DEBUG)
                        System.out.println("PipingRules.updateFreePipeRun " + u + " " + lengthChange);
                checkExpandPathLeg(u, lengthChange);
-               if (u.start.isInline() || u.end.isInline() || u.start.isFixed() || u.end.isFixed())
+               if (u.start.isInline() || u.end.isInline() || u.start.asFixedAngle()|| u.end.asFixedAngle())
                        processPathLeg(u, true, false);
        }
 
@@ -527,15 +543,15 @@ public class PipingRules {
                        pathLegPoints.add(u.end);
 
                        // TODO : values can be cached in the loop
-                       for (int i = 1; i < pathLegPoints.size(); i++) {
+                       for (int i = 0; i < pathLegPoints.size(); i++) {
                                PipeControlPoint icp = pathLegPoints.get(i);
 
-                               PipeControlPoint prev = pathLegPoints.get(i - 1);
-                               
+                               PipeControlPoint prev = i > 0 ? pathLegPoints.get(i - 1) : null;
+                               PipeControlPoint next = i < pathLegPoints.size() - 1 ? pathLegPoints.get(i + 1) : null;
 
                                if (icp.isVariableLength()) {
-                                       if (i != pathLegPoints.size() - 1) {
-                                               PipeControlPoint next = pathLegPoints.get(i + 1);
+                                       if (prev != null && next != null) {
+                                               
                                                recalcline = recalcline | updateVariableLength(icp,  prev, next);
 
                                        } else {
@@ -544,11 +560,11 @@ public class PipingRules {
                                                // 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);
+                                               updateVariableLengthEnd(icp, prev != null ? prev : next);
                                        }
 
 
-                               } else if (!prev.isVariableLength()) {
+                               } 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.
@@ -587,18 +603,18 @@ public class PipingRules {
                        ep = new Vector3d(u.endPoint);
                        ep.sub(u.offset);
                        
-                       for (int i = 1; i < pathLegPoints.size(); i++) {
+                       for (int i = 0; i < pathLegPoints.size(); i++) {
                                PipeControlPoint icp = pathLegPoints.get(i);
 
-                               PipeControlPoint prev = pathLegPoints.get(i - 1);
-                               if (prev.isDualInline())
+                               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.getSubPoint().get(0);
                                
 
                                if (icp.isVariableLength()) {
-                                       if (i != pathLegPoints.size() - 1) {
-                                               PipeControlPoint next;
-                                               next = pathLegPoints.get(i + 1);
+                                       if (prev != null && next != null) {
                                                recalcline = recalcline | updateVariableLength(icp,  prev, next);
 
                                        } else {
@@ -607,9 +623,9 @@ public class PipingRules {
                                                // 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);
+                                               updateVariableLengthEnd(icp, prev != null ? prev : next);
                                        }
-                               } else if (!prev.isVariableLength()) {
+                               } 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.
@@ -669,6 +685,11 @@ public class PipingRules {
                        // must be removed
                        
                        if (icp.isDeletable()) {
+                           if (!allowInsertRemove) {
+                               icp.setLength(0.0001);
+                               triedIR = true;
+                               return false;
+                           }
                                if (DEBUG)
                                        System.out.println("PipingRules.updateVariableLength removing " + icp);
                                icp._remove();
@@ -698,7 +719,7 @@ public class PipingRules {
                                                                                                        // position of the
                                                                                                        // component
                                dir.add(prevPos);
-                               PipeControlPoint scp = insertStraight(prev, icp, dir, length);
+                               insertStraight(prev, icp, dir, length);
                                return true;
                        } else {
                                triedIR = true;
@@ -708,47 +729,49 @@ public class PipingRules {
        }
        
        private static void updateVariableLengthEnd(PipeControlPoint icp,  PipeControlPoint prev) {
-           Vector3d currentPos = icp.getWorldPosition();
-        Vector3d prevPos = prev.getWorldPosition();
-        
-        Vector3d dir = new Vector3d();
-        dir.sub(currentPos, prevPos);
-        
-           boolean simple = true;
-           if (simple) {
-               double currentLength = (dir.length() - prev.getInlineLength()) * 2.0;
-               icp.setLength(currentLength);
-           } else {
-               double currentLength = icp.getLength();
-               if (currentLength < MathTools.NEAR_ZERO) {
-                       currentLength = (dir.length() - prev.getInlineLength()) * 2.0;
-               }
-               
-               if (dir.lengthSquared() > MathTools.NEAR_ZERO)
-                       dir.normalize();
-               Point3d endPos = new Point3d(dir);
-               endPos.scale(currentLength * 0.5);
-               endPos.add(currentPos); // this is the free end of the component
-    
-               double offset = prev.getInlineLength();
-               Point3d beginPos = new Point3d(dir);
-               beginPos.scale(offset);
-               beginPos.add(prevPos); // this is the connected end of the component
-    
-               double l = beginPos.distance(endPos);
-               
-               if (Double.isNaN(l))
-                       System.out.println("Length for " + icp + " is NaN");
-    
-               dir.scale(l * 0.5);
-               beginPos.add(dir); // center position
-    
-               if (DEBUG)
-                       System.out.println("PipingRules.updateInlineControlPoints() setting variable length to " + l);
-               icp.setLength(l);
-    
-               icp.setWorldPosition(new Vector3d(beginPos));
-           }
+               Vector3d currentPos = icp.getWorldPosition();
+               Vector3d prevPos = prev.getWorldPosition();
+               
+               Vector3d dir = new Vector3d();
+               dir.sub(currentPos, prevPos);
+               
+               boolean simple = currentUpdates.contains(icp);
+               if (simple) {
+                       // Update based on position -> adjust length
+                       double currentLength = (dir.length() - prev.getInlineLength()) * 2.0;
+                       icp.setLength(currentLength);
+               } else {
+                       // Update based on neighbour movement -> adjust length and position, so that free end stays in place.
+                       double currentLength = icp.getLength();
+                       if (currentLength < MathTools.NEAR_ZERO) {
+                               currentLength = (dir.length() - prev.getInlineLength()) * 2.0;
+                       }
+                       
+                       if (dir.lengthSquared() > MathTools.NEAR_ZERO)
+                               dir.normalize();
+                       Point3d endPos = new Point3d(dir);
+                       endPos.scale(currentLength * 0.5);
+                       endPos.add(currentPos); // this is the free end of the component
+       
+                       double offset = prev.getInlineLength();
+                       Point3d beginPos = new Point3d(dir);
+                       beginPos.scale(offset);
+                       beginPos.add(prevPos); // this is the connected end of the component
+       
+                       double l = beginPos.distance(endPos);
+                       
+                       if (Double.isNaN(l))
+                               System.out.println("Length for " + icp + " is NaN");
+       
+                       dir.scale(l * 0.5);
+                       beginPos.add(dir); // center position
+       
+                       if (DEBUG)
+                               System.out.println("PipingRules.updateInlineControlPoints() setting variable length to " + l);
+                       icp.setLength(l);
+       
+                       icp.setWorldPosition(new Vector3d(beginPos));
+               }
        }
 
        private static void ppNoOffset(UpdateStruct2 u) throws Exception {
@@ -828,6 +851,14 @@ public class PipingRules {
                }
 
                Vector3d directedDirection = direction(dcp, dcpStart ? Direction.NEXT : Direction.PREVIOUS);
+               if (directedDirection == null) {
+                       //updateTurnControlPointTurn(dcp, dcp.getPrevious(), dcp.getNext());
+                       updateTurnControlPointTurn(dcp, null, null);
+                       directedDirection = direction(dcp, dcpStart ? Direction.NEXT : Direction.PREVIOUS);
+                       if (directedDirection == null) {
+                               return;
+                       }
+               }
                Point3d directedEndPoint = new Point3d(u.endPoint);
                if (u.hasOffsets)
                        directedEndPoint.add(u.offset);
@@ -848,8 +879,8 @@ public class PipingRules {
                double distance = t.length();
                boolean aligned = (distance < ALLOWED_OFFSET);
                if (aligned) {
-                   if (u.start.isInline() || u.end.isInline() || u.start.isFixed() || u.end.isFixed())
-                   processPathLeg(u, true, false);
+                       if (u.start.isInline() || u.end.isInline() || u.start.asFixedAngle() || u.end.asFixedAngle())
+                               processPathLeg(u, true, false);
                        checkExpandPathLeg(u, lengthChange, inlineEnd);
                        
                } else {
@@ -870,14 +901,15 @@ public class PipingRules {
                                if (other.isVariableAngle()) {
 
                                        // TODO calculate needed space from next run end.
-                                       if (mu[0] < 1.0) {
+                                   double space = spaceForTurn(other);
+                                       if (mu[0] < space) {
                                                if (dcpStart) {
                                                        closest.set(u.startPoint);
                                                } else {
                                                        closest.set(u.endPoint);
                                                }
                                                Vector3d v = new Vector3d(directedDirection);
-                                               v.scale(spaceForTurn(other));
+                                               v.scale(space);
                                                closest.add(v);
                                        }
 
@@ -997,17 +1029,27 @@ public class PipingRules {
                                }
                                
                                p1 = dcp.getWorldPosition();
-                               // FIXME: calculate position of the elbows properly.
+                               Vector3d v = new Vector3d();
                                if (!u.reversed)
-                                       p1.add(dir1);
+                                       v.set(dir1);
                                else
-                                       p1.add(dir2);
+                                       v.set(dir2);
+                               
+                               // Reserve space for 90 deg elbow
+                               double off = dcp1.getPipeRun().getTurnRadius();
+                               v.scale(off);
+                               p1.add(v);
 
                                if (!u.reversed)
                                        p2 = MathTools.closestPointOnStraight(new Point3d(p1), position2, dir2);
                                else
                                        p2 = MathTools.closestPointOnStraight(new Point3d(p1), position1, dir1);
 
+                               // By default, the elbows are placed next to each other, by using 90 deg angles.
+                               // If the distance between elbows is not enough, we must move the other elbow (and create more shallow angle elbows)
+                               if (MathTools.distance(p1, p2) < off*2.05) {
+                                       p2.add(v);
+                               }
                                
                                PipeControlPoint tcp1 = insertElbow(dcp, next, p1);
                                PipeControlPoint tcp2 = insertElbow(tcp1, next, p2);
@@ -1047,7 +1089,7 @@ public class PipingRules {
                // TODO : this returns now space for 90 deg turn.
                // The challenge: position of tcp affects the turn angle, which then affects the required space. Perhaps we need to iterate...
                // Additionally, if the path legs contain offset, using just positions of opposite path leg ends is not enough,    
-               return tcp.getPipeRun().getTurnRadius();
+               return ((TurnComponent)tcp.getPipelineComponent()).getTurnRadius();
        }
 
        private static void insertElbowUpdate(UpdateStruct2 u, PipeControlPoint dcp, PipeControlPoint next, boolean dcpStart, Vector3d position, Vector3d directedDirection) throws Exception{
@@ -1107,15 +1149,7 @@ public class PipingRules {
                        // this won't work properly if inline control points are not updated
                        PipeControlPoint startPrev = u.start.getPrevious();
                        if (startPrev != null) {
-                               double a;
-                               if (!u.hasOffsets) {
-                                       a = updateTurnControlPointTurn(u.start, startPrev, u.end);
-                               } else {
-                                       Vector3d ep = new Vector3d(u.endPoint);
-                                       ep.sub(u.offset);
-                                       a = updateTurnControlPointTurn(u.start, u.startPoint, startPrev.getPosition(), ep);
-
-                               }
+                               double a = updateTurnControlPointTurn(u.start, null, u.dir);
                                if (a < MIN_TURN_ANGLE && u.start.isDeletable())
                                        startRemoved = true;
                                else if (lengthChange == PathLegUpdateType.PREV || lengthChange == PathLegUpdateType.PREV_S) {
@@ -1132,14 +1166,8 @@ public class PipingRules {
 
                        PipeControlPoint endNext = u.end.getNext();
                        if (endNext != null) {
-                               double a;
-                               if (!u.hasOffsets) {
-                                       a = updateTurnControlPointTurn(u.end, u.start, endNext);
-                               } else {
-                                       Vector3d sp = new Vector3d(u.startPoint);
-                                       sp.add(u.offset);
-                                       a = updateTurnControlPointTurn(u.end, u.endPoint, sp, endNext.getPosition());
-                               }
+                               // TODO: u.end, u.dir, null
+                               double a = updateTurnControlPointTurn(u.end, null, null);
                                if (a < MIN_TURN_ANGLE && u.end.isDeletable())
                                        endRemoved = true;
                                else if (lengthChange == PathLegUpdateType.NEXT || lengthChange == PathLegUpdateType.NEXT_S) {
@@ -1306,7 +1334,8 @@ public class PipingRules {
                
                if (updateEnds) {
                        if (u.start.isTurn()) {
-                               updateTurnControlPointTurn(u.start, u.start.getPrevious(), u.start.getNext());
+                               //updateTurnControlPointTurn(u.start, u.start.getPrevious(), u.start.getNext());
+                               updateTurnControlPointTurn(u.start, null, null);
 //                             updatePathLegPrev(u.start, u.start, PathLegUpdateType.NONE);
                        } else if (u.start.isEnd()) {
                                updateEndComponentControlPoint(u.start, u.startPoint, u.endPoint);
@@ -1314,7 +1343,8 @@ public class PipingRules {
                                updateControlPointOrientation(u.start);
                        }
                        if (u.end.isTurn()) {
-                               updateTurnControlPointTurn(u.end, u.end.getPrevious(), u.end.getNext());
+                               //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);
@@ -1343,6 +1373,7 @@ public class PipingRules {
        // end,Point3d endPoint, Vector3d dir, boolean hasOffsets,int iter, boolean
        // reversed, ArrayList<ExpandIterInfo> toRemove) throws TransactionException
        // {
+       @SuppressWarnings("unused")
        private static void processPathLegNoOffset(UpdateStruct2 u) throws Exception {
                if (DEBUG)
                        System.out.println("PipingRules.processPathLeg " + u.start + " " + u.end);
@@ -1462,24 +1493,9 @@ public class PipingRules {
        private static void updateEndComponentControlPoint(PipeControlPoint ecp, Vector3d start, Vector3d end) throws Exception {
                if (DEBUG)
                        System.out.println("PipingRules.updateEndComponentControlPoint() " + ecp);
-               // PipeControlPoint next = ecp.getNext();
-               // PipeControlPoint prev = ecp.getPrevious();
-               // if (next != null) {
-               // end = G3DTools.getPoint(next.getLocalPosition());
-               // start = G3DTools.getPoint(ecp.getLocalPosition());
-               // } else if (prev != null) {
-               // end = G3DTools.getPoint(ecp.getLocalPosition());
-               // start = G3DTools.getPoint(prev.getLocalPosition());
-               // } else {
-               // // TODO : warning?
-               // return;
-               // }
-               // Vector3d dir = new Vector3d (end);
-               // dir.sub(start);
-               // dir.normalize();
-               // G3DTools.setTuple(ecp.getDirection(), dir);
-               if (!ecp.isFixed())
-                       updateControlPointOrientation(ecp);
+               //FIXME : end control point cannot be fixed!
+               //if (!ecp.isFixed())
+               updateControlPointOrientation(ecp);
 
                for (PipeControlPoint pcp : ecp.getSubPoint()) {
                        // TODO update position
@@ -1527,100 +1543,88 @@ public class PipingRules {
                }
        }
 
-       /**
-        * Recalculates turn control point's internal data (turn angle and offset)
-        * 
-        * @param tcp
-        * @param prev
-        * @param next
-        */
-       private static double updateTurnControlPointTurn(PipeControlPoint tcp, PipeControlPoint prev, PipeControlPoint next) {
-               if (DEBUG)
-                       System.out.println("PipingTools.updateTurnControlPointTurn()" + tcp);
-               
-               if (!tcp.isFixed()) {
-                   if (next == null || prev == null)
-                   return Math.PI; // FIXME : argh
-               Vector3d middlePoint = tcp.getWorldPosition();
-               Vector3d nextPoint = next.getWorldPosition();
-               Vector3d prevPoint = prev.getWorldPosition();
-               return updateTurnControlPointTurn(tcp, middlePoint, prevPoint, nextPoint);
-               } else {
-                   Vector3d dir;
-                   if (!tcp._getReversed()) {
-                   if (prev == null)
-                   return Math.PI; // FIXME : argh
-                   
-                   Vector3d middlePoint = tcp.getWorldPosition();
-                   Vector3d prevPoint = prev.getWorldPosition();
-                   dir = new Vector3d();
-                   dir.sub(middlePoint, prevPoint);        
-                  
-                   } else {
-                       if (next == null)
-                    return Math.PI; // FIXME : argh
-                
-                Vector3d middlePoint = tcp.getWorldPosition();
-                Vector3d nextPoint = next.getWorldPosition();
-                dir = new Vector3d();
-                dir.sub(nextPoint,middlePoint);
-                   }
-                   dir.normalize();
-            
-            Quat4d q = PipeControlPoint.getControlPointOrientationQuat(dir, tcp.getRotationAngle() != null ? tcp.getRotationAngle() : 0.0);
-            Vector3d v = new Vector3d();
-            MathTools.rotate(q, MathTools.Y_AXIS,v);
-            tcp.setTurnAxis(v);
-            tcp.setWorldOrientation(q);
-            return tcp.getTurnAngle();
+       private static double updateTurnControlPointTurn(PipeControlPoint tcp, Vector3d prev, Vector3d next) {
+               if (next == null) {
+                       UpdateStruct2 us = createUS(tcp, Direction.NEXT, 0, new ArrayList<PipingRules.ExpandIterInfo>(), tcp);
+                       if (us != null)
+                               next = us.dir;
                }
-       }
-
-       /**
-        * Recalculates turn control point's internal data (turn angle and offset)
-        * 
-        * @param tcp
-        * @param middlePoint
-        * @param nextPoint
-        * @param prevPoint
-        */
-       private static double updateTurnControlPointTurn(PipeControlPoint tcp, Vector3d middlePoint, Vector3d prevPoint, Vector3d nextPoint) {
-
-               Vector3d dir1 = new Vector3d(middlePoint);
-               dir1.sub(prevPoint);
-               Vector3d dir2 = new Vector3d(nextPoint);
-               dir2.sub(middlePoint);
-               if (DEBUG)
-                       System.out.println("PipingTools.updateTurnControlPointTurn " + tcp + " " + prevPoint + " " + middlePoint + " " + nextPoint);
-               return updateTurnControlPointTurn(tcp, dir1, dir2);
-       }
-
-       private static double updateTurnControlPointTurn(PipeControlPoint tcp, Vector3d dir1, Vector3d dir2) {
-               double turnAngle = dir1.angle(dir2);
-
-               double angle = Math.PI - turnAngle;
-
-               Vector3d turnAxis = new Vector3d();
-               turnAxis.cross(dir1, dir2);
-               if (turnAxis.lengthSquared() > MathTools.NEAR_ZERO) {
-                       double elbowRadius = tcp.getPipelineComponent().getPipeRun().getTurnRadius();
-                       double R = elbowRadius / Math.tan(angle * 0.5);
+               if (prev == null) {
+                       UpdateStruct2 us = createUS(tcp, Direction.PREVIOUS, 0, new ArrayList<PipingRules.ExpandIterInfo>(), tcp);
+                       if (us != null) {
+                               prev = us.dir;
+                       }
+               }
+               
+               if (!tcp.asFixedAngle()) {
+                       
+                       
+                       if (next == null || prev == null) {
+                               if (tcp.getTurnAngle() != null)
+                                       return tcp.getTurnAngle();
+                               return Math.PI; // FIXME : argh
+                       }
+                       double turnAngle = prev.angle(next);
+       
+                       double angle = Math.PI - turnAngle;
+       
+                       Vector3d turnAxis = new Vector3d();
+                       turnAxis.cross(prev, next);
+                       if (turnAxis.lengthSquared() > MathTools.NEAR_ZERO) {
+                               double elbowRadius = ((TurnComponent)tcp.getPipelineComponent()).getTurnRadius();
+                               double R = elbowRadius / Math.tan(angle * 0.5);
+                               
+                               turnAxis.normalize();
+                               tcp.setTurnAngle(turnAngle);
+                               tcp.setLength(R);// setComponentOffsetValue(R);
+                               tcp.setTurnAxis(turnAxis);
+       //                      tcp.setPosition(tcp.getPosition());
+                       } else {
+                               turnAngle = 0.0;
+                               tcp.setTurnAngle(0.0);
+                               tcp.setLength(0.0);
+                               tcp.setTurnAxis(new Vector3d(MathTools.Y_AXIS));
+                       }
                        
-                       turnAxis.normalize();
-                       tcp.setTurnAngle(turnAngle);
-                       tcp.setLength(R);// setComponentOffsetValue(R);
-                       tcp.setTurnAxis(turnAxis);
-//                     tcp.setPosition(tcp.getPosition());
+                       updateControlPointOrientation(tcp);
+                       
+                       if (DEBUG)
+                               System.out.println("PipingTools.updateTurnControlPointTurn " + prev + " " + next + " " + turnAngle + " " + turnAxis);
+                       return turnAngle;
                } else {
-                       turnAngle = 0.0;
-                       tcp.setTurnAngle(0.0);
-                       tcp.setLength(0.0);
-                       tcp.setTurnAxis(new Vector3d(MathTools.Y_AXIS));
+                       
+                       if (prev != null && next != null) {
+                               // Nothing to do
+                       } else if (prev == null) {
+                               if (!tcp._getReversed())
+                                       tcp.setReversed(true);
+                       } else if (next == null) {
+                               if (tcp._getReversed())
+                                       tcp.setReversed(false);
+                       }
+                       
+                       Vector3d dir = null;
+                       if (!tcp._getReversed()) {
+                               dir = prev;
+                       } else {
+                               dir = next;
+                               dir.negate();
+                       }
+                       if (dir == null) {
+                               return Math.PI; // FIXME : argh
+                       }
+                       
+                       Quat4d q = PipeControlPoint.getControlPointOrientationQuat(dir, tcp.getRotationAngle() != null ? tcp.getRotationAngle() : 0.0);
+                       Vector3d v = new Vector3d();
+                       MathTools.rotate(q, MathTools.Y_AXIS,v);
+                       tcp.setTurnAxis(v);
+                       tcp.setWorldOrientation(q);
+                       if (tcp.getTurnAngle() != null)
+                               return tcp.getTurnAngle();
+                       return Math.PI; // FIXME : argh
                }
-               updateControlPointOrientation(tcp);
-               if (DEBUG)
-                       System.out.println("PipingTools.updateTurnControlPointTurn " + dir1 + " " + dir2 + " " + turnAngle + " " + turnAxis);
-               return turnAngle;
+               
+               
        }
        
        public static List<PipeControlPoint> getControlPoints(PipeRun pipeRun) {
@@ -1630,8 +1634,9 @@ public class PipingRules {
                PipeControlPoint pcp = pipeRun.getControlPoints().iterator().next();
                while (pcp.getPrevious() != null) {
                        PipeControlPoint prev = pcp.getPrevious();
-                       if (prev.getPipeRun() != pipeRun)
-                               break;
+                       if (prev.getPipeRun() != pipeRun && prev.getPipeRun() != null) { // bypass possible corruption
+                           break;
+                       }
                        pcp = prev;
                }
                if (pcp.isDualSub()) {
@@ -1648,7 +1653,63 @@ public class PipingRules {
        }
        
        public static void reverse(PipeRun pipeRun) {
-               List<PipeControlPoint> list = getControlPoints(pipeRun);
+               
+               while (true) {
+                       List<PipeControlPoint> points = getControlPoints(pipeRun);
+                       PipeControlPoint pcp = points.get(0);
+                       if (pcp.isSizeChange() && pcp.getSubPoint().size() > 0) {
+                               pipeRun = pcp.getPipeRun();
+                       } else {
+                               break;
+                       }
+               }
+               List<PipeRun> all = new ArrayList<PipeRun>();
+               List<List<PipeControlPoint>> pcps = new ArrayList<List<PipeControlPoint>>();
+               while (true) {
+                       all.add(pipeRun);
+                       List<PipeControlPoint> points = getControlPoints(pipeRun);
+                       pcps.add(points);
+                       PipeControlPoint pcp = points.get(points.size()-1);
+                       if (pcp.getSubPoint().size() > 0) {
+                               pipeRun = pcp.getSubPoint().get(0).getPipeRun();
+                       } else {
+                               break;
+                       }
+               }
+               for (int i = 0 ; i < all.size(); i++) {
+                       List<PipeControlPoint> list = pcps.get(i);
+                       _reverse(list);
+               }
+               for (int i = 0 ; i < all.size(); i++) {
+                       boolean last = i == all.size() - 1;
+                       List<PipeControlPoint> list = pcps.get(i);
+                       
+                       if (!last) {
+                               List<PipeControlPoint> list2 = pcps.get(i+1);
+                               PipeControlPoint prev = list.get(list.size()-1);
+                               PipeControlPoint next = list2.get(0);
+                               System.out.println();
+                               if (prev == next) {
+                                       // Reverse the component on the boundary.
+                                       InlineComponent ic = (InlineComponent)prev.getPipelineComponent();
+                                       PipeRun r1 = ic.getPipeRun();
+                                       PipeRun r2 = ic.getAlternativePipeRun();
+                                       if (r1 == null || r2 == null)
+                                               throw new RuntimeException("Components on PipeRun changes should refer to bot PipeRuns");
+                                       ic.deattach();
+                                       r2.addChild(ic);
+                                       ic.setPipeRun(r2);
+                                       ic.setAlternativePipeRun(r1);
+                               } else {
+                                       throw new RuntimeException("PipeRun changes should contain shared control points");
+                               }
+                               
+                       }
+               }
+                       
+       }
+       
+       private static void _reverse(List<PipeControlPoint> list) {
                if (list.size() <= 1)
                        return; // nothing to do.
                
@@ -1663,21 +1724,25 @@ public class PipingRules {
                                PipeControlPoint next = list.get(i+1);
                                if (next.isDualInline())
                                        next = next.getSubPoint().get(0);
-                               current.setNext(null);
+                               if (current.getNext() == next)
+                                       current.setNext(null);
                                current.setPrevious(next);
                                if (currentSub != null) {
-                                       currentSub.setNext(null);
-                                       currentSub.setPrevious(next);           
+                                       if (currentSub.getNext() == next)
+                                               currentSub.setNext(null);
+                                       currentSub.setPrevious(next);       
                                }
                        } else if (last) {
                                PipeControlPoint prev = list.get(i-1);
                                
-                               current.setPrevious(null);
+                               if (current.getPrevious() == prev)
+                                       current.setPrevious(null);
                                current.setNext(prev);
                                
                                if (currentSub != null) {
-                                       currentSub.setPrevious(null);
-                                       currentSub.setNext(prev);               
+                                       if (currentSub.getPrevious() == prev)
+                                               currentSub.setPrevious(null);
+                                       currentSub.setNext(prev);       
                                }
                        } else {
                                PipeControlPoint prev = list.get(i-1);
@@ -1691,10 +1756,17 @@ public class PipingRules {
                                
                                if (currentSub != null) {
                                        currentSub.setPrevious(next);
-                                       currentSub.setNext(prev);               
+                                       currentSub.setNext(prev);       
                                }
                                
                        }
+                       //if (current.isTurn() && current.isFixed()) {
+                       if (current.asFixedAngle()) {
+                               current.setReversed(!current._getReversed());
+                       }
+                       if (current.isInline() && current.isReverse()) {
+                               current.setReversed(!current._getReversed());
+                       }   
                }
        }
        
@@ -1735,11 +1807,19 @@ public class PipingRules {
                }
                List<PipeControlPoint> runPcps = getControlPoints(pipeRun);
                if (runPcps.size() != count) {
-                       System.out.println("Run " + pipeRun.getName() + " contains unconnected control points");
+                       System.out.println("Run " + pipeRun.getName() + " contains unconnected control points, found " + runPcps.size() + " connected, " + pcps.size() + " total.");
+                       for (PipeControlPoint pcp : pcps) {
+                           if (!runPcps.contains(pcp)) {
+                               System.out.println("Unconnected " + pcp + " " + pcp.getPipelineComponent());
+                           }
+                       }
                }
                for (PipeControlPoint pcp : pcps) {
+                   if (pcp.getPipeRun() == null) {
+                       System.out.println("PipeRun ref missing " + pcp + " " + pcp.getPipelineComponent());
+                   }
                        if (!pcp.isDirected() && pcp.getNext() == null && pcp.getPrevious() == null)
-                               System.out.println("Orphan undirected " + pcp);
+                               System.out.println("Orphan undirected " + pcp + " " + pcp.getPipelineComponent());
                }
                for (PipeControlPoint pcp : pcps) {
                        if (pcp.getParentPoint() == null) {
@@ -1769,8 +1849,8 @@ public class PipingRules {
        }
        
        public static void splitVariableLengthComponent(PipelineComponent newComponent, InlineComponent splittingComponent, boolean assignPos) throws Exception{
-               assert(!splittingComponent.getControlPoint().isFixed());
-               assert(!(newComponent instanceof  InlineComponent && !newComponent.getControlPoint().isFixed()));
+               assert(!splittingComponent.getControlPoint().isFixedLength());
+               assert(!(newComponent instanceof  InlineComponent && !newComponent.getControlPoint().isFixedLength()));
                PipeControlPoint newCP = newComponent.getControlPoint();
                PipeControlPoint splittingCP = splittingComponent.getControlPoint();
                PipeControlPoint nextCP = splittingCP.getNext();
@@ -1778,19 +1858,19 @@ public class PipingRules {
                
                /* there are many different cases to insert new component when
                   it splits existing VariableLengthinlineComponent.
-           
-              1. VariableLengthComponet is connected from both sides:
-                 - insert new component between VariableLength component and component connected to it
-                 - insert new VariableLengthComponent between inserted component and component selected in previous step
+               
+                  1. VariableLengthComponet is connected from both sides:
+                         - insert new component between VariableLength component and component connected to it
+                         - insert new VariableLengthComponent between inserted component and component selected in previous step
                
                   2. VariableLengthComponent is connected from one side
-                    - Use previous case or:
-                    - Insert new component to empty end
-                    - Insert new VariableLength component to inserted components empty end
-                    
+                        - Use previous case or:
+                        - Insert new component to empty end
+                        - Insert new VariableLength component to inserted components empty end
+                        
                   3. VariableLength is not connected to any component.
-                    - Should not be possible, at least in current implementation.
-                    - Could be done using second case
+                        - Should not be possible, at least in current implementation.
+                        - Could be done using second case
 
                */
                
@@ -1798,7 +1878,6 @@ public class PipingRules {
                        // this should not be possible
                        throw new RuntimeException("VariableLengthComponent " + splittingComponent + " is not connected to anything.");
                }
-               double reservedLength = splittingComponent.getControlPoint().getLength();
                double newLength = newComponent.getControlPoint().getLength();
                
                
@@ -1834,21 +1913,20 @@ public class PipingRules {
                vn.interpolate(next, 0.5);
                
                
-               PipeControlPoint newVariableLengthCP = null;//insertStraight(pcp1, pcp2, pos, length);
                if (nextCP == null) {
                        newCP.insert(splittingCP, Direction.NEXT);
-                       newVariableLengthCP = insertStraight(newCP, Direction.NEXT, new Vector3d(vn), ln);
+                       insertStraight(newCP, Direction.NEXT, new Vector3d(vn), ln);
                        splittingCP.setWorldPosition(new Vector3d(vp));
 //                     ControlPointTools.setWorldPosition(splittingCP, vp);
 //                     splittingCP.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, lp);
                } else if (prevCP == null) {
                        newCP.insert(splittingCP, Direction.PREVIOUS);
-                       newVariableLengthCP = insertStraight(newCP, Direction.PREVIOUS, new Vector3d(vp), lp);
+                       insertStraight(newCP, Direction.PREVIOUS, new Vector3d(vp), lp);
                        splittingCP.setWorldPosition(new Vector3d(vn));
 //                     splittingCP.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, ln);
                } else {
                        newCP.insert(splittingCP, nextCP);
-                       newVariableLengthCP = insertStraight(newCP, nextCP, new Vector3d(vn), ln);
+                       insertStraight(newCP, nextCP, new Vector3d(vn), ln);
                        splittingCP.setWorldPosition(new Vector3d(vp));
 //                     splittingCP.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, lp);
                }