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