]> gerrit.simantics Code Review - simantics/3d.git/blobdiff - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipingRules.java
Showing error messages when components overlap each other
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / scenegraph / controlpoint / PipingRules.java
index 6c121d80857da7b7c920c37f73d774b14d191b62..688e50f9f6485824a6f51efb4abfcf86e85eaf80 100644 (file)
@@ -2,9 +2,7 @@ package org.simantics.plant3d.scenegraph.controlpoint;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import javax.vecmath.Point3d;
 import javax.vecmath.Quat4d;
@@ -12,7 +10,6 @@ import javax.vecmath.Vector3d;
 
 import org.simantics.g3d.math.MathTools;
 import org.simantics.plant3d.scenegraph.InlineComponent;
-import org.simantics.plant3d.scenegraph.Nozzle;
 import org.simantics.plant3d.scenegraph.P3DRootNode;
 import org.simantics.plant3d.scenegraph.PipeRun;
 import org.simantics.plant3d.scenegraph.PipelineComponent;
@@ -25,8 +22,9 @@ public class PipingRules {
        private static final boolean DEBUG = false;
        private static final boolean DUMMY = false;
 
-       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_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 final int REMOVE_NONE = 0;
        private static final int REMOVE_START = 1;
@@ -34,11 +32,16 @@ public class PipingRules {
        private static final int REMOVE_BOTH = 3;
        
 
+       // PathLeg iteration indicator. NEXT_S > NEXT > NONE  PREV_S > PREV > NONE 
        private enum PathLegUpdateType {
-               NONE, PREV, NEXT, PREV_S, NEXT_S
+               NONE,   // Only current path leg needs to be updated  (for example, inline comp was moved)
+               PREV,   // Current and previous path leg need to be updated 
+               NEXT,   // Current and next path leg need to be updated
+               PREV_S, // Current and previous two path legs need to be updated (turn was moved, which affect other path leg end turns, and thus following path legs
+               NEXT_S  // Current and next two path legs need to be updated
        };
        
-       private static boolean enabled = true;
+       private static boolean enabled = true;  // 
        private static boolean updating = false;
        private static boolean allowInsertRemove = true;
        private static boolean triedIR = false;
@@ -113,12 +116,12 @@ public class PipingRules {
                        allowInsertRemove = allowIR;
                        triedIR = false;
                        validate(pcp.getPipeRun());
-                       if (pcp.isPathLegEnd()) {
+                       if (pcp.asPathLegEnd()) {
                                updatePathLegEndControlPoint(pcp); // FIXME: Rules won't work properly, if they are not run twice.
-                               updatePathLegEndControlPoint(pcp);
+                               //updatePathLegEndControlPoint(pcp);
                        } else {
                                updateInlineControlPoint(pcp);
-                               updateInlineControlPoint(pcp);
+                               //updateInlineControlPoint(pcp);
                        }
                        validate(pcp.getPipeRun());
                        if (!allowInsertRemove)
@@ -139,10 +142,6 @@ public class PipingRules {
        public static boolean isEnabled() {
                return enabled;
        }
-       
-//     private void commit() {
-//             root.getNodeMap().commit();
-//     }
 
        public static class ExpandIterInfo {
                // these two are turn control points
@@ -218,15 +217,15 @@ public class PipingRules {
                        System.out.println("PipingRules.insertElbow() " + pcp1 + " " + pcp2 + " " + pos);
                if (pcp1.getNext() == pcp2 && pcp2.getPrevious() == pcp1) {
                        
-               } else if (pcp1.getNext() == pcp2 && pcp1.isDualInline() && pcp2.getPrevious() == pcp1.getSubPoint().get(0)) {
-                       pcp1 = pcp1.getSubPoint().get(0);       
+               } else if (pcp1.getNext() == pcp2 && pcp1.isDualInline() && pcp2.getPrevious() == pcp1.getDualSub()) {
+                       pcp1 = pcp1.getDualSub();       
                } else if (pcp1.getPrevious() == pcp2 && pcp2.getNext() == pcp1) {
                        PipeControlPoint t = pcp1;
                        pcp1 = pcp2;
                        pcp2 = t;
-               } else if (pcp2.isDualInline() && pcp1.getPrevious() == pcp2.getSubPoint().get(0) && pcp2.getNext() == pcp1) {
+               } else if (pcp2.isDualInline() && pcp1.getPrevious() == pcp2.getDualSub() && pcp2.getNext() == pcp1) {
                        PipeControlPoint t = pcp1;
-                       pcp1 = pcp2.getSubPoint().get(0);
+                       pcp1 = pcp2.getDualSub();
                        pcp2 = t;
                } else {
                        throw new RuntimeException();
@@ -234,7 +233,7 @@ public class PipingRules {
                TurnComponent elbow = ComponentUtils.createTurn((P3DRootNode)pcp1.getRootNode());
                PipeControlPoint pcp = elbow.getControlPoint();
                if (pcp1.isDualInline())
-                       pcp1 = pcp1.getSubPoint().get(0);
+                       pcp1 = pcp1.getDualSub();
                String name = pcp1.getPipeRun().getUniqueName("Elbow");
                elbow.setName(name);
                pcp1.getPipeRun().addChild(elbow);
@@ -251,15 +250,15 @@ public class PipingRules {
                        System.out.println("PipingRules.insertStraight() " + pcp1 + " " + pcp2 + " " + pos);
                if (pcp1.getNext() == pcp2 && pcp2.getPrevious() == pcp1) {
                        
-               } else if (pcp1.getNext() == pcp2 && pcp1.isDualInline() && pcp2.getPrevious() == pcp1.getSubPoint().get(0)) {
-                       pcp1 = pcp1.getSubPoint().get(0);       
+               } else if (pcp1.getNext() == pcp2 && pcp1.isDualInline() && pcp2.getPrevious() == pcp1.getDualSub()) {
+                       pcp1 = pcp1.getDualSub();       
                } else if (pcp1.getPrevious() == pcp2 && pcp2.getNext() == pcp1) {
                        PipeControlPoint t = pcp1;
                        pcp1 = pcp2;
                        pcp2 = t;
-               } else if (pcp2.isDualInline() && pcp1.getPrevious() == pcp2.getSubPoint().get(0) && pcp2.getNext() == pcp1) {
+               } else if (pcp2.isDualInline() && pcp1.getPrevious() == pcp2.getDualSub() && pcp2.getNext() == pcp1) {
                        PipeControlPoint t = pcp1;
-                       pcp1 = pcp2.getSubPoint().get(0);
+                       pcp1 = pcp2.getDualSub();
                        pcp2 = t;
                } else {
                        throw new RuntimeException();
@@ -267,7 +266,7 @@ public class PipingRules {
                InlineComponent component = ComponentUtils.createStraight((P3DRootNode)pcp1.getRootNode());
                PipeControlPoint scp = component.getControlPoint();
                if (pcp1.isDualInline())
-                       pcp1 = pcp1.getSubPoint().get(0);
+                       pcp1 = pcp1.getDualSub();
                String name = pcp1.getPipeRun().getUniqueName("Pipe");
                component.setName(name);
                pcp1.getPipeRun().addChild(component);
@@ -287,7 +286,7 @@ public class PipingRules {
                InlineComponent component = ComponentUtils.createStraight((P3DRootNode)pcp.getRootNode());
                PipeControlPoint scp = component.getControlPoint();
                if (pcp.isDualInline() && direction == Direction.NEXT)
-                       pcp = pcp.getSubPoint().get(0);
+                       pcp = pcp.getDualSub();
                String name = pcp.getPipeRun().getUniqueName("Pipe");
                component.setName(name);
                pcp.getPipeRun().addChild(component);
@@ -301,14 +300,7 @@ public class PipingRules {
        }
 
        private static void updatePathLegNext(PipeControlPoint start, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
-
                UpdateStruct2 us = createUS(start, Direction.NEXT, 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; 
@@ -317,14 +309,7 @@ public class PipingRules {
        }
        
        private static void updatePathLegPrev(PipeControlPoint start, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
-               // TODO: this method is not symmetric with updatePathLegNext, which may alter lengthChange parameter?
                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; 
@@ -375,6 +360,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());
@@ -461,7 +447,7 @@ public class PipingRules {
        private static boolean asDirected(PipeControlPoint pcp, Direction direction) {
                if (pcp.isDirected())
                        return true;
-               if (pcp.isTurn() && pcp.isFixed()) {
+               if (pcp.asFixedAngle()) {
                        if (!pcp._getReversed())
                                return direction == Direction.NEXT;
                        else
@@ -475,11 +461,27 @@ public class PipingRules {
        }
  
        private static void updatePathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
+           boolean rs = true;
+           boolean re = true;
+           if (lengthChange == PathLegUpdateType.NONE) {
+               rs = false;
+               re = false;
+           }
+           updatePathLeg(u, lengthChange, rs, re);
+       }
+       
+       private static void updatePathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange, boolean rs, boolean re) throws Exception {
                int directed = 0;
                if (asDirected(u.start, Direction.NEXT))
                        directed++;
                if (asDirected(u.end, Direction.PREVIOUS))
                        directed++;
+               if (rs)
+                   u.start.getPipelineComponent().setError(null);
+               if (re)
+                   u.end.getPipelineComponent().setError(null);
+        for (PipeControlPoint pcp : u.list)
+            pcp.getPipelineComponent().setError(null);
                switch (directed) {
                case 0:
                        updateFreePathLeg(u, lengthChange);
@@ -498,7 +500,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);
        }
 
@@ -511,10 +513,12 @@ public class PipingRules {
                
                if (checkSizes) {
                        // create offsets for leg ends.
-                       MathTools.mad(start, u.dir, u.start.getInlineLength());
-                       MathTools.mad(end, u.dir, -u.end.getInlineLength());
+                   if (u.start.isTurn())
+                       MathTools.mad(start, u.dir, u.start.getInlineLength());
+                   if (u.end.isTurn())
+                       MathTools.mad(end, u.dir, -u.end.getInlineLength());   
                }
-               
+
                boolean recalcline = false;
                if (!u.hasOffsets) {
                        
@@ -532,16 +536,28 @@ public class PipingRules {
                                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);
                        }
                        pathLegPoints.add(u.end);
 
-                       // TODO : values can be cached in the loop
+                       // updateInlineControlPoint keeps components between path leg ends, but does not ensure that fixed length components do no overlap each other
+                       
+                       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);
 
@@ -609,7 +625,7 @@ public class PipingRules {
                                PipeControlPoint next = i < pathLegPoints.size() - 1 ? pathLegPoints.get(i + 1) : null;
                                
                                if (prev != null && prev.isDualInline())
-                                       prev = prev.getSubPoint().get(0);
+                                       prev = prev.getDualSub();
                                
 
                                if (icp.isVariableLength()) {
@@ -644,50 +660,82 @@ public class PipingRules {
                if (recalcline) {
                        u.list.clear();
                        u.start.findNextEnd(u.list);
+               } 
+               if (checkSizes) {
+                   double pathLegLength = MathTools.distance(u.startPoint, u.endPoint);
+            double availableLength = pathLegLength;
+            if (u.start.isTurn())
+                availableLength -= u.start.getInlineLength();
+            if (u.end.isTurn())
+                availableLength -= u.end.getInlineLength();
+            for (PipeControlPoint pcp : u.list) {
+                if (!pcp.isVariableLength())
+                    availableLength-= pcp.getLength();
+                   }
+            if (availableLength < 0.0) {
+                u.start.getPipelineComponent().setError("Not enough available space");
+                u.end.getPipelineComponent().setError("Not enough available space");
+                for (PipeControlPoint pcp : u.list)
+                    pcp.getPipelineComponent().setError("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) {
+           if (prev != null) {
+              checkOverlap(icp, prev);
+           }
+           if (next != null)
+               checkOverlap(icp, next);
+       }
+       
+       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 updateVariableLength(PipeControlPoint icp, PipeControlPoint prev,  PipeControlPoint next) {
                Vector3d prevPos = prev.getWorldPosition();
                Vector3d nextPos = next.getWorldPosition();
                
                Vector3d dir = new Vector3d(nextPos);
                dir.sub(prevPos);
-               double l = dir.lengthSquared(); // distance between
-                                                                               // control points
-                                                                               // (square)
-               double l2prev = prev.getInlineLength(); // distance
-                                                                                                                                       // taken
-                                                                                                                                       // by
-                                                                                                                                       // components
+               double l = dir.length();                // distance between control points
+               double l2prev = prev.getInlineLength(); // distance taken by components
                double l2next = next.getInlineLength();
                double l2 = l2prev + l2next;
-               double l2s = MathTools.square(l2);
-               if (l2s < l) { // check if there is enough space for
-                                               // variable length component.
+               double length = l - l2;                 // true length of the variable length component
+               if (length >= MIN_INLINE_LENGTH) {      // check if there is enough space for variable length component.
                        // components fit
                        dir.normalize();
-                       double length = Math.sqrt(l) - l2; // true length of
-                                                                                               // the variable
-                                                                                               // length
-                                                                                               // component
-                       dir.scale(length * 0.5 + l2prev); // calculate
-                                                                                               // center
-                                                                                               // position of
-                                                                                               // the component
+                       dir.scale(length * 0.5 + l2prev);   // calculate center position of the component
                        dir.add(prevPos);
                        icp.setWorldPosition(dir);
                        icp.setLength(length);
                        return false;
                } else {
-                       // components leave no space to the component and it
-                       // must be removed
-                       
+                       // components leave no space to the component and it must be removed
                        if (icp.isDeletable()) {
+                           if (!allowInsertRemove) {
+                               icp.setLength(MIN_INLINE_LENGTH);
+                               icp.getPipelineComponent().setError("Not enough available space");
+                               triedIR = true;
+                               return false;
+                           }
                                if (DEBUG)
                                        System.out.println("PipingRules.updateVariableLength removing " + icp);
                                icp._remove();
                                return true;
+                       } else {
+                           icp.setLength(MIN_INLINE_LENGTH);
+                           icp.getPipelineComponent().setError("Not enough available space");
                        }
                        return false;
                }
@@ -706,14 +754,10 @@ public class PipingRules {
                if (l > l2s) {
                        if (allowInsertRemove) {
                                dir.normalize();
-                               double length = Math.sqrt(l) - l2; // true length of the
-                                                                                                       // variable length
-                                                                                                       // component
-                               dir.scale(length * 0.5 + l2prev); // calculate center
-                                                                                                       // position of the
-                                                                                                       // component
+                               double length = Math.sqrt(l) - l2; // true length of the variable length component
+                               dir.scale(length * 0.5 + l2prev); // calculate center position of the component
                                dir.add(prevPos);
-                               PipeControlPoint scp = insertStraight(prev, icp, dir, length);
+                               insertStraight(prev, icp, dir, length);
                                return true;
                        } else {
                                triedIR = true;
@@ -873,7 +917,7 @@ 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())
+                       if (u.start.isInline() || u.end.isInline() || u.start.asFixedAngle() || u.end.asFixedAngle())
                                processPathLeg(u, true, false);
                        checkExpandPathLeg(u, lengthChange, inlineEnd);
                        
@@ -1083,7 +1127,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{
@@ -1367,6 +1411,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);
@@ -1387,7 +1432,7 @@ public class PipingRules {
        private static void updateOffsetPoint(PipeControlPoint sccp, Vector3d offset) {
                Vector3d world = sccp.getWorldPosition();
                world.add(offset);
-               PipeControlPoint ocp = sccp.getSubPoint().iterator().next();
+               PipeControlPoint ocp = sccp.getDualSub();
                ocp.setWorldPosition(world);
        }
 
@@ -1423,7 +1468,7 @@ public class PipingRules {
                if (canCalc) {
                        boolean branchUpdate = false;
                        PipeControlPoint becp = null;
-                       for (PipeControlPoint pcp : icp.getSubPoint())
+                       for (PipeControlPoint pcp : icp.getChildPoints())
                                if (pcp.isNonDirected()) {
                                        branchUpdate = true;
                                        becp = pcp;
@@ -1486,26 +1531,11 @@ 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);
-
-               for (PipeControlPoint pcp : ecp.getSubPoint()) {
+               //FIXME : end control point cannot be fixed!
+               //if (!ecp.isFixed())
+               updateControlPointOrientation(ecp);
+
+               for (PipeControlPoint pcp : ecp.getChildPoints()) {
                        // TODO update position
                        updatePathLegEndControlPoint(pcp);
                }
@@ -1539,7 +1569,7 @@ public class PipingRules {
                        System.out.println("PipingRules.updateBranchControlPointBranches() " + bcp);
                if (bcp.isDualInline())
                        return;
-               Collection<PipeControlPoint> branches = bcp.getSubPoint();
+               Collection<PipeControlPoint> branches = bcp.getChildPoints();
                if (branches.size() == 0) {
                        if (DEBUG)
                                System.out.println("No Branches found");
@@ -1564,7 +1594,7 @@ public class PipingRules {
                        }
                }
                
-               if (!tcp.isFixed()) {
+               if (!tcp.asFixedAngle()) {
                        
                        
                        if (next == null || prev == null) {
@@ -1579,7 +1609,7 @@ public class PipingRules {
                        Vector3d turnAxis = new Vector3d();
                        turnAxis.cross(prev, next);
                        if (turnAxis.lengthSquared() > MathTools.NEAR_ZERO) {
-                               double elbowRadius = tcp.getPipelineComponent().getPipeRun().getTurnRadius();
+                               double elbowRadius = ((TurnComponent)tcp.getPipelineComponent()).getTurnRadius();
                                double R = elbowRadius / Math.tan(angle * 0.5);
                                
                                turnAxis.normalize();
@@ -1665,7 +1695,7 @@ public class PipingRules {
                while (true) {
                        List<PipeControlPoint> points = getControlPoints(pipeRun);
                        PipeControlPoint pcp = points.get(0);
-                       if (pcp.isSizeChange() && pcp.getSubPoint().size() > 0) {
+                       if (pcp.isSizeChange() && pcp.getChildPoints().size() > 0) {
                                pipeRun = pcp.getPipeRun();
                        } else {
                                break;
@@ -1678,8 +1708,12 @@ public class PipingRules {
                        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();
+                       if (pcp.getChildPoints().size() > 0) {
+                               PipeRun pipeRun2 = pcp.getChildPoints().get(0).getPipeRun();
+                               if (pipeRun == pipeRun2)
+                                   break;
+                               else
+                                   pipeRun = pipeRun2;
                        } else {
                                break;
                        }
@@ -1727,11 +1761,11 @@ public class PipingRules {
                        PipeControlPoint current = list.get(i);
                        PipeControlPoint currentSub = null;
                        if (current.isDualInline())
-                               currentSub = current.getSubPoint().get(0);
+                               currentSub = current.getDualSub();
                        if (first) {
                                PipeControlPoint next = list.get(i+1);
                                if (next.isDualInline())
-                                       next = next.getSubPoint().get(0);
+                                       next = next.getDualSub();
                                if (current.getNext() == next)
                                        current.setNext(null);
                                current.setPrevious(next);
@@ -1756,7 +1790,7 @@ public class PipingRules {
                                PipeControlPoint prev = list.get(i-1);
                                PipeControlPoint next = list.get(i+1);
                                if (next.isDualInline())
-                                       next = next.getSubPoint().get(0);
+                                       next = next.getDualSub();
                                
                                
                                current.setPrevious(next);
@@ -1768,7 +1802,8 @@ public class PipingRules {
                                }
                                
                        }
-                       if (current.isTurn() && current.isFixed()) {
+                       //if (current.isTurn() && current.isFixed()) {
+                       if (current.asFixedAngle()) {
                                current.setReversed(!current._getReversed());
                        }
                        if (current.isInline() && current.isReverse()) {
@@ -1777,30 +1812,7 @@ public class PipingRules {
                }
        }
        
-       public static void merge(PipeRun run1, PipeRun r2) {
-               Map<PipeControlPoint, Vector3d> positions = new HashMap<PipeControlPoint, Vector3d>();
-               Map<PipeControlPoint, Quat4d> orientations = new HashMap<PipeControlPoint, Quat4d>();
-               for (PipeControlPoint pcp : r2.getControlPoints()) {
-                       positions.put(pcp, pcp.getWorldPosition());
-                       orientations.put(pcp, pcp.getWorldOrientation());
-               }
-               for (PipeControlPoint pcp : r2.getControlPoints()) {
-                       r2.deattachChild(pcp);
-                       run1.addChild(pcp);
-                       PipelineComponent component = pcp.getPipelineComponent();
-                       if (component != null) {
-                               if (!(component instanceof Nozzle)) {
-                                       component.deattach();
-                                       run1.addChild(component);
-                               } else {
-                                       Nozzle n = (Nozzle)component;
-                                       n.setPipeRun(run1);
-                               }
-                       }
-               }
-               r2.remove();
-               
-       }
+       
        
        public static void validate(PipeRun pipeRun) {
                if (pipeRun == null)
@@ -1832,7 +1844,7 @@ public class PipingRules {
                        if (pcp.getParentPoint() == null) {
                                PipeControlPoint sub = null;
                                if (pcp.isDualInline())
-                                       sub = pcp.getSubPoint().get(0);
+                                       sub = pcp.getDualSub();
                                PipeControlPoint next = pcp.getNext();
                                PipeControlPoint prev = pcp.getPrevious();
                                if (next != null) {
@@ -1856,8 +1868,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();
@@ -1885,7 +1897,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();
                
                
@@ -1921,21 +1932,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);
                }
@@ -1945,7 +1955,7 @@ public class PipingRules {
        
        public static void addSizeChange(boolean reversed, PipeRun pipeRun, PipeRun other, InlineComponent reducer, PipeControlPoint previous, PipeControlPoint next) {
                PipeControlPoint pcp = reducer.getControlPoint();
-               PipeControlPoint ocp = pcp.getSubPoint().get(0);
+               PipeControlPoint ocp = pcp.getDualSub();
                if (!reversed) {
                        String name = pipeRun.getUniqueName("Reducer");
                        reducer.setName(name);