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