/******************************************************************************* * Copyright (c) 2007 VTT Technical Research Centre of Finland and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.processeditor.actions; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.resource.ImageDescriptor; import org.simantics.db.Graph; import org.simantics.db.GraphRequestAdapter; import org.simantics.db.GraphRequestStatus; import org.simantics.db.GraphRequestWithResult; import org.simantics.db.Resource; import org.simantics.layer0.utils.EntityFactory; import org.simantics.layer0.utils.IEntity; import org.simantics.processeditor.Activator; import org.simantics.processeditor.ProcessResource; import org.simantics.processeditor.common.ControlPointTools; import org.simantics.processeditor.common.PipeComponentProvider; import org.simantics.processeditor.common.PipingTools2; import org.simantics.processeditor.common.PipingTools2.Direction; import org.simantics.processeditor.dialogs.PipelineDialog; import org.simantics.processeditor.gizmo.PositionSelectionGizmo; import org.simantics.processeditor.stubs.BranchEndControlPoint; import org.simantics.processeditor.stubs.PipeControlPoint; import org.simantics.processeditor.stubs.PipeRun; import org.simantics.processeditor.stubs.PipelineComponent; import org.simantics.processeditor.stubs.VariableLengthInlineComponent; import org.simantics.processeditor.views.ProcessEditor; import org.simantics.proconf.g3d.actions.InteractiveAction; import org.simantics.proconf.g3d.base.ConstraintDetector; import org.simantics.proconf.g3d.base.G3DAPI; import org.simantics.proconf.g3d.base.G3DTools; import org.simantics.proconf.g3d.base.MathTools; import org.simantics.proconf.g3d.base.ThreeDimensionalEditorBase; import org.simantics.proconf.g3d.common.StructuredResourceSelection; import org.simantics.proconf.g3d.dnd.DropListener; import org.simantics.proconf.g3d.scenegraph.IGraphicsNode; import org.simantics.utils.datastructures.Pair; import com.jme.renderer.ColorRGBA; import com.jme.scene.Geometry; import com.jme.scene.Line; import com.jme.scene.state.MaterialState; /** * Action for Routing Pipes * * FIXME : does several thing that should be done by PipingTools. * TODO : instead of using lines to show route of pipe, generate pipe and change it real-time * * @author MLMARKO * */ public class RoutePipeAction extends InteractiveAction implements DropListener, SplitPointListener { private static final ImageDescriptor X_AXIS_ICON = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/x-axis.png"); private static final ImageDescriptor Y_AXIS_ICON = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/y-axis.png"); private static final ImageDescriptor Z_AXIS_ICON = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/z-axis.png"); private static final ImageDescriptor X_PLANE_ICON = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/x-plane.png"); private static final ImageDescriptor Y_PLANE_ICON = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/y-plane.png"); private static final ImageDescriptor Z_PLANE_ICON = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/z-plane.png"); private static final ImageDescriptor CAMERA_ICON = Activator.imageDescriptorFromPlugin("org.simantics.proconf.g3d", "icons/eye.png"); private Action xAxisAction; private Action yAxisAction; private Action zAxisAction; private Action xPlaneAction; private Action yPlaneAction; private Action zPlaneAction; private Action cameraAction; ConstraintDetector detector = null; public RoutePipeAction(ThreeDimensionalEditorBase parent) { super(parent); detector = new ConstraintDetector(parent); xAxisAction = new Action("X",Action.AS_RADIO_BUTTON) { public void run() { if (lock == LockType.X) setLockType(LockType.NONE,false); else setLockType(LockType.X,false); } }; xAxisAction.setImageDescriptor(X_AXIS_ICON); xAxisAction.setToolTipText("Lock X-Axis"); yAxisAction = new Action("Y",Action.AS_RADIO_BUTTON) { public void run() { if (lock == LockType.Y) setLockType(LockType.NONE,false); else setLockType(LockType.Y,false); } }; yAxisAction.setImageDescriptor(Y_AXIS_ICON); yAxisAction.setToolTipText("Lock Y-Axis"); zAxisAction = new Action("Z",Action.AS_RADIO_BUTTON) { public void run() { if (lock == LockType.Z) setLockType(LockType.NONE,false); else setLockType(LockType.Z,false); } }; zAxisAction.setImageDescriptor(Z_AXIS_ICON); zAxisAction.setToolTipText("Lock Z-Axis"); xPlaneAction = new Action("X",Action.AS_RADIO_BUTTON) { public void run() { if (lock == LockType.YZ) setLockType(LockType.NONE,false); else setLockType(LockType.YZ,false); } }; xPlaneAction.setImageDescriptor(X_PLANE_ICON); xPlaneAction.setToolTipText("Lock X-Plane"); yPlaneAction = new Action("Y",Action.AS_RADIO_BUTTON) { public void run() { if (lock == LockType.XZ) setLockType(LockType.NONE,false); else setLockType(LockType.XZ,false); } }; yPlaneAction.setImageDescriptor(Y_PLANE_ICON); yPlaneAction.setToolTipText("Lock Y-Plane"); zPlaneAction = new Action("Z",Action.AS_RADIO_BUTTON) { public void run() { if (lock == LockType.XY) setLockType(LockType.NONE,false); else setLockType(LockType.XY,false); } }; zPlaneAction.setImageDescriptor(Z_PLANE_ICON); zPlaneAction.setToolTipText("Lock Z-Plane"); cameraAction = new Action("C", Action.AS_CHECK_BOX) { public void run() { useCamera = this.isChecked(); } }; cameraAction.setImageDescriptor(CAMERA_ICON); cameraAction.setToolTipText("Use camera"); splitPointAction = new SelectSplitPointAction(parent,this); } public void fillToolBar(IToolBarManager manager) { manager.add(cameraAction); cameraAction.setChecked(useCamera); manager.add(xAxisAction); manager.add(yAxisAction); manager.add(zAxisAction); manager.add(xPlaneAction); manager.add(yPlaneAction); manager.add(zPlaneAction); } enum LockType {NONE,X,Y,Z,XY,YZ,XZ,CUSTOM}; LockType lock = LockType.NONE; private void setLockType(LockType type, boolean force) { if (force || lock != LockType.CUSTOM) { lock = type; } xAxisAction.setChecked(false); yAxisAction.setChecked(false); zAxisAction.setChecked(false); xPlaneAction.setChecked(false); yPlaneAction.setChecked(false); zPlaneAction.setChecked(false); xAxisAction.setEnabled(true); yAxisAction.setEnabled(true); zAxisAction.setEnabled(true); xPlaneAction.setEnabled(true); yPlaneAction.setEnabled(true); zPlaneAction.setEnabled(true); switch (lock) { case X: xAxisAction.setChecked(true); break; case Y: yAxisAction.setChecked(true); break; case Z: zAxisAction.setChecked(true); break; case XY: zPlaneAction.setChecked(true); break; case XZ: yPlaneAction.setChecked(true); break; case YZ: xPlaneAction.setChecked(true); break; case CUSTOM: xAxisAction.setEnabled(false); yAxisAction.setEnabled(false); zAxisAction.setEnabled(false); xPlaneAction.setEnabled(false); yPlaneAction.setEnabled(false); zPlaneAction.setEnabled(false); break; } } private double BRANCH_SNAP_DISTANCE = 0.05; private double NOZZLE_SNAP_DISTANCE = 0.05; private double istep = 10.0; private int decimals = 2; private double pipeDiameter = 0.2; private double elbowRadius = 0.5; private double eps = 0.001; private ArrayList controlPoints = new ArrayList(); private Point3d currentPoint = new Point3d(); private Point3d lastPoint = new Point3d(); private Vector3d customLockDir = null; private Line selectionLine; private List pipeShapes = new ArrayList(); private MaterialState ms; private Resource selectedPort = null; private PositionType selectedType = null; private Resource beginComponentResource = null; private Resource endComponentResource = null; private Resource endComponentPort = null; private PositionType endPortType = null; private Resource highlightedResource = null; private boolean useCamera = false; private List> positions = null; private SelectSplitPointAction splitPointAction; private PositionSelectionGizmo gizmo = null; private enum ToolState{NOT_ACTIVE, INITIALIZING, SELECTING_POSITION, SELECTING_SPLIT, ROUTING}; private ToolState state = ToolState.NOT_ACTIVE; @Override public void activate() { state = ToolState.INITIALIZING; controlPoints.clear(); if (beginComponentResource == null) { List mos = parent.getSelectionAdapter().getSelectedObjects(); if (mos.size() != 1) { end(); return; } beginComponentResource = mos.get(0).getResource(); } parent.getSession().asyncRead(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { positions = checkStartNode(g,beginComponentResource); if (positions.size() == 0) { positions = null; end(); } else { state = ToolState.SELECTING_POSITION; } return GraphRequestStatus.transactionComplete(); } }); if (ms == null) { ms = parent.getRenderingComponent().getDisplaySystem().getRenderer().createMaterialState(); ms.setEmissive(new ColorRGBA(1.f,1.f,1.f,1.f)); } } @Override public void deactivate() { for (Line l : pipeShapes) l.removeFromParent(); pipeShapes.clear(); if (selectionLine != null) selectionLine.removeFromParent(); selectionLine = null; customLockDir = null; setLockType(LockType.NONE,true); beginComponentResource = null; endComponentResource = null; detector.clearConstraintHighlights(); state = ToolState.NOT_ACTIVE; selectedPort = null; selectedType = null; } private List> checkStartNode(Graph g, Resource resource) { List> positions = new ArrayList>(); IEntity beginComponent = EntityFactory.create(g, resource); if (beginComponent.isInstanceOf(ProcessResource.plant3Dresource.Nozzle)) { if (PipingTools2.isFreeNozzle(beginComponent)) { positions.add(new Pair(beginComponent.getSingleRelatedObject(ProcessResource.plant3Dresource.HasControlPoint).getResource(),PositionType.NEXT)); } } else if (beginComponent.isInstanceOf(ProcessResource.plant3Dresource.VariableLengthInlineComponent)) { // variable length inline component is exception from other pipeline components, // since a new pipe can branch it VariableLengthInlineComponent vlic = new VariableLengthInlineComponent(beginComponent); PipeControlPoint pcp = vlic.getControlPoint(); if (pcp.getNext() == null) { positions.add(new Pair(pcp.getResource(),PositionType.NEXT)); } else if (pcp.getPrevious() == null) { positions.add(new Pair(pcp.getResource(),PositionType.PREVIOUS)); } positions.add(new Pair(pcp.getResource(),PositionType.SPLIT)); } else if (beginComponent.isInstanceOf(ProcessResource.plant3Dresource.EndComponent)) { PipelineComponent component = new PipelineComponent(beginComponent); PipeControlPoint pcp = component.getControlPoint(); if (pcp.getNext() == null && pcp.getPrevious() == null) { throw new RuntimeException("End component " + beginComponent.getResource() + " is not connected to anything."); //positions.add(new Pair(pcp.getResource(),PositionType.NEXT)); } for (PipeControlPoint p : pcp.getSubPoint()) { if (p.getNext() == null && p.getPrevious() == null) { positions.add(new Pair(p.getResource(),PositionType.NEXT)); } } } else if (beginComponent.isInstanceOf(ProcessResource.plant3Dresource.PipelineComponent)) { PipelineComponent component = new PipelineComponent(beginComponent); PipeControlPoint pcp = component.getControlPoint(); if (pcp.getNext() == null) { positions.add(new Pair(pcp.getResource(),PositionType.NEXT)); } else if (pcp.getPrevious() == null) { positions.add(new Pair(pcp.getResource(),PositionType.PREVIOUS)); } if (!beginComponent.isInstanceOf(ProcessResource.plant3Dresource.SizeChangeComponent)|| !beginComponent.isInstanceOf(ProcessResource.plant3Dresource.OffsetComponent)) { for (PipeControlPoint p : pcp.getSubPoint()) { if (p.getNext() == null && p.getPrevious() == null) { positions.add(new Pair(p.getResource(),PositionType.NEXT)); } } } } else { return positions; } return positions; } @Override public void update() { switch (state) { case NOT_ACTIVE: return; // TODO : throw Exception? case INITIALIZING: return; case SELECTING_POSITION: updateSelectPosition(); break; case SELECTING_SPLIT: updateSelectSplit(); break; case ROUTING: updateRouting(); break; } return; } private void updateSelectPosition() { if (positions == null) { throw new RuntimeException("positions must be loaded before select position can be activated"); } if (selectedPort != null) { throw new RuntimeException("position is already selected"); } if (positions.size() == 1) { selectedPort = positions.get(0).first; selectedType = positions.get(0).second; state = ToolState.INITIALIZING; if (requiresNewPipeRun()){ if(!getNewPipeRunSpecs()) { end(); return; } } if (selectedType == PositionType.SPLIT) { startSplitting(); } else { startRouting(); } } else if (gizmo == null) { state = ToolState.INITIALIZING; // asyncRead! parent.getSession().asyncRead(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { List> pos = new ArrayList>(); for (Pair p : positions) { IEntity entity = EntityFactory.create(g,p.first); Point3d position = ControlPointTools.getRealPosition(entity, p.second); pos.add(new Pair(position,p.second)); } gizmo = new PositionSelectionGizmo(parent, pos); parent.setGizmo(gizmo); parent.getRenderingComponent().getNoShadowRoot().attachChild(gizmo.getNode()); state = ToolState.SELECTING_POSITION; return GraphRequestStatus.transactionComplete(); } }); } else { gizmo.update(); if (input.keyPressed(KeyEvent.VK_ESCAPE)) { state = ToolState.INITIALIZING; parent.setGizmo(null); gizmo = null; end(); return; } if (gizmo.getSelected() >= 0 && input.mouseClicked() && input.clickButton() == MouseEvent.BUTTON1) { state = ToolState.INITIALIZING; // asyncRead! parent.setGizmo(null); selectedPort = positions.get(gizmo.getSelected()).first; selectedType = positions.get(gizmo.getSelected()).second; gizmo = null; if (selectedType == PositionType.SPLIT) { startSplitting(); return; } else { startRouting(); } } if (useCamera) { parent.getDefaultAction().update(); return; } } } private boolean requiresNewPipeRun() { GraphRequestWithResult createsNewPipeline = new GraphRequestWithResult() { @Override public Boolean performWithResult(Graph g) throws Exception { if(g.isInstanceOf(selectedPort, ProcessResource.plant3Dresource.NozzleControlPoint)) return true; if (selectedType == PositionType.SPLIT) return true; return false; } }; parent.getSession().syncRead(createsNewPipeline); return createsNewPipeline.getResult(); } private boolean getNewPipeRunSpecs() { PipelineDialog dialog; dialog = new PipelineDialog(parent.getRenderingComposite().getShell(),pipeDiameter,elbowRadius); if (dialog.open() == PipelineDialog.CANCEL) { end(); return false; } pipeDiameter = dialog.getPipeDiameter(); elbowRadius = dialog.getTurnRadius(); return true; } private void startRouting() { state = ToolState.INITIALIZING; parent.getSession().asyncRead(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { PipeControlPoint pcp = new PipeControlPoint(g,selectedPort); lastPoint = ControlPointTools.getRealPosition(pcp, selectedType);//G3DTools.getPoint(pcp.getWorldPosition()); if (pcp.isInstanceOf(ProcessResource.plant3Dresource.DirectedControlPoint)) { lock = LockType.CUSTOM; customLockDir = ControlPointTools.getDirectedControlPointDirection(pcp); } else if (pcp.isInstanceOf(ProcessResource.plant3Dresource.FixedLengthControlPoint)|| pcp.isInstanceOf(ProcessResource.plant3Dresource.TurnControlPoint)) { lock = LockType.CUSTOM; if (selectedType == PositionType.NEXT) customLockDir = ControlPointTools.getPathLegDirection(pcp, Direction.NEXT); else customLockDir = ControlPointTools.getPathLegDirection(pcp, Direction.PREVIOUS); } else { lock = LockType.NONE; } IEntity pipeRun = ControlPointTools.getPipeRun(pcp); if (pipeRun != null) { pipeDiameter = pipeRun.getSingleRelatedScalarDouble(ProcessResource.plant3Dresource.HasPipeDiameter); elbowRadius = pipeRun.getSingleRelatedScalarDouble(ProcessResource.plant3Dresource.HasTurnRadius); } return GraphRequestStatus.transactionComplete(); } @Override public void requestCompleted(GraphRequestStatus status) { createLine(); state = ToolState.ROUTING; } }); } private void startSplitting() { parent.getSession().asyncRead(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { PipeControlPoint pcp = new PipeControlPoint(g,selectedPort); Point3d p1 = new Point3d(); Point3d p2 = new Point3d(); ControlPointTools.getInlineControlPointEnds(pcp, p1, p2); splitPointAction.setSplit(p1, p2); splitPointAction.activate(); state = ToolState.SELECTING_SPLIT; return GraphRequestStatus.transactionComplete(); } }); } private void updateSelectSplit() { if (splitPointAction.active()) { splitPointAction.update(); return; } else { throw new RuntimeException("SplitPointAction should be active"); } } @Override public void setSplitPoint(Point3d point) { splitPointAction.deactivate(); if (point == null) { end(); return; } else { lastPoint = point; createLine(); state = ToolState.ROUTING; } } private void updateRouting() { if(input.keyPressed(KeyEvent.VK_ESCAPE)) { controlPoints.clear(); end(); return; } if (input.keyPressed(KeyEvent.VK_C)) { useCamera = !useCamera; cameraAction.setChecked(useCamera); } if (useCamera) { parent.getDefaultAction().update(); return; } parent.getSession().syncRead(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph g) throws Exception { Vector3d o = new Vector3d(); Vector3d d = new Vector3d(); parent.createPickRay(o, d); if (!updateCurrentPoint(o, d)) return GraphRequestStatus.transactionComplete(); //Point3d startPoint = new Point3d(); double mu[] = new double[2]; IEntity endTo = null; PositionType endType = null; IEntity endPort = null; if (parent.getSelectionAdapter().getHighlightSelection().size() > 0) { highlightedResource = parent.getSelectionAdapter().getHighlightSelection().getSelectionList().get(0); } else { highlightedResource = null; } if (highlightedResource != null) { IEntity highlightNode = EntityFactory.create(g,highlightedResource); if (lock == LockType.NONE) { if (highlightNode.isInstanceOf(ProcessResource.plant3Dresource.Nozzle) && endingToNozzle(highlightNode,o,d)) { endTo = highlightNode; } else if (highlightNode.isInstanceOf(ProcessResource.plant3Dresource.VariableLengthInlineComponent)) { endTo = highlightNode; endType = endingToStraight(new VariableLengthInlineComponent(highlightNode),mu,o,d); } else if (highlightNode.isInstanceOf(ProcessResource.plant3Dresource.PipelineComponent) && (endPort = endingToComponent(highlightNode,o,d)) != null) { endTo = highlightNode; } else { updateRoute(o,d); } } else { if (highlightNode.isInstanceOf(ProcessResource.plant3Dresource.VariableLengthInlineComponent) && (endType = endingLockToStraight(new VariableLengthInlineComponent(highlightNode),mu)) != null) { endTo = highlightNode; } else if (highlightNode.isInstanceOf(ProcessResource.plant3Dresource.Nozzle) && endingLockToNozzle(highlightNode)) { endTo = highlightNode; } else if (highlightNode.isInstanceOf(ProcessResource.plant3Dresource.PipelineComponent) && (endPort = endingLockToComponent(highlightNode)) != null) { endTo = highlightNode; } else { updateRoute(o,d); } } } else { updateRoute(o,d); } parent.setViewChanged(true); if (input.mouseClicked()) { if (input.clickButton() == MouseEvent.BUTTON1) { if (controlPoints.size() > 0) { addPoint(); setLockType(LockType.NONE,true); if (endTo != null) { endComponentResource = endTo.getResource(); if (endPort != null) endComponentPort = endPort.getResource(); endPortType = endType; endPiping(); } } 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 (input.clickButton() == MouseEvent.BUTTON2){ detector.updateConstraintReference(); } else if (input.clickButton() == MouseEvent.BUTTON3){ endPiping(); } } return GraphRequestStatus.transactionComplete(); } }); } private void createLine() { controlPoints.add(new Point3d(lastPoint)); Line line = new Line(); line.setRenderState(ms); PipeComponentProvider.createStraightEdges(line, currentPoint, currentPoint, pipeDiameter*0.5); pipeShapes.add(line); parent.getRenderingComponent().getNoShadowRoot().attachChild(line); line.setCullMode(Geometry.CULL_NEVER); } /** * Adds current point to pipeline * */ private void addPoint() { controlPoints.add(new Point3d(currentPoint)); Line line = new Line(); line.setRenderState(ms); PipeComponentProvider.createStraightEdges(line, controlPoints.get(controlPoints.size() - 1), currentPoint, pipeDiameter*0.5); pipeShapes.add(line); parent.getRenderingComponent().getNoShadowRoot().attachChild(line); line.setCullMode(Geometry.CULL_NEVER); lastPoint.set(currentPoint); } /** * Updates tool graphics for current point */ private void updateCurrentPoint() { PipeComponentProvider.createStraightEdges(pipeShapes.get(pipeShapes.size() - 1), controlPoints.get(controlPoints.size() - 1), currentPoint, pipeDiameter*0.5); } /** * Removes last point from pipeline */ public void removePoint() { if (controlPoints.size() < 2) return; controlPoints.remove(controlPoints.size() - 1); pipeShapes.get(pipeShapes.size() - 1).removeFromParent(); pipeShapes.remove(pipeShapes.size() - 1); PipeComponentProvider.createStraightEdges(pipeShapes.get(pipeShapes.size() - 1), controlPoints.get(controlPoints.size() - 1), currentPoint, pipeDiameter*0.5); lastPoint.set(controlPoints.get(controlPoints.size()-1)); if (controlPoints.size() < 2 && customLockDir != null) { setLockType(LockType.CUSTOM, true); } } private boolean endingToNozzle(IEntity nozzle,Vector3d o, Vector3d d) { IEntity pcp = nozzle.getSingleRelatedObject(ProcessResource.plant3Dresource.HasControlPoint); if (pcp != null && (pcp.getAtMostOneRelatedObject(ProcessResource.plant3Dresource.HasNext) != null || pcp.getAtMostOneRelatedObject(ProcessResource.plant3Dresource.HasPrevious) != null)) return false; // nozzle is already connected to pipe currentPoint = G3DTools.getPoint(nozzle.getSingleRelatedObject(ProcessResource.g3dResource.HasWorldPosition)); Point3d previousPipePoint = controlPoints.get(controlPoints.size() - 1); Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint)); if (p != null) { if (p.distance(currentPoint) > NOZZLE_SNAP_DISTANCE) { return false; } } updateCurrentPoint(); setInfoText("Connect to nozzle " + currentPoint); return true; } private PositionType endingToStraight(VariableLengthInlineComponent s, double mu[], Vector3d o, Vector3d d) { String info = ""; Point3d sStart = new Point3d(); Point3d sEnd = new Point3d(); //detector.clearConstraintHighlights(); Point3d previousPipePoint = controlPoints.get(controlPoints.size() - 1); //String st = ""; if (lock == LockType.NONE) { Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint)); if (p != null) { currentPoint = 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(); } PipingTools2.getInlineComponentEnds(s, sStart, sEnd); Vector3d sDir = new Vector3d(sEnd); sDir.sub(sStart); MathTools.intersectStraightStraight(sStart, sDir, o, d, currentPoint, 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) { currentPoint.set(sStart); connectPrev = true; } else if (mu[0] > 1.0) { currentPoint.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 + currentPoint + " " + 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 IEntity endingToComponent(IEntity component, Vector3d o, Vector3d d) { // TODO : scan all empty pcps of the component and select closest one. return null; } private PositionType endingLockToStraight(VariableLengthInlineComponent s, double mu[]) { Point3d sStart = new Point3d();//G3DTools.getPoint(s.getHasControlPoint().getPreviousPoint().getLocalPosition()); Point3d sEnd = new Point3d(); //G3DTools.getPoint(s.getHasControlPoint().getNextPoint().getLocalPosition()); PipingTools2.getInlineComponentEnds(s, sStart, sEnd); Vector3d sDir = new Vector3d(sEnd); sDir.sub(sStart); Vector3d dir = new Vector3d(currentPoint); Point3d prev = controlPoints.get(controlPoints.size() - 1); 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) { currentPoint.set(branchPoint); updateCurrentPoint(); setInfoText("Make branch (l) :" + currentPoint + " " + Math.max(0.0, Math.min(mu[0], 1.0)) + " " + routePoint.lengthSquared()); return PositionType.SPLIT; } return null; } private boolean endingLockToNozzle(IEntity nozzle) { Vector3d dir = new Vector3d(currentPoint); Point3d prev = controlPoints.get(controlPoints.size() - 1); dir.sub(prev); Point3d nozzleLoc = G3DTools.getPoint(nozzle.getSingleRelatedObject(ProcessResource.g3dResource.HasWorldPosition)); double u[] = new double[1]; Vector3d closest = MathTools.closestPointOnStraight(new Point3d(nozzleLoc), new Point3d(prev), new Vector3d(dir), u); double dist = nozzleLoc.distanceSquared(new Point3d(closest)); if (dist < BRANCH_SNAP_DISTANCE) { // FIXME : directions should be checked (insert an elbow) currentPoint.set(nozzleLoc); updateCurrentPoint(); setInfoText("Connect to nozzle (l) :" + currentPoint); return true; } //System.out.println(u[0]); return false; } private IEntity endingLockToComponent(IEntity component) { // we'll must scan all free pcp's and their direction to accept the connection. return null; } private void updateRoute(Vector3d o, Vector3d d) { detector.clearConstraintHighlights(); Point3d previousPipePoint = controlPoints.get(controlPoints.size() - 1); String s = ""; if (lock == LockType.NONE) { Point3d p = detector.getSnappedPoint(o, d, new Vector3d(previousPipePoint)); if (p != null) currentPoint = p; s += detector.getSnapString(); } else { Vector3d dir = new Vector3d(currentPoint); dir.sub(previousPipePoint); Point3d p = detector.getPointSnap(new Vector3d(previousPipePoint), dir); if (p != null) currentPoint = p; s += detector.getSnapString(); } updateCurrentPoint(); s += currentPoint.toString(); setInfoText(s); } private boolean updateCurrentPoint(Vector3d o, Vector3d d) { if (lock != LockType.CUSTOM) { if (input.keyPressed(KeyEvent.VK_X)) { if (lock == LockType.X) setLockType(LockType.YZ,false); else setLockType(LockType.X,false); } if (input.keyPressed(KeyEvent.VK_Y)) { if (lock == LockType.Y) setLockType(LockType.XZ,false); else setLockType(LockType.Y,false); } if (input.keyPressed(KeyEvent.VK_Z)) { if (lock == LockType.Z) setLockType(LockType.XY,false); else setLockType(LockType.Z,false); } if (input.keyPressed(KeyEvent.VK_N)) { setLockType(LockType.NONE,false); } if (input.keyPressed(KeyEvent.VK_BACK_SPACE)) { removePoint(); } } Vector3d point = new Vector3d(lastPoint); boolean step = ((input.moveModifiers() & MouseEvent.CTRL_DOWN_MASK) > 0); switch(lock) { case X: MathTools.intersectStraightStraight(point, new Vector3d(1.0,0.0,0.0), o,d, currentPoint, new Vector3d()); if (step) { currentPoint.x = Math.round(istep * currentPoint.x) / istep; BigDecimal bx = new BigDecimal(currentPoint.x); bx.setScale(decimals, BigDecimal.ROUND_HALF_UP); currentPoint.x = bx.doubleValue(); } break; case Y: MathTools.intersectStraightStraight(point, new Vector3d(0.0,1.0,0.0), o,d, currentPoint, new Vector3d()); if (step) { currentPoint.y = Math.round(istep * currentPoint.y) / istep; BigDecimal bx = new BigDecimal(currentPoint.y); bx.setScale(decimals, BigDecimal.ROUND_HALF_UP); currentPoint.y = bx.doubleValue(); } break; case Z: MathTools.intersectStraightStraight(point, new Vector3d(0.0,0.0,1.0), o,d, currentPoint, new Vector3d()); if (step) { currentPoint.z = Math.round(istep * currentPoint.z) / istep; BigDecimal bx = new BigDecimal(currentPoint.z); bx.setScale(decimals, BigDecimal.ROUND_HALF_UP); currentPoint.z = bx.doubleValue(); }break; case XY: MathTools.intersectStraightPlane(o, d, point, new Vector3d(0.0,0.0,1.0), currentPoint); break; case XZ: MathTools.intersectStraightPlane(o, d, point, new Vector3d(0.0,1.0,0.0), currentPoint); break; case YZ: MathTools.intersectStraightPlane(o, d, point, new Vector3d(1.0,0.0,0.0), currentPoint); break; case NONE: Vector3d normal = parent.getCamera().getUnNormalizedHeading(); normal.normalize(); MathTools.intersectStraightPlane(o, d, point, normal, currentPoint); break; case CUSTOM: MathTools.intersectStraightStraight(point, new Vector3d(customLockDir), o,d, currentPoint, new Vector3d()); double dist = MathTools.distanceFromPlane(new Vector3d(currentPoint), customLockDir, lastPoint); if (dist < 0.0) currentPoint.set(lastPoint); break; default: return false; } return true; } private ArrayList filterPoints() { ArrayList filteredControlPoints = new ArrayList(); // this loop filters control points that are not needed for (int i = 0; i < controlPoints.size() - 2; i++) { Point3d start = controlPoints.get(i); if (i == 0) filteredControlPoints.add(start); Point3d middle = controlPoints.get(i+1); Point3d end = controlPoints.get(i+2); Vector3d dir1 = new Vector3d(middle); dir1.sub(start); Vector3d dir2 = new Vector3d(end); dir2.sub(middle); double angle = dir1.angle(dir2); if (angle > eps && angle < (Math.PI - eps)) filteredControlPoints.add(middle); // if angle is near PI pipe turns back to where it started // if angle is near zero, pipe is straight and there's no need for control point if (i == controlPoints.size() - 3) filteredControlPoints.add(end); } return filteredControlPoints; } private PipeControlPoint connectPipeStart(Graph graph, PipeRun pipeRun, boolean reversed) { PipeControlPoint pcp = new PipeControlPoint(graph,selectedPort); IEntity beginComponent = EntityFactory.create(graph,beginComponentResource); if (beginComponent.isInstanceOf(ProcessResource.plant3Dresource.Nozzle)) { PipingTools2.linkNozzleAndPipeRun(beginComponent, pipeRun); // TODO : set diameters same //reversed = false; } else if (beginComponent.isInstanceOf(ProcessResource.plant3Dresource.VariableLengthInlineComponent)) { switch (selectedType) { case NEXT: { PipeControlPoint tcp = createTurn(graph, pipeRun, 0); connectControlPoints(pcp, tcp, reversed); return tcp; } case PREVIOUS: { PipeControlPoint tcp = createTurn(graph, pipeRun, 0); connectControlPoints(pcp, tcp, reversed); return tcp; } case SPLIT: //reversed = false; // 1. create (non visible) splitting component. PipelineComponent newComponent = PipingTools2.instantiatePipelineComponent(graph, PipingTools2.getPipeRun(beginComponent).getResource(), ProcessResource.plant3Dresource.BranchSplitComponent); PipeControlPoint mainCP = newComponent.getControlPoint(); // 2. create control point for the branch BranchEndControlPoint becp = BranchEndControlPoint.createDefault(graph); mainCP.addSubPoint(becp); pipeRun.addControlPoints(becp); pcp = becp.toPipeControlPoint(); ControlPointTools.setWorldPosition(mainCP, controlPoints.get(0)); PipingTools2.splitVariableLengthComponent(newComponent, beginComponent); } } else if (beginComponent.isInstanceOf(ProcessResource.plant3Dresource.PipelineComponent)) { //if (selectedType == PositionType.PREVIOUS) //reversed = true; //else //reversed = false; } else { throw new RuntimeException("unknown starting component"); } return pcp; } private PipeControlPoint connectPipeEnd(Graph graph, PipeRun pipeline, boolean reversed) { PipeControlPoint pcp = null; IEntity endComponent = null; if (endComponentResource != null) endComponent = EntityFactory.create(graph, endComponentResource); if (endComponent == null) { return null; } else if (endComponent.isInstanceOf(ProcessResource.plant3Dresource.Nozzle)){ pcp = new PipeControlPoint(endComponent.getSingleRelatedObject(ProcessResource.plant3Dresource.HasControlPoint)); PipingTools2.linkNozzleAndPipeRun(endComponent, pipeline); } else if (endComponent.isInstanceOf(ProcessResource.plant3Dresource.VariableLengthInlineComponent)) { assert(endPortType != null); if (endPortType == PositionType.SPLIT) { //System.out.println(lastPoint + " " + currentPoint + " " + positions.get(positions.size() - 1)); Point3d pos = lastPoint; // 1. create (non visible) splitting component. PipelineComponent newComponent = PipingTools2.instantiatePipelineComponent(graph, PipingTools2.getPipeRun(endComponent).getResource(), ProcessResource.plant3Dresource.BranchSplitComponent); PipeControlPoint mainCP = newComponent.getControlPoint(); // 2. create control point for the branch BranchEndControlPoint becp = BranchEndControlPoint.createDefault(graph); mainCP.addSubPoint(becp); pipeline.addControlPoints(becp); pcp = becp.toPipeControlPoint(); ControlPointTools.setWorldPosition(mainCP, pos); PipingTools2.splitVariableLengthComponent(newComponent, endComponent); } else { } } else if (endComponent.isInstanceOf(ProcessResource.plant3Dresource.FixedLengthInlineComponent)) { // attach to selected port, reverse the piperun if needed pcp = new PipeControlPoint(graph,endComponentPort); if (!reversed && pcp.getPrevious() != null || reversed && pcp.getNext() != null) { PipingTools2.reversePipeRun(ControlPointTools.getPipeRun(pcp)); } } return pcp; } private PipeControlPoint createTurn(Graph coreTC,PipeRun pipeRun, int i) { PipelineComponent elbow = PipingTools2.instantiatePipelineComponent(coreTC,pipeRun.getResource(), ProcessResource.plant3Dresource.Elbow); G3DAPI.setWorldPosition(elbow, controlPoints.get(i)); return elbow.getControlPoint(); } private PipeControlPoint createInline(Graph graph, PipeRun pipeRun, int i) { Point3d p1 = controlPoints.get(i-1); Point3d p2 = controlPoints.get(i); Vector3d v = new Vector3d(p2); v.sub(p1); double length = v.length(); v.scale(0.5); v.add(p1); PipelineComponent straight = PipingTools2.instantiatePipelineComponent(graph,pipeRun.getResource(), ProcessResource.plant3Dresource.Straight); G3DAPI.setWorldPosition(straight, v); straight.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, length); return straight.getControlPoint(); } private void connectControlPoints(PipeControlPoint previous, PipeControlPoint pcp, boolean reversed) { if (previous != null) { PipeControlPoint sccp; PipeControlPoint ocp; if (previous.isInstanceOf(ProcessResource.plant3Dresource.DualInlineControlPoint)) { sccp = previous; ocp = sccp.getSubPoint().iterator().next(); } else if (previous.isInstanceOf(ProcessResource.plant3Dresource.DualSubControlPoint)) { ocp = previous; sccp = ocp.getSubPointOf(); } else { if (!reversed) { previous.setNext(pcp); pcp.setPrevious(previous); } else { previous.setPrevious(pcp); pcp.setNext(previous); } return; } if (!reversed) { sccp.setNext(pcp); ocp.setNext(pcp); pcp.setPrevious(ocp); } else { sccp.setPrevious(pcp); ocp.setPrevious(pcp); pcp.setNext(sccp); } } } private void endPiping() { state = ToolState.NOT_ACTIVE; if (controlPoints.size() > 2) // if there's only two control points, filtering does nothing controlPoints = filterPoints(); if (controlPoints.size() > 1) { parent.getSession().asyncWrite(new GraphRequestAdapter() { @Override public GraphRequestStatus perform(Graph graph) throws Exception { PipeRun pipeline = null; boolean reversed; PipelineComponent beginComponent = new PipelineComponent(graph,beginComponentResource); if (beginComponent.isInstanceOf(ProcessResource.plant3Dresource.Nozzle) || selectedType == PositionType.SPLIT) { // pipeline = PipeRun.createDefault(graph); ((ProcessEditor) parent).getPlant(graph).addChild(pipeline); pipeline.setPipeDiameter(pipeDiameter); pipeline.setTurnRadius(elbowRadius); reversed = false; } else if (beginComponent.isInstanceOf(ProcessResource.plant3Dresource.SizeChangeComponent)|| beginComponent.isInstanceOf(ProcessResource.plant3Dresource.OffsetComponent)){ PipeControlPoint pcp = new PipeControlPoint(graph,selectedPort); if (selectedType == PositionType.NEXT) { // get the piperun from offsetpoint reversed = false; pipeline = pcp.getSubPoint().iterator().next().getControlPointOfPipeRun(); } else if (selectedType == PositionType.PREVIOUS) { reversed = true; pipeline = pcp.getControlPointOfPipeRun(); } else { throw new RuntimeException("Wrong PsoitionType " + selectedType + " for a SizeChangeComponent"); } } else if (beginComponent.isInstanceOf(ProcessResource.plant3Dresource.PipelineComponent)) { pipeline = new PipeRun(beginComponent.getParent()); if (selectedType == PositionType.PREVIOUS) { reversed = true; } else { reversed = false; } } else { throw new RuntimeException("Cannot start routing pipe : object not supported!"); } PipeControlPoint previous = null; for (int i = 0; i < controlPoints.size(); i++) { PipeControlPoint pcp = null; if (i == 0) { pcp = connectPipeStart(graph, pipeline,reversed); } else { pcp = createInline(graph, pipeline, i); connectControlPoints(previous, pcp, reversed); previous = pcp; if (i == controlPoints.size() - 1) { pcp = connectPipeEnd(graph, pipeline, reversed); } else { pcp = createTurn(graph,pipeline,i); } } if (pcp != null) { connectControlPoints(previous, pcp, reversed); //pipeline.addSgetHasControlPointSet().add(pcp); previous = pcp; } } return GraphRequestStatus.transactionComplete(); } @Override public void requestCompleted(GraphRequestStatus status) { endThreaded(); } }); } else { endThreaded(); } } private void endThreaded() { parent.getRenderingComposite().getDisplay().asyncExec(new Runnable() { @Override public void run() { end(); } }); } @Override public void init() { this.setText("Route pipe"); this.setToolTipText("Starts routing a new pipeline"); this.setImageDescriptor(Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/Straight.png")); } @Override public boolean usable(Graph g, List resources) { if (resources.size() != 1) { return false; } return checkStartNode(g,resources.get(0)).size() > 0; } public boolean acceptDrop(StructuredResourceSelection s, Resource[] ids) { if (s.size() != 1) return false; if (ids == null) return false; if (ids.length != 1) return false; final Resource dropped = ids[0]; final Resource target = s.iterator().next(); GraphRequestWithResult query = new GraphRequestWithResult() { @Override public Boolean performWithResult(Graph g) throws Exception { if(!g.isInstanceOf(dropped, ProcessResource.plant3Dresource.VariableLengthInlineComponent)) return false; // TODO : check that type is not abstract List list = new ArrayList(); list.add(target); return usable(g, list); } }; parent.getSession().syncRead(query); return query.getResult(); } public void doDrop(StructuredResourceSelection s, Resource[] ids) { beginComponentResource = s.iterator().next(); parent.setCurrentAction(this); } public void setInfoText(String text) { } }