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