1 package org.simantics.plant3d.actions;
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;
11 import javax.vecmath.Point3d;
12 import javax.vecmath.Tuple3d;
13 import javax.vecmath.Vector3d;
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;
46 import vtk.vtkTextActor;
48 public class RoutePipeAction extends vtkAction {
50 X, Y, Z, XY, YZ, XZ, NONE, CUSTOM
53 LockType lock = LockType.NONE;
54 private double BRANCH_SNAP_DISTANCE = 0.05;
55 private double NOZZLE_SNAP_DISTANCE = 0.05;
57 private double istep = 10.0;
58 private int decimals = 2;
60 private P3DRootNode root;
61 private PipelineComponent startComponent;
62 private PipeRun pipeRun;
64 private TranslateAxisGizmo translateAxisGizmo = new TranslateAxisGizmo();
65 private SplitPointSelectionGizmo splitPointSelectionGizmo;
66 private TerminalSelectionGizmo terminalSelectionGizmo;
67 private NodeMap<vtkProp,INode> nodeMap;
69 private enum ToolState{NOT_ACTIVE, INITIALIZING, SELECTING_POSITION, SELECTING_SPLIT, ROUTING};
70 private ToolState state = ToolState.NOT_ACTIVE;
72 private ConstraintDetector detector = new DummyConstraintDetector();
74 private boolean useDefault = false;
75 private Vector3d direction = null;
76 private Vector3d previousPosition = null;
77 private Vector3d currentPosition = null;
81 PipelineComponent endTo = null;
82 PositionType endType = null;
83 PipeControlPoint endPort = null;
85 boolean reversed = false;
87 private Set<PositionType> allowed = new HashSet<PositionType>();
90 public RoutePipeAction(InteractiveVtkPanel panel, P3DRootNode 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);
100 public void setComponent(PipelineComponent component) {
101 this.startComponent = component;
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);
112 public void deattach() {
114 startComponent = null;
121 public void attach() {
122 if (startComponent == null)
126 ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {
131 } catch (Exception e) {
133 ExceptionUtils.logAndShowError(e);
141 // private void attachUI() {
142 // //panel.setCursor(activeCursor);
143 // translateAxisGizmo.attach(panel.GetRenderer());
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);
161 private List<PipelineComponent> added = new ArrayList<PipelineComponent>();
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);
175 setLockType(LockType.NONE, false);
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);
184 setLockType(LockType.NONE, false);
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);
193 setLockType(LockType.NONE, false);
197 if (e.getKeyCode() == KeyEvent.VK_X) {
198 if (lock != LockType.X)
199 setLockType(LockType.X,false);
201 setLockType(LockType.NONE,false);
203 if (e.getKeyCode() == KeyEvent.VK_Y) {
204 if (lock != LockType.Y)
205 setLockType(LockType.Y,false);
207 setLockType(LockType.NONE, false);
209 if (e.getKeyCode() == KeyEvent.VK_Z) {
210 if (lock != LockType.Z)
211 setLockType(LockType.Z, false);
213 setLockType(LockType.NONE, false);
217 if (e.getKeyCode() == KeyEvent.VK_C) {
218 useDefault = !useDefault;
219 System.out.println("UseDefault " + useDefault);
230 private void update() {
233 private void update(double x, double y) {
236 return; // TODO : throw Exception?
239 case SELECTING_POSITION:
241 case SELECTING_SPLIT:
250 boolean startRemovable = false;
252 private void activate() throws Exception {
253 state = ToolState.INITIALIZING;
256 if (allowed.size() == 1) {
257 pipeRun = startComponent.getPipeRun();
258 PipeControlPoint start = startComponent.getControlPoint();
259 boolean requiresBranching = false;
260 if (start.getNext() == null)
262 else if (start.getPrevious() == null) {
265 requiresBranching = true;
268 if (requiresBranching) {
269 activateSplit(start);
271 activateNextPrev(start);
273 } else if (allowed.size() == 0) {
274 panel.useDefaultAction();
275 state = ToolState.NOT_ACTIVE;
278 terminalSelectionGizmo.setComponent(startComponent, allowed);
279 terminalSelectionGizmo.attach(panel.GetRenderer());
280 state = ToolState.SELECTING_POSITION;
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;
294 pipeRun = start.getPipeRun();
295 setPreviousPosition(start.getWorldPosition());
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;
308 lock = LockType.NONE;
310 Vector3d v = new Vector3d();
312 start.getControlPointEnds(v, previousPosition);
314 start.getControlPointEnds(previousPosition,v);
316 } else if (startComponent instanceof TurnComponent) {
317 if (start.isFixed()) {
318 direction = startComponent.getControlPoint().getPathLegDirection(reversed ? Direction.PREVIOUS : Direction.NEXT);
319 lock = LockType.CUSTOM;
322 lock = LockType.NONE;
324 } else if (startComponent instanceof EndComponent) {
325 throw new Exception("Not supported");
329 throw new Exception("Not supported");
331 currentPosition = new Vector3d(previousPosition);
332 state = ToolState.ROUTING;
333 if (direction != null) {
334 direction.normalize();
337 startRemovable = start.isDeletable();
338 start.setDeletable(false);
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);
352 start.setNext(straightCP);
353 straightCP.setPrevious(start);
355 start.setPrevious(straightCP);
356 straightCP.setNext(start);
359 translateAxisGizmo.attach(panel.GetRenderer());
360 setPreviousPosition(previousPosition);
361 updateCurrentPoint();
364 private void setPreviousPosition(Vector3d v) {
365 previousPosition = new Vector3d(v);
366 if (translateAxisGizmo.isAttached())
367 translateAxisGizmo.setPosition(previousPosition);
370 private void activateBranch(PipeControlPoint start) throws Exception{
371 pipeRun = start.getPipeRun();
372 setPreviousPosition(start.getWorldPosition());
375 lock = LockType.NONE;
377 currentPosition = new Vector3d(previousPosition);
378 state = ToolState.ROUTING;
379 if (direction != null) {
380 direction.normalize();
383 startRemovable = start.isDeletable();
384 start.setDeletable(false);
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);
396 start.setNext(straightCP);
397 straightCP.setPrevious(start);
400 start.setPrevious(straightCP);
401 straightCP.setNext(start);
405 translateAxisGizmo.attach(panel.GetRenderer());
406 setPreviousPosition(previousPosition);
407 updateCurrentPoint();
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;
418 public void deactivate() {
419 for (PipelineComponent component : added) {
420 component.getControlPoint().setDeletable(true);
424 startComponent.getControlPoint().setDeletable(startRemovable);
428 setLockType(LockType.NONE, true);
429 startComponent = null;
435 currentPosition = null;
436 previousPosition = null;
437 startRemovable = false;
438 detector.clearConstraintHighlights();
439 state = ToolState.NOT_ACTIVE;
445 private void setLockType(LockType type, boolean force) {
446 if (force || lock != LockType.CUSTOM) {
452 translateAxisGizmo.setType(6);
455 translateAxisGizmo.setType(0);
458 translateAxisGizmo.setType(1);
461 translateAxisGizmo.setType(2);
464 translateAxisGizmo.setType(3);
467 translateAxisGizmo.setType(4);
470 translateAxisGizmo.setType(5);
478 public void mousePressed(MouseEvent e) {
480 panel.getDefaultAction().mousePressed(e);
485 public void mouseReleased(MouseEvent e) {
487 panel.getDefaultAction().mouseReleased(e);
492 public void mouseClicked(MouseEvent e) {
494 panel.getDefaultAction().mouseClicked(e);
497 if (state == ToolState.ROUTING) {
499 if (e.getClickCount() == 1) {
500 if (e.getButton() == MouseEvent.BUTTON1) {
501 if (this.added.size() > 0) {
503 setLockType(LockType.NONE,true);
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;
519 } else if (e.getButton() ==MouseEvent.BUTTON2){
520 // detector.updateConstraintReference();
521 } else if (e.getButton() == MouseEvent.BUTTON3){
525 } catch(Exception err) {
526 err.printStackTrace();
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();
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());
544 panel.useDefaultAction();
546 } catch (Exception err) {
547 ExceptionUtils.logAndShowError(err);
548 panel.useDefaultAction();
552 } else if (state == ToolState.SELECTING_SPLIT) {
553 if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) {
554 Tuple3d t = splitPointSelectionGizmo.getSplitPoint();
555 splitPointSelectionGizmo.deattach();
557 panel.useDefaultAction();
561 Vector3d pos = new Vector3d(t);
562 InlineComponent branchSplit = createBranchSplit((InlineComponent)startComponent, pos);
563 PipeControlPoint branchSplitCP = branchSplit.getControlPoint();
565 PipeRun newRun = new PipeRun();
566 String n = root.getUniqueName("PipeRun");
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;
576 } catch (Exception err) {
577 ExceptionUtils.logAndShowError(err);
578 panel.useDefaultAction();
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);
597 public void mouseMoved(MouseEvent e) {
599 panel.getDefaultAction().mouseMoved(e);
602 step = ((e.getModifiers() & MouseEvent.CTRL_DOWN_MASK) > 0);
603 update(e.getX(), e.getY());
607 public void mouseDragged(MouseEvent e) {
609 panel.getDefaultAction().mouseDragged(e);
615 private List<INode> isOverNode(int x, int y) {
616 List<INode> nodes = new ArrayList<INode>();
617 vtkProp picked[] = panel.pick2(x, y);
619 for (int i = 0; i < picked.length; i++) {
620 nodes.add(nodeMap.getNode(picked[i]));
628 private void updateRouting(double x, double y) {
629 // if(input.keyPressed(KeyEvent.VK_ESCAPE)) {
630 // controlPoints.clear();
634 // if (input.keyPressed(KeyEvent.VK_C)) {
635 // useCamera = !useCamera;
636 // cameraAction.setChecked(useCamera);
639 //panel.getDefaultAction().update();
647 Ray ray = vtkUtil.createMouseRay(panel.GetRenderer(),x, y);
648 Vector3d o = new Vector3d(ray.pos);
649 Vector3d d = ray.dir;
652 if (!updateCurrentPoint(o, d))
654 //Point3d startPoint = new Point3d();
655 double mu[] = new double[2];
659 INode hoverObject = null;
661 List<INode> hover = isOverNode((int)x,(int)y);
662 if (hover.size() > 0) {
663 hoverObject = hover.get(0);
665 // System.out.println(hoverObject + " " + getLast());
666 if (hoverObject != null) {
667 if (hoverObject.equals(getLast()) ) {
669 for (int i = 1; i < hover.size(); i++) {
670 hoverObject = hover.get(i);
671 if (!getLast().equals(hoverObject)) {
680 // System.out.println(hoverObject);
681 if (hoverObject != null) {
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;
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;
705 if (added.contains(endTo))
719 private boolean updateCurrentPoint(Vector3d o, Vector3d d) {
721 Vector3d point = new Vector3d(this.previousPosition);
725 MathTools.intersectStraightStraight(point, new Vector3d(1.0,0.0,0.0), o,d, currentPosition, new Vector3d());
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();
734 MathTools.intersectStraightStraight(point, new Vector3d(0.0,1.0,0.0), o,d, currentPosition, new Vector3d());
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();
743 MathTools.intersectStraightStraight(point, new Vector3d(0.0,0.0,1.0), o,d, currentPosition, new Vector3d());
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();
751 MathTools.intersectStraightPlane(o, d, point, new Vector3d(0.0,0.0,1.0), currentPosition);
754 MathTools.intersectStraightPlane(o, d, point, new Vector3d(0.0,1.0,0.0), currentPosition);
757 MathTools.intersectStraightPlane(o, d, point, new Vector3d(1.0,0.0,0.0), currentPosition);
760 Vector3d normal = new Vector3d(panel.GetRenderer().GetActiveCamera().GetDirectionOfProjection());
763 MathTools.intersectStraightPlane(o, d, point, normal, currentPosition);
766 MathTools.intersectStraightStraight(point, new Vector3d(direction), o,d, currentPosition, new Vector3d());
767 double dist = MathTools.distanceFromPlane(new Vector3d(currentPosition), direction, previousPosition);
769 currentPosition.set(previousPosition);
777 private Vector3d getLockDir() {
782 return new Vector3d(1,0,0);
784 return new Vector3d(0,1,0);
786 return new Vector3d(0,0,1);
791 private void updateRoute(Vector3d o, Vector3d d) {
792 detector.clearConstraintHighlights();
793 Point3d previousPipePoint = new Point3d(previousPosition);
795 if (lock == LockType.NONE) {
796 Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint));
798 currentPosition = new Vector3d(p);
799 s += detector.getSnapString();
802 Vector3d dir = new Vector3d(currentPosition);
803 dir.sub(previousPipePoint);
804 Point3d p = detector.getPointSnap(new Vector3d(previousPipePoint), dir);
806 currentPosition = new Vector3d(p);
807 s += detector.getSnapString();
811 updateCurrentPoint();
812 s += currentPosition.toString();
816 vtkTextActor infoActor;
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();
832 infoActor.SetPosition(10,10);
833 panel.GetRenderer().AddActor(infoActor);
835 infoActor.SetInput(text);
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));
848 if (MathTools.distance(p, currentPosition) > NOZZLE_SNAP_DISTANCE) {
853 updateCurrentPoint();
855 setInfoText("Connect to nozzle " + currentPosition);
860 private PositionType endingToStraight(INode straightNode, double mu[], Vector3d o, Vector3d d) {
861 InlineComponent s = (InlineComponent)straightNode;
863 Point3d sStart = new Point3d();
864 Point3d sEnd = new Point3d();
865 s.getControlPointEnds(sStart, sEnd);
866 //detector.clearConstraintHighlights();
868 Point3d previousPipePoint = new Point3d(previousPosition);
870 if (lock == LockType.NONE) {
871 Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint));
873 currentPosition = new Vector3d(p);
874 // snapping is detected, check if snapped point can create branch with straight
875 PositionType t = endingLockToStraight(s, mu);
878 // if not, we'll have to remove highlight that was added when snapped point was detected
879 detector.clearConstraintHighlights();
883 Vector3d sDir = new Vector3d(sEnd);
885 MathTools.intersectStraightStraight(sStart, sDir, o, d, currentPosition, new Point3d(), mu);
889 throw new RuntimeException("Lock shouldn't be on");
893 updateCurrentPoint();
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;
903 currentPosition.set(sStart);
906 else if (mu[0] > 1.0) {
907 currentPosition.set(sEnd);
910 boolean connect = false;
912 PipeControlPoint pcp = s.getControlPoint();
913 if (pcp.getPrevious() == null)
915 } else if (connectNext) {
916 PipeControlPoint pcp = s.getControlPoint();
917 if (pcp.getNext() == null)
921 updateCurrentPoint();
924 info += "Connect pipes :";
926 info += "Make Branch :";
928 setInfoText(info + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0)));
931 return PositionType.NEXT;
933 return PositionType.PREVIOUS;
937 return PositionType.SPLIT;
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)
948 } else if (component instanceof TurnComponent) {
949 if (pcp.getNext() == null || pcp.getPrevious() == 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)
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);
969 Vector3d dir = new Vector3d(currentPosition);
970 Point3d prev = new Point3d(previousPosition);
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);
984 updateCurrentPoint();
986 setInfoText("Make branch (l) :" + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0)) + " " + routePoint.lengthSquared());
987 return PositionType.SPLIT;
992 private boolean endingLockToNozzle(INode nozzleNode) {
993 Nozzle nozzle = (Nozzle)nozzleNode;
994 Vector3d dir = new Vector3d(currentPosition);
995 Point3d prev = new Point3d(previousPosition);
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);
1008 //System.out.println(u[0]);
1012 private PipeControlPoint endingLockToComponent(INode componentNode) {
1013 // we'll must scan all free pcp's and their direction to accept the connection.
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);
1029 added.add(straight);
1031 turnCP.setDeletable(false); // mark turnCP nonDeletable so that PipingRules won't delete it immediately.
1034 previousCP.setNext(turnCP);
1035 turnCP.setPrevious(previousCP);
1036 turnCP.setNext(straightCP);
1037 straightCP.setPrevious(turnCP);
1039 previousCP.setPrevious(turnCP);
1040 turnCP.setNext(previousCP);
1041 turnCP.setPrevious(straightCP);
1042 straightCP.setNext(turnCP);
1045 turnCP.setWorldPosition(currentPosition);
1046 turnCP.setTurnAngle(0.0);
1047 turnCP.setLength(0.0);
1048 straightCP.setWorldPosition(currentPosition);
1049 straightCP.setLength(0.0);
1051 setPreviousPosition(currentPosition);
1052 updateCurrentPoint();
1059 * Updates tool graphics for current point
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();
1069 v.add(previousPosition);
1070 straight.getControlPoint().setWorldPosition(v);
1071 straight.getControlPoint().setLength(length);
1073 PipingRules.positionUpdate(straight.getControlPoint(),false);
1074 } catch (Exception e) {
1075 // TODO Auto-generated catch block
1076 e.printStackTrace();
1080 private PipelineComponent getLast() {
1081 if (added.size() == 0)
1082 return startComponent;
1083 return added.get(added.size()-1);
1088 * Removes last point from pipeline
1090 public void removePoint() {
1091 if (added.size() < 3)
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());
1100 setPreviousPosition(startComponent.getWorldPosition());
1101 if (direction != null)
1102 setLockType(LockType.CUSTOM, true);
1107 private void endPiping() throws Exception {
1108 state = ToolState.NOT_ACTIVE;
1110 if (endTo != null) {
1111 PipeControlPoint endCP = endTo.getControlPoint();
1112 if (endType == null || endType == PositionType.NEXT || endType == PositionType.PREVIOUS) {
1114 PipelineComponent current = getLast();
1115 PipeControlPoint currentCP = current.getControlPoint();
1117 boolean requiresReverse = false;
1118 if (!reversed && endCP.getPrevious() != null) {
1119 requiresReverse = true;
1120 } else if (reversed && endCP.getNext() != null) {
1121 requiresReverse = true;
1123 PipeRun other = endCP.getPipeRun();
1124 boolean mergeRuns = pipeRun.equalSpecs(other);
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);
1133 // Runs have compatible specs and must be merged
1134 if (pipeRun != other) // FIXME: temporary workaround.
1135 PipingRules.merge(pipeRun, other);
1137 currentCP.setNext(endCP);
1138 endCP.setPrevious(currentCP);
1140 currentCP.setPrevious(endCP);
1141 endCP.setNext(currentCP);
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);
1149 Vector3d endPos = endCP.getWorldPosition();
1150 Vector3d currentPos = currentCP.getWorldPosition();
1151 Vector3d v = new Vector3d(endPos);
1156 PipingRules.addSizeChange(reversed, pipeRun, other, reducer, currentCP, endCP);
1158 pcp.setWorldPosition(v);
1159 reducer.updateParameters();
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());
1171 PipelineComponent current = getLast();
1172 PipeControlPoint currentCP = current.getControlPoint();
1176 pcp.setPrevious(currentCP);
1177 currentCP.setNext(pcp);
1179 pcp.setNext(currentCP);
1180 currentCP.setPrevious(pcp);
1184 PipingRules.positionUpdate(endCP);
1186 panel.useDefaultAction();