]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.plant3d/src/org/simantics/plant3d/actions/RoutePipeAction.java
Highlight constrain points
[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                 nodeMap.commit("Route pipe");
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                 for (PipelineComponent component : added) {
426                         component.getControlPoint().setDeletable(true);
427                 }
428                 
429                 added.clear();
430                 startComponent.getControlPoint().setDeletable(startRemovable);
431
432                 direction = null;
433
434                 setLockType(LockType.NONE, true);
435                 startComponent = null;
436                 endTo = null;
437                 endPort = null;
438                 endType = null;
439                 pipeRun = null;
440                 allowed.clear();
441                 currentPosition = null;
442                 previousPosition = null;
443                 startRemovable = false;
444                 detector.clearConstraintHighlights();
445                 state = ToolState.NOT_ACTIVE;
446                 setEnabled(false);              
447
448         }
449         
450         private void setLockType(LockType type, boolean force) {
451                 if (force || lock != LockType.CUSTOM) {
452                         lock = type;
453                         
454                         switch (lock) {
455                         case CUSTOM:
456                         case NONE:
457                                 translateAxisGizmo.setType(6);
458                                 break;
459                         case X:
460                                 translateAxisGizmo.setType(0);
461                                 break;
462                         case Y:
463                                 translateAxisGizmo.setType(1);
464                                 break;
465                         case Z:
466                                 translateAxisGizmo.setType(2);
467                                 break;
468                         case XY:
469                                 translateAxisGizmo.setType(3);
470                                 break;
471                         case XZ:
472                                 translateAxisGizmo.setType(4);
473                                 break;
474                         case YZ:
475                                 translateAxisGizmo.setType(5);
476                                 break;
477                                 
478                         }
479                 }
480         }
481         
482         @Override
483         public boolean mousePressed(MouseEvent e) {
484                 if (useDefault) {
485                         getDefaultAction().mousePressed(e);
486                 }
487                 return true;
488         }
489
490         @Override
491         public boolean mouseReleased(MouseEvent e) {
492                 if (useDefault) {
493                         getDefaultAction().mouseReleased(e);
494                 }
495                 return true;
496         }
497         
498         @Override
499         public boolean mouseWheelMoved(MouseWheelEvent e) {
500                 if (useDefault) {
501                         getDefaultAction().mouseWheelMoved(e);
502                 }
503                 return true;
504         }
505         
506         @Override
507         public boolean mouseClicked(MouseEvent e) {
508                 if (useDefault) {
509                         getDefaultAction().mouseClicked(e);
510                         return true;
511                 }
512                 if (state == ToolState.ROUTING) {
513                         try {
514                                 if (e.getClickCount() == 1) {
515                                         if (e.getButton() == MouseEvent.BUTTON1) {
516                                                 if (this.added.size() > 0) {
517                                                         
518                                                         setLockType(LockType.NONE,true);
519                                                         if (endTo != null) {
520                                                                 
521                                                                 endPiping();
522                                                         } else {
523                                                                 addPoint();
524                                                         }
525                                                 } else {
526                                                         throw new RuntimeException("kjf");
527                 //                      // user was selecting position of branch
528                 //                    lastPoint.set(startPoint);
529                 //                    controlPoints.add(new Point3d(startPoint));
530                 //                    if (selectionLine != null)
531                 //                      selectionLine.removeFromParent();
532                 //                    selectionLine = null;
533                                                 }
534                                         } else if (e.getButton() ==MouseEvent.BUTTON2){
535                                             updateConstraints();
536                                         } else if (e.getButton() == MouseEvent.BUTTON3){      
537                                                 endPiping();
538                                         }
539                                 }
540                         } catch(Exception err) {
541                                 err.printStackTrace();
542                         }
543                 } else if (state == ToolState.SELECTING_POSITION) {
544                         if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) {
545                                 int type = panel.getPickType();
546                                 //panel.setPickType(0);
547                                 panel.setPickType(5);
548                                 vtkProp[] picked = panel.pick(e.getX(), e.getY());
549                                 panel.setPickType(type);
550                                 PositionType position = terminalSelectionGizmo.getPickedPosition(picked);
551                                 if (position != null) {
552                                         terminalSelectionGizmo.deattach();
553                                         try {
554                                         if (position == PositionType.SPLIT) {
555                                                 activateSplit(startComponent.getControlPoint());
556                                         } else if (position == PositionType.NEXT || position == PositionType.PREVIOUS) {
557                                                 reversed = position == PositionType.PREVIOUS;
558                                                 activateNextPrev(startComponent.getControlPoint());
559                                         } else {
560                                                 panel.useDefaultAction();
561                                         }
562                                         } catch (Exception err) {
563                                                 ExceptionUtils.logAndShowError(err);
564                                                 panel.useDefaultAction();
565                                         }
566                                 }
567                         } 
568                 } else if (state == ToolState.SELECTING_SPLIT) {
569                         if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) {
570                                 Tuple3d t = splitPointSelectionGizmo.getSplitPoint();
571                                 splitPointSelectionGizmo.deattach();
572                                 if (t == null) {
573                                         panel.useDefaultAction();
574                                         return true;
575                                 }
576                                 try {
577                                         Vector3d pos = new Vector3d(t);
578                                         InlineComponent branchSplit = ComponentUtils.createBranchSplit((InlineComponent)startComponent, pos);
579                                         PipeControlPoint branchSplitCP = branchSplit.getControlPoint();
580                                         reversed = false;
581                                         PipeRun newRun = new PipeRun();
582                                         String n = root.getUniqueName("PipeRun");
583                                         newRun.setName(n);
584                                         root.addChild(newRun);
585                                         PipeControlPoint pcp = new PipeControlPoint(branchSplit,newRun);
586                                         branchSplitCP.children.add(pcp);
587                                         pcp.parent = branchSplitCP;
588                                         pcp.setWorldOrientation(branchSplitCP.getWorldOrientation());
589                                         pcp.setWorldPosition(branchSplitCP.getWorldPosition());
590                                         startComponent = branchSplit;
591                                         activateBranch(pcp);
592                                 } catch (Exception err) {
593                                         ExceptionUtils.logAndShowError(err);
594                                         panel.useDefaultAction();
595                                 }
596                         }
597                 }
598                 return true;
599         }
600         
601         private void updateConstraints() {
602             detector.clearConstraints();
603             constraintPointGizmo.clearPositions();
604             if (hoverObject == null) {
605                 if (constraintPointGizmo.isAttached())
606                     constraintPointGizmo.deattach();
607                 return;
608             }
609             if (hoverObject instanceof Nozzle) {
610             Nozzle n = (Nozzle)hoverObject;
611             detector.addContraintPoint(new Point3d(n.getWorldPosition()));
612             } else if (hoverObject instanceof InlineComponent) {
613                 InlineComponent c = (InlineComponent)hoverObject;
614                 Point3d p1 = new Point3d();
615                 Point3d p2 = new Point3d();
616                 c.getEnds(p1, p2);
617                 detector.addContraintPoint(p1);
618                 detector.addContraintPoint(p2);
619                 detector.addContraintPoint(new Point3d(c.getWorldPosition()));
620             } else if (hoverObject instanceof TurnComponent) {
621                 TurnComponent n = (TurnComponent)hoverObject;
622             detector.addContraintPoint(new Point3d(n.getWorldPosition()));
623             }
624             if (detector.getConstraintPoints().size() > 0) {
625                 for (Point3d p : detector.getConstraintPoints()) {
626                     constraintPointGizmo.addPosition(new Vector3d(p));
627                 }
628                 if (constraintPointGizmo.isAttached())
629                     constraintPointGizmo.deattach();
630                 constraintPointGizmo.attach(panel);
631             }
632         }
633         
634         @Override
635         public boolean mouseMoved(MouseEvent e) {
636                 if (useDefault) {
637                         getDefaultAction().mouseMoved(e);
638                         return true;
639                 }
640                 step = ((e.getModifiers() & MouseEvent.CTRL_MASK) > 0);
641                 update(e.getX(), e.getY());
642                 return true;
643         }
644         
645         @Override
646         public boolean mouseDragged(MouseEvent e) {
647                 if (useDefault)
648                         getDefaultAction().mouseDragged(e);
649                 return true;
650         }
651         
652
653         
654         
655         private List<INode> isOverNode(int x, int y) {
656                 List<INode> nodes = new ArrayList<INode>(); 
657                 vtkProp picked[] = panel.pick2(x, y);
658                 if (picked !=null) {
659                         for (int i = 0; i < picked.length; i++) {
660                                 nodes.add(nodeMap.getNode(picked[i]));
661                         }
662                 }
663                 return nodes;
664         }
665         
666         INode hoverObject = null;
667
668         private void updateRouting(double x, double y) {
669 //              if(input.keyPressed(KeyEvent.VK_ESCAPE)) {
670 //                      controlPoints.clear();
671 //                      end();
672 //                      return;
673 //              }
674 //              if (input.keyPressed(KeyEvent.VK_C)) {
675 //                      useCamera = !useCamera;
676 //                      cameraAction.setChecked(useCamera);
677 //              }
678                 if (useDefault) {
679                         //panel.getDefaultAction().update();
680                         return;
681                 }
682                 
683                 endTo = null;
684                 endType = null;
685                 endPort = null;
686
687                 Ray ray = vtkUtil.createMouseRay(panel.getRenderer(),x, y);
688                 Vector3d o = new Vector3d(ray.pos);
689                 Vector3d d = ray.dir;
690                 
691                 
692                 if (!updateCurrentPoint(o, d))
693                         return;
694                 //Point3d startPoint = new Point3d();
695                 double mu[] = new double[2];
696                 
697                 
698                 
699                 
700                 
701                 List<INode> hover = isOverNode((int)x,(int)y);
702                 if (hover.size() > 0) {
703                         hoverObject = hover.get(0);
704                 } else {
705                     hoverObject = null;
706                 }
707 //              System.out.println(hoverObject + " " + getLast());
708                 if (hoverObject != null) {
709                         if (hoverObject.equals(getLast()) ) {
710                                 boolean set = false;
711                                 for (int i = 1; i < hover.size(); i++) {
712                                         hoverObject = hover.get(i);
713                                         if (!getLast().equals(hoverObject)) {
714                                                 set = true;
715                                                 break;
716                                         }
717                                 }
718                                 if (!set)
719                                         hoverObject = null;
720                         }
721                 }
722 //              System.out.println(hoverObject);
723                 if (hoverObject != null) {
724
725                         if (lock == LockType.NONE) {
726                                 if (hoverObject instanceof Nozzle && endingToNozzle(hoverObject,o,d)) {
727                                         endTo = (Nozzle)hoverObject;
728                                 } else if (hoverObject instanceof InlineComponent && ((InlineComponent)hoverObject).isVariableLength()) {
729                                         endTo = (InlineComponent)hoverObject;
730                                         endType = endingToStraight(endTo,mu,o,d);     
731                                         if (endType == null)
732                                             endTo = null;
733                                 } else if (hoverObject instanceof PipelineComponent && (endPort = endingToComponent(hoverObject,o,d)) != null) {
734                                         endTo = (PipelineComponent)hoverObject;
735                                 } else {
736                                         updateRoute(o,d); 
737                                 }
738                         } else {  
739                                 if (hoverObject instanceof InlineComponent && ((InlineComponent)hoverObject).isVariableLength() && (endType = endingLockToStraight(hoverObject,mu)) != null) {
740                                         endTo = (InlineComponent)hoverObject;
741                                 } else if (hoverObject instanceof Nozzle && endingLockToNozzle(hoverObject)) {
742                                         endTo = (Nozzle)hoverObject;
743                                 } else if ((hoverObject instanceof PipelineComponent) &&  ((endPort = endingLockToComponent(hoverObject)) != null)) {
744                                         endTo = (PipelineComponent)hoverObject;
745                                 } else {
746                                         updateRoute(o,d);
747                                 }
748                         }
749                         if (added.contains(endTo))
750                                 endTo = null;
751
752                 } else {
753                         updateRoute(o,d);
754                 }
755                 
756                 panel.refresh();
757         }
758         
759         private boolean updateCurrentPoint(Vector3d o, Vector3d d) {
760
761                 Vector3d point = new Vector3d(this.previousPosition);
762                 
763                 switch(lock) {
764                 case X:
765                         MathTools.intersectStraightStraight(point, new Vector3d(1.0,0.0,0.0), o,d, currentPosition, new Vector3d());
766                         if (step) {
767                                 currentPosition.x = Math.round(istep * currentPosition.x) / istep;
768                                 BigDecimal bx = new BigDecimal(currentPosition.x);
769                                 bx.setScale(decimals, BigDecimal.ROUND_HALF_UP);
770                                 currentPosition.x = bx.doubleValue();
771                         }
772                         break;
773                 case Y:
774                         MathTools.intersectStraightStraight(point, new Vector3d(0.0,1.0,0.0), o,d, currentPosition, new Vector3d());
775                         if (step) {
776                                 currentPosition.y = Math.round(istep * currentPosition.y) / istep;
777                                 BigDecimal bx = new BigDecimal(currentPosition.y);
778                                 bx.setScale(decimals, BigDecimal.ROUND_HALF_UP);
779                                 currentPosition.y = bx.doubleValue();
780                         }
781                         break;
782                 case Z:
783                         MathTools.intersectStraightStraight(point, new Vector3d(0.0,0.0,1.0), o,d, currentPosition, new Vector3d());
784                         if (step) {
785                                 currentPosition.z = Math.round(istep * currentPosition.z) / istep;
786                                 BigDecimal bx = new BigDecimal(currentPosition.z);
787                                 bx.setScale(decimals, BigDecimal.ROUND_HALF_UP);
788                                 currentPosition.z = bx.doubleValue();
789                         }break;
790                 case XY:
791                         MathTools.intersectStraightPlane(o, d, point, new Vector3d(0.0,0.0,1.0), currentPosition);
792                         break;
793                 case XZ:
794                         MathTools.intersectStraightPlane(o, d, point, new Vector3d(0.0,1.0,0.0), currentPosition);
795                         break;
796                 case YZ:
797                         MathTools.intersectStraightPlane(o, d, point, new Vector3d(1.0,0.0,0.0), currentPosition);
798                         break;
799                 case NONE:
800                         Vector3d normal = new Vector3d(panel.getRenderer().GetActiveCamera().GetDirectionOfProjection());
801                         normal.normalize();
802                         
803                         MathTools.intersectStraightPlane(o, d, point, normal, currentPosition);
804                         break;
805                 case CUSTOM:
806                         MathTools.intersectStraightStraight(point, new Vector3d(direction), o,d, currentPosition, new Vector3d());
807                         double dist = MathTools.distanceFromPlane(new Vector3d(currentPosition), direction, previousPosition);
808                         if (dist < 0.0)
809                                 currentPosition.set(previousPosition);
810                         break;
811                 default:
812                         return false;
813                 }
814                 return true;
815         }
816         
817         private Vector3d getLockDir() {
818                 switch (lock) {
819                 case CUSTOM:
820                         return direction;
821                 case X:
822                         return new Vector3d(1,0,0);
823                 case Y:
824                         return new Vector3d(0,1,0);
825                 case Z:
826                         return new Vector3d(0,0,1);
827                 }
828                 return null;
829         }
830         
831         private void updateRoute(Vector3d o, Vector3d d) {
832                 detector.clearConstraintHighlights();
833                 Point3d previousPipePoint = new Point3d(previousPosition);
834                 String s = "";
835                 if (lock == LockType.NONE) {
836                         Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint));
837                         if (p != null)
838                                 currentPosition = new Vector3d(p);
839                         s += detector.getSnapString();
840
841                 } else {
842                         Vector3d dir = new Vector3d(currentPosition);
843                         dir.sub(previousPipePoint);
844                         Point3d p = detector.getPointSnap(new Vector3d(previousPipePoint), dir);
845                         if (p != null)
846                                 currentPosition = new Vector3d(p);
847                         s += detector.getSnapString();
848
849                 }
850 //              System.out.println(previousPosition + " -> " + currentPosition);
851 //              double dist = MathTools.distance(previousPosition, currentPosition);
852 //              if (dist < pipeRun.getTurnRadius()) {
853 //                      s += "Too close";
854 //                      Vector3d v = new Vector3d(currentPosition);
855 //                      v.sub(previousPosition);
856 //                      double vl = v.length();
857 //                      if (vl > MathTools.NEAR_ZERO) {
858 //                              v.scale(1.0/vl);
859 //                      } else {
860 //
861 //                              return;
862 //                      }
863 //                      v.scale(pipeRun.getTurnRadius());
864 //                      v.add(previousPosition);
865 //                      currentPosition.set(v);
866 //              }
867                 
868                 updateCurrentPoint();
869                 s += currentPosition.toString();
870                 setInfoText(s);
871         }
872         
873         vtkTextActor infoActor;
874         
875         private void setInfoText(String text) {
876                 //System.out.println(text);
877                 if (infoActor == null) {
878                         infoActor = new vtkTextActor();
879                         infoActor.GetTextProperty().SetColor(0.0, 0.0, 0.0);
880                         infoActor.GetTextProperty().ShadowOff();
881                         infoActor.GetTextProperty().ItalicOff();
882                         infoActor.GetTextProperty().BoldOff();
883                         infoActor.GetTextProperty().SetFontSize(18);
884                         infoActor.GetTextProperty().Delete();
885                         infoActor.GetProperty().SetColor(0.0, 0.0, 0.0);
886                         infoActor.GetProperty().Delete();
887
888                         
889                         infoActor.SetPosition(10,10);
890                         panel.getRenderer().AddActor(infoActor);
891                 }
892                 infoActor.SetInput(text);
893         }
894         
895         private boolean endingToNozzle(INode nozzleNode,Vector3d o, Vector3d d) {
896                 Nozzle nozzle = (Nozzle)nozzleNode;
897                 PipeControlPoint pcp  =nozzle.getControlPoint();
898                 if (pcp != null && (pcp.getNext() != null ||
899                                                         pcp.getPrevious() != null))
900                         return false; // nozzle is already connected to pipe
901                 currentPosition = pcp.getWorldPosition();
902                 Point3d previousPipePoint = new Point3d(previousPosition);
903                 Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint));
904                 if (p != null) {
905                         if (MathTools.distance(p, currentPosition) > NOZZLE_SNAP_DISTANCE) {
906                                 return false;
907                         }
908                 } 
909                 
910                 updateCurrentPoint();
911                 
912                 setInfoText("Connect to nozzle " + currentPosition);
913                 return true;
914         
915         }
916         
917         private PositionType endingToStraight(INode straightNode, double mu[], Vector3d o, Vector3d d) {
918                 InlineComponent s = (InlineComponent)straightNode;
919                 String info = "";
920                 Point3d sStart = new Point3d();
921                 Point3d sEnd = new Point3d();
922                 s.getEnds(sStart, sEnd);
923                 //detector.clearConstraintHighlights();
924                 
925                 Point3d previousPipePoint = new Point3d(previousPosition);
926                 Point3d currentPipePoint = new Point3d(currentPosition);
927                 //String st = "";
928                 if (lock == LockType.NONE) {
929                         Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint));
930                         if (p != null) {
931                                 currentPosition = new Vector3d(p);
932                                 // snapping is detected, check if snapped point can create branch with straight
933                                 PositionType t = endingLockToStraight(s, mu);
934                                 if (t != null)
935                                         return t;
936                                 // if not, we'll have to remove highlight that was added when snapped point was detected
937                                 detector.clearConstraintHighlights();   
938                         }
939                         
940                         Vector3d sDir = new Vector3d(sEnd);
941                         sDir.sub(sStart);
942                         MathTools.intersectStraightStraight(sStart, sDir, o, d, currentPosition, new Point3d(), mu);
943                         
944
945                 } else {
946                         throw new RuntimeException("Lock shouldn't be on");
947
948                 }
949                 
950                 updateCurrentPoint();
951                 
952                 // branch point must lie between straight's ends. If connection point is exactly
953                 // on straight end user may want to connect pipes to each other
954                 // TODO : take account sizes of inline components)
955                 
956                 boolean connectPrev = false;
957                 boolean connectNext = false;
958                 boolean branch = false;
959                 if (mu[0] < 0.1) {
960                         connectPrev = true;
961                 }
962                 else if (mu[0] > 0.9) {
963                         connectNext = true;
964                 }
965
966                   
967                 if (connectPrev) {
968                         PipeControlPoint pcp = s.getControlPoint();
969                         if (pcp.getPrevious() != null)
970                                 connectPrev = false;
971                 } else if (connectNext) {
972                         PipeControlPoint pcp = s.getControlPoint();
973                         if (pcp.getNext() != null)
974                                 connectNext = false;
975                 } else {
976                     Vector3d dir = s.getControlPoint().getPathLegDirection(Direction.NEXT);
977                     Vector3d currDir = getLast().getControlPoint().getPathLegDirection(Direction.NEXT);
978                     dir.normalize();
979                     currDir.normalize();
980                     double dot = dir.dot(currDir);
981                     System.out.println(dot + " " + currDir + " " + dir);
982                     if (dot > 0.95 || dot < -0.95) {
983                         // pipes are almost in the same direction, creating a branch is not feasible.
984                         branch = false;
985                     } else {
986                         branch = true;
987                     }
988                 }
989                 
990                         
991                 if (connectNext || connectPrev)
992                         info += "Connect pipes :";
993                 else if (branch)
994                         info += "Create a Branch :";
995                 
996                 setInfoText(info + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0)));
997                 if (connectNext) {
998                     currentPosition.set(sEnd);
999                     updateCurrentPoint();
1000                         return PositionType.NEXT;
1001                 } else if (connectPrev){
1002                     currentPosition.set(sStart);
1003                     updateCurrentPoint();
1004                         return PositionType.PREVIOUS;
1005                 } else if (branch) {
1006                     return PositionType.SPLIT;
1007                 } else {
1008                     currentPosition.set(currentPipePoint);
1009                     updateCurrentPoint();
1010                     return null;
1011                 }
1012                         
1013         }
1014         
1015         private PipeControlPoint endingToComponent(INode componentNode, Vector3d o, Vector3d d) {
1016                 PipelineComponent component = (PipelineComponent)componentNode;
1017                 PipeControlPoint pcp = component.getControlPoint();
1018                 if (component instanceof EndComponent) {
1019                         if (pcp.getNext() != null || pcp.getPrevious() != null)
1020                                 return null;
1021                         return pcp;
1022                 } else if (component instanceof TurnComponent) {
1023                         if (pcp.getNext() == null || pcp.getPrevious() == null)
1024                                 return pcp;
1025                         return null;
1026                 } else if (component instanceof InlineComponent) {
1027                         // TODO : scan all empty pcps of the component and select closest one.
1028                         if (pcp.getNext() == null || pcp.getPrevious() == null)
1029                                 return pcp;
1030                         return null;
1031                 }
1032
1033                 return null;
1034         }
1035         
1036         private PositionType endingLockToStraight(INode straightNode, double mu[]) {
1037                 InlineComponent s = (InlineComponent)straightNode;
1038                 Point3d sStart = new Point3d();
1039                 Point3d sEnd = new Point3d(); 
1040                 s.getControlPoint().getInlineControlPointEnds(sStart, sEnd);  
1041                 Vector3d sDir = new Vector3d(sEnd);
1042                 sDir.sub(sStart);
1043                 Vector3d dir = new Vector3d(currentPosition);
1044                 Point3d prev = new Point3d(previousPosition);
1045                 dir.sub(prev);
1046                 // intersection point in pipe where branch would be inserted to
1047                 Vector3d branchPoint = new Vector3d();
1048                 // intersection point in straight pipe that is currently routed
1049                 Vector3d routePoint = new Vector3d();
1050                 MathTools.intersectStraightStraight(sStart, sDir, new Vector3d(prev), dir, branchPoint, routePoint, mu);
1051                 routePoint.sub(branchPoint);
1052                 // startPoint of branch must be between pipe ends
1053                 // TODO : take account sizes of elbows (or other components)
1054                 // branch point must be between pipe ends and intersection points must be quite close to each other
1055                 if (mu[0] > 0.0 && mu[0] < 1.0 && routePoint.lengthSquared() < BRANCH_SNAP_DISTANCE) {
1056                         currentPosition.set(branchPoint);
1057                         
1058                         updateCurrentPoint();
1059                         
1060                         setInfoText("Create a branch (l) :" + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0)) + " " + routePoint.lengthSquared());
1061                         return PositionType.SPLIT;
1062                 }
1063                 return null;
1064         }
1065         
1066         private boolean endingLockToNozzle(INode nozzleNode) {
1067                 Nozzle nozzle = (Nozzle)nozzleNode;
1068                 Vector3d dir = new Vector3d(currentPosition);
1069                 Point3d prev = new Point3d(previousPosition);
1070                 dir.sub(prev);
1071                 Vector3d nozzleLoc = nozzle.getWorldPosition();
1072                 double u[] = new double[1];
1073                 Vector3d closest = MathTools.closestPointOnStraight(new Point3d(nozzleLoc), new Point3d(prev), new Vector3d(dir), u);
1074                 double dist = MathTools.distanceSquared(nozzleLoc,closest);
1075                 if (dist < BRANCH_SNAP_DISTANCE) {
1076                         // FIXME : directions should be checked (insert an elbow)
1077                         currentPosition.set(nozzleLoc);
1078                         updateCurrentPoint();
1079                         setInfoText("Connect to nozzle (l) :" + currentPosition);
1080                         return true;
1081                 } 
1082                 //System.out.println(u[0]);
1083                 return false;
1084         }
1085         
1086         private PipeControlPoint endingLockToComponent(INode componentNode) {
1087                 // we'll must scan all free pcp's and their direction to accept the connection.
1088                 return null;
1089         }
1090         
1091         private void addPoint() throws Exception {
1092                 InlineComponent previous = (InlineComponent)getLast();
1093                 PipeControlPoint previousCP = previous.getControlPoint();
1094                 TurnComponent turn = ComponentUtils.createTurn(root);
1095                 InlineComponent straight = ComponentUtils.createStraight(root);
1096                 PipeControlPoint turnCP = turn.getControlPoint();
1097                 PipeControlPoint straightCP = straight.getControlPoint();
1098                 straight.setName(pipeRun.getUniqueName("Pipe"));
1099                 turn.setName(pipeRun.getUniqueName("Elbow"));
1100                 pipeRun.addChild(turn);
1101                 pipeRun.addChild(straight);
1102                 added.add(turn);
1103                 added.add(straight);
1104                 
1105                 turnCP.setDeletable(false); // mark turnCP nonDeletable so that PipingRules won't delete it immediately.
1106                 
1107                 if (!reversed) {
1108                         previousCP.setNext(turnCP);
1109                         turnCP.setPrevious(previousCP);
1110                         turnCP.setNext(straightCP);
1111                         straightCP.setPrevious(turnCP);
1112                 } else {
1113                         previousCP.setPrevious(turnCP);
1114                         turnCP.setNext(previousCP);
1115                         turnCP.setPrevious(straightCP);
1116                         straightCP.setNext(turnCP);
1117                 }
1118                 
1119                 turnCP.setWorldPosition(currentPosition);
1120                 turnCP.setTurnAngle(0.0);
1121                 turnCP.setLength(0.0);
1122                 straightCP.setWorldPosition(currentPosition);
1123                 straightCP.setLength(0.0);
1124                 
1125                 setPreviousPosition(currentPosition);
1126                 updateCurrentPoint();
1127                 
1128                 
1129                 
1130         }
1131         
1132         /**
1133          * Updates tool graphics for current point 
1134          */
1135         private void updateCurrentPoint() {
1136                 InlineComponent straight = (InlineComponent)added.get(added.size()-1);
1137                 // TODO: the inline length is from previous update step.
1138                 double l;
1139                 if (!reversed)
1140                         l = straight.getPrevious().getControlPoint().getInlineLength();
1141                 else
1142                         l = straight.getNext().getControlPoint().getInlineLength();
1143                 Vector3d v = new Vector3d();
1144                 v.sub(currentPosition, previousPosition);
1145                 double length = v.length();
1146                 if (length > MathTools.NEAR_ZERO) {
1147                         v.scale(1.0/length);
1148                         v.scale(0.5*(length+l));
1149                         v.add(previousPosition);
1150                         straight.getControlPoint().setWorldPosition(v);
1151                         straight.getControlPoint().setLength(length);
1152                 }
1153                 try {
1154                         PipingRules.positionUpdate(straight.getControlPoint(),false);
1155                 } catch (Exception e) {
1156                         // TODO Auto-generated catch block
1157                         e.printStackTrace();
1158                 }
1159         }
1160         
1161         private PipelineComponent getLast() {
1162                 if (added.size() == 0)
1163                         return startComponent;
1164                 return added.get(added.size()-1);
1165         }
1166         
1167         
1168         /**
1169          * Removes last point from pipeline
1170          */
1171         public void removePoint() {
1172                 if (added.size() < 3)
1173                         return;
1174                 InlineComponent straight = (InlineComponent)added.remove(added.size()-1);
1175                 TurnComponent turn = (TurnComponent)added.remove(added.size()-1);
1176                 straight.getControlPoint().remove();
1177                 turn.getControlPoint().remove();
1178                 if (added.size() > 1) {
1179                         setPreviousPosition(added.get(added.size()-2).getWorldPosition());
1180                 } else {
1181                         setPreviousPosition(startComponent.getWorldPosition());
1182                         if (direction != null)
1183                                 setLockType(LockType.CUSTOM, true);
1184                 }
1185                 
1186         }
1187         
1188         private void endPiping() throws Exception {
1189                 state = ToolState.NOT_ACTIVE;
1190                 
1191                 if (endTo != null) {
1192                         ComponentUtils.connect(getLast(), endTo, endType, currentPosition);
1193                 }
1194                 panel.useDefaultAction();
1195         }
1196 }