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