]> gerrit.simantics Code Review - simantics/3d.git/blobdiff - org.simantics.plant3d/src/org/simantics/plant3d/actions/RoutePipeAction.java
Disable DB level undo, when model modifying actions are active
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / actions / RoutePipeAction.java
index 9773a9eae8c77a5c3c3a1bf350ffadaee6a0dc39..c7c7234ef0f679a4b038253aa6a73b4c1be14a7c 100644 (file)
@@ -13,20 +13,27 @@ import javax.vecmath.Point3d;
 import javax.vecmath.Tuple3d;
 import javax.vecmath.Vector3d;
 
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Label;
+import org.simantics.db.Resource;
 import org.simantics.g3d.math.MathTools;
 import org.simantics.g3d.math.Ray;
 import org.simantics.g3d.scenegraph.NodeMap;
 import org.simantics.g3d.scenegraph.base.INode;
+import org.simantics.g3d.toolbar.ToolComposite;
 import org.simantics.g3d.tools.ConstraintDetector;
-import org.simantics.g3d.tools.DummyConstraintDetector;
 import org.simantics.g3d.vtk.gizmo.TranslateAxisGizmo;
 import org.simantics.g3d.vtk.swt.InteractiveVtkComposite;
 import org.simantics.g3d.vtk.swt.vtkSwtAction;
 import org.simantics.g3d.vtk.utils.vtkUtil;
 import org.simantics.plant3d.Activator;
+import org.simantics.plant3d.gizmo.ConstraintPointGizmo;
 import org.simantics.plant3d.gizmo.SplitPointSelectionGizmo;
 import org.simantics.plant3d.gizmo.TerminalSelectionGizmo;
-import org.simantics.plant3d.ontology.Plant3D;
 import org.simantics.plant3d.scenegraph.EndComponent;
 import org.simantics.plant3d.scenegraph.InlineComponent;
 import org.simantics.plant3d.scenegraph.Nozzle;
