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