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