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