]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.plant3d/src/org/simantics/plant3d/actions/RoutePipeAction.java
Get inline component direction irrespectively of connectivity
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / actions / RoutePipeAction.java
1 package org.simantics.plant3d.actions;
2
3 import java.awt.event.KeyEvent;
4 import java.awt.event.MouseEvent;
5 import java.awt.event.MouseWheelEvent;
6 import java.math.BigDecimal;
7 import java.util.ArrayList;
8 import java.util.HashSet;
9 import java.util.List;
10 import java.util.Set;
11
12 import javax.vecmath.Point3d;
13 import javax.vecmath.Tuple3d;
14 import javax.vecmath.Vector3d;
15
16 import org.eclipse.swt.SWT;
17 import org.eclipse.swt.events.SelectionAdapter;
18 import org.eclipse.swt.events.SelectionEvent;
19 import org.eclipse.swt.widgets.Button;
20 import org.eclipse.swt.widgets.Combo;
21 import org.eclipse.swt.widgets.Label;
22 import org.simantics.db.Resource;
23 import org.simantics.g3d.math.MathTools;
24 import org.simantics.g3d.math.Ray;
25 import org.simantics.g3d.scenegraph.NodeMap;
26 import org.simantics.g3d.scenegraph.base.INode;
27 import org.simantics.g3d.toolbar.ToolComposite;
28 import org.simantics.g3d.tools.ConstraintDetector;
29 import org.simantics.g3d.vtk.gizmo.TranslateAxisGizmo;
30 import org.simantics.g3d.vtk.swt.InteractiveVtkComposite;
31 import org.simantics.g3d.vtk.swt.vtkSwtAction;
32 import org.simantics.g3d.vtk.utils.vtkUtil;
33 import org.simantics.plant3d.Activator;
34 import org.simantics.plant3d.gizmo.ConstraintPointGizmo;
35 import org.simantics.plant3d.gizmo.SplitPointSelectionGizmo;
36 import org.simantics.plant3d.gizmo.TerminalSelectionGizmo;
37 import org.simantics.plant3d.scenegraph.EndComponent;
38 import org.simantics.plant3d.scenegraph.InlineComponent;
39 import org.simantics.plant3d.scenegraph.Nozzle;
40 import org.simantics.plant3d.scenegraph.P3DRootNode;
41 import org.simantics.plant3d.scenegraph.PipeRun;
42 import org.simantics.plant3d.scenegraph.PipelineComponent;
43 import org.simantics.plant3d.scenegraph.TurnComponent;
44 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint;
45 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction;
46 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType;
47 import org.simantics.plant3d.scenegraph.controlpoint.PipingRules;
48 import org.simantics.plant3d.utils.ComponentUtils;
49 import org.simantics.utils.threads.ThreadUtils;
50 import org.simantics.utils.ui.ExceptionUtils;
51
52 import vtk.vtkProp;
53 import vtk.vtkTextActor;
54
55 public class RoutePipeAction extends vtkSwtAction {
56         enum LockType {
57                 X, Y, Z, XY, XZ, YZ, NONE, CUSTOM
58         };
59
60         LockType lock = LockType.NONE;
61         boolean lockForced;
62         private double BRANCH_SNAP_DISTANCE = 0.05;
63         private double NOZZLE_SNAP_DISTANCE = 0.05;
64         private static double BRANCH_DOT_PRODUCT = 0.95; // dot product value used for prevent branch creation
65         private static double ALIGN_DOT_PRODUCT = 0.99; // dot product for creating turn when connecting pipes
66
67         private double istep = 10.0;
68         private int decimals = 2;
69
70         private P3DRootNode root;
71         protected PipelineComponent startComponent;
72         protected PipeRun pipeRun;
73         private boolean allowBranches;
74
75         protected TranslateAxisGizmo translateAxisGizmo = new TranslateAxisGizmo();
76         private SplitPointSelectionGizmo splitPointSelectionGizmo;
77         private ConstraintPointGizmo constraintPointGizmo;
78         private TerminalSelectionGizmo terminalSelectionGizmo;
79         private NodeMap<Resource,vtkProp,INode> nodeMap;
80         
81         protected enum ToolState{NOT_ACTIVE, INITIALIZING, SELECTING_POSITION, SELECTING_SPLIT, ROUTING};
82         protected ToolState state = ToolState.NOT_ACTIVE;
83         
84         private ConstraintDetector detector;// = new DummyConstraintDetector();
85         
86         protected boolean useDefault = false;
87         protected Vector3d direction = null;
88         protected Vector3d previousPosition = null;
89         protected Vector3d currentPosition = null;
90         
91         boolean step = false;
92         
93         PipelineComponent endTo = null;
94         PositionType endType = null;
95         PipeControlPoint endPort = null;
96         
97         boolean reversed = false;
98         
99         private Set<PositionType> allowed = new HashSet<PositionType>();
100         
101         protected ToolComposite toolComposite;
102     protected Combo axisCombo;
103     protected Button cameraButton;
104         
105         public RoutePipeAction(InteractiveVtkComposite panel, P3DRootNode root, ToolComposite toolComposite) {
106             this(panel,root, toolComposite, true);
107         }
108         
109         public RoutePipeAction(InteractiveVtkComposite panel, P3DRootNode root, ToolComposite toolComposite, boolean allowBranches) {
110                 super(panel);
111                 this.root = root;
112                 this.allowBranches = allowBranches;
113                 setText("Route Pipe");
114                 setImageDescriptor(Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/Straight.png"));
115                 nodeMap = root.getNodeMap();
116                 splitPointSelectionGizmo = new SplitPointSelectionGizmo(panel);
117                 terminalSelectionGizmo = new TerminalSelectionGizmo(panel);
118                 constraintPointGizmo = new ConstraintPointGizmo(panel);
119                 detector = new org.simantics.g3d.vtk.swt.ConstraintDetector(panel);
120                 this.toolComposite = toolComposite;
121         }
122         
123         public void setComponent(PipelineComponent component) {
124                 this.startComponent = component;
125                 allowed.clear();
126                 if (this.startComponent.getNext() == null)
127                         allowed.add(PositionType.NEXT);
128                 if (this.startComponent.getPrevious() == null && !(this.startComponent instanceof Nozzle))
129                         allowed.add(PositionType.PREVIOUS);
130                 if (allowBranches && this.startComponent instanceof InlineComponent && !this.startComponent.getControlPoint().isFixedLength())
131                         allowed.add(PositionType.SPLIT);
132                 setEnabled(allowed.size() > 0);
133         }
134         
135         protected void createTools(ToolComposite toolComposite) {
136         Label label = new Label(toolComposite, SWT.READ_ONLY);
137         label.setText("Route direction:");
138         axisCombo = new Combo(toolComposite, SWT.READ_ONLY);
139         axisCombo.add("X");
140         axisCombo.add("Y");
141         axisCombo.add("Z");
142         axisCombo.add("XY");
143         axisCombo.add("XZ");
144         axisCombo.add("YZ");
145         axisCombo.add("None");
146         axisCombo.add("Custom");
147         axisCombo.addSelectionListener(new SelectionAdapter() {
148             @Override
149             public void widgetSelected(SelectionEvent e) {
150                 Combo c = (Combo)e.getSource();
151                 setLockType(LockType.values()[c.getSelectionIndex()],false);
152                 panel.getComponent().setFocus();
153                 
154             }
155         });
156         axisCombo.select(lock.ordinal());
157         cameraButton = new Button(toolComposite, SWT.TOGGLE);
158         cameraButton.setText("Camera");
159         cameraButton.setSelection(useDefault);
160         cameraButton.addSelectionListener(new SelectionAdapter() {
161             @Override
162             public void widgetSelected(SelectionEvent e) {
163                 setUseDefault(((Button)e.getSource()).getSelection());
164                 panel.getComponent().setFocus();
165             }
166         });
167         Button close = new Button(toolComposite, SWT.PUSH);
168         close.setText("Close");
169         close.addSelectionListener(new SelectionAdapter() {
170             public void widgetSelected(SelectionEvent e) {
171                 panel.useDefaultAction();
172             };
173         });
174         toolComposite.relayout();
175     }
176
177     public void deattach() {
178         deactivate();
179         setDBUndo(true);
180         if (toolComposite != null) {
181             toolComposite.clear();
182             axisCombo = null;
183             cameraButton = null;
184         }
185
186         startComponent = null;
187
188         deattachUI();
189         super.deattach();
190         panel.refresh();
191     }
192         
193         public void attach() {
194                 if (startComponent == null)
195                         return;
196                 if (toolComposite != null) {
197                    createTools(toolComposite); 
198             }
199                 setDBUndo(false);
200                 super.attach();
201                 ThreadUtils.asyncExec(panel.getThreadQueue(), new Runnable() {
202                         public void run() {
203 //                              attachUI();
204                                 try {
205                                         activate();
206                                 } catch (Exception e) {
207                                         deattach();
208                                         ExceptionUtils.logAndShowError(e);
209                                 }
210                                 
211                         }
212                 });
213                 
214         }
215         
216 //      private void attachUI() {
217 //              //panel.setCursor(activeCursor);
218 //              translateAxisGizmo.attach(panel.GetRenderer());
219 //      }
220         
221         private void deattachUI() {
222                 //panel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
223                 panel.lock();
224                 if (translateAxisGizmo.isAttached())
225                         translateAxisGizmo.deattach();
226                 if (splitPointSelectionGizmo.isAttached())
227                         splitPointSelectionGizmo.deattach();
228                 if (terminalSelectionGizmo.isAttached())
229                         terminalSelectionGizmo.deattach();
230                 if (constraintPointGizmo.isAttached())
231                     constraintPointGizmo.deattach();
232                 if (infoActor != null) {
233                         panel.getRenderer().RemoveActor(infoActor);
234                         infoActor.Delete();
235                         infoActor = null;
236                 }
237                 panel.unlock();
238         }
239         
240         protected List<PipelineComponent> added = new ArrayList<PipelineComponent>();
241         
242         @Override
243         public boolean keyPressed(KeyEvent e) {
244                 if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
245                         panel.useDefaultAction();
246                 if (lock != LockType.CUSTOM || !lockForced) {
247                         if ((e.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) > 0) {
248                                 if (e.getKeyCode() == KeyEvent.VK_X) {
249                                         if (lock != LockType.XY && lock != LockType.XZ) {
250                                                 setLockType(LockType.XY, false);
251                                         } else if (lock == LockType.XY) {
252                                                 setLockType(LockType.XZ, false);
253                                         } else {
254                                                 setLockType(LockType.NONE, false);
255                                         }
256                                 }
257                                 if (e.getKeyCode() == KeyEvent.VK_Y) {
258                                         if (lock != LockType.XY && lock != LockType.YZ) {
259                                                 setLockType(LockType.XY, false);
260                                         } else if (lock == LockType.XY) {
261                                                 setLockType(LockType.YZ, false);
262                                         } else {
263                                                 setLockType(LockType.NONE, false);
264                                         }
265                                 }
266                                 if (e.getKeyCode() == KeyEvent.VK_Z) {
267                                         if (lock != LockType.XZ && lock != LockType.YZ) {
268                                                 setLockType(LockType.XZ, false);
269                                         } else if (lock == LockType.XZ) {
270                                                 setLockType(LockType.YZ, false);
271                                         } else {
272                                                 setLockType(LockType.NONE, false);
273                                         }
274                                 }
275                         } else {
276                                 if (e.getKeyCode() == KeyEvent.VK_X) {
277                                         if (lock != LockType.X)
278                                                 setLockType(LockType.X,false);
279                                         else
280                                                 setLockType(LockType.NONE,false);
281                                 }
282                                 if (e.getKeyCode() == KeyEvent.VK_Y) {
283                                         if (lock != LockType.Y)
284                                                 setLockType(LockType.Y,false);
285                                         else
286                                                 setLockType(LockType.NONE, false);
287                                 }
288                                 if (e.getKeyCode() == KeyEvent.VK_Z) {
289                                         if (lock != LockType.Z)
290                                                 setLockType(LockType.Z, false);
291                                         else
292                                                 setLockType(LockType.NONE, false);
293                                 }
294                                 if (e.getKeyCode() == KeyEvent.VK_L && direction != null) {
295                                     if (lock != LockType.CUSTOM)
296                         setLockType(LockType.CUSTOM, false);
297                     else
298                         setLockType(LockType.NONE, false);
299                                 }
300                         }
301                 }
302                 if (e.getKeyCode() == KeyEvent.VK_C) {
303                         setUseDefault(!useDefault);
304                 }
305                 
306                 update();
307                 return true;
308         }
309         
310         private void setUseDefault(boolean b) {
311             useDefault = b;
312             if (useDefault)
313             setInfoText("Rotating camera");
314         if (cameraButton != null)
315             cameraButton.setSelection(useDefault);
316         }
317         
318         
319         private void update() {
320                 panel.refresh();
321         }
322         private void update(double x, double y) {
323                 switch (state) {
324                 case NOT_ACTIVE:
325                         return; // TODO : throw Exception?
326                 case INITIALIZING:
327                         return;
328                 case SELECTING_POSITION:
329                         return;
330                 case SELECTING_SPLIT:
331                         return;
332                 case ROUTING:
333                         updateRouting(x,y);
334                         break;
335                 }
336                 return;
337         }
338         
339         boolean startRemovable = false;
340         
341         protected void activate() throws Exception {
342                 state = ToolState.INITIALIZING;
343                 added.clear();
344                 
345                 if (allowed.size() == 1) {
346                         pipeRun = startComponent.getPipeRun();
347                         PipeControlPoint start = startComponent.getControlPoint();
348                         boolean requiresBranching = false;
349                         if (start.getNext() == null)
350                                 reversed = false;
351                         else if (start.getPrevious() == null) {
352                                 reversed = true;
353                         } else {
354                                 requiresBranching = true;
355                         }
356                         
357                         if (requiresBranching) {
358                                 activateSplit(start);
359                         } else {
360                                 activateNextPrev(start);
361                         }
362                 } else if (allowed.size() == 0) {
363                         panel.useDefaultAction();
364                         state = ToolState.NOT_ACTIVE;
365                         return;
366                 } else {
367                         terminalSelectionGizmo.setComponent(startComponent, allowed);
368                         terminalSelectionGizmo.attach(panel);
369                         state = ToolState.SELECTING_POSITION;
370                         update();
371                 }
372                 
373         }
374         
375         
376         
377         protected void activateNextPrev(PipeControlPoint start) throws Exception{
378                 if (!reversed && start.isDualInline())
379                         start = start.getDualSub();
380                 else if (reversed && start.isDualSub())
381                         start = start.parent;
382                 
383                 pipeRun = start.getPipeRun();
384                 setPreviousPosition(start.getWorldPosition());
385                 
386                 boolean startWithTurn = false;
387                 if (startComponent instanceof Nozzle) {
388                         direction = startComponent.getControlPoint().getDirectedControlPointDirection();
389                         lock = LockType.CUSTOM;
390                         lockForced = true;
391                 } else if (startComponent instanceof PipelineComponent){
392                         if (startComponent instanceof InlineComponent) {
393                                 direction = startComponent.getControlPoint().getInlineDir();
394                                 if (reversed) direction.negate();
395                                 lock = LockType.CUSTOM;
396                                 lockForced = true;
397                                 if (((InlineComponent) startComponent).isVariableLength()) {
398                                         startWithTurn = true;
399                                         direction = null;
400                                         lock = LockType.NONE;
401                                         lockForced = false;
402                                 } 
403                                 Vector3d v = new Vector3d();
404                                 if (!reversed) {
405                                         start.getControlPointEnds(v, previousPosition);
406                                 } else {
407                                         start.getControlPointEnds(previousPosition,v);
408                                 }
409                         } else if (startComponent instanceof TurnComponent) {
410                                 if (start.asFixedAngle()) {
411                                         direction = startComponent.getControlPoint().getPathLegDirection(reversed ? Direction.PREVIOUS : Direction.NEXT);
412                                         lock = LockType.CUSTOM;
413                                         lockForced = start.isFixedAngle();
414                                 } else {
415                                         direction = null;
416                                         lock = LockType.NONE;
417                                         lockForced = false;
418                                 }
419                         } else if (startComponent instanceof EndComponent) {
420                                 throw new Exception("Not supported");
421                         }
422                         
423                 } else {
424                         throw new Exception("Not supported");
425                 }
426                 currentPosition = new Vector3d(previousPosition);
427                 state = ToolState.ROUTING;
428                 if (direction != null) {
429                         direction.normalize();
430                         
431                 } 
432                 startRemovable = start.isDeletable();
433                 start.setDeletable(false);
434                 
435                 if (startWithTurn) {
436                         addPoint();
437                 } else {
438                         if (direction != null)
439                                 currentPosition.add(direction);
440                         InlineComponent straight = ComponentUtils.createStraight(root);
441                         PipeControlPoint straightCP = straight.getControlPoint();
442                         straight.setName(pipeRun.getUniqueName("Pipe"));
443                         pipeRun.addChild(straight);
444                         added.add(straight);
445         
446                         if (!reversed) {
447                                 start.setNext(straightCP);
448                                 straightCP.setPrevious(start);
449                         } else {
450                                 start.setPrevious(straightCP);
451                                 straightCP.setNext(start);
452                         }
453                 }
454                 translateAxisGizmo.attach(panel);
455                 setPreviousPosition(previousPosition);
456                 updateCurrentPoint();
457                 updateWidget();
458         }
459         
460         protected void setPreviousPosition(Vector3d v) {
461                 previousPosition = new Vector3d(v);
462                 if (translateAxisGizmo.isAttached())
463                         translateAxisGizmo.setPosition(previousPosition);
464         }
465         
466         private void activateBranch(PipeControlPoint start) throws Exception{
467                 pipeRun = start.getPipeRun();
468                 setPreviousPosition(start.getWorldPosition());
469                 
470                 direction = null;
471                 lock = LockType.NONE;
472                 
473                 currentPosition = new Vector3d(previousPosition);
474                 state = ToolState.ROUTING;
475                 if (direction != null) {
476                         direction.normalize();
477                         
478                 } 
479                 startRemovable = start.isDeletable();
480                 start.setDeletable(false);
481                 
482                 
483                 if (direction != null)
484                         currentPosition.add(direction);
485                 InlineComponent straight = ComponentUtils.createStraight(root);
486                 PipeControlPoint straightCP = straight.getControlPoint();
487                 straight.setName(pipeRun.getUniqueName("Pipe"));
488                 pipeRun.addChild(straight);
489                 added.add(straight);
490
491                 if (!reversed) {
492                         start.setNext(straightCP);
493                         straightCP.setPrevious(start);
494                         
495                 } else {
496                         start.setPrevious(straightCP);
497                         straightCP.setNext(start);
498                         
499                 }
500                 
501                 translateAxisGizmo.attach(panel);
502                 setPreviousPosition(previousPosition);
503                 updateCurrentPoint();
504                 updateWidget();
505         }
506         
507         private void activateSplit(PipeControlPoint start) throws Exception{
508                 Point3d p1 = new Point3d();
509                 Point3d p2 = new Point3d();
510                 start.getInlineControlPointEnds(p1, p2);
511                 splitPointSelectionGizmo.setSplit(p1, p2);
512                 splitPointSelectionGizmo.attach(panel);
513                 state = ToolState.SELECTING_SPLIT;
514         }
515         public void deactivate() {
516             if (added.size() > 0) {
517             for (PipelineComponent component : added) {
518                         component.getControlPoint().setDeletable(true);
519                 }
520                 
521             for (PipelineComponent comp : added) {
522                 PipingRules.requestUpdate(comp.getControlPoint());
523             }
524             try {
525                 PipingRules.update();
526                 nodeMap.commit("Route pipe");
527             } catch (Exception e) {
528                 ExceptionUtils.logAndShowError(e);
529             }
530             added.clear();        
531         }
532                 
533                 startComponent.getControlPoint().setDeletable(startRemovable);
534
535                 direction = null;
536
537                 setLockType(LockType.NONE, true);
538                 startComponent = null;
539                 endTo = null;
540                 endPort = null;
541                 endType = null;
542                 pipeRun = null;
543                 allowed.clear();
544                 currentPosition = null;
545                 previousPosition = null;
546                 startRemovable = false;
547                 detector.clearConstraintHighlights();
548                 state = ToolState.NOT_ACTIVE;
549                 setEnabled(false);              
550
551         }
552         
553         private void setLockType(LockType type, boolean force) {
554             if (type == LockType.CUSTOM && (direction == null || added.size() > 0)) {
555                 // nothing to do..
556             } else if (force || (lock != LockType.CUSTOM || !lockForced || added.size() > 0) ) {
557                     
558                         lock = type;
559                         
560                         switch (lock) {
561                         case CUSTOM:
562                         case NONE:
563                                 translateAxisGizmo.setType(6);
564                                 break;
565                         case X:
566                                 translateAxisGizmo.setType(0);
567                                 break;
568                         case Y:
569                                 translateAxisGizmo.setType(1);
570                                 break;
571                         case Z:
572                                 translateAxisGizmo.setType(2);
573                                 break;
574                         case XY:
575                                 translateAxisGizmo.setType(3);
576                                 break;
577                         case XZ:
578                                 translateAxisGizmo.setType(4);
579                                 break;
580                         case YZ:
581                                 translateAxisGizmo.setType(5);
582                                 break;
583                                 
584                         }
585                         
586                 }
587                 updateWidget();
588         }
589         
590         private void updateWidget() {
591             if (axisCombo != null) {
592             axisCombo.select(lock.ordinal());
593             axisCombo.setEnabled(!lockForced || lock != LockType.CUSTOM);
594             }
595         }
596         
597         @Override
598         public boolean mousePressed(MouseEvent e) {
599                 if (useDefault) {
600                         getDefaultAction().mousePressed(e);
601                 }
602                 return true;
603         }
604
605         @Override
606         public boolean mouseReleased(MouseEvent e) {
607                 if (useDefault) {
608                         getDefaultAction().mouseReleased(e);
609                 }
610                 return true;
611         }
612         
613         @Override
614         public boolean mouseWheelMoved(MouseWheelEvent e) {
615                 if (useDefault) {
616                         getDefaultAction().mouseWheelMoved(e);
617                 }
618                 return true;
619         }
620         
621         @Override
622         public boolean mouseClicked(MouseEvent e) {
623                 if (useDefault) {
624                         getDefaultAction().mouseClicked(e);
625                         return true;
626                 }
627                 if (state == ToolState.ROUTING) {
628                         try {
629                                 if (e.getClickCount() == 1) {
630                                         if (e.getButton() == MouseEvent.BUTTON1) {
631                                                 if (this.added.size() > 0) {
632                                                         
633                                                         setLockType(LockType.NONE,true);
634                                                         if (endTo != null) {
635                                                                 
636                                                                 endPiping();
637                                                         } else {
638                                                                 addPoint();
639                                                         }
640                                                 } else {
641                                                         throw new RuntimeException("RoutePipeAction initialization has failed, no added components found");
642                 //                      // user was selecting position of branch
643                 //                    lastPoint.set(startPoint);
644                 //                    controlPoints.add(new Point3d(startPoint));
645                 //                    if (selectionLine != null)
646                 //                      selectionLine.removeFromParent();
647                 //                    selectionLine = null;
648                                                 }
649                                         } else if (e.getButton() ==MouseEvent.BUTTON2){
650                                             updateConstraints();
651                                         } else if (e.getButton() == MouseEvent.BUTTON3){      
652                                                 endPiping();
653                                         }
654                                 }
655                         } catch(Exception err) {
656                                 err.printStackTrace();
657                         }
658                 } else if (state == ToolState.SELECTING_POSITION) {
659                         if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) {
660                                 int type = panel.getPickType();
661                                 //panel.setPickType(0);
662                                 panel.setPickType(5);
663                                 vtkProp[] picked = panel.pick(e.getX(), e.getY());
664                                 panel.setPickType(type);
665                                 PositionType position = terminalSelectionGizmo.getPickedPosition(picked);
666                                 if (position != null) {
667                                         terminalSelectionGizmo.deattach();
668                                         try {
669                                         if (position == PositionType.SPLIT) {
670                                                 activateSplit(startComponent.getControlPoint());
671                                         } else if (position == PositionType.NEXT || position == PositionType.PREVIOUS) {
672                                                 reversed = position == PositionType.PREVIOUS;
673                                                 activateNextPrev(startComponent.getControlPoint());
674                                         } else {
675                                                 panel.useDefaultAction();
676                                         }
677                                         } catch (Exception err) {
678                                                 ExceptionUtils.logAndShowError(err);
679                                                 panel.useDefaultAction();
680                                         }
681                                 }
682                         } 
683                 } else if (state == ToolState.SELECTING_SPLIT) {
684                         if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) {
685                                 Tuple3d t = splitPointSelectionGizmo.getSplitPoint();
686                                 splitPointSelectionGizmo.deattach();
687                                 if (t == null) {
688                                         panel.useDefaultAction();
689                                         return true;
690                                 }
691                                 try {
692                                         Vector3d pos = new Vector3d(t);
693                                         InlineComponent branchSplit = ComponentUtils.createBranchSplit((InlineComponent)startComponent, pos);
694                                         PipeControlPoint branchSplitCP = branchSplit.getControlPoint();
695                                         reversed = false;
696                                         PipeRun newRun = new PipeRun();
697                                         String n = root.getUniqueName("PipeRun");
698                                         newRun.setName(n);
699                                         root.addChild(newRun);
700                                         PipeControlPoint pcp = new PipeControlPoint(branchSplit,newRun);
701                                         branchSplitCP.children.add(pcp);
702                                         pcp.parent = branchSplitCP;
703                                         pcp.setWorldOrientation(branchSplitCP.getWorldOrientation());
704                                         pcp.setWorldPosition(branchSplitCP.getWorldPosition());
705                                         startComponent = branchSplit;
706                                         activateBranch(pcp);
707                                 } catch (Exception err) {
708                                         ExceptionUtils.logAndShowError(err);
709                                         panel.useDefaultAction();
710                                 }
711                         }
712                 }
713                 return true;
714         }
715         
716         private void updateConstraints() {
717             detector.clearConstraints();
718             constraintPointGizmo.clearPositions();
719             if (hoverObject == null) {
720                 if (constraintPointGizmo.isAttached())
721                     constraintPointGizmo.deattach();
722                 return;
723             }
724             if (hoverObject instanceof Nozzle) {
725             Nozzle n = (Nozzle)hoverObject;
726             detector.addContraintPoint(new Point3d(n.getWorldPosition()));
727             } else if (hoverObject instanceof InlineComponent) {
728                 InlineComponent c = (InlineComponent)hoverObject;
729                 Point3d p1 = new Point3d();
730                 Point3d p2 = new Point3d();
731                 c.getEnds(p1, p2);
732                 detector.addContraintPoint(p1);
733                 detector.addContraintPoint(p2);
734                 detector.addContraintPoint(new Point3d(c.getWorldPosition()));
735             } else if (hoverObject instanceof TurnComponent) {
736                 TurnComponent n = (TurnComponent)hoverObject;
737             detector.addContraintPoint(new Point3d(n.getWorldPosition()));
738             }
739             if (detector.getConstraintPoints().size() > 0) {
740                 for (Point3d p : detector.getConstraintPoints()) {
741                     constraintPointGizmo.addPosition(new Vector3d(p));
742                 }
743                 if (constraintPointGizmo.isAttached())
744                     constraintPointGizmo.deattach();
745                 constraintPointGizmo.attach(panel);
746             }
747         }
748         
749         @Override
750         public boolean mouseMoved(MouseEvent e) {
751                 if (useDefault) {
752                         getDefaultAction().mouseMoved(e);
753                         return true;
754                 }
755                 step = ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0);
756                 update(e.getX(), e.getY());
757                 return true;
758         }
759         
760         @Override
761         public boolean mouseDragged(MouseEvent e) {
762                 if (useDefault)
763                         getDefaultAction().mouseDragged(e);
764                 return true;
765         }
766         
767
768         
769         
770         private List<INode> isOverNode(int x, int y) {
771                 List<INode> nodes = new ArrayList<INode>(); 
772                 vtkProp picked[] = panel.pick2(x, y);
773                 if (picked !=null) {
774                         for (int i = 0; i < picked.length; i++) {
775                                 nodes.add(nodeMap.getNode(picked[i]));
776                         }
777                 }
778                 return nodes;
779         }
780         
781         INode hoverObject = null;
782
783         protected void updateRouting(double x, double y) {
784                 if (useDefault) {
785                         //panel.getDefaultAction().update();
786                         return;
787                 }
788                 
789                 endTo = null;
790                 endType = null;
791                 endPort = null;
792
793                 Ray ray = vtkUtil.createMouseRay(panel.getRenderer(),x, y);
794                 Vector3d o = new Vector3d(ray.pos);
795                 Vector3d d = ray.dir;
796                 
797                 
798                 if (!updateCurrentPoint(o, d))
799                         return;
800                 //Point3d startPoint = new Point3d();
801                 double mu[] = new double[2];
802                 
803                 
804                 
805                 
806                 
807                 List<INode> hover = isOverNode((int)x,(int)y);
808                 if (hover.size() > 0) {
809                         hoverObject = hover.get(0);
810                 } else {
811                     hoverObject = null;
812                 }
813 //              System.out.println(hoverObject + " " + getLast());
814                 if (hoverObject != null) {
815                         if (hoverObject.equals(getLast()) ) {
816                                 boolean set = false;
817                                 for (int i = 1; i < hover.size(); i++) {
818                                         hoverObject = hover.get(i);
819                                         if (!getLast().equals(hoverObject)) {
820                                                 set = true;
821                                                 break;
822                                         }
823                                 }
824                                 if (!set)
825                                         hoverObject = null;
826                         }
827                 }
828 //              System.out.println(hoverObject);
829                 if (hoverObject != null) {
830
831                         if (lock == LockType.NONE) {
832                                 if (hoverObject instanceof Nozzle && endingToNozzle(hoverObject,o,d)) {
833                                         endTo = (Nozzle)hoverObject;
834                                 } else if (hoverObject instanceof InlineComponent && ((InlineComponent)hoverObject).isVariableLength()) {
835                                         endTo = (InlineComponent)hoverObject;
836                                         endType = endingToStraight(endTo,mu,o,d);     
837                                         if (endType == null)
838                                             endTo = null;
839                                 } else if (hoverObject instanceof PipelineComponent && (endPort = endingToComponent(hoverObject,o,d)) != null) {
840                                         endTo = (PipelineComponent)hoverObject;
841                                 } else {
842                                         updateRoute(o,d); 
843                                 }
844                         } else {  
845                                 if (hoverObject instanceof InlineComponent && ((InlineComponent)hoverObject).isVariableLength() && (endType = endingLockToStraight(hoverObject,mu, new Point3d(currentPosition))) != null) {
846                                         endTo = (InlineComponent)hoverObject;
847                                 } else if (hoverObject instanceof Nozzle && endingLockToNozzle(hoverObject)) {
848                                         endTo = (Nozzle)hoverObject;
849                                 } else if ((hoverObject instanceof PipelineComponent) &&  ((endPort = endingLockToComponent(hoverObject)) != null)) {
850                                         endTo = (PipelineComponent)hoverObject;
851                                 } else {
852                                         updateRoute(o,d);
853                                 }
854                         }
855                         if (added.contains(endTo))
856                                 endTo = null;
857
858                 } else {
859                         updateRoute(o,d);
860                 }
861                 
862                 panel.refresh();
863         }
864         
865         protected boolean updateCurrentPoint(Vector3d o, Vector3d d) {
866
867                 Vector3d point = new Vector3d(this.previousPosition);
868                 
869                 switch(lock) {
870                 case X:
871                         MathTools.intersectStraightStraight(point, new Vector3d(1.0,0.0,0.0), o,d, currentPosition, new Vector3d());
872                         if (step) {
873                                 currentPosition.x = Math.round(istep * currentPosition.x) / istep;
874                                 BigDecimal bx = new BigDecimal(currentPosition.x);
875                                 bx.setScale(decimals, BigDecimal.ROUND_HALF_UP);
876                                 currentPosition.x = bx.doubleValue();
877                         }
878                         break;
879                 case Y:
880                         MathTools.intersectStraightStraight(point, new Vector3d(0.0,1.0,0.0), o,d, currentPosition, new Vector3d());
881                         if (step) {
882                                 currentPosition.y = Math.round(istep * currentPosition.y) / istep;
883                                 BigDecimal bx = new BigDecimal(currentPosition.y);
884                                 bx.setScale(decimals, BigDecimal.ROUND_HALF_UP);
885                                 currentPosition.y = bx.doubleValue();
886                         }
887                         break;
888                 case Z:
889                         MathTools.intersectStraightStraight(point, new Vector3d(0.0,0.0,1.0), o,d, currentPosition, new Vector3d());
890                         if (step) {
891                                 currentPosition.z = Math.round(istep * currentPosition.z) / istep;
892                                 BigDecimal bx = new BigDecimal(currentPosition.z);
893                                 bx.setScale(decimals, BigDecimal.ROUND_HALF_UP);
894                                 currentPosition.z = bx.doubleValue();
895                         }break;
896                 case XY:
897                         MathTools.intersectStraightPlane(o, d, point, new Vector3d(0.0,0.0,1.0), currentPosition);
898                         break;
899                 case XZ:
900                         MathTools.intersectStraightPlane(o, d, point, new Vector3d(0.0,1.0,0.0), currentPosition);
901                         break;
902                 case YZ:
903                         MathTools.intersectStraightPlane(o, d, point, new Vector3d(1.0,0.0,0.0), currentPosition);
904                         break;
905                 case NONE:
906                         Vector3d normal = new Vector3d(panel.getRenderer().GetActiveCamera().GetDirectionOfProjection());
907                         normal.normalize();
908                         
909                         MathTools.intersectStraightPlane(o, d, point, normal, currentPosition);
910                         break;
911                 case CUSTOM:
912                         MathTools.intersectStraightStraight(point, new Vector3d(direction), o,d, currentPosition, new Vector3d());
913                         double dist = MathTools.distanceFromPlane(new Vector3d(currentPosition), direction, previousPosition);
914                         if (dist < 0.0)
915                                 currentPosition.set(previousPosition);
916                         break;
917                 default:
918                         return false;
919                 }
920                 return true;
921         }
922         
923         @SuppressWarnings("unused")
924         private Vector3d getLockDir() {
925                 switch (lock) {
926                 case CUSTOM:
927                         return direction;
928                 case X:
929                         return new Vector3d(1,0,0);
930                 case Y:
931                         return new Vector3d(0,1,0);
932                 case Z:
933                         return new Vector3d(0,0,1);
934                 default:
935                         return null;
936                 }
937         }
938         
939         protected void updateRoute(Vector3d o, Vector3d d) {
940                 detector.clearConstraintHighlights();
941                 Point3d previousPipePoint = new Point3d(previousPosition);
942                 String s = "";
943                 if (lock == LockType.NONE) {
944                         Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint));
945                         if (p != null)
946                                 currentPosition = new Vector3d(p);
947                         s += detector.getSnapString();
948
949                 } else {
950                         Vector3d dir = new Vector3d(currentPosition);
951                         dir.sub(previousPipePoint);
952                         Point3d p = detector.getPointSnap(new Vector3d(previousPipePoint), dir);
953                         if (p != null)
954                                 currentPosition = new Vector3d(p);
955                         s += detector.getSnapString();
956
957                 }
958 //              System.out.println(previousPosition + " -> " + currentPosition);
959 //              double dist = MathTools.distance(previousPosition, currentPosition);
960 //              if (dist < pipeRun.getTurnRadius()) {
961 //                      s += "Too close";
962 //                      Vector3d v = new Vector3d(currentPosition);
963 //                      v.sub(previousPosition);
964 //                      double vl = v.length();
965 //                      if (vl > MathTools.NEAR_ZERO) {
966 //                              v.scale(1.0/vl);
967 //                      } else {
968 //
969 //                              return;
970 //                      }
971 //                      v.scale(pipeRun.getTurnRadius());
972 //                      v.add(previousPosition);
973 //                      currentPosition.set(v);
974 //              }
975                 
976                 updateCurrentPoint();
977                 s += currentPosition.toString();
978                 setInfoText(s);
979         }
980         
981         vtkTextActor infoActor;
982         
983         private void setInfoText(String text) {
984                 //System.out.println(text);
985                 if (infoActor == null) {
986                         infoActor = new vtkTextActor();
987                         infoActor.GetTextProperty().SetColor(0.0, 0.0, 0.0);
988                         infoActor.GetTextProperty().ShadowOff();
989                         infoActor.GetTextProperty().ItalicOff();
990                         infoActor.GetTextProperty().BoldOff();
991                         infoActor.GetTextProperty().SetFontSize(18);
992                         infoActor.GetTextProperty().Delete();
993                         infoActor.GetProperty().SetColor(0.0, 0.0, 0.0);
994                         infoActor.GetProperty().Delete();
995
996                         
997                         infoActor.SetPosition(10,10);
998                         panel.getRenderer().AddActor(infoActor);
999                 }
1000                 infoActor.SetInput(text);
1001         }
1002         
1003         private boolean endingToNozzle(INode nozzleNode,Vector3d o, Vector3d d) {
1004                 Nozzle nozzle = (Nozzle)nozzleNode;
1005                 PipeControlPoint pcp  =nozzle.getControlPoint();
1006                 if (pcp != null && (pcp.getNext() != null ||
1007                                                         pcp.getPrevious() != null))
1008                         return false; // nozzle is already connected to pipe
1009                 currentPosition = pcp.getWorldPosition();
1010                 Point3d previousPipePoint = new Point3d(previousPosition);
1011                 Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint));
1012                 if (p != null) {
1013                         if (MathTools.distance(p, currentPosition) > NOZZLE_SNAP_DISTANCE) {
1014                                 return false;
1015                         }
1016                 } 
1017                 
1018                 updateCurrentPoint();
1019                 
1020                 setInfoText("Connect to nozzle " + currentPosition);
1021                 return true;
1022         
1023         }
1024         
1025         private PositionType endingToStraight(INode straightNode, double mu[], Vector3d o, Vector3d d) {
1026 //          if (!allowBranches) {
1027 //              updateCurrentPoint();
1028 //            return null;
1029 //          }
1030                 InlineComponent s = (InlineComponent)straightNode;
1031                 
1032                 Point3d sStart = new Point3d();
1033                 Point3d sEnd = new Point3d();
1034                 s.getEnds(sStart, sEnd);
1035                 //detector.clearConstraintHighlights();
1036                 
1037                 Point3d previousPipePoint = new Point3d(previousPosition);
1038                 Point3d currentPipePoint = new Point3d(currentPosition);
1039                 //String st = "";
1040                 if (lock == LockType.NONE) {
1041                         Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint));
1042                         if (p != null) {
1043                                 currentPosition = new Vector3d(p);
1044                                 // snapping is detected, check if snapped point can create branch with straight
1045                                 PositionType t = endingLockToStraight(s, mu, currentPipePoint);
1046                                 if (t != null)
1047                                         return t;
1048                                 // if not, we'll have to remove highlight that was added when snapped point was detected
1049                                 detector.clearConstraintHighlights();   
1050                         }
1051                         
1052                         Vector3d sDir = new Vector3d(sEnd);
1053                         sDir.sub(sStart);
1054                         MathTools.intersectStraightStraight(sStart, sDir, o, d, currentPosition, new Point3d(), mu);
1055                         
1056
1057                 } else {
1058                         throw new RuntimeException("Lock shouldn't be on");
1059
1060                 }
1061                 
1062                 updateCurrentPoint();
1063                 
1064                 // branch point must lie between straight's ends. If connection point is exactly
1065                 // on straight end user may want to connect pipes to each other
1066                 // TODO : take account sizes of inline components)
1067                 return endingToStraight(mu, s, sStart, sEnd, currentPipePoint);
1068         }
1069         
1070         private PositionType endingToStraight(double mu[], InlineComponent s, Point3d sStart, Point3d sEnd , Point3d currentPipePoint) {
1071             String info = "";
1072             
1073             boolean connectPrev = false;
1074         boolean connectNext = false;
1075         boolean branch = false;
1076         if (mu[0] < 0.1) {
1077             connectPrev = true;
1078         }
1079         else if (mu[0] > 0.9) {
1080             connectNext = true;
1081         }
1082
1083           
1084         if (connectPrev) {
1085             PipeControlPoint pcp = s.getControlPoint();
1086             if (pcp.getPrevious() != null)
1087                 connectPrev = false;
1088         } else if (connectNext) {
1089             PipeControlPoint pcp = s.getControlPoint();
1090             if (pcp.getNext() != null)
1091                 connectNext = false;
1092         } else {
1093             Vector3d dir = s.getControlPoint().getPathLegDirection(Direction.NEXT);
1094             Vector3d currDir = getLast().getControlPoint().getPathLegDirection(Direction.NEXT);
1095             dir.normalize();
1096             currDir.normalize();
1097             double dot = dir.dot(currDir);
1098             System.out.println(dot + " " + currDir + " " + dir);
1099             if (dot > BRANCH_DOT_PRODUCT || dot < -BRANCH_DOT_PRODUCT) {
1100                 // pipes are almost in the same direction, creating a branch is not feasible.
1101                 branch = false;
1102             } else {
1103                 branch = true;
1104             }
1105         }
1106         
1107             
1108         if (connectNext || connectPrev)
1109             info += "Connect pipes :";
1110         else if (branch)
1111             info += "Create a Branch :";
1112         
1113         setInfoText(info + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0)));
1114         if (connectNext) {
1115             currentPosition.set(sEnd);
1116             updateCurrentPoint();
1117             return PositionType.NEXT;
1118         } else if (connectPrev){
1119             currentPosition.set(sStart);
1120             updateCurrentPoint();
1121             return PositionType.PREVIOUS;
1122         } else if (branch && allowBranches) {
1123             return PositionType.SPLIT;
1124         } else {
1125             currentPosition.set(currentPipePoint);
1126             updateCurrentPoint();
1127             return null;
1128         }
1129         }
1130         
1131         private PipeControlPoint endingToComponent(INode componentNode, Vector3d o, Vector3d d) {
1132                 PipelineComponent component = (PipelineComponent)componentNode;
1133                 PipeControlPoint pcp = component.getControlPoint();
1134                 PipeControlPoint connect = null;
1135                 if (component instanceof EndComponent) {
1136                         if (pcp.getNext() != null || pcp.getPrevious() != null)
1137                                 return null;
1138                         connect =  pcp;
1139                 } else if (component instanceof TurnComponent) {
1140                         if (pcp.getNext() == null || pcp.getPrevious() == null)
1141                                 connect = pcp;
1142                         else
1143                             return null;
1144                 } else if (component instanceof InlineComponent) {
1145                         // TODO : scan all empty pcps of the component and select closest one.
1146                         if (pcp.getNext() == null || pcp.getPrevious() == null)
1147                                 connect =  pcp;
1148                         else
1149                             return null;
1150                 }
1151                 if (connect != null) {
1152                     currentPosition.set(connect.getWorldPosition());
1153             updateCurrentPoint();
1154                     setInfoText("Connect to " + component.getName());
1155                     return connect;
1156                 }
1157                 return null;
1158         }
1159         
1160         private PositionType endingLockToStraight(INode straightNode, double mu[], Point3d currentPipePoint) {
1161 //          if (!allowBranches) {
1162 //            updateCurrentPoint();
1163 //            return null;
1164 //        }
1165                 InlineComponent s = (InlineComponent)straightNode;
1166                 Point3d sStart = new Point3d();
1167                 Point3d sEnd = new Point3d(); 
1168                 s.getControlPoint().getInlineControlPointEnds(sStart, sEnd);  
1169                 Vector3d sDir = new Vector3d(sEnd);
1170                 sDir.sub(sStart);
1171                 Vector3d dir = new Vector3d(currentPosition);
1172                 Point3d prev = new Point3d(previousPosition);
1173                 dir.sub(prev);
1174                 // intersection point in pipe where branch would be inserted to
1175                 Vector3d branchPoint = new Vector3d();
1176                 // intersection point in straight pipe that is currently routed
1177                 Vector3d routePoint = new Vector3d();
1178                 MathTools.intersectStraightStraight(sStart, sDir, new Vector3d(prev), dir, branchPoint, routePoint, mu);
1179                 routePoint.sub(branchPoint);
1180                 // startPoint of branch must be between pipe ends
1181                 // TODO : take account sizes of elbows (or other components)
1182                 // branch point must be between pipe ends and intersection points must be quite close to each other
1183                 
1184                 if (routePoint.lengthSquared() > BRANCH_SNAP_DISTANCE)
1185                     return null;
1186                 
1187                 return endingToStraight(mu, s, sStart, sEnd, currentPipePoint);
1188                 
1189 //              if (mu[0] > 0.0 && mu[0] < 1.0 && routePoint.lengthSquared() < BRANCH_SNAP_DISTANCE) {
1190 //                      currentPosition.set(branchPoint);
1191 //                      
1192 //                      updateCurrentPoint();
1193 //                      
1194 //                      setInfoText("Create a branch (l) :" + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0)) + " " + routePoint.lengthSquared());
1195 //                      return PositionType.SPLIT;
1196 //              }
1197 //              return null;
1198         }
1199         
1200         private boolean endingLockToNozzle(INode nozzleNode) {
1201                 Nozzle nozzle = (Nozzle)nozzleNode;
1202                 Vector3d dir = new Vector3d(currentPosition);
1203                 Point3d prev = new Point3d(previousPosition);
1204                 dir.sub(prev);
1205                 Vector3d nozzleLoc = nozzle.getWorldPosition();
1206                 double u[] = new double[1];
1207                 Vector3d closest = MathTools.closestPointOnStraight(new Point3d(nozzleLoc), new Point3d(prev), new Vector3d(dir), u);
1208                 double dist = MathTools.distanceSquared(nozzleLoc,closest);
1209                 if (dist < BRANCH_SNAP_DISTANCE) {
1210                         // FIXME : directions should be checked (insert an elbow)
1211                         currentPosition.set(nozzleLoc);
1212                         updateCurrentPoint();
1213                         setInfoText("Connect to nozzle (l) :" + currentPosition);
1214                         return true;
1215                 } 
1216                 //System.out.println(u[0]);
1217                 return false;
1218         }
1219         
1220         private PipeControlPoint endingLockToComponent(INode componentNode) {
1221                 // we'll must scan all free pcp's and their direction to accept the connection.
1222                 return null;
1223         }
1224         
1225         protected void addTurn() throws Exception{
1226             InlineComponent previous = (InlineComponent)getLast();
1227         PipeControlPoint previousCP = previous.getControlPoint();
1228         TurnComponent turn = ComponentUtils.createTurn(root);
1229         PipeControlPoint turnCP = turn.getControlPoint();
1230         turn.setName(pipeRun.getUniqueName("Elbow"));
1231         pipeRun.addChild(turn);
1232         added.add(turn);
1233         turnCP.setDeletable(false); // mark turnCP nonDeletable so that PipingRules won't delete it immediately.
1234         if (!reversed) {
1235             previousCP.setNext(turnCP);
1236             turnCP.setPrevious(previousCP);
1237         } else {
1238             previousCP.setPrevious(turnCP);
1239             turnCP.setNext(previousCP);
1240         }
1241         
1242         turnCP.setWorldPosition(currentPosition);
1243         turnCP.setTurnAngle(0.0);
1244         turnCP.setLength(0.0);
1245         
1246         }
1247         
1248         protected void addStraight() throws Exception{
1249         TurnComponent turn = (TurnComponent)getLast();
1250         PipeControlPoint turnCP = turn.getControlPoint();
1251         
1252         InlineComponent straight = ComponentUtils.createStraight(root);
1253         
1254         PipeControlPoint straightCP = straight.getControlPoint();
1255         straight.setName(pipeRun.getUniqueName("Pipe"));
1256         
1257         
1258         pipeRun.addChild(straight);
1259         
1260         added.add(straight);
1261         
1262         
1263         
1264         if (!reversed) {
1265             turnCP.setNext(straightCP);
1266             straightCP.setPrevious(turnCP);
1267         } else {
1268             turnCP.setPrevious(straightCP);
1269             straightCP.setNext(turnCP);
1270         }
1271         
1272         turnCP.setWorldPosition(currentPosition);
1273         turnCP.setTurnAngle(0.0);
1274         turnCP.setLength(0.0);
1275         straightCP.setWorldPosition(currentPosition);
1276         straightCP.setLength(0.0);
1277             
1278         }
1279         
1280         protected void addPoint() throws Exception {
1281                 addTurn();
1282                 addStraight();
1283                 setPreviousPosition(currentPosition);
1284                 updateCurrentPoint();
1285                 
1286                 
1287                 
1288         }
1289         
1290         /**
1291          * Updates tool graphics for current point 
1292          */
1293         protected void updateCurrentPoint() {
1294                 InlineComponent straight = (InlineComponent)added.get(added.size()-1);
1295                 // TODO: the inline length is from previous update step.
1296                 double l;
1297                 if (!reversed)
1298                         l = straight.getPrevious().getControlPoint().getInlineLength();
1299                 else
1300                         l = straight.getNext().getControlPoint().getInlineLength();
1301                 Vector3d v = new Vector3d();
1302                 v.sub(currentPosition, previousPosition);
1303                 double length = v.length();
1304                 if (length > MathTools.NEAR_ZERO) {
1305                         v.scale(1.0/length);
1306                         v.scale(0.5*(length+l));
1307                         v.add(previousPosition);
1308                         straight.getControlPoint().setWorldPosition(v);
1309                         straight.getControlPoint().setLength(length);
1310                 }
1311                 try {
1312                         PipingRules.positionUpdate(straight.getControlPoint(),false);
1313                 } catch (Exception e) {
1314                         // TODO Auto-generated catch block
1315                         e.printStackTrace();
1316                 }
1317         }
1318         
1319         private PipelineComponent getLast() {
1320                 if (added.size() == 0)
1321                         return startComponent;
1322                 return added.get(added.size()-1);
1323         }
1324         
1325         
1326         /**
1327          * Removes last point from pipeline
1328          */
1329         public void removePoint() {
1330                 if (added.size() < 3)
1331                         return;
1332                 InlineComponent straight = (InlineComponent)added.remove(added.size()-1);
1333                 TurnComponent turn = (TurnComponent)added.remove(added.size()-1);
1334                 straight.getControlPoint().remove();
1335                 turn.getControlPoint().remove();
1336                 if (added.size() > 1) {
1337                         setPreviousPosition(added.get(added.size()-2).getWorldPosition());
1338                 } else {
1339                         setPreviousPosition(startComponent.getWorldPosition());
1340                         if (direction != null)
1341                                 setLockType(LockType.CUSTOM, true);
1342                 }
1343                 
1344         }
1345         
1346         protected void endPiping() throws Exception {
1347                 state = ToolState.NOT_ACTIVE;
1348                 
1349                 if (endTo != null) {
1350                     if (endType == PositionType.NEXT || endType == PositionType.PREVIOUS && endTo instanceof InlineComponent) {
1351                         Vector3d dir = endTo.getControlPoint().getPathLegDirection(Direction.NEXT);
1352                     Vector3d currDir = getLast().getControlPoint().getPathLegDirection(Direction.NEXT);
1353                     dir.normalize();
1354                     currDir.normalize();
1355                     double dot = dir.dot(currDir);
1356                     System.out.println(dot + " " + currDir + " " + dir);
1357                     if (dot < ALIGN_DOT_PRODUCT && dot> -ALIGN_DOT_PRODUCT) {
1358                         addTurn();    
1359                     }
1360                         
1361                     }
1362                         ComponentUtils.connect(getLast(), endTo, endType, currentPosition);
1363                 }
1364                 panel.useDefaultAction();
1365         }
1366 }