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