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