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