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