package org.simantics.plant3d.actions; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.vecmath.Point3d; import javax.vecmath.Tuple3d; import javax.vecmath.Vector3d; import org.simantics.g3d.math.MathTools; import org.simantics.g3d.math.Ray; import org.simantics.g3d.scenegraph.NodeMap; import org.simantics.g3d.scenegraph.base.INode; import org.simantics.g3d.tools.ConstraintDetector; import org.simantics.g3d.tools.DummyConstraintDetector; import org.simantics.g3d.vtk.gizmo.TranslateAxisGizmo; import org.simantics.g3d.vtk.swt.InteractiveVtkComposite; import org.simantics.g3d.vtk.swt.vtkSwtAction; import org.simantics.g3d.vtk.utils.vtkUtil; import org.simantics.plant3d.Activator; import org.simantics.plant3d.gizmo.SplitPointSelectionGizmo; import org.simantics.plant3d.gizmo.TerminalSelectionGizmo; import org.simantics.plant3d.ontology.Plant3D; import org.simantics.plant3d.scenegraph.EndComponent; import org.simantics.plant3d.scenegraph.InlineComponent; import org.simantics.plant3d.scenegraph.Nozzle; import org.simantics.plant3d.scenegraph.P3DRootNode; import org.simantics.plant3d.scenegraph.PipeRun; import org.simantics.plant3d.scenegraph.PipelineComponent; import org.simantics.plant3d.scenegraph.TurnComponent; import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint; import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction; import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.PositionType; import org.simantics.plant3d.scenegraph.controlpoint.PipingRules; import org.simantics.plant3d.utils.ComponentUtils; import org.simantics.utils.threads.ThreadUtils; import org.simantics.utils.ui.ExceptionUtils; import vtk.vtkProp; import vtk.vtkTextActor; public class RoutePipeAction extends vtkSwtAction { enum LockType { X, Y, Z, XY, YZ, XZ, NONE, CUSTOM }; LockType lock = LockType.NONE; private double BRANCH_SNAP_DISTANCE = 0.05; private double NOZZLE_SNAP_DISTANCE = 0.05; private double istep = 10.0; private int decimals = 2; private P3DRootNode root; private PipelineComponent startComponent; private PipeRun pipeRun; private TranslateAxisGizmo translateAxisGizmo = new TranslateAxisGizmo(); private SplitPointSelectionGizmo splitPointSelectionGizmo; private TerminalSelectionGizmo terminalSelectionGizmo; private NodeMap nodeMap; private enum ToolState{NOT_ACTIVE, INITIALIZING, SELECTING_POSITION, SELECTING_SPLIT, ROUTING}; private ToolState state = ToolState.NOT_ACTIVE; private ConstraintDetector detector = new DummyConstraintDetector(); private boolean useDefault = false; private Vector3d direction = null; private Vector3d previousPosition = null; private Vector3d currentPosition = null; boolean step = false; PipelineComponent endTo = null; PositionType endType = null; PipeControlPoint endPort = null; boolean reversed = false; private Set allowed = new HashSet(); public RoutePipeAction(InteractiveVtkComposite panel, P3DRootNode root) { super(panel); this.root = root; setText("Route Pipe"); setImageDescriptor(Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/Straight.png")); nodeMap = root.getNodeMap(); splitPointSelectionGizmo = new SplitPointSelectionGizmo(panel); terminalSelectionGizmo = new TerminalSelectionGizmo(panel); } public void setComponent(PipelineComponent component) { this.startComponent = component; allowed.clear(); if (this.startComponent.getNext() == null) allowed.add(PositionType.NEXT); if (this.startComponent.getPrevious() == null && !(this.startComponent instanceof Nozzle)) allowed.add(PositionType.PREVIOUS); if (this.startComponent instanceof InlineComponent && !this.startComponent.getControlPoint().isFixed()) allowed.add(PositionType.SPLIT); setEnabled(allowed.size() > 0); } public void deattach() { deactivate(); startComponent = null; nodeMap.commit("Route pipe"); deattachUI(); super.deattach(); panel.refresh(); } public void attach() { if (startComponent == null) return; super.attach(); ThreadUtils.asyncExec(panel.getThreadQueue(), new Runnable() { public void run() { // attachUI(); try { activate(); } catch (Exception e) { deattach(); ExceptionUtils.logAndShowError(e); } } }); } // private void attachUI() { // //panel.setCursor(activeCursor); // translateAxisGizmo.attach(panel.GetRenderer()); // } private void deattachUI() { //panel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); panel.lock(); if (translateAxisGizmo.isAttached()) translateAxisGizmo.deattach(); if (splitPointSelectionGizmo.isAttached()) splitPointSelectionGizmo.deattach(); if (terminalSelectionGizmo.isAttached()) terminalSelectionGizmo.deattach(); if (infoActor != null) { panel.getRenderer().RemoveActor(infoActor); infoActor.Delete(); infoActor = null; } panel.unlock(); } private List added = new ArrayList(); @Override public boolean keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) panel.useDefaultAction(); if (lock != LockType.CUSTOM) { if ((e.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) > 0) { if (e.getKeyCode() == KeyEvent.VK_X) { if (lock != LockType.XY && lock != LockType.XZ) { setLockType(LockType.XY, false); } else if (lock == LockType.XY) { setLockType(LockType.XZ, false); } else { setLockType(LockType.NONE, false); } } if (e.getKeyCode() == KeyEvent.VK_Y) { if (lock != LockType.XY && lock != LockType.YZ) { setLockType(LockType.XY, false); } else if (lock == LockType.XY) { setLockType(LockType.YZ, false); } else { setLockType(LockType.NONE, false); } } if (e.getKeyCode() == KeyEvent.VK_Z) { if (lock != LockType.XZ && lock != LockType.YZ) { setLockType(LockType.XZ, false); } else if (lock == LockType.XZ) { setLockType(LockType.YZ, false); } else { setLockType(LockType.NONE, false); } } } else { if (e.getKeyCode() == KeyEvent.VK_X) { if (lock != LockType.X) setLockType(LockType.X,false); else setLockType(LockType.NONE,false); } if (e.getKeyCode() == KeyEvent.VK_Y) { if (lock != LockType.Y) setLockType(LockType.Y,false); else setLockType(LockType.NONE, false); } if (e.getKeyCode() == KeyEvent.VK_Z) { if (lock != LockType.Z) setLockType(LockType.Z, false); else setLockType(LockType.NONE, false); } } } if (e.getKeyCode() == KeyEvent.VK_C) { useDefault = !useDefault; System.out.println("UseDefault " + useDefault); } update(); return true; } private void update() { panel.refresh(); } private void update(double x, double y) { switch (state) { case NOT_ACTIVE: return; // TODO : throw Exception? case INITIALIZING: return; case SELECTING_POSITION: return; case SELECTING_SPLIT: return; case ROUTING: updateRouting(x,y); break; } return; } boolean startRemovable = false; private void activate() throws Exception { state = ToolState.INITIALIZING; added.clear(); if (allowed.size() == 1) { pipeRun = startComponent.getPipeRun(); PipeControlPoint start = startComponent.getControlPoint(); boolean requiresBranching = false; if (start.getNext() == null) reversed = false; else if (start.getPrevious() == null) { reversed = true; } else { requiresBranching = true; } if (requiresBranching) { activateSplit(start); } else { activateNextPrev(start); } } else if (allowed.size() == 0) { panel.useDefaultAction(); state = ToolState.NOT_ACTIVE; return; } else { terminalSelectionGizmo.setComponent(startComponent, allowed); terminalSelectionGizmo.attach(panel); state = ToolState.SELECTING_POSITION; update(); } } private void activateNextPrev(PipeControlPoint start) throws Exception{ if (!reversed && start.isDualInline()) start = start.getSubPoint().get(0); else if (reversed && start.isDualSub()) start = start.parent; pipeRun = start.getPipeRun(); setPreviousPosition(start.getWorldPosition()); boolean startWithTurn = false; if (startComponent instanceof Nozzle) { direction = startComponent.getControlPoint().getDirectedControlPointDirection(); lock = LockType.CUSTOM; } else if (startComponent instanceof PipelineComponent){ if (startComponent instanceof InlineComponent) { direction = startComponent.getControlPoint().getPathLegDirection(reversed ? Direction.PREVIOUS : Direction.NEXT); lock = LockType.CUSTOM; if (startComponent.getType().equals(Plant3D.URIs.Builtin_Straight)) { startWithTurn = true; direction = null; lock = LockType.NONE; } Vector3d v = new Vector3d(); if (!reversed) { start.getControlPointEnds(v, previousPosition); } else { start.getControlPointEnds(previousPosition,v); } } else if (startComponent instanceof TurnComponent) { if (start.isFixed()) { direction = startComponent.getControlPoint().getPathLegDirection(reversed ? Direction.PREVIOUS : Direction.NEXT); lock = LockType.CUSTOM; } else { direction = null; lock = LockType.NONE; } } else if (startComponent instanceof EndComponent) { throw new Exception("Not supported"); } } else { throw new Exception("Not supported"); } currentPosition = new Vector3d(previousPosition); state = ToolState.ROUTING; if (direction != null) { direction.normalize(); } startRemovable = start.isDeletable(); start.setDeletable(false); if (startWithTurn) { addPoint(); } else { if (direction != null) currentPosition.add(direction); InlineComponent straight = ComponentUtils.createStraight(root); PipeControlPoint straightCP = straight.getControlPoint(); straight.setName(pipeRun.getUniqueName("Pipe")); pipeRun.addChild(straight); added.add(straight); if (!reversed) { start.setNext(straightCP); straightCP.setPrevious(start); } else { start.setPrevious(straightCP); straightCP.setNext(start); } } translateAxisGizmo.attach(panel); setPreviousPosition(previousPosition); updateCurrentPoint(); } private void setPreviousPosition(Vector3d v) { previousPosition = new Vector3d(v); if (translateAxisGizmo.isAttached()) translateAxisGizmo.setPosition(previousPosition); } private void activateBranch(PipeControlPoint start) throws Exception{ pipeRun = start.getPipeRun(); setPreviousPosition(start.getWorldPosition()); direction = null; lock = LockType.NONE; currentPosition = new Vector3d(previousPosition); state = ToolState.ROUTING; if (direction != null) { direction.normalize(); } startRemovable = start.isDeletable(); start.setDeletable(false); if (direction != null) currentPosition.add(direction); InlineComponent straight = ComponentUtils.createStraight(root); PipeControlPoint straightCP = straight.getControlPoint(); straight.setName(pipeRun.getUniqueName("Pipe")); pipeRun.addChild(straight); added.add(straight); if (!reversed) { start.setNext(straightCP); straightCP.setPrevious(start); } else { start.setPrevious(straightCP); straightCP.setNext(start); } translateAxisGizmo.attach(panel); setPreviousPosition(previousPosition); updateCurrentPoint(); } private void activateSplit(PipeControlPoint start) throws Exception{ Point3d p1 = new Point3d(); Point3d p2 = new Point3d(); start.getInlineControlPointEnds(p1, p2); splitPointSelectionGizmo.setSplit(p1, p2); splitPointSelectionGizmo.attach(panel); state = ToolState.SELECTING_SPLIT; } public void deactivate() { for (PipelineComponent component : added) { component.getControlPoint().setDeletable(true); } added.clear(); startComponent.getControlPoint().setDeletable(startRemovable); direction = null; setLockType(LockType.NONE, true); startComponent = null; endTo = null; endPort = null; endType = null; pipeRun = null; allowed.clear(); currentPosition = null; previousPosition = null; startRemovable = false; detector.clearConstraintHighlights(); state = ToolState.NOT_ACTIVE; setEnabled(false); } private void setLockType(LockType type, boolean force) { if (force || lock != LockType.CUSTOM) { lock = type; switch (lock) { case CUSTOM: case NONE: translateAxisGizmo.setType(6); break; case X: translateAxisGizmo.setType(0); break; case Y: translateAxisGizmo.setType(1); break; case Z: translateAxisGizmo.setType(2); break; case XY: translateAxisGizmo.setType(3); break; case XZ: translateAxisGizmo.setType(4); break; case YZ: translateAxisGizmo.setType(5); break; } } } @Override public boolean mousePressed(MouseEvent e) { if (useDefault) { getDefaultAction().mousePressed(e); } return true; } @Override public boolean mouseReleased(MouseEvent e) { if (useDefault) { getDefaultAction().mouseReleased(e); } return true; } @Override public boolean mouseClicked(MouseEvent e) { if (useDefault) { getDefaultAction().mouseClicked(e); return true; } if (state == ToolState.ROUTING) { try { if (e.getClickCount() == 1) { if (e.getButton() == MouseEvent.BUTTON1) { if (this.added.size() > 0) { setLockType(LockType.NONE,true); if (endTo != null) { endPiping(); } else { addPoint(); } } else { throw new RuntimeException("kjf"); // // user was selecting position of branch // lastPoint.set(startPoint); // controlPoints.add(new Point3d(startPoint)); // if (selectionLine != null) // selectionLine.removeFromParent(); // selectionLine = null; } } else if (e.getButton() ==MouseEvent.BUTTON2){ // detector.updateConstraintReference(); } else if (e.getButton() == MouseEvent.BUTTON3){ endPiping(); } } } catch(Exception err) { err.printStackTrace(); } } else if (state == ToolState.SELECTING_POSITION) { if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) { int type = panel.getPickType(); //panel.setPickType(0); panel.setPickType(5); vtkProp[] picked = panel.pick(e.getX(), e.getY()); panel.setPickType(type); PositionType position = terminalSelectionGizmo.getPickedPosition(picked); if (position != null) { terminalSelectionGizmo.deattach(); try { if (position == PositionType.SPLIT) { activateSplit(startComponent.getControlPoint()); } else if (position == PositionType.NEXT || position == PositionType.PREVIOUS) { reversed = position == PositionType.PREVIOUS; activateNextPrev(startComponent.getControlPoint()); } else { panel.useDefaultAction(); } } catch (Exception err) { ExceptionUtils.logAndShowError(err); panel.useDefaultAction(); } } } } else if (state == ToolState.SELECTING_SPLIT) { if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) { Tuple3d t = splitPointSelectionGizmo.getSplitPoint(); splitPointSelectionGizmo.deattach(); if (t == null) { panel.useDefaultAction(); return true; } try { Vector3d pos = new Vector3d(t); InlineComponent branchSplit = createBranchSplit((InlineComponent)startComponent, pos); PipeControlPoint branchSplitCP = branchSplit.getControlPoint(); reversed = false; PipeRun newRun = new PipeRun(); String n = root.getUniqueName("PipeRun"); newRun.setName(n); root.addChild(newRun); PipeControlPoint pcp = new PipeControlPoint(branchSplit,newRun); branchSplitCP.children.add(pcp); pcp.parent = branchSplitCP; pcp.setWorldOrientation(branchSplitCP.getWorldOrientation()); pcp.setWorldPosition(branchSplitCP.getWorldPosition()); startComponent = branchSplit; activateBranch(pcp); } catch (Exception err) { ExceptionUtils.logAndShowError(err); panel.useDefaultAction(); } } } return true; } private InlineComponent createBranchSplit(InlineComponent component, Vector3d pos) throws Exception{ InlineComponent branchSplit = ComponentUtils.createBranchSplit(root); String branchName = component.getPipeRun().getUniqueName("Branch"); branchSplit.setName(branchName); component.getPipeRun().addChild(branchSplit); PipeControlPoint branchSplitCP = branchSplit.getControlPoint(); branchSplitCP.setWorldPosition(pos); PipingRules.splitVariableLengthComponent(branchSplit, component, false); return branchSplit; } @Override public boolean mouseMoved(MouseEvent e) { if (useDefault) { getDefaultAction().mouseMoved(e); return true; } step = ((e.getModifiers() & MouseEvent.CTRL_DOWN_MASK) > 0); update(e.getX(), e.getY()); return true; } @Override public boolean mouseDragged(MouseEvent e) { if (useDefault) getDefaultAction().mouseDragged(e); return true; } private List isOverNode(int x, int y) { List nodes = new ArrayList(); vtkProp picked[] = panel.pick2(x, y); if (picked !=null) { for (int i = 0; i < picked.length; i++) { nodes.add(nodeMap.getNode(picked[i])); } } return nodes; } private void updateRouting(double x, double y) { // if(input.keyPressed(KeyEvent.VK_ESCAPE)) { // controlPoints.clear(); // end(); // return; // } // if (input.keyPressed(KeyEvent.VK_C)) { // useCamera = !useCamera; // cameraAction.setChecked(useCamera); // } if (useDefault) { //panel.getDefaultAction().update(); return; } endTo = null; endType = null; endPort = null; Ray ray = vtkUtil.createMouseRay(panel.getRenderer(),x, y); Vector3d o = new Vector3d(ray.pos); Vector3d d = ray.dir; if (!updateCurrentPoint(o, d)) return; //Point3d startPoint = new Point3d(); double mu[] = new double[2]; INode hoverObject = null; List hover = isOverNode((int)x,(int)y); if (hover.size() > 0) { hoverObject = hover.get(0); } // System.out.println(hoverObject + " " + getLast()); if (hoverObject != null) { if (hoverObject.equals(getLast()) ) { boolean set = false; for (int i = 1; i < hover.size(); i++) { hoverObject = hover.get(i); if (!getLast().equals(hoverObject)) { set = true; break; } } if (!set) hoverObject = null; } } // System.out.println(hoverObject); if (hoverObject != null) { if (lock == LockType.NONE) { if (hoverObject instanceof Nozzle && endingToNozzle(hoverObject,o,d)) { endTo = (Nozzle)hoverObject; } else if (hoverObject instanceof InlineComponent && ((InlineComponent)hoverObject).isVariableLength()) { endTo = (InlineComponent)hoverObject; endType = endingToStraight(endTo,mu,o,d); } else if (hoverObject instanceof PipelineComponent && (endPort = endingToComponent(hoverObject,o,d)) != null) { endTo = (PipelineComponent)hoverObject; } else { updateRoute(o,d); } } else { if (hoverObject instanceof InlineComponent && ((InlineComponent)hoverObject).isVariableLength() && (endType = endingLockToStraight(hoverObject,mu)) != null) { endTo = (InlineComponent)hoverObject;; } else if (hoverObject instanceof Nozzle && endingLockToNozzle(hoverObject)) { endTo = (Nozzle)hoverObject; } else if ((hoverObject instanceof PipelineComponent) && ((endPort = endingLockToComponent(hoverObject)) != null)) { endTo = (PipelineComponent)hoverObject; } else { updateRoute(o,d); } } if (added.contains(endTo)) endTo = null; } else { updateRoute(o,d); } panel.refresh(); } private boolean updateCurrentPoint(Vector3d o, Vector3d d) { Vector3d point = new Vector3d(this.previousPosition); switch(lock) { case X: MathTools.intersectStraightStraight(point, new Vector3d(1.0,0.0,0.0), o,d, currentPosition, new Vector3d()); if (step) { currentPosition.x = Math.round(istep * currentPosition.x) / istep; BigDecimal bx = new BigDecimal(currentPosition.x); bx.setScale(decimals, BigDecimal.ROUND_HALF_UP); currentPosition.x = bx.doubleValue(); } break; case Y: MathTools.intersectStraightStraight(point, new Vector3d(0.0,1.0,0.0), o,d, currentPosition, new Vector3d()); if (step) { currentPosition.y = Math.round(istep * currentPosition.y) / istep; BigDecimal bx = new BigDecimal(currentPosition.y); bx.setScale(decimals, BigDecimal.ROUND_HALF_UP); currentPosition.y = bx.doubleValue(); } break; case Z: MathTools.intersectStraightStraight(point, new Vector3d(0.0,0.0,1.0), o,d, currentPosition, new Vector3d()); if (step) { currentPosition.z = Math.round(istep * currentPosition.z) / istep; BigDecimal bx = new BigDecimal(currentPosition.z); bx.setScale(decimals, BigDecimal.ROUND_HALF_UP); currentPosition.z = bx.doubleValue(); }break; case XY: MathTools.intersectStraightPlane(o, d, point, new Vector3d(0.0,0.0,1.0), currentPosition); break; case XZ: MathTools.intersectStraightPlane(o, d, point, new Vector3d(0.0,1.0,0.0), currentPosition); break; case YZ: MathTools.intersectStraightPlane(o, d, point, new Vector3d(1.0,0.0,0.0), currentPosition); break; case NONE: Vector3d normal = new Vector3d(panel.getRenderer().GetActiveCamera().GetDirectionOfProjection()); normal.normalize(); MathTools.intersectStraightPlane(o, d, point, normal, currentPosition); break; case CUSTOM: MathTools.intersectStraightStraight(point, new Vector3d(direction), o,d, currentPosition, new Vector3d()); double dist = MathTools.distanceFromPlane(new Vector3d(currentPosition), direction, previousPosition); if (dist < 0.0) currentPosition.set(previousPosition); break; default: return false; } return true; } private Vector3d getLockDir() { switch (lock) { case CUSTOM: return direction; case X: return new Vector3d(1,0,0); case Y: return new Vector3d(0,1,0); case Z: return new Vector3d(0,0,1); } return null; } private void updateRoute(Vector3d o, Vector3d d) { detector.clearConstraintHighlights(); Point3d previousPipePoint = new Point3d(previousPosition); String s = ""; if (lock == LockType.NONE) { Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint)); if (p != null) currentPosition = new Vector3d(p); s += detector.getSnapString(); } else { Vector3d dir = new Vector3d(currentPosition); dir.sub(previousPipePoint); Point3d p = detector.getPointSnap(new Vector3d(previousPipePoint), dir); if (p != null) currentPosition = new Vector3d(p); s += detector.getSnapString(); } updateCurrentPoint(); s += currentPosition.toString(); setInfoText(s); } vtkTextActor infoActor; private void setInfoText(String text) { //System.out.println(text); if (infoActor == null) { infoActor = new vtkTextActor(); infoActor.GetTextProperty().SetColor(0.0, 0.0, 0.0); infoActor.GetTextProperty().ShadowOff(); infoActor.GetTextProperty().ItalicOff(); infoActor.GetTextProperty().BoldOff(); infoActor.GetTextProperty().SetFontSize(18); infoActor.GetTextProperty().Delete(); infoActor.GetProperty().SetColor(0.0, 0.0, 0.0); infoActor.GetProperty().Delete(); infoActor.SetPosition(10,10); panel.getRenderer().AddActor(infoActor); } infoActor.SetInput(text); } private boolean endingToNozzle(INode nozzleNode,Vector3d o, Vector3d d) { Nozzle nozzle = (Nozzle)nozzleNode; PipeControlPoint pcp =nozzle.getControlPoint(); if (pcp != null && (pcp.getNext() != null || pcp.getPrevious() != null)) return false; // nozzle is already connected to pipe currentPosition = pcp.getWorldPosition(); Point3d previousPipePoint = new Point3d(previousPosition); Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint)); if (p != null) { if (MathTools.distance(p, currentPosition) > NOZZLE_SNAP_DISTANCE) { return false; } } updateCurrentPoint(); setInfoText("Connect to nozzle " + currentPosition); return true; } private PositionType endingToStraight(INode straightNode, double mu[], Vector3d o, Vector3d d) { InlineComponent s = (InlineComponent)straightNode; String info = ""; Point3d sStart = new Point3d(); Point3d sEnd = new Point3d(); s.getControlPointEnds(sStart, sEnd); //detector.clearConstraintHighlights(); Point3d previousPipePoint = new Point3d(previousPosition); //String st = ""; if (lock == LockType.NONE) { Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint)); if (p != null) { currentPosition = new Vector3d(p); // snapping is detected, check if snapped point can create branch with straight PositionType t = endingLockToStraight(s, mu); if (t != null) return t; // if not, we'll have to remove highlight that was added when snapped point was detected detector.clearConstraintHighlights(); } Vector3d sDir = new Vector3d(sEnd); sDir.sub(sStart); MathTools.intersectStraightStraight(sStart, sDir, o, d, currentPosition, new Point3d(), mu); } else { throw new RuntimeException("Lock shouldn't be on"); } updateCurrentPoint(); // branch point must lie between straight's ends. If connection point is exactly // on straight end user may want to connect pipes to each other // TODO : take account sizes of inline components) // TODO : actually make connection if its detected boolean connectPrev = false; boolean connectNext = false; if (mu[0] < 0.0) { currentPosition.set(sStart); connectPrev = true; } else if (mu[0] > 1.0) { currentPosition.set(sEnd); connectNext = true; } boolean connect = false; if (connectPrev) { PipeControlPoint pcp = s.getControlPoint(); if (pcp.getPrevious() == null) connect = true; } else if (connectNext) { PipeControlPoint pcp = s.getControlPoint(); if (pcp.getNext() == null) connect = true; } updateCurrentPoint(); if (connect) info += "Connect pipes :"; else info += "Make Branch :"; setInfoText(info + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0))); if (connect) { if (connectNext) { return PositionType.NEXT; } else { return PositionType.PREVIOUS; } } return PositionType.SPLIT; } private PipeControlPoint endingToComponent(INode componentNode, Vector3d o, Vector3d d) { PipelineComponent component = (PipelineComponent)componentNode; PipeControlPoint pcp = component.getControlPoint(); if (component instanceof EndComponent) { if (pcp.getNext() != null || pcp.getPrevious() != null) return null; return pcp; } else if (component instanceof TurnComponent) { if (pcp.getNext() == null || pcp.getPrevious() == null) return pcp; return null; } else if (component instanceof InlineComponent) { // TODO : scan all empty pcps of the component and select closest one. if (pcp.getNext() == null || pcp.getPrevious() == null) return pcp; return null; } return null; } private PositionType endingLockToStraight(INode straightNode, double mu[]) { InlineComponent s = (InlineComponent)straightNode; Point3d sStart = new Point3d();//G3DTools.getPoint(s.getHasControlPoint().getPreviousPoint().getLocalPosition()); Point3d sEnd = new Point3d(); //G3DTools.getPoint(s.getHasControlPoint().getNextPoint().getLocalPosition()); s.getControlPoint().getInlineControlPointEnds(sStart, sEnd); Vector3d sDir = new Vector3d(sEnd); sDir.sub(sStart); Vector3d dir = new Vector3d(currentPosition); Point3d prev = new Point3d(previousPosition); dir.sub(prev); // intersection point in pipe where branch would be inserted to Vector3d branchPoint = new Vector3d(); // intersection point in straight pipe that is currently routed Vector3d routePoint = new Vector3d(); MathTools.intersectStraightStraight(sStart, sDir, new Vector3d(prev), dir, branchPoint, routePoint, mu); routePoint.sub(branchPoint); // startPoint of branch must be between pipe ends // TODO : take account sizes of elbows (or other components) // branch point must be between pipe ends and intersection points must be quite close to each othert if (mu[0] > 0.0 && mu[0] < 1.0 && routePoint.lengthSquared() < BRANCH_SNAP_DISTANCE) { currentPosition.set(branchPoint); updateCurrentPoint(); setInfoText("Make branch (l) :" + currentPosition + " " + Math.max(0.0, Math.min(mu[0], 1.0)) + " " + routePoint.lengthSquared()); return PositionType.SPLIT; } return null; } private boolean endingLockToNozzle(INode nozzleNode) { Nozzle nozzle = (Nozzle)nozzleNode; Vector3d dir = new Vector3d(currentPosition); Point3d prev = new Point3d(previousPosition); dir.sub(prev); Vector3d nozzleLoc = nozzle.getWorldPosition(); double u[] = new double[1]; Vector3d closest = MathTools.closestPointOnStraight(new Point3d(nozzleLoc), new Point3d(prev), new Vector3d(dir), u); double dist = MathTools.distanceSquared(nozzleLoc,closest); if (dist < BRANCH_SNAP_DISTANCE) { // FIXME : directions should be checked (insert an elbow) currentPosition.set(nozzleLoc); updateCurrentPoint(); setInfoText("Connect to nozzle (l) :" + currentPosition); return true; } //System.out.println(u[0]); return false; } private PipeControlPoint endingLockToComponent(INode componentNode) { // we'll must scan all free pcp's and their direction to accept the connection. return null; } private void addPoint() throws Exception { InlineComponent previous = (InlineComponent)getLast(); PipeControlPoint previousCP = previous.getControlPoint(); TurnComponent turn = ComponentUtils.createTurn(root); InlineComponent straight = ComponentUtils.createStraight(root); PipeControlPoint turnCP = turn.getControlPoint(); PipeControlPoint straightCP = straight.getControlPoint(); straight.setName(pipeRun.getUniqueName("Pipe")); turn.setName(pipeRun.getUniqueName("Elbow")); pipeRun.addChild(turn); pipeRun.addChild(straight); added.add(turn); added.add(straight); turnCP.setDeletable(false); // mark turnCP nonDeletable so that PipingRules won't delete it immediately. if (!reversed) { previousCP.setNext(turnCP); turnCP.setPrevious(previousCP); turnCP.setNext(straightCP); straightCP.setPrevious(turnCP); } else { previousCP.setPrevious(turnCP); turnCP.setNext(previousCP); turnCP.setPrevious(straightCP); straightCP.setNext(turnCP); } turnCP.setWorldPosition(currentPosition); turnCP.setTurnAngle(0.0); turnCP.setLength(0.0); straightCP.setWorldPosition(currentPosition); straightCP.setLength(0.0); setPreviousPosition(currentPosition); updateCurrentPoint(); } /** * Updates tool graphics for current point */ private void updateCurrentPoint() { InlineComponent straight = (InlineComponent)added.get(added.size()-1); // TODO: the inline length is from previous update step. double l = straight.getPrevious().getControlPoint().getInlineLength(); Vector3d v = new Vector3d(); v.sub(currentPosition, previousPosition); double length = v.length(); if (length > MathTools.NEAR_ZERO) { v.scale(1.0/length); v.scale(0.5*(length+l)); v.add(previousPosition); straight.getControlPoint().setWorldPosition(v); straight.getControlPoint().setLength(length); } try { PipingRules.positionUpdate(straight.getControlPoint(),false); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private PipelineComponent getLast() { if (added.size() == 0) return startComponent; return added.get(added.size()-1); } /** * Removes last point from pipeline */ public void removePoint() { if (added.size() < 3) return; InlineComponent straight = (InlineComponent)added.remove(added.size()-1); TurnComponent turn = (TurnComponent)added.remove(added.size()-1); straight.getControlPoint().remove(); turn.getControlPoint().remove(); if (added.size() > 1) { setPreviousPosition(added.get(added.size()-2).getWorldPosition()); } else { setPreviousPosition(startComponent.getWorldPosition()); if (direction != null) setLockType(LockType.CUSTOM, true); } } private void endPiping() throws Exception { state = ToolState.NOT_ACTIVE; if (endTo != null) { PipeControlPoint endCP = endTo.getControlPoint(); if (endType == null || endType == PositionType.NEXT || endType == PositionType.PREVIOUS) { PipelineComponent current = getLast(); PipeControlPoint currentCP = current.getControlPoint(); boolean requiresReverse = false; if (!reversed && endCP.getPrevious() != null) { requiresReverse = true; } else if (reversed && endCP.getNext() != null) { requiresReverse = true; } PipeRun other = endCP.getPipeRun(); boolean mergeRuns = pipeRun.equalSpecs(other); if (requiresReverse) { // Pipe line must be traversible with next/previous relations without direction change. // Now the component, where we are connecting the created pipeline is defined in different order. PipingRules.reverse(other); } if (mergeRuns) { // Runs have compatible specs and must be merged if (pipeRun != other) // FIXME: temporary workaround. PipingRules.merge(pipeRun, other); if (!reversed) { currentCP.setNext(endCP); endCP.setPrevious(currentCP); } else { currentCP.setPrevious(endCP); endCP.setNext(currentCP); } } else { // Runs do not have compatible specs, and a reducer must be attached in between. InlineComponent reducer = ComponentUtils.createReducer(root); PipeControlPoint pcp = reducer.getControlPoint(); PipeControlPoint ocp = pcp.getSubPoint().get(0); Vector3d endPos = endCP.getWorldPosition(); Vector3d currentPos = currentCP.getWorldPosition(); Vector3d v = new Vector3d(endPos); v.sub(currentPos); v.scale(0.5); v.add(currentPos); PipingRules.addSizeChange(reversed, pipeRun, other, reducer, currentCP, endCP); pcp.setWorldPosition(v); reducer.updateParameters(); } } else if (endType == PositionType.SPLIT) { InlineComponent branchSplit = createBranchSplit((InlineComponent)endTo, currentPosition); PipeControlPoint branchSplitCP = branchSplit.getControlPoint(); PipeControlPoint pcp = new PipeControlPoint(branchSplit,pipeRun); branchSplitCP.children.add(pcp); pcp.parent = branchSplitCP; pcp.setWorldOrientation(branchSplitCP.getWorldOrientation()); pcp.setWorldPosition(branchSplitCP.getWorldPosition()); PipelineComponent current = getLast(); PipeControlPoint currentCP = current.getControlPoint(); if(!reversed) { pcp.setPrevious(currentCP); currentCP.setNext(pcp); } else { pcp.setNext(currentCP); currentCP.setPrevious(pcp); } } PipingRules.positionUpdate(endCP); } panel.useDefaultAction(); } }