@@ -47,34 +54,39 @@ import vtk.vtkTextActor;
 
 public class RoutePipeAction extends vtkSwtAction {
        enum LockType {
-               X, Y, Z, XY, YZ, XZ, NONE, CUSTOM
+               X, Y, Z, XY, XZ, YZ, NONE, CUSTOM
        };
 
        LockType lock = LockType.NONE;
+       boolean lockForced;
        private double BRANCH_SNAP_DISTANCE = 0.05;
        private double NOZZLE_SNAP_DISTANCE = 0.05;
+       private static double BRANCH_DOT_PRODUCT = 0.95; // dot product value used for prevent branch creation
+       private static double ALIGN_DOT_PRODUCT = 0.99; // dot product for creating turn when connecting pipes
 
        private double istep = 10.0;
        private int decimals = 2;
 
        private P3DRootNode root;
-       private PipelineComponent startComponent;
-       private PipeRun pipeRun;
+       protected PipelineComponent startComponent;
+       protected PipeRun pipeRun;
+       private boolean allowBranches;
 
-       private TranslateAxisGizmo translateAxisGizmo = new TranslateAxisGizmo();
+       protected TranslateAxisGizmo translateAxisGizmo = new TranslateAxisGizmo();
        private SplitPointSelectionGizmo splitPointSelectionGizmo;
+       private ConstraintPointGizmo constraintPointGizmo;
        private TerminalSelectionGizmo terminalSelectionGizmo;
-       private NodeMap<vtkProp,INode> nodeMap;
+       private NodeMap<Resource,vtkProp,INode> nodeMap;
        
-       private enum ToolState{NOT_ACTIVE, INITIALIZING, SELECTING_POSITION, SELECTING_SPLIT, ROUTING};
-       private ToolState state = ToolState.NOT_ACTIVE;
+       protected enum ToolState{NOT_ACTIVE, INITIALIZING, SELECTING_POSITION, SELECTING_SPLIT, ROUTING};
+       protected ToolState state = ToolState.NOT_ACTIVE;
        
        private ConstraintDetector detector;// = new DummyConstraintDetector();
        
-       private boolean useDefault = false;
-       private Vector3d direction = null;
-       private Vector3d previousPosition = null;
-       private Vector3d currentPosition = null;
+       protected boolean useDefault = false;
+       protected Vector3d direction = null;
+       protected Vector3d previousPosition = null;
+       protected Vector3d currentPosition = null;
        
        boolean step = false;
        
@@ -86,16 +98,26 @@ public class RoutePipeAction extends vtkSwtAction {
        
        private Set<PositionType> allowed = new HashSet<PositionType>();
        
+       protected ToolComposite toolComposite;
+    protected Combo axisCombo;
+    protected Button cameraButton;
        
-       public RoutePipeAction(InteractiveVtkComposite panel, P3DRootNode root) {
+       public RoutePipeAction(InteractiveVtkComposite panel, P3DRootNode root, ToolComposite toolComposite) {
+           this(panel,root, toolComposite, true);
+       }
+       
+       public RoutePipeAction(InteractiveVtkComposite panel, P3DRootNode root, ToolComposite toolComposite, boolean allowBranches) {
                super(panel);
                this.root = root;
+               this.allowBranches = allowBranches;
                setText("Route Pipe");
                setImageDescriptor(Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/Straight.png"));
                nodeMap = root.getNodeMap();
                splitPointSelectionGizmo = new SplitPointSelectionGizmo(panel);
                terminalSelectionGizmo = new TerminalSelectionGizmo(panel);
+               constraintPointGizmo = new ConstraintPointGizmo(panel);
                detector = new org.simantics.g3d.vtk.swt.ConstraintDetector(panel);
+               this.toolComposite = toolComposite;
        }
        
        public void setComponent(PipelineComponent component) {
@@ -105,24 +127,76 @@ public class RoutePipeAction extends vtkSwtAction {
                        allowed.add(PositionType.NEXT);
                if (this.startComponent.getPrevious() == null && !(this.startComponent instanceof Nozzle))
                        allowed.add(PositionType.PREVIOUS);
-               if (this.startComponent instanceof InlineComponent && !this.startComponent.getControlPoint().isFixed())
+               if (allowBranches && this.startComponent instanceof InlineComponent && !this.startComponent.getControlPoint().isFixedLength())
                        allowed.add(PositionType.SPLIT);
                setEnabled(allowed.size() > 0);
        }
        
-       public void deattach() {
-               deactivate();
-               startComponent = null;
-               nodeMap.commit("Route pipe");
-               deattachUI();
-               super.deattach();
-               panel.refresh();
-       }
+       protected void createTools(ToolComposite toolComposite) {
+        Label label = new Label(toolComposite, SWT.READ_ONLY);
+        label.setText("Route direction:");
+        axisCombo = new Combo(toolComposite, SWT.READ_ONLY);
+        axisCombo.add("X");
+        axisCombo.add("Y");
+        axisCombo.add("Z");
+        axisCombo.add("XY");
+        axisCombo.add("XZ");
+        axisCombo.add("YZ");
+        axisCombo.add("None");
+        axisCombo.add("Custom");
+        axisCombo.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                Combo c = (Combo)e.getSource();
+                setLockType(LockType.values()[c.getSelectionIndex()],false);
+                panel.getComponent().setFocus();
+                
+            }
+        });
+        axisCombo.select(lock.ordinal());
+        cameraButton = new Button(toolComposite, SWT.TOGGLE);
+        cameraButton.setText("Camera");
+        cameraButton.setSelection(useDefault);
+        cameraButton.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                setUseDefault(((Button)e.getSource()).getSelection());
+                panel.getComponent().setFocus();
+            }
+        });
+        Button close = new Button(toolComposite, SWT.PUSH);
+        close.setText("Close");
+        close.addSelectionListener(new SelectionAdapter() {
+            public void widgetSelected(SelectionEvent e) {
+                panel.useDefaultAction();
+            };
+        });
+        toolComposite.relayout();
+    }
+
+    public void deattach() {
+        deactivate();
+        setDBUndo(true);
+        if (toolComposite != null) {
+            toolComposite.clear();
+            axisCombo = null;
+            cameraButton = null;
+        }
+
+        startComponent = null;
+
+        deattachUI();
+        super.deattach();
+        panel.refresh();
+    }
        
        public void attach() {
                if (startComponent == null)
                        return;
-               
+               if (toolComposite != null) {
+                  createTools(toolComposite); 
+           }
+               setDBUndo(false);
                super.attach();
                ThreadUtils.asyncExec(panel.getThreadQueue(), new Runnable() {
                        public void run() {
@@ -153,6 +227,8 @@ public class RoutePipeAction extends vtkSwtAction {
                        splitPointSelectionGizmo.deattach();
                if (terminalSelectionGizmo.isAttached())
                        terminalSelectionGizmo.deattach();
+               if (constraintPointGizmo.isAttached())
+                   constraintPointGizmo.deattach();
                if (infoActor != null) {
                        panel.getRenderer().RemoveActor(infoActor);
                        infoActor.Delete();
@@ -161,13 +237,13 @@ public class RoutePipeAction extends vtkSwtAction {
                panel.unlock();
        }
        
-       private List<PipelineComponent> added = new ArrayList<PipelineComponent>();
+       protected List<PipelineComponent> added = new ArrayList<PipelineComponent>();
        
        @Override
        public boolean keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
                        panel.useDefaultAction();
-               if (lock != LockType.CUSTOM) {
+               if (lock != LockType.CUSTOM || !lockForced) {
                        if ((e.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) > 0) {
                                if (e.getKeyCode() == KeyEvent.VK_X) {
                                        if (lock != LockType.XY && lock != LockType.XZ) {
@@ -215,20 +291,30 @@ public class RoutePipeAction extends vtkSwtAction {
                                        else
                                                setLockType(LockType.NONE, false);
                                }
+                               if (e.getKeyCode() == KeyEvent.VK_L && direction != null) {
+                                   if (lock != LockType.CUSTOM)
+                        setLockType(LockType.CUSTOM, false);
+                    else
+                        setLockType(LockType.NONE, false);
+                               }
                        }
                }
                if (e.getKeyCode() == KeyEvent.VK_C) {
-                       useDefault = !useDefault;
-                       System.out.println("UseDefault " + useDefault);
+                       setUseDefault(!useDefault);
                }
                
-               
-               
-               
                update();
                return true;
        }
        
+       private void setUseDefault(boolean b) {
+           useDefault = b;
+           if (useDefault)
+            setInfoText("Rotating camera");
+        if (cameraButton != null)
+            cameraButton.setSelection(useDefault);
+       }
+       
        
        private void update() {
                panel.refresh();
@@ -252,7 +338,7 @@ public class RoutePipeAction extends vtkSwtAction {
        
        boolean startRemovable = false;
        
-       private void activate() throws Exception {
+       protected void activate() throws Exception {
                state = ToolState.INITIALIZING;
                added.clear();
                
@@ -288,9 +374,9 @@ public class RoutePipeAction extends vtkSwtAction {
        
        
        
-       private void activateNextPrev(PipeControlPoint start) throws Exception{
+       protected void activateNextPrev(PipeControlPoint start) throws Exception{
                if (!reversed && start.isDualInline())
-                       start = start.getSubPoint().get(0);
+                       start = start.getDualSub();
                else if (reversed && start.isDualSub())
                        start = start.parent;
                
@@ -301,14 +387,17 @@ public class RoutePipeAction extends vtkSwtAction {
                if (startComponent instanceof Nozzle) {
                        direction = startComponent.getControlPoint().getDirectedControlPointDirection();
                        lock = LockType.CUSTOM;
+                       lockForced = true;
                } else if (startComponent instanceof PipelineComponent){
                        if (startComponent instanceof InlineComponent) {
                                direction = startComponent.getControlPoint().getPathLegDirection(reversed ? Direction.PREVIOUS : Direction.NEXT);
                                lock = LockType.CUSTOM;
+                               lockForced = true;
                                if (((InlineComponent) startComponent).isVariableLength()) {
                                        startWithTurn = true;
                                        direction = null;
                                        lock = LockType.NONE;
+                                       lockForced = false;
                                } 
                                Vector3d v = new Vector3d();
                                if (!reversed) {
@@ -317,12 +406,14 @@ public class RoutePipeAction extends vtkSwtAction {
                                        start.getControlPointEnds(previousPosition,v);
                                }
                        } else if (startComponent instanceof TurnComponent) {
-                               if (start.isFixed()) {
+                               if (start.asFixedAngle()) {
                                        direction = startComponent.getControlPoint().getPathLegDirection(reversed ? Direction.PREVIOUS : Direction.NEXT);
                                        lock = LockType.CUSTOM;
+                                       lockForced = start.isFixedAngle();
                                } else {
                                        direction = null;
                                        lock = LockType.NONE;
+                                       lockForced = false;
                                }
                        } else if (startComponent instanceof EndComponent) {
                                throw new Exception("Not supported");
@@ -362,9 +453,10 @@ public class RoutePipeAction extends vtkSwtAction {
                translateAxisGizmo.attach(panel);
                setPreviousPosition(previousPosition);
                updateCurrentPoint();
+               updateWidget();
        }
        
-       private void setPreviousPosition(Vector3d v) {
+       protected void setPreviousPosition(Vector3d v) {
                previousPosition = new Vector3d(v);
                if (translateAxisGizmo.isAttached())
                        translateAxisGizmo.setPosition(previousPosition);
@@ -408,6 +500,7 @@ public class RoutePipeAction extends vtkSwtAction {
                translateAxisGizmo.attach(panel);
                setPreviousPosition(previousPosition);
                updateCurrentPoint();
+               updateWidget();
        }
        
        private void activateSplit(PipeControlPoint start) throws Exception{
@@ -419,11 +512,23 @@ public class RoutePipeAction extends vtkSwtAction {
                state = ToolState.SELECTING_SPLIT;
        }
        public void deactivate() {
-               for (PipelineComponent component : added) {
-                       component.getControlPoint().setDeletable(true);
-               }
+           if (added.size() > 0) {
+           for (PipelineComponent component : added) {
+                       component.getControlPoint().setDeletable(true);
+               }
+               
+            for (PipelineComponent comp : added) {
+                PipingRules.requestUpdate(comp.getControlPoint());
+            }
+            try {
+                PipingRules.update();
+                nodeMap.commit("Route pipe");
+            } catch (Exception e) {
+                ExceptionUtils.logAndShowError(e);
+            }
+            added.clear();        
+        }
                
-               added.clear();
                startComponent.getControlPoint().setDeletable(startRemovable);
 
                direction = null;
@@ -440,13 +545,15 @@ public class RoutePipeAction extends vtkSwtAction {
                startRemovable = false;
                detector.clearConstraintHighlights();
                state = ToolState.NOT_ACTIVE;
-               setEnabled(false);
-               
+               setEnabled(false);              
 
        }
        
        private void setLockType(LockType type, boolean force) {
-               if (force || lock != LockType.CUSTOM) {
+           if (type == LockType.CUSTOM && (direction == null || added.size() > 0)) {
+               // nothing to do..
+           } else if (force || (lock != LockType.CUSTOM || !lockForced || added.size() > 0) ) {
+                   
                        lock = type;
                        
                        switch (lock) {
@@ -474,7 +581,16 @@ public class RoutePipeAction extends vtkSwtAction {
                                break;
                                
                        }
+                       
                }
+               updateWidget();
+       }
+       
+       private void updateWidget() {
+           if (axisCombo != null) {
+            axisCombo.select(lock.ordinal());
+            axisCombo.setEnabled(!lockForced || lock != LockType.CUSTOM);
+           }
        }
        
        @Override
@@ -521,7 +637,7 @@ public class RoutePipeAction extends vtkSwtAction {
                                                                addPoint();
                                                        }
                                                } else {
-                                                       throw new RuntimeException("kjf");
+                                                       throw new RuntimeException("RoutePipeAction initialization has failed, no added components found");
                //                      // user was selecting position of branch
                //                    lastPoint.set(startPoint);
                //                    controlPoints.add(new Point3d(startPoint));
@@ -598,7 +714,10 @@ public class RoutePipeAction extends vtkSwtAction {
        
        private void updateConstraints() {
            detector.clearConstraints();
+           constraintPointGizmo.clearPositions();
            if (hoverObject == null) {
+               if (constraintPointGizmo.isAttached())
+                   constraintPointGizmo.deattach();
                return;
            }
            if (hoverObject instanceof Nozzle) {
@@ -611,10 +730,19 @@ public class RoutePipeAction extends vtkSwtAction {
                c.getEnds(p1, p2);
                detector.addContraintPoint(p1);
                detector.addContraintPoint(p2);
+               detector.addContraintPoint(new Point3d(c.getWorldPosition()));
            } else if (hoverObject instanceof TurnComponent) {
                TurnComponent n = (TurnComponent)hoverObject;
             detector.addContraintPoint(new Point3d(n.getWorldPosition()));
            }
+           if (detector.getConstraintPoints().size() > 0) {
+               for (Point3d p : detector.getConstraintPoints()) {
+                   constraintPointGizmo.addPosition(new Vector3d(p));
+               }
+               if (constraintPointGizmo.isAttached())
+                   constraintPointGizmo.deattach();
+               constraintPointGizmo.attach(panel);
+           }
        }
        
        @Override
@@ -651,16 +779,7 @@ public class RoutePipeAction extends vtkSwtAction {
        
        INode hoverObject = null;
 
-       private void updateRouting(double x, double y) {
-//             if(input.keyPressed(KeyEvent.VK_ESCAPE)) {
-//                     controlPoints.clear();
-//                     end();
-//                     return;
-//             }
-//             if (input.keyPressed(KeyEvent.VK_C)) {
-//                     useCamera = !useCamera;
-//                     cameraAction.setChecked(useCamera);
-//             }
+       protected void updateRouting(double x, double y) {
                if (useDefault) {
                        //panel.getDefaultAction().update();
                        return;
@@ -714,13 +833,15 @@ public class RoutePipeAction extends vtkSwtAction {
                                } else if (hoverObject instanceof InlineComponent && ((InlineComponent)hoverObject).isVariableLength()) {
                                        endTo = (InlineComponent)hoverObject;
                                        endType = endingToStraight(endTo,mu,o,d);     
+                                       if (endType == null)
+                                           endTo = null;
                                } else if (hoverObject instanceof PipelineComponent && (endPort = endingToComponent(hoverObject,o,d)) != null) {
                                        endTo = (PipelineComponent)hoverObject;
                                } else {
                                        updateRoute(o,d); 
                                }
                        } else {  
-                               if (hoverObject instanceof InlineComponent && ((InlineComponent)hoverObject).isVariableLength() && (endType = endingLockToStraight(hoverObject,mu)) != null) {
+                               if (hoverObject instanceof InlineComponent && ((InlineComponent)hoverObject).isVariableLength() && (endType = endingLockToStraight(hoverObject,mu, new Point3d(currentPosition))) != null) {
                                        endTo = (InlineComponent)hoverObject;
                                } else if (hoverObject instanceof Nozzle && endingLockToNozzle(hoverObject)) {
                                        endTo = (Nozzle)hoverObject;
@@ -740,7 +861,7 @@ public class RoutePipeAction extends vtkSwtAction {
                panel.refresh();
        }
        
-       private boolean updateCurrentPoint(Vector3d o, Vector3d d) {
+       protected boolean updateCurrentPoint(Vector3d o, Vector3d d) {
 
                Vector3d point = new Vector3d(this.previousPosition);
                
@@ -798,6 +919,7 @@ public class RoutePipeAction extends vtkSwtAction {
                return true;
        }
        
+       @SuppressWarnings("unused")
        private Vector3d getLockDir() {
                switch (lock) {
                case CUSTOM:
@@ -808,11 +930,12 @@ public class RoutePipeAction extends vtkSwtAction {
                        return new Vector3d(0,1,0);
                case Z:
                        return new Vector3d(0,0,1);
+               default:
+                       return null;
                }
-               return null;
        }
        
-       private void updateRoute(Vector3d o, Vector3d d) {
+       protected void updateRoute(Vector3d o, Vector3d d) {
                detector.clearConstraintHighlights();
                Point3d previousPipePoint = new Point3d(previousPosition);
                String s = "";
@@ -899,28 +1022,32 @@ public class RoutePipeAction extends vtkSwtAction {
        }
        
        private PositionType endingToStraight(INode straightNode, double mu[], Vector3d o, Vector3d d) {
+//         if (!allowBranches) {
+//             updateCurrentPoint();
+//            return null;
+//         }
                InlineComponent s = (InlineComponent)straightNode;
-               String info = "";
+               
                Point3d sStart = new Point3d();
                Point3d sEnd = new Point3d();
                s.getEnds(sStart, sEnd);
                //detector.clearConstraintHighlights();
                
                Point3d previousPipePoint = new Point3d(previousPosition);
+               Point3d currentPipePoint = new Point3d(currentPosition);
                //String st = "";
                if (lock == LockType.NONE) {
                        Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint));
                        if (p != null) {
                                currentPosition = new Vector3d(p);
                                // snapping is detected, check if snapped point can create branch with straight
-                               PositionType t = endingLockToStraight(s, mu);
+                               PositionType t = endingLockToStraight(s, mu, currentPipePoint);
                                if (t != null)
                                        return t;
                                // if not, we'll have to remove highlight that was added when snapped point was detected
-                               detector.clearConstraintHighlights();
-                       } 
+                               detector.clearConstraintHighlights();   
+                       }
                        
-
                        Vector3d sDir = new Vector3d(sEnd);
                        sDir.sub(sStart);
                        MathTools.intersectStraightStraight(sStart, sDir, o, d, currentPosition, new Point3d(), mu);
@@ -936,71 +1063,104 @@ public class RoutePipeAction extends vtkSwtAction {
                // branch point must lie between straight's ends. If connection point is exactly
                // on straight end user may want to connect pipes to each other
                // TODO : take account sizes of inline components)
-               // TODO : actually make connection if its detected
-               boolean connectPrev = false;
-               boolean connectNext = false;
-               
-               if (mu[0] < 0.0) {
-                       currentPosition.set(sStart);
-                       connectPrev = true;
-               }
-               else if (mu[0] > 1.0) {
-                       currentPosition.set(sEnd);
-                       connectNext = true;
-               }
-               boolean connect = false;
-               if (connectPrev) {
-                       PipeControlPoint pcp = s.getControlPoint();
-                       if (pcp.getPrevious() == null)
-                               connect = true;
-               } else if (connectNext) {
-                       PipeControlPoint pcp = s.getControlPoint();
-                       if (pcp.getNext() == null)
-                               connect = true;
-               }
-               
-               updateCurrentPoint();
-               
-               if (connect)
-                       info += "Connect pipes :";
-               else
-                       info += "Make Branch :";
-               
-               setInfoText(info + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0)));
-               if (connect) {
-                       if (connectNext) {
-                               return PositionType.NEXT;
-                       } else {
-                               return PositionType.PREVIOUS;
-                       }
-                               
-               }
-               return PositionType.SPLIT;
-                       
+               return endingToStraight(mu, s, sStart, sEnd, currentPipePoint);
+       }
+       
+       private PositionType endingToStraight(double mu[], InlineComponent s, Point3d sStart, Point3d sEnd , Point3d currentPipePoint) {
+           String info = "";
+           
+           boolean connectPrev = false;
+        boolean connectNext = false;
+        boolean branch = false;
+        if (mu[0] < 0.1) {
+            connectPrev = true;
+        }
+        else if (mu[0] > 0.9) {
+            connectNext = true;
+        }
+
+          
+        if (connectPrev) {
+            PipeControlPoint pcp = s.getControlPoint();
+            if (pcp.getPrevious() != null)
+                connectPrev = false;
+        } else if (connectNext) {
+            PipeControlPoint pcp = s.getControlPoint();
+            if (pcp.getNext() != null)
+                connectNext = false;
+        } else {
+            Vector3d dir = s.getControlPoint().getPathLegDirection(Direction.NEXT);
+            Vector3d currDir = getLast().getControlPoint().getPathLegDirection(Direction.NEXT);
+            dir.normalize();
+            currDir.normalize();
+            double dot = dir.dot(currDir);
+            System.out.println(dot + " " + currDir + " " + dir);
+            if (dot > BRANCH_DOT_PRODUCT || dot < -BRANCH_DOT_PRODUCT) {
+                // pipes are almost in the same direction, creating a branch is not feasible.
+                branch = false;
+            } else {
+                branch = true;
+            }
+        }
+        
+            
+        if (connectNext || connectPrev)
+            info += "Connect pipes :";
+        else if (branch)
+            info += "Create a Branch :";
+        
+        setInfoText(info + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0)));
+        if (connectNext) {
+            currentPosition.set(sEnd);
+            updateCurrentPoint();
+            return PositionType.NEXT;
+        } else if (connectPrev){
+            currentPosition.set(sStart);
+            updateCurrentPoint();
+            return PositionType.PREVIOUS;
+        } else if (branch && allowBranches) {
+            return PositionType.SPLIT;
+        } else {
+            currentPosition.set(currentPipePoint);
+            updateCurrentPoint();
+            return null;
+        }
        }
        
        private PipeControlPoint endingToComponent(INode componentNode, Vector3d o, Vector3d d) {
                PipelineComponent component = (PipelineComponent)componentNode;
                PipeControlPoint pcp = component.getControlPoint();
+               PipeControlPoint connect = null;
                if (component instanceof EndComponent) {
                        if (pcp.getNext() != null || pcp.getPrevious() != null)
                                return null;
-                       return pcp;
+                       connect =  pcp;
                } else if (component instanceof TurnComponent) {
                        if (pcp.getNext() == null || pcp.getPrevious() == null)
-                               return pcp;
-                       return null;
+                               connect = pcp;
+                       else
+                           return null;
                } else if (component instanceof InlineComponent) {
                        // TODO : scan all empty pcps of the component and select closest one.
                        if (pcp.getNext() == null || pcp.getPrevious() == null)
-                               return pcp;
-                       return null;
+                               connect =  pcp;
+                       else
+                           return null;
+               }
+               if (connect != null) {
+                   currentPosition.set(connect.getWorldPosition());
+            updateCurrentPoint();
+                   setInfoText("Connect to " + component.getName());
+                   return connect;
                }
-
                return null;
        }
        
-       private PositionType endingLockToStraight(INode straightNode, double mu[]) {
+       private PositionType endingLockToStraight(INode straightNode, double mu[], Point3d currentPipePoint) {
+//         if (!allowBranches) {
+//            updateCurrentPoint();
+//            return null;
+//        }
                InlineComponent s = (InlineComponent)straightNode;
                Point3d sStart = new Point3d();
                Point3d sEnd = new Point3d(); 
@@ -1019,15 +1179,21 @@ public class RoutePipeAction extends vtkSwtAction {
                // startPoint of branch must be between pipe ends
                // TODO : take account sizes of elbows (or other components)
                // branch point must be between pipe ends and intersection points must be quite close to each other
-               if (mu[0] > 0.0 && mu[0] < 1.0 && routePoint.lengthSquared() < BRANCH_SNAP_DISTANCE) {
-                       currentPosition.set(branchPoint);
-                       
-                       updateCurrentPoint();
-                       
-                       setInfoText("Make branch (l) :" + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0)) + " " + routePoint.lengthSquared());
-                       return PositionType.SPLIT;
-               }
-               return null;
+               
+               if (routePoint.lengthSquared() > BRANCH_SNAP_DISTANCE)
+                   return null;
+               
+               return endingToStraight(mu, s, sStart, sEnd, currentPipePoint);
+               
+//             if (mu[0] > 0.0 && mu[0] < 1.0 && routePoint.lengthSquared() < BRANCH_SNAP_DISTANCE) {
+//                     currentPosition.set(branchPoint);
+//                     
+//                     updateCurrentPoint();
+//                     
+//                     setInfoText("Create a branch (l) :" + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0)) + " " + routePoint.lengthSquared());
+//                     return PositionType.SPLIT;
+//             }
+//             return null;
        }
        
        private boolean endingLockToNozzle(INode nozzleNode) {
@@ -1055,40 +1221,64 @@ public class RoutePipeAction extends vtkSwtAction {
                return null;
        }
        
-       private void addPoint() throws Exception {
-               InlineComponent previous = (InlineComponent)getLast();
-               PipeControlPoint previousCP = previous.getControlPoint();
-               TurnComponent turn = ComponentUtils.createTurn(root);
-               InlineComponent straight = ComponentUtils.createStraight(root);
-               PipeControlPoint turnCP = turn.getControlPoint();
-               PipeControlPoint straightCP = straight.getControlPoint();
-               straight.setName(pipeRun.getUniqueName("Pipe"));
-               turn.setName(pipeRun.getUniqueName("Elbow"));
-               pipeRun.addChild(turn);
-               pipeRun.addChild(straight);
-               added.add(turn);
-               added.add(straight);
-               
-               turnCP.setDeletable(false); // mark turnCP nonDeletable so that PipingRules won't delete it immediately.
-               
-               if (!reversed) {
-                       previousCP.setNext(turnCP);
-                       turnCP.setPrevious(previousCP);
-                       turnCP.setNext(straightCP);
-                       straightCP.setPrevious(turnCP);
-               } else {
-                       previousCP.setPrevious(turnCP);
-                       turnCP.setNext(previousCP);
-                       turnCP.setPrevious(straightCP);
-                       straightCP.setNext(turnCP);
-               }
-               
-               turnCP.setWorldPosition(currentPosition);
-               turnCP.setTurnAngle(0.0);
-               turnCP.setLength(0.0);
-               straightCP.setWorldPosition(currentPosition);
-               straightCP.setLength(0.0);
-               
+       protected void addTurn() throws Exception{
+           InlineComponent previous = (InlineComponent)getLast();
+        PipeControlPoint previousCP = previous.getControlPoint();
+        TurnComponent turn = ComponentUtils.createTurn(root);
+        PipeControlPoint turnCP = turn.getControlPoint();
+        turn.setName(pipeRun.getUniqueName("Elbow"));
+        pipeRun.addChild(turn);
+        added.add(turn);
+        turnCP.setDeletable(false); // mark turnCP nonDeletable so that PipingRules won't delete it immediately.
+        if (!reversed) {
+            previousCP.setNext(turnCP);
+            turnCP.setPrevious(previousCP);
+        } else {
+            previousCP.setPrevious(turnCP);
+            turnCP.setNext(previousCP);
+        }
+        
+        turnCP.setWorldPosition(currentPosition);
+        turnCP.setTurnAngle(0.0);
+        turnCP.setLength(0.0);
+        
+       }
+       
+       protected void addStraight() throws Exception{
+        TurnComponent turn = (TurnComponent)getLast();
+        PipeControlPoint turnCP = turn.getControlPoint();
+        
+        InlineComponent straight = ComponentUtils.createStraight(root);
+        
+        PipeControlPoint straightCP = straight.getControlPoint();
+        straight.setName(pipeRun.getUniqueName("Pipe"));
+        
+        
+        pipeRun.addChild(straight);
+        
+        added.add(straight);
+        
+        
+        
+        if (!reversed) {
+            turnCP.setNext(straightCP);
+            straightCP.setPrevious(turnCP);
+        } else {
+            turnCP.setPrevious(straightCP);
+            straightCP.setNext(turnCP);
+        }
+        
+        turnCP.setWorldPosition(currentPosition);
+        turnCP.setTurnAngle(0.0);
+        turnCP.setLength(0.0);
+        straightCP.setWorldPosition(currentPosition);
+        straightCP.setLength(0.0);
+           
+       }
+       
+       protected void addPoint() throws Exception {
+               addTurn();
+               addStraight();
                setPreviousPosition(currentPosition);
                updateCurrentPoint();
                
@@ -1099,7 +1289,7 @@ public class RoutePipeAction extends vtkSwtAction {
        /**
         * Updates tool graphics for current point 
         */
-       private void updateCurrentPoint() {
+       protected void updateCurrentPoint() {
                InlineComponent straight = (InlineComponent)added.get(added.size()-1);
                // TODO: the inline length is from previous update step.
                double l;
@@ -1152,10 +1342,22 @@ public class RoutePipeAction extends vtkSwtAction {
                
        }
        
-       private void endPiping() throws Exception {
+       protected void endPiping() throws Exception {
                state = ToolState.NOT_ACTIVE;
                
                if (endTo != null) {
+                   if (endType == PositionType.NEXT || endType == PositionType.PREVIOUS && endTo instanceof InlineComponent) {
+                       Vector3d dir = endTo.getControlPoint().getPathLegDirection(Direction.NEXT);
+                   Vector3d currDir = getLast().getControlPoint().getPathLegDirection(Direction.NEXT);
+                   dir.normalize();
+                   currDir.normalize();
+                   double dot = dir.dot(currDir);
+                   System.out.println(dot + " " + currDir + " " + dir);
+                   if (dot < ALIGN_DOT_PRODUCT && dot> -ALIGN_DOT_PRODUCT) {
+                       addTurn();    
+                   }
+                       
+                   }
                        ComponentUtils.connect(getLast(), endTo, endType, currentPosition);
                }
                panel.useDefaultAction();