]> gerrit.simantics Code Review - simantics/3d.git/commitdiff
Merge "Fix error in inserting dual inline components"
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Mon, 6 Apr 2020 12:43:28 +0000 (12:43 +0000)
committerGerrit Code Review <gerrit2@simantics>
Mon, 6 Apr 2020 12:43:28 +0000 (12:43 +0000)
org.simantics.g3d.vtk/src/org/simantics/g3d/vtk/common/AbstractVTKNodeMap.java
org.simantics.plant3d/src/org/simantics/plant3d/actions/AddComponentAction.java
org.simantics.plant3d/src/org/simantics/plant3d/dialog/ComponentSelectionDialog.java
org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/InlineComponent.java
org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipeControlPoint.java
org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipingRules.java
org.simantics.plant3d/src/org/simantics/plant3d/utils/ComponentUtils.java

index ba62ddbfdb64ca14745420ec7d17b145a1e0ee06..bc7c35528fde4a33f07cc0c5755ecf27e781f1de 100644 (file)
@@ -372,12 +372,14 @@ public abstract class AbstractVTKNodeMap<DBObject,E extends INode> implements VT
        private boolean useFullSyncWithUndo = false;
        
        protected void update(ReadGraph graph) throws DatabaseException {
-               synchronized (syncMutex) {
-                       if (DEBUG) System.out.println("Graph update start");
-                       
-                       if (runUndo && useFullSyncWithUndo) {
+               if (DEBUG) System.out.println("Graph update start");
+               
+               if (runUndo && useFullSyncWithUndo) {
+                       synchronized (syncMutex) {
                                reset(graph);
-                       } else {
+                       }
+               } else {
+                       synchronized (syncMutex) {
                                graphUpdates = true;
                                for (DBObject domainObject : mapping.getDomainModified()) {
                                        @SuppressWarnings("unchecked")
@@ -385,18 +387,24 @@ public abstract class AbstractVTKNodeMap<DBObject,E extends INode> implements VT
                                        if (rangeObject != null)
                                                graphModified.add(rangeObject);
                                }
-                               mapping.updateRange(graph);
+                       
+                       }
+                       
+                       mapping.updateRange(graph);
+                       
+                       synchronized (syncMutex) {
                                graphModified.clear();
                                syncDeletes();
-                               clearDeletes();
-                               graphUpdates = false;
-               }
-                       
-                       if (mapping.isRangeModified() && !runUndo && !runRedo)
-                               commit((String)null);
+                       }
                        
-                       if (DEBUG) System.out.println("Graph update done");
+                       clearDeletes();
+                       graphUpdates = false;
                }
+               
+               if (mapping.isRangeModified() && !runUndo && !runRedo)
+                       commit((String)null);
+               
+               if (DEBUG) System.out.println("Graph update done");
        }
        
        @Override
@@ -438,7 +446,6 @@ public abstract class AbstractVTKNodeMap<DBObject,E extends INode> implements VT
         * This code here synchronizes removed and added objects to collect deletable objects. (a deletable object is one which is removed but not added).  
         * 
         */
-       @SuppressWarnings("unused")
        protected void syncDeletes() {
                deleteUC.clear();
                for (Pair<E, String> n : removed) {
@@ -460,7 +467,6 @@ public abstract class AbstractVTKNodeMap<DBObject,E extends INode> implements VT
        /**
         * Clears deletable objects from mapping cache.
         */
-       @SuppressWarnings("unused")
        protected void clearDeletes() {
                if (DEBUG && delete.size() > 0) System.out.println("Delete");
                for (E n : delete) {
index 8dcee92610f00f8fea6afc1a1114d949cc787e1f..dae8c629b4335a7bfacb88503086d7d34285311b 100644 (file)
@@ -76,13 +76,13 @@ public class AddComponentAction extends vtkSwtAction {
                                allowed.add(PositionType.NEXT);
                        }  
                } else {
-                       if (component.getNext() == null) {
+                       if (component.getNext() == null || component.getControlPoint().isVariableLength()) {
                                allowed.add(PositionType.NEXT);
                        }
-                       if (component.getPrevious() == null) {
+                       if (component.getPrevious() == null || component.getControlPoint().isVariableLength()) {
                                allowed.add(PositionType.PREVIOUS);
                        }
-                       if (component instanceof InlineComponent && !component.getControlPoint().isFixedLength()){
+                       if (component instanceof InlineComponent && component.getControlPoint().isVariableLength()){
                                allowed.add(PositionType.SPLIT);
                        }
                }
index da764775d509a3b17185e8ca76fe401c51e640e4..df00bc39f468804efcde3b91afa6d133087c1000 100644 (file)
@@ -134,6 +134,9 @@ public class ComponentSelectionDialog extends Dialog implements ISelectionChange
                dialogSettings = settings.getSection(DIALOG);
                if (dialogSettings == null)
                        dialogSettings = settings.addNewSection(DIALOG);
+               
+               if (component.getNext() != null && component.getPrevious() != null)
+                       insertPosition = PositionType.PREVIOUS;
        }
        
        public void setLengthFactor(double lengthFactor) {
@@ -267,6 +270,10 @@ public class ComponentSelectionDialog extends Dialog implements ISelectionChange
                horizFillData.applyTo(buttonComposite);
                GridLayoutFactory.fillDefaults().numColumns(3).applyTo(buttonComposite);
 
+               startButton.setSelection(insertPosition == PositionType.PREVIOUS);
+               middleButton.setSelection(insertPosition == PositionType.SPLIT);
+               endButton.setSelection(insertPosition == PositionType.NEXT);
+               
                startButton.setEnabled(false);
                middleButton.setEnabled(false);
                endButton.setEnabled(false);
@@ -290,7 +297,6 @@ public class ComponentSelectionDialog extends Dialog implements ISelectionChange
                                updateInsertPosition(PositionType.NEXT);
                        }
                });
-               endButton.setSelection(true);
 
                label = new Label(composite, SWT.NONE);
                label.setText("Name");
@@ -428,9 +434,9 @@ public class ComponentSelectionDialog extends Dialog implements ISelectionChange
        private void updateInsertPosition(PositionType type) {
                if (insertPosition == type)
                        return;
-               endButton.setSelection(type == PositionType.NEXT);
-               middleButton.setSelection(type == PositionType.SPLIT);
                startButton.setSelection(type == PositionType.PREVIOUS);
+               middleButton.setSelection(type == PositionType.SPLIT);
+               endButton.setSelection(type == PositionType.NEXT);
                insertPosition = type;
        }
 
@@ -481,7 +487,15 @@ public class ComponentSelectionDialog extends Dialog implements ISelectionChange
                } else {
                        lenghtAdjustable = ((selected.getType() == Type.INLINE)
                                        && (selected.isVariable() || selected.isModifiable()));
-                       if (insertAdjustable) {
+                       
+                       if (component.getNext() != null && component.getPrevious() != null) {
+                               // We are inserting to a fully connected variable length component
+                               // only allow insertion within the component
+                               startButton.setEnabled(false);
+                               middleButton.setEnabled(false);
+                               endButton.setEnabled(false);
+                               updateInsertPosition(PositionType.PREVIOUS);
+                       } else if (insertAdjustable) {
                                switch (selected.getType()) {
                                case END:
                                        startButton.setEnabled(false);
@@ -566,7 +580,7 @@ public class ComponentSelectionDialog extends Dialog implements ISelectionChange
                                        rotationAngleText.setEnabled(true);
                                        if (angle == null) {
                                                ok = false;
-                                               if (msg == null) msg = "Please provide a rotation angle";
+                                               if (msg == null) msg = "Please provide a turn angle";
                                        }
                                } else {
                                        // this should not happen, since end components should not have variable, or
index ed4917df6717cfc78fd726afbafde98abe727423..966bb42187266b853af082f8b35da4ac04f6b458 100644 (file)
@@ -174,6 +174,8 @@ public class InlineComponent extends PipelineComponent {
                                        map.put("radius2", pipeRun2.getPipeDiameter() * 0.5);
                                }
                                if (controlPoint.isOffset() && !componentCalculatedOffset) {
+                                       if (getPipeRun() != null && getAlternativePipeRun() != null)
+                                               updateOffset();
                                        map.put("offset", controlPoint.getOffset());
                                }
                        }
index 0c7e3b26ed4935f297f94c12ab7386a01faaf488..cd4f407b22b6f65e3e80a91c2bc4cc5fae2df9a6 100644 (file)
@@ -462,7 +462,7 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                }
                if (this.rotationAngle != null && Math.abs(this.rotationAngle-rotationAngle) < MathTools.NEAR_ZERO)
                        return;
-               if (Objects.equals(rotationAngle, rotationAngle))
+               if (Objects.equals(this.rotationAngle, rotationAngle))
                        return;
                this.rotationAngle = rotationAngle;
                firePropertyChanged("rotationAngle");
@@ -788,9 +788,15 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
        }
        
        /**
-        * Returns direction vector. 
+        * Returns direction vector pointing towards an adjacent component for
+        * directed control points or turn control points with one open end. 
         * 
-        * For directed control points, always returns outwards pointing vector.
+        * Always returns an outwards pointing vector.
+        * 
+        * For any other type of component, the return value is null.
+        * 
+        * For turn components this only return a non-null value for the unconnected
+        * end of the component. 
         * 
         * @param direction
         * @return normalized vector, or null
@@ -834,6 +840,7 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                     Vector3d offset = new Vector3d();
                     MathTools.rotate(q2, v, offset);
                     MathTools.rotate(q, offset, dir);
+                    dir.negate();
                     dir.normalize();
                     return dir;
                 }
@@ -852,107 +859,70 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
         */
        public Vector3d getPathLegDirection(Direction direction) {
                if (direction == Direction.NEXT) {
-                       if (next != null) {
-                               PipeControlPoint pcp = this;
-                               if (pcp.isDualInline()) {
-                                       pcp = pcp.getDualSub();
-                               }
-                               Vector3d v = new Vector3d();
-                               v.sub(next.getWorldPosition(),pcp.getWorldPosition());
-                               if (v.lengthSquared() > MathTools.NEAR_ZERO)
-                    v.normalize();
-                else
-                    return null;
-                               return v;
-                       } else {
-                               if (previous == null) {
-                                       if (!isDirected())
-                                               throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
-                                       return getDirectedControlPointDirection();
+                       return getPathLegDirectionNext();
+               } else {
+                       return getPathLegDirectionPrevious();
+               }
+       }
 
-                               } else {
-                                       if (isVariableAngle() && !asFixedAngle())
-                                               throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
-                                       if (isInline()) {
-                                               PipeControlPoint pcp = this;
-                                               if (pcp.isDualSub()) {
-                                                       pcp = pcp.getParentPoint();
-                                               }
-                                               Vector3d v = new Vector3d();
-                                               v.sub(pcp.getWorldPosition(),previous.getWorldPosition());
-                                               if (v.lengthSquared() > MathTools.NEAR_ZERO)
-                               v.normalize();
-                                               else
-                                                   return null;
-                                               return v;
-                                       } else if (isDirected()) {
-                                               return getDirectedControlPointDirection();
-                                       } else if (isEnd()) {
-                                               Vector3d v = new Vector3d();
-                                               v.sub(getWorldPosition(),previous.getWorldPosition());
-                                               if (v.lengthSquared() > MathTools.NEAR_ZERO)
-                            v.normalize();
-                        else
-                            return null;
-                                               return v;
-                                       } else if (isTurn() && asFixedAngle() && !_getReversed()) {
-                                               return getDirection(Direction.NEXT);
-                                       }
-                                       throw new RuntimeException("Missing implementation " + this);
-                               }
-                       }
+       public Vector3d getPathLegDirectionPrevious() {
+               if (previous != null) {
+                       PipeControlPoint pcp = this;
+                       if (isDualSub()) 
+                               pcp = getParentPoint();
+                       Vector3d v = new Vector3d();
+                       v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
+                       if (v.lengthSquared() > MathTools.NEAR_ZERO)
+                       v.normalize();
+                   else
+                       return null;
+                       return v;
+               } else if (isDirected()) {
+                       Vector3d v = getDirectedControlPointDirection();
+                       v.negate();
+                       return v;
+               } else if (next == null)  {
+                       throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
+               } else if (isVariableAngle() && !asFixedAngle()) {
+                       throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
+               } else if (isInline() || isEnd()) {
+                       Vector3d v = getPathLegDirectionNext();
+                       if (v != null) v.negate();
+                       return v;
+               } else if (isTurn() && asFixedAngle() && _getReversed()) {
+                       return getDirection(Direction.PREVIOUS);
                } else {
-                       if (previous != null) {
-                               PipeControlPoint pcp = this;
-                               if (isDualSub()) 
-                                       pcp = getParentPoint();
-                               Vector3d v = new Vector3d();
-                               v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
-                               if (v.lengthSquared() > MathTools.NEAR_ZERO)
-                    v.normalize();
-                else
-                    return null;
-                               return v;
-                       } else {
-                               if (next == null)  {
-                                       if (!isDirected())
-                                               throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
-                                       Vector3d v = getDirectedControlPointDirection();
-                                       v.negate();
-                                       return v;
-                               } else {
-                                       if (isVariableAngle() && !asFixedAngle())
-                                               throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
-                                       if (isInline()) {
-                                               PipeControlPoint pcp = this;
-                                               if (pcp.isDualInline()) {
-                                                       pcp = pcp.getDualSub();
-                                               }
-                                               Vector3d v = new Vector3d();
-                                               v.sub(pcp.getWorldPosition(),next.getWorldPosition());
-                                               if (v.lengthSquared() > MathTools.NEAR_ZERO)
-                            v.normalize();
-                        else
-                            return null;
-                                               return v;
-                                       } else if (isDirected()) {
-                                               Vector3d v = getDirectedControlPointDirection();
-                                               v.negate();
-                                               return v;
-                                       } else if (isEnd()) {
-                                               Vector3d v = new Vector3d();
-                                               v.sub(getWorldPosition(),next.getWorldPosition());
-                                               if (v.lengthSquared() > MathTools.NEAR_ZERO)
-                            v.normalize();
-                        else
-                            return null;
-                                               return v;
-                                       } else if (isTurn() && asFixedAngle() && _getReversed()) {
-                                               return getDirection(Direction.PREVIOUS);
-                                       }
-                                       throw new RuntimeException("Missing implementation " + this);
-                               }
+                       throw new RuntimeException("Missing implementation " + this);
+               }
+       }
+
+       public Vector3d getPathLegDirectionNext() {
+               if (next != null) {
+                       PipeControlPoint pcp = this;
+                       if (pcp.isDualInline()) {
+                               pcp = pcp.getDualSub();
                        }
+                       Vector3d v = new Vector3d();
+                       v.sub(next.getWorldPosition(),pcp.getWorldPosition());
+                       if (v.lengthSquared() > MathTools.NEAR_ZERO)
+                       v.normalize();
+                   else
+                       return null;
+                       return v;
+               } else if (isDirected()) {
+                       return getDirectedControlPointDirection();
+               } else if (previous == null) {
+                       throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
+               } else if (isVariableAngle() && !asFixedAngle()) {
+                       throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
+               } else if (isInline() || isEnd()) {
+                       Vector3d v = getPathLegDirectionPrevious();
+                       if (v != null) v.negate();
+                       return v;
+               } else if (isTurn() && asFixedAngle() && !_getReversed()) {
+                       return getDirection(Direction.NEXT);
+               } else {
+                       throw new RuntimeException("Missing implementation " + this);
                }
        }
 
@@ -1648,6 +1618,21 @@ public class PipeControlPoint extends G3DNode implements IP3DNode {
                updateSubPoint();
        }
 
+       public void orientToDirection(Vector3d dir) {
+               Double angleO = getRotationAngle();
+               double angle = 0.0;
+               if (angleO != null)
+                       angle = angleO;
+               boolean reversed = _getReversed();
+               Quat4d q = null;
+               if (dir != null) {
+                   q = getControlPointOrientationQuat(dir, angle, reversed);
+               } else {
+                   q = getControlPointOrientationQuat(angle, reversed);
+               }
+               setWorldOrientation(q);
+       }
+
        @Override
        public String toString() {
                return getClass().getName() + "@" + Integer.toHexString(hashCode());
index 542534c3eebf983af835d4deefc7d735f70a5ee4..e930b8d6d07094035a188933141c4519085c5382 100644 (file)
@@ -294,7 +294,7 @@ public class PipingRules {
                scp.setWorldPosition(pos);
                Vector3d dir = new Vector3d();
                dir.sub(pcp2.getWorldPosition(), pcp1.getWorldPosition());
-               updateControlPointOrientation(scp, dir);
+               scp.orientToDirection(dir);
                scp.setLength(length);
                validate(scp.getPipeRun());
                return scp;
@@ -381,10 +381,43 @@ public class PipingRules {
 
        }
 
+       /**
+        * Calculate offset based on a given fixed component direction.
+        * 
+        * The desired component direction is provided as an input to this method,
+        * unlike the direction vector that is calculated by calculateOffset.
+        * 
+        * The returned offset vector is always perpendicular to the given direction
+        * vector.
+        * 
+        * @param startPoint Start point of leg
+        * @param endPoint   End point of leg
+        * @param start      Starting component of leg
+        * @param list       Inline components between start and end
+        * @param end        Ending component of leg
+        * @param dir        Direction at which the offset is calculated
+        * @param offset     A vector object to receive the offset vector values
+        * @return True if offsets are present
+        */
        public static boolean calculateDirectedOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, Vector3d dir, Vector3d offset) {
                return calculateOffset(startPoint, endPoint, start, list, end, dir, offset, true);
        }
 
+       /**
+        * Calculate offset and direction vectors for a path leg so that the given chain
+        * of components starts and ends at the given coordinates
+        * 
+        * The returned direction and offset vectors are perpendicular to each other.
+        * 
+        * @param startPoint Start point of the leg
+        * @param endPoint   End point of the leg
+        * @param start      Starting component of the leg
+        * @param list       Inline components between start and end
+        * @param end        Ending component of the leg
+        * @param dir        A vector object to receive the component direction vector
+        * @param offset     A vector object to receive the offset vector
+        * @return True if offsets are present
+        */
        public static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, Vector3d dir, Vector3d offset) {
                return calculateOffset(startPoint, endPoint, start, list, end, dir, offset, false);
        }
@@ -397,8 +430,11 @@ public class PipingRules {
                } else {
                        Vector3d sp = new Vector3d(startPoint);
                        Point3d ep = new Point3d(endPoint);
-                       dir.set(ep);
-                       dir.sub(sp);
+                       if (!directed) {
+                               dir.set(ep);
+                               dir.sub(sp);
+                       }
+                       
                        double l = dir.lengthSquared(); 
                        if (l > MathTools.NEAR_ZERO)
                                dir.scale(1.0/Math.sqrt(l));
@@ -1080,15 +1116,19 @@ public class PipingRules {
                
                Point3d otherPosition = new Point3d(dcpStart ? u.endPoint : u.startPoint);
                if (u.hasOffsets) {
-                       Vector3d dir = new Vector3d(), offset = new Vector3d();
+                       Vector3d dir = dcp.getDirection(dcpStart ? Direction.NEXT : Direction.PREVIOUS);
+                       if (!dcpStart)
+                               dir.negate();
+                       
+                       Vector3d offset = new Vector3d();
                        calculateDirectedOffset(u.startPoint, u.endPoint, u.start, u.list, u.end, dir, offset);
                        u.dir = dir;
                        u.offset = offset;
                        
                        if (dcpStart)
-                               otherPosition.add(offset);
-                       else
                                otherPosition.sub(offset);
+                       else
+                               otherPosition.add(offset);
                }
 
                double mu[] = new double[2];
@@ -1236,7 +1276,7 @@ public class PipingRules {
                Point3d position1 = new Point3d(u.startPoint);
                Point3d position2 = new Point3d(u.endPoint);
                
-               Vector3d dir = new Vector3d(), offset = new Vector3d();
+               Vector3d dir = u.start.getDirection(Direction.NEXT), offset = new Vector3d();
                calculateDirectedOffset(new Vector3d(position1), new Vector3d(position2), u.start, u.list, u.end, dir, offset);
                
                Point3d position1offset = new Point3d(position1);
@@ -1638,7 +1678,7 @@ public class PipingRules {
                        } else if (u.start.isEnd()) {
                                updateEndComponentControlPoint(u.start, u.dir);
                        } else if (u.start.isInline()) {
-                               updateControlPointOrientation(u.start, u.dir);
+                               u.start.orientToDirection(u.dir);
                        }
                        if (u.end.isTurn()) {
                                //updateTurnControlPointTurn(u.end, u.end.getPrevious(), u.end.getNext());
@@ -1647,7 +1687,7 @@ public class PipingRules {
                        } else if (u.end.isEnd()) {
                                updateEndComponentControlPoint(u.end, u.dir);
                        } else if (u.end.isInline()) {
-                               updateControlPointOrientation(u.end, u.dir);
+                               u.end.orientToDirection(u.dir);
                        }
 
                } else {
@@ -1776,7 +1816,7 @@ public class PipingRules {
                        System.out.println(" " + newInlinePoint);
 
                icp.setWorldPosition(newInlinePoint);
-               updateControlPointOrientation(icp, dir);
+               icp.orientToDirection(dir);
        }
 
        /**
@@ -1793,7 +1833,7 @@ public class PipingRules {
                        System.out.println("PipingRules.updateEndComponentControlPoint() " + ecp);
                
                if (!ecp.isFixed()) // prevent overriding nozzle orientations..
-                  updateControlPointOrientation(ecp, dir);
+                       ecp.orientToDirection(dir);
 
                for (PipeControlPoint pcp : ecp.getChildPoints()) {
                        // TODO update position
@@ -1801,21 +1841,6 @@ public class PipingRules {
                }
        }
 
-       private static void updateControlPointOrientation(PipeControlPoint pcp, Vector3d dir) {
-               Double angleO = pcp.getRotationAngle();
-               double angle = 0.0;
-               if (angleO != null)
-                       angle = angleO;
-               boolean reversed = pcp._getReversed();
-               Quat4d q = null;
-               if (dir != null) {
-                   q = pcp.getControlPointOrientationQuat(dir, angle, reversed);
-               } else {
-                   q = pcp.getControlPointOrientationQuat(angle, reversed);
-               }
-               pcp.setWorldOrientation(q);
-       }
-
        /**
         * Updates all branches when branch's position has been changed
         * 
@@ -1881,7 +1906,7 @@ public class PipingRules {
                                tcp.setTurnAxis(new Vector3d(MathTools.Y_AXIS));
                        }
                        
-                       updateControlPointOrientation(tcp,prev);
+                       tcp.orientToDirection(prev);
                        
                        if (DEBUG)
                                System.out.println("PipingTools.updateTurnControlPointTurn " + prev + " " + next + " " + turnAngle + " " + turnAxis);
index 6bbabe1e251c9feaf3f10e1767a56ce827ec91f0..913fceb9178276045828d04d9546000e39b62dd2 100644 (file)
@@ -378,9 +378,6 @@ public class ComponentUtils {
        public static PipelineComponent addComponent(P3DRootNode root, PipelineComponent component,  InsertInstruction inst) throws Exception {
                
                PipelineComponent newComponent = ComponentUtils.createComponent(root, inst.typeUri);
-               if (inst.name != null)
-                       newComponent.setName(inst.name);
-               
                PipeControlPoint newPcp = newComponent.getControlPoint();
                
                PipeControlPoint toPcp = component.getControlPoint();
@@ -408,6 +405,8 @@ public class ComponentUtils {
                        sizeChange = ((InlineComponent)newComponent).isSizeChange();
                }
                
+               // Calculate component position and direction vectors
+               // 'dir' is a unit vector that represents the direction from 'component' to 'newComponent'
                if (toPcp.isInline()) {
                        switch (position) {
                        case NEXT: 
@@ -437,6 +436,7 @@ public class ComponentUtils {
                                break;
                        case PREVIOUS:
                                pos = new Vector3d(start);
+                               dir.negate();
                                break;
                        case SPLIT:
                                pos = new Vector3d(toPcp.getWorldPosition());
@@ -444,9 +444,9 @@ public class ComponentUtils {
                        default:
                                break;
                        }
-
                } else if (toPcp.isDirected()) {
-                       dir = new Vector3d(toPcp.getDirection(Direction.NEXT));
+                       // 'dir' always points out of a nozzle regardless of insertion direction
+                       dir = new Vector3d(toPcp.getDirectedControlPointDirection());
                        pos = new Vector3d(toPcp.getWorldPosition());
                } else if (toPcp.isTurn() && toPcp.asFixedAngle()) {
                        dir = new Vector3d(toPcp.getDirection(position == PositionType.NEXT ? Direction.NEXT : Direction.PREVIOUS));
@@ -469,8 +469,12 @@ public class ComponentUtils {
                        }
                }
                
-               String name = component.getPipeRun().getUniqueName(typeName);
-               newComponent.setName(name);
+               if (inst.name != null) {
+                       newComponent.setName(inst.name);
+               } else {
+                       String name = component.getPipeRun().getUniqueName(typeName);
+                       newComponent.setName(name);
+               }
 
                pipeRun.addChild(newComponent);
                if (newPcp.isSizeChange())
@@ -487,18 +491,22 @@ public class ComponentUtils {
                } else if (newComponent instanceof TurnComponent) {
                    TurnComponent turnComponent = (TurnComponent)newComponent;
                    if  (turnComponent.isVariableAngle()) {
-                               newPcp.setTurnAngle(inst.angle);
+                               newPcp.setTurnAngle(Math.toRadians(inst.angle));
                                newComponent.setParameter("turnAngle", inst.angle);
                    }
                    if (inst.rotationAngle != null)
                 ((TurnComponent) newComponent).setRotationAngle(inst.rotationAngle);
                }
                
-               
                newComponent.updateParameters();
                
                Vector3d v = new Vector3d(dir);
                if (insertAdjustable) {
+                       // Prevent moving of adjacent components - always insert at end of a connected variable length component
+                       if (position == PositionType.NEXT && component.getNext() != null ||
+                               position == PositionType.PREVIOUS && component.getPrevious() != null)
+                               insertPosition = PositionType.PREVIOUS;
+                       
                        if (insertPosition == PositionType.NEXT)
                                v.scale(newComponent.getControlPoint().getInlineLength());
                        else if (insertPosition == PositionType.SPLIT)
@@ -508,12 +516,11 @@ public class ComponentUtils {
                } else {
                        v.scale(newComponent.getControlPoint().getInlineLength());
                }
+               
                switch (position) {
                case NEXT:
-                       pos.add(v);
-                       break;
                case PREVIOUS:
-                       pos.sub(v);
+                       pos.add(v);
                        break;
                case SPLIT:
                        break;
@@ -525,14 +532,20 @@ public class ComponentUtils {
                case NEXT: 
                        if (toPcp.isDualInline())
                                toPcp = toPcp.getDualSub();
-                       newPcp.insert(toPcp, Direction.NEXT);
                        newPcp.setWorldPosition(pos);
+                       if (toPcp.getNext() != null)
+                               PipingRules.splitVariableLengthComponent(newComponent, (InlineComponent)component, false);
+                       else
+                               newPcp.insert(toPcp, Direction.NEXT);
                        break;
                case PREVIOUS:
                        if (toPcp.isDualSub())
                                toPcp = toPcp.parent;
-                       newPcp.insert(toPcp, Direction.PREVIOUS);
                        newPcp.setWorldPosition(pos);
+                       if (toPcp.getPrevious() != null)
+                               PipingRules.splitVariableLengthComponent(newComponent, (InlineComponent)component, false);
+                       else
+                               newPcp.insert(toPcp, Direction.PREVIOUS);
                        break;
                case SPLIT:
                        PipingRules.splitVariableLengthComponent(newComponent, (InlineComponent)component, true);