import org.simantics.g2d.participant.KeyUtil;\r
import org.simantics.g2d.participant.MouseUtil;\r
import org.simantics.g2d.participant.TransformUtil;\r
-import org.simantics.g2d.routing.RouterFactory;\r
import org.simantics.g2d.utils.GeometryUtils;\r
import org.simantics.sysdyn.ui.editor.participant.SysdynElementClassProviders.ISysdynElementClassProvider;\r
import org.simantics.sysdyn.ui.editor.routing.DependencyRouter;\r
+import org.simantics.sysdyn.ui.editor.routing.FlowRouter;\r
import org.simantics.sysdyn.ui.elements2.AuxiliaryFactory;\r
import org.simantics.sysdyn.ui.elements2.CloudFactory;\r
import org.simantics.sysdyn.ui.elements2.InputFactory;\r
*/\r
public class PointerInteractor extends org.simantics.g2d.diagram.participant.pointertool.PointerInteractor {\r
\r
- @Dependency Selection selection;\r
- @Dependency KeyUtil keys;\r
- @Dependency TransformUtil util;\r
- @Dependency PickContext pickContext;\r
- @Dependency MouseUtil mice;\r
- @Reference TerminalPainter terminalPainter;\r
- \r
- public PointerInteractor(boolean clickSelect, boolean boxSelect, boolean dragElement, boolean dndDragElement, boolean connect, boolean doubleClickEdit, IElementClassProvider newConnectionClassProvider) {\r
- super(clickSelect, boxSelect, dragElement, dndDragElement, connect, doubleClickEdit, newConnectionClassProvider);\r
- }\r
-\r
- @EventHandler(priority = TOOL_PRIORITY)\r
- public boolean handlePress(MouseButtonPressedEvent me) {\r
- if (!connects()) return false;\r
-// if (me.button != MouseEvent.LEFT_BUTTON) return false;\r
- if (getHint(Hints.KEY_TOOL) != Hints.POINTERTOOL) return false;\r
- assertDependencies();\r
- Point2D curCanvasPos = util.controlToCanvas(me.controlPosition, null);\r
- \r
- // Pick Terminal\r
- TerminalInfo ti = pickTerminal(me.controlPosition);\r
- \r
- if((me.stateMask & MouseEvent.ALT_MASK) == 0) return false;\r
- \r
- if(elementClassProvider != null) {\r
- ConnectTool2 bsi = null;\r
- if (ti != null) {\r
- \r
- IElement terminalElement = ti.e;\r
- \r
-// ElementClass connectionClass = null;\r
- if( me.button == MouseEvent.LEFT_BUTTON) {\r
- if(terminalElement.getElementClass().getId().equals(CloudFactory.class.getSimpleName())) return false;\r
-// diagram.setHint(DiagramHints.ROUTE_ALGORITHM, RouterFactory.create(false, false));\r
- diagram.setHint(DiagramHints.ROUTE_ALGORITHM, new DependencyRouter());\r
-// connectionClass = elementClassProvider.get(ConnectionClasses.DEPENDENCY);\r
- ISysdynElementClassProvider secp = (ISysdynElementClassProvider)elementClassProvider;\r
- secp.put(ElementClasses.CONNECTION, elementClassProvider.get(ConnectionClasses.DEPENDENCY));\r
- \r
- } else if (me.button == MouseEvent.RIGHT_BUTTON) {\r
- String id = terminalElement.getElementClass().getId();\r
- if(id.equals(AuxiliaryFactory.class.getSimpleName())\r
- || id.equals(InputFactory.class.getSimpleName())\r
- || id.equals(ModuleFactory.class.getSimpleName())) return false;\r
- diagram.setHint(DiagramHints.ROUTE_ALGORITHM, RouterFactory.create(true, false));\r
-// connectionClass = elementClassProvider.get(ConnectionClasses.FLOW);\r
- ISysdynElementClassProvider secp = (ISysdynElementClassProvider)elementClassProvider;\r
- secp.put(ElementClasses.CONNECTION, elementClassProvider.get(ConnectionClasses.FLOW));\r
- \r
- }\r
- \r
- IConnectionAdvisor advisor = diagram.getHint(DiagramHints.CONNECTION_ADVISOR);\r
- if (advisor == null || (advisor != null && advisor.canBeginConnection(null, ti.e, ti.t))) {\r
-// bsi = new ConnectTool(diagram, connectionClass, ti.e, ti.t, me.mouseId, curCanvasPos);\r
- bsi = new SysdynConnectTool(ti, me.mouseId, curCanvasPos);\r
- }\r
- } \r
- else if (me.button == MouseEvent.RIGHT_BUTTON) {\r
- \r
- /*\r
- ISnapAdvisor snapAdvisor = getHint(DiagramHints.SNAP_ADVISOR);\r
- if(snapAdvisor != null) \r
- snapAdvisor.snap(curCanvasPos);\r
- */\r
- // Start connection out of thin air, without a terminal.\r
- diagram.setHint(DiagramHints.ROUTE_ALGORITHM, RouterFactory.create(true, true));\r
-// bsi = new ConnectTool(diagram, elementClassProvider.get(ConnectionClasses.FLOW), null, null, me.mouseId, curCanvasPos);\r
- ISysdynElementClassProvider secp = (ISysdynElementClassProvider)elementClassProvider;\r
- secp.put(ElementClasses.CONNECTION, elementClassProvider.get(ConnectionClasses.FLOW));\r
- bsi = new SysdynConnectTool(null, me.mouseId, curCanvasPos); // FIXME\r
- }\r
- \r
- if (bsi != null) {\r
- getContext().add(bsi);\r
- return true;\r
- }\r
- \r
- }\r
- return false;\r
- }\r
- \r
- public List<TerminalInfo> pickTerminals(Point2D controlPos)\r
- {\r
- Rectangle2D controlPickRect = new Rectangle2D.Double(controlPos.getX()-PointerInteractor.PICK_DIST, controlPos.getY()-PointerInteractor.PICK_DIST, PointerInteractor.PICK_DIST*2+1, PointerInteractor.PICK_DIST*2+1);\r
- Shape canvasPickRect = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());\r
- List<TerminalInfo> ti = TerminalUtil.pickTerminals(diagram, canvasPickRect, false, true);\r
- return ti;\r
- }\r
+ @Dependency Selection selection;\r
+ @Dependency KeyUtil keys;\r
+ @Dependency TransformUtil util;\r
+ @Dependency PickContext pickContext;\r
+ @Dependency MouseUtil mice;\r
+ @Reference TerminalPainter terminalPainter;\r
+\r
+ public PointerInteractor(boolean clickSelect, boolean boxSelect, boolean dragElement, boolean dndDragElement, boolean connect, boolean doubleClickEdit, IElementClassProvider newConnectionClassProvider) {\r
+ super(clickSelect, boxSelect, dragElement, dndDragElement, connect, doubleClickEdit, newConnectionClassProvider);\r
+ }\r
+\r
+ @EventHandler(priority = TOOL_PRIORITY)\r
+ public boolean handlePress(MouseButtonPressedEvent me) {\r
+ if (!connects()) return false;\r
+ if (getHint(Hints.KEY_TOOL) != Hints.POINTERTOOL) return false;\r
+ assertDependencies();\r
+ Point2D curCanvasPos = util.controlToCanvas(me.controlPosition, null);\r
+\r
+ // Pick Terminal\r
+ TerminalInfo ti = pickTerminal(me.controlPosition);\r
+\r
+ if((me.stateMask & MouseEvent.ALT_MASK) == 0) return false;\r
+\r
+ if(elementClassProvider != null) {\r
+ ConnectTool2 bsi = null;\r
+ if (ti != null) {\r
+\r
+ IElement terminalElement = ti.e;\r
+\r
+ if( me.button == MouseEvent.LEFT_BUTTON) {\r
+ if(terminalElement.getElementClass().getId().equals(CloudFactory.class.getSimpleName())) return false;\r
+ diagram.setHint(DiagramHints.ROUTE_ALGORITHM, new DependencyRouter());\r
+ diagram.setHint(DiagramHints.KEY_USE_CONNECTION_FLAGS, false);\r
+ ISysdynElementClassProvider secp = (ISysdynElementClassProvider)elementClassProvider;\r
+ secp.put(ElementClasses.CONNECTION, elementClassProvider.get(ConnectionClasses.DEPENDENCY));\r
+\r
+ } else if (me.button == MouseEvent.RIGHT_BUTTON) {\r
+ String id = terminalElement.getElementClass().getId();\r
+ if(id.equals(AuxiliaryFactory.class.getSimpleName())\r
+ || id.equals(InputFactory.class.getSimpleName())\r
+ || id.equals(ModuleFactory.class.getSimpleName())) return false;\r
+ diagram.setHint(DiagramHints.ROUTE_ALGORITHM, new FlowRouter(false));\r
+ diagram.setHint(DiagramHints.KEY_USE_CONNECTION_FLAGS, true);\r
+ ISysdynElementClassProvider secp = (ISysdynElementClassProvider)elementClassProvider;\r
+ secp.put(ElementClasses.CONNECTION, elementClassProvider.get(ConnectionClasses.FLOW));\r
+\r
+ }\r
+\r
+ IConnectionAdvisor advisor = diagram.getHint(DiagramHints.CONNECTION_ADVISOR);\r
+ if (advisor == null || (advisor != null && advisor.canBeginConnection(null, ti.e, ti.t))) {\r
+ bsi = new SysdynConnectTool(ti, me.mouseId, curCanvasPos);\r
+ }\r
+ } \r
+ else if (me.button == MouseEvent.RIGHT_BUTTON) {\r
+ // Start connection out of thin air, without a terminal.\r
+ diagram.setHint(DiagramHints.ROUTE_ALGORITHM, new FlowRouter(false));\r
+ diagram.setHint(DiagramHints.KEY_USE_CONNECTION_FLAGS, true);\r
+ ISysdynElementClassProvider secp = (ISysdynElementClassProvider)elementClassProvider;\r
+ secp.put(ElementClasses.CONNECTION, elementClassProvider.get(ConnectionClasses.FLOW));\r
+ bsi = new SysdynConnectTool(null, me.mouseId, curCanvasPos); // FIXME\r
+ }\r
+\r
+ if (bsi != null) {\r
+ getContext().add(bsi);\r
+ return true;\r
+ }\r
+\r
+ }\r
+ return false;\r
+ }\r
+\r
+ public List<TerminalInfo> pickTerminals(Point2D controlPos)\r
+ {\r
+ Rectangle2D controlPickRect = new Rectangle2D.Double(controlPos.getX()-PointerInteractor.PICK_DIST, controlPos.getY()-PointerInteractor.PICK_DIST, PointerInteractor.PICK_DIST*2+1, PointerInteractor.PICK_DIST*2+1);\r
+ Shape canvasPickRect = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());\r
+ List<TerminalInfo> ti = TerminalUtil.pickTerminals(diagram, canvasPickRect, false, true);\r
+ return ti;\r
+ }\r
}\r
import java.awt.geom.AffineTransform;\r
import java.awt.geom.Path2D;\r
import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
import java.util.ArrayList;\r
import java.util.Collection;\r
import java.util.Collections;\r
import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;\r
import org.simantics.g2d.connection.IConnectionAdvisor;\r
import org.simantics.g2d.diagram.DiagramHints;\r
-import org.simantics.g2d.diagram.DiagramUtils;\r
import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil;\r
import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo;\r
import org.simantics.scenegraph.g2d.nodes.ShapeNode;\r
import org.simantics.structural2.modelingRules.ConnectionJudgement;\r
import org.simantics.sysdyn.SysdynResource;\r
+import org.simantics.sysdyn.ui.editor.routing.FlowRouter;\r
+import org.simantics.sysdyn.ui.elements2.ValveFactory.ValveSceneGraph;\r
import org.simantics.sysdyn.ui.elements2.connections.ConnectionClasses;\r
import org.simantics.ui.SimanticsUI;\r
import org.simantics.utils.datastructures.Callback;\r
return segments;\r
}\r
\r
+ public interface SysdynConnection extends IConnection { }\r
+ \r
protected void updateSG() {\r
if (controlPoints.isEmpty())\r
return;\r
final List<Segment> segments = toSegments(controlPoints);\r
//System.out.println("controlpoints: " + controlPoints);\r
//System.out.println("segments: " + segments);\r
- router.route(new IConnection() {\r
+ router.route(new SysdynConnection() {\r
@Override\r
public Collection<? extends Object> getSegments() {\r
return segments;\r
}\r
\r
private Connector getConnector(ControlPoint cp) {\r
- Connector c = new Connector();\r
+ Connector c = new Connector();\r
c.x = cp.getPosition().getX();\r
c.y = cp.getPosition().getY();\r
\r
TerminalInfo ti = cp.getAttachedTerminal();\r
if (ti != null && (ti == startFlag || ti != endFlag)) {\r
- //System.out.println("CP1: " + cp);\r
- c.parentObstacle = DiagramUtils.getObstacleShape(ti.e);\r
+ if(ti.e.getElementClass().containsClass(ValveSceneGraph.class)) {\r
+ Rectangle2D bounds = ElementUtils.getElementBoundsOnDiagram(ti.e).getBounds2D();\r
+ c.parentObstacle = new Rectangle2D.Double(\r
+ bounds.getCenterX() - FlowRouter.OFFSET,\r
+ bounds.getCenterY() - FlowRouter.OFFSET, \r
+ FlowRouter.OFFSET * 2,\r
+ FlowRouter.OFFSET * 2);\r
+ } else {\r
+ c.parentObstacle = ElementUtils.getElementBoundsOnDiagram(ti.e).getBounds2D();\r
+ }\r
ConnectionDirectionUtil.determineAllowedDirections(c);\r
} else {\r
- //System.out.println("CP2: " + cp);\r
c.parentObstacle = GeometryUtils.transformRectangle(AffineTransform.getTranslateInstance(c.x, c.y),\r
BranchPointClass.DEFAULT_IMAGE2.getBounds());\r
c.allowedDirections = toAllowedDirections(cp.getDirection());\r
final Terminal st = startTerminal != null ? startTerminal.t : null;\r
\r
if(se.equals(endElement)) return null;\r
+ if(Boolean.FALSE.equals(diagram.getHint(DiagramHints.KEY_USE_CONNECTION_FLAGS)) && endElement == null) {\r
+ return null;\r
+ } else {\r
+ System.out.println("FOK");\r
+ }\r
\r
if(endElement == null && endTerminal == null)\r
return advisor.canBeConnected(null, se, st, endElement, endTerminal);\r
}\r
\r
@Override\r
- protected void createConnection() {\r
+ protected void createConnection() {\r
+ \r
+ if(this.connectionJudgment == null) return;\r
\r
final UndoContext uctx = diagram.getHint(DiagramModelHints.KEY_UNDO_CONTEXT);\r
final ConnectionJudgement judgment = this.connectionJudgment;\r
\r
import java.awt.geom.Arc2D;\r
import java.awt.geom.Path2D;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
\r
import org.simantics.g2d.routing.IConnection;\r
import org.simantics.g2d.routing.IConnection.Connector;\r
import org.simantics.g2d.routing.IRouter2;\r
-import org.simantics.sysdyn.ui.elements2.connections.Dependencies;\r
+import org.simantics.sysdyn.ui.elements2.connections.Arcs;\r
import org.simantics.utils.datastructures.Pair;\r
\r
public class DependencyRouter implements IRouter2 {\r
Connector end = connection.getEnd(seg);\r
\r
Pair<Arc2D, Path2D> shapes = new Pair<Arc2D, Path2D>(new Arc2D.Double(), new Path2D.Double());\r
- Dependencies.createArrowShape(shapes,\r
+ createArrowShape(shapes,\r
begin.parentObstacle,\r
end.parentObstacle,\r
0.1);\r
connection.setPath(seg, path);\r
}\r
\r
+ \r
+ /*\r
+ * Total length of the arrow is ARROW_LENGTH1 + ARROW_LENGTH2\r
+ */\r
+ public static double ARROW_LENGTH1 = 0.2;\r
+ public static double ARROW_LENGTH2 = 1.0;\r
+ public static double ARROW_WIDTH = 0.5;\r
+ \r
+\r
+ private static Path2D createArrow(Path2D shape, double x, double y, double dx, double dy) {\r
+ if(shape == null)\r
+ shape = new Path2D.Double();\r
+ else\r
+ shape.reset();\r
+ \r
+ shape.moveTo(x+ARROW_LENGTH1*dx, y+ARROW_LENGTH1*dy);\r
+ x -= ARROW_LENGTH2*dx;\r
+ y -= ARROW_LENGTH2*dy;\r
+ shape.lineTo(x-ARROW_WIDTH*dy, y+ARROW_WIDTH*dx);\r
+ shape.lineTo(x+ARROW_WIDTH*dy, y-ARROW_WIDTH*dx);\r
+ shape.closePath();\r
+ return shape;\r
+ }\r
+ \r
+ public static Arc2D createArc(Arc2D arc, Rectangle2D tail, Rectangle2D head, double angle) {\r
+ double x0 = tail.getCenterX();\r
+ double y0 = tail.getCenterY();\r
+ double x1 = head.getCenterX();\r
+ double y1 = head.getCenterY();\r
+ \r
+// System.out.println("createArrowShape " + x0 + " " + y0 + " " + x1 + " " + y1);\r
+ \r
+ double offset = \r
+ Math.abs(angle) < 1.0e-6\r
+ ? 1e3 * Math.signum(angle)\r
+ : Math.tan(Math.PI*0.5-angle)*0.5;\r
+ \r
+ double cx = 0.5*(x0+x1) + offset * (y1-y0);\r
+ double cy = 0.5*(y0+y1) + offset * (x0-x1);\r
+ double dx0 = x0 - cx;\r
+ double dy0 = y0 - cy;\r
+ double dx1 = x1 - cx;\r
+ double dy1 = y1 - cy;\r
+ \r
+ double r = Math.sqrt(dx0*dx0 + dy0*dy0);\r
+ \r
+// Rectangle2D bounds = new Rectangle2D.Double();\r
+// tail.getBounds(bounds);\r
+ double angle0 = Arcs.nextIntersectingAngle(cx, cy, r, \r
+ Math.atan2(-dy0, dx0), tail, angle < 0.0);\r
+// head.getBounds(bounds);\r
+ double angle1 = Arcs.nextIntersectingAngle(cx, cy, r, \r
+ Math.atan2(-dy1, dx1), head, angle > 0.0);\r
+ double extent = angle1-angle0;\r
+ //double arcAngle = angle0;\r
+ if(angle < 0.0) {\r
+ double temp = angle0; \r
+ angle0 = angle1;\r
+ angle1 = temp;\r
+ extent = -extent;\r
+ } \r
+ if(extent < 0)\r
+ extent += Math.PI*2.0;\r
+ else if(extent >= 360.0)\r
+ extent -= Math.PI*2.0;\r
+ if(arc == null)\r
+ arc = new Arc2D.Double();\r
+ arc.setArc(cx-r, cy-r, 2*r, 2*r, \r
+ Math.toDegrees(angle0), \r
+ Math.toDegrees(extent), \r
+ Arc2D.OPEN);\r
+// \r
+ return arc;\r
+ }\r
+ \r
+ public static Point2D computeCenter(Rectangle2D tail, Rectangle2D head, double angle) {\r
+\r
+ double x0 = tail.getCenterX();\r
+ double y0 = tail.getCenterY();\r
+ double x1 = head.getCenterX();\r
+ double y1 = head.getCenterY();\r
+ \r
+// System.out.println("createArrowShape " + x0 + " " + y0 + " " + x1 + " " + y1);\r
+ \r
+ double offset = \r
+ Math.abs(angle) < 1.0e-6\r
+ ? 1e3 * Math.signum(angle)\r
+ : Math.tan(Math.PI*0.5-angle)*0.5;\r
+ \r
+ double cx = 0.5*(x0+x1) + offset * (y1-y0);\r
+ double cy = 0.5*(y0+y1) + offset * (x0-x1);\r
+ \r
+ return new Point2D.Double(cx, cy);\r
+ \r
+ }\r
+ \r
+ public static Pair<Arc2D, Path2D> createArrowShape(Pair<Arc2D, Path2D> shapes, Rectangle2D tail, Rectangle2D head, double angle) {\r
+ if(shapes == null || shapes.first == null || shapes.second == null) {\r
+ shapes = new Pair<Arc2D, Path2D>(new Arc2D.Double(), new Path2D.Double());\r
+ }\r
+\r
+ createArc(shapes.first, tail, head, angle);\r
+ \r
+ double angle0 = Math.toRadians(shapes.first.getAngleStart());\r
+ double angle1 = Math.toRadians(shapes.first.getAngleStart() + shapes.first.getAngleExtent());\r
+ double x = Math.cos(angle > 0.0 ? angle1 : angle0);\r
+ double y = -Math.sin(angle > 0.0 ? angle1 : angle0);\r
+ double r = shapes.first.getHeight() / 2;\r
+ \r
+ createArrow(shapes.second, shapes.first.getCenterX() + r*x, shapes.first.getCenterY() + r*y, \r
+ angle < 0.0 ? -y : y, \r
+ angle > 0.0 ? -x : x);\r
+\r
+ return shapes;\r
+ \r
+ }\r
}\r
--- /dev/null
+package org.simantics.sysdyn.ui.editor.routing;\r
+\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Path2D;\r
+import java.awt.geom.PathIterator;\r
+import java.awt.geom.Rectangle2D;\r
+import java.util.Collection;\r
+\r
+import org.simantics.g2d.routing.Constants;\r
+import org.simantics.g2d.routing.IConnection;\r
+import org.simantics.g2d.routing.IConnection.Connector;\r
+import org.simantics.g2d.routing.IRouter2;\r
+import org.simantics.sysdyn.ui.editor.participant.SysdynConnectTool.SysdynConnection;\r
+import org.simantics.sysdyn.ui.elements2.connections.Flows;\r
+\r
+public class FlowRouter implements IRouter2{\r
+\r
+ SysdynLocalRouter localRouter;\r
+ \r
+ public static final double OFFSET = 1.0;\r
+\r
+ public FlowRouter() {\r
+ this(false);\r
+ }\r
+\r
+ public FlowRouter(boolean roundCorners) {\r
+ this.localRouter = new SysdynLocalRouter();\r
+ }\r
+\r
+ private Path2D route(double beginX, double beginY, int sDir, Rectangle2D beginObstacle,\r
+ double endX, double endY, int tDir, Rectangle2D endObstacle) {\r
+ localRouter.sx = beginX;\r
+ localRouter.sy = beginY;\r
+ if(beginObstacle == null) {\r
+ localRouter.aMinX = beginX;\r
+ localRouter.aMinY = beginY;\r
+ localRouter.aMaxX = beginX;\r
+ localRouter.aMaxY = beginY;\r
+ }\r
+ else {\r
+ localRouter.aMinX = beginObstacle.getMinX();\r
+ localRouter.aMinY = beginObstacle.getMinY();\r
+ localRouter.aMaxX = beginObstacle.getMaxX();\r
+ localRouter.aMaxY = beginObstacle.getMaxY();\r
+ }\r
+ localRouter.sourceDirection = sDir;\r
+\r
+ localRouter.tx = endX;\r
+ localRouter.ty = endY;\r
+ if(endObstacle == null) {\r
+ localRouter.bMinX = endX;\r
+ localRouter.bMinY = endY;\r
+ localRouter.bMaxX = endX;\r
+ localRouter.bMaxY = endY;\r
+ }\r
+ else {\r
+ localRouter.bMinX = endObstacle.getMinX();\r
+ localRouter.bMinY = endObstacle.getMinY();\r
+ localRouter.bMaxX = endObstacle.getMaxX();\r
+ localRouter.bMaxY = endObstacle.getMaxY();\r
+ }\r
+ localRouter.targetDirection = tDir;\r
+ \r
+ // adjust flows to start and stop within the obstacle\r
+ if(sDir == Constants.EAST || sDir == Constants.WEST) {\r
+ localRouter.aMinY = localRouter.aMinY + OFFSET;\r
+ localRouter.aMaxY = localRouter.aMaxY - OFFSET;\r
+ }\r
+ if(tDir == Constants.EAST || tDir == Constants.WEST) {\r
+ localRouter.bMinY = localRouter.bMinY + OFFSET;\r
+ localRouter.bMaxY = localRouter.bMaxY - OFFSET;\r
+ }\r
+ if(sDir == Constants.SOUTH || sDir == Constants.NORTH) {\r
+ localRouter.aMinX = localRouter.aMinX + OFFSET;\r
+ localRouter.aMaxX = localRouter.aMaxX - OFFSET;\r
+ }\r
+ if(tDir == Constants.SOUTH || tDir == Constants.NORTH) {\r
+ localRouter.bMinX = localRouter.bMinX + OFFSET;\r
+ localRouter.bMaxX = localRouter.bMaxX - OFFSET;\r
+ }\r
+ if(localRouter.sx > localRouter.aMaxX)\r
+ localRouter.sx = localRouter.aMaxX;\r
+ if(localRouter.sx < localRouter.aMinX)\r
+ localRouter.sx = localRouter.aMinX;\r
+ if(localRouter.sy > localRouter.aMaxY)\r
+ localRouter.sy = localRouter.aMaxY;\r
+ if(localRouter.sy < localRouter.aMinY)\r
+ localRouter.sy = localRouter.aMinY;\r
+ if(localRouter.tx > localRouter.bMaxX)\r
+ localRouter.tx = localRouter.bMaxX;\r
+ if(localRouter.tx < localRouter.bMinX)\r
+ localRouter.tx = localRouter.bMinX;\r
+ if(localRouter.ty > localRouter.bMaxY)\r
+ localRouter.ty = localRouter.bMaxY;\r
+ if(localRouter.ty < localRouter.bMinY)\r
+ localRouter.ty = localRouter.bMinY;\r
+\r
+ localRouter.route();\r
+\r
+ Path2D completePath = new Path2D.Double();\r
+ \r
+ double[] coordinates = new double[localRouter.points.size()];\r
+ for(int i = 0; i < localRouter.points.size() ; i++)\r
+ coordinates[i] = localRouter.points.get(i);\r
+ \r
+ boolean vertical = false;\r
+ \r
+ if(coordinates.length > 3) {\r
+ if(coordinates[0] == coordinates[2]) {\r
+ // starts to south or north\r
+ double[] tempCoordinates = new double[coordinates.length - 1];\r
+ for(int i = 0; i < coordinates.length - 1 ; i++)\r
+ tempCoordinates[i] = coordinates[i + 1];\r
+ \r
+ coordinates = new double[tempCoordinates.length];\r
+ for(int i = 0; i < tempCoordinates.length ; i++)\r
+ coordinates[i] = tempCoordinates[i];\r
+ vertical = true;\r
+ }\r
+ } \r
+\r
+ Flows.createOffsetLine(completePath, vertical, OFFSET, coordinates);\r
+ Flows.createOffsetLine(completePath, vertical, -OFFSET, coordinates);\r
+// return localRouter.path;\r
+ \r
+// Flows.createLines(completePath, false, beginObstacle, endObstacle);\r
+ return completePath;\r
+ }\r
+\r
+ @Override\r
+ public void route(IConnection connection) {\r
+ \r
+ if(!(connection instanceof SysdynConnection)) {\r
+ return;\r
+ }\r
+ Collection<?> segments = connection.getSegments();\r
+ if(segments.size() == 1)\r
+ for(Object seg : segments) {\r
+ Connector begin = connection.getBegin(seg);\r
+ Connector end = connection.getEnd(seg);\r
+\r
+ double bestLength = Double.POSITIVE_INFINITY;\r
+ Path2D bestPath = null;\r
+\r
+// for(int sDir : Constants.POSSIBLE_DIRECTIONS[begin.allowedDirections])\r
+ for(int sDir : Constants.POSSIBLE_DIRECTIONS[15])\r
+// for(int tDir : Constants.POSSIBLE_DIRECTIONS[end.allowedDirections]) {\r
+ for(int tDir : Constants.POSSIBLE_DIRECTIONS[15]) {\r
+ Path2D path = route(begin.x, begin.y, sDir, begin.parentObstacle,\r
+ end.x, end.y, tDir, end.parentObstacle);\r
+\r
+ double length = pathCost(path);\r
+ if(length < bestLength) {\r
+ bestLength = length;\r
+ bestPath = path;\r
+ }\r
+ }\r
+\r
+ if(bestPath != null)\r
+ connection.setPath(seg, bestPath);\r
+ }\r
+ }\r
+\r
+ final static AffineTransform IDENTITY = new AffineTransform();\r
+\r
+ static double pathCost(Path2D path) {\r
+ double length = 0.0;\r
+ PathIterator it = path.getPathIterator(IDENTITY);\r
+ double[] temp = new double[6];\r
+ double x=0.0, y=0.0;\r
+ double bendCount = 0.0;\r
+ while(!it.isDone()) {\r
+ bendCount += 1.0;\r
+ if(it.currentSegment(temp) != PathIterator.SEG_MOVETO)\r
+ length += Math.abs(x - temp[0] + y - temp[1]);\r
+ x = temp[0];\r
+ y = temp[1];\r
+ it.next();\r
+ }\r
+ //return length * (6.0 + bendCount);\r
+ return bendCount - 1.0 / length;\r
+ }\r
+\r
+}\r
--- /dev/null
+package org.simantics.sysdyn.ui.editor.routing;\r
+\r
+import java.awt.geom.Path2D;\r
+import java.util.ArrayList;\r
+\r
+import org.simantics.g2d.routing.Constants;\r
+\r
+public class SysdynLocalRouter {\r
+\r
+ static final double OFFSET = 1.0;\r
+ \r
+ double aMinX;\r
+ double aMinY;\r
+ double aMaxX;\r
+ double aMaxY;\r
+\r
+ double bMinX;\r
+ double bMinY;\r
+ double bMaxX;\r
+ double bMaxY;\r
+\r
+ double sx;\r
+ double sy;\r
+\r
+ double tx;\r
+ double ty;\r
+\r
+ int sourceDirection;\r
+ int targetDirection;\r
+\r
+ ArrayList<Double> points;\r
+ Path2D path;\r
+\r
+ public SysdynLocalRouter() {\r
+ }\r
+\r
+ /**\r
+ * Case where both source and target connection directions are to east.\r
+ */\r
+ void routeEast() {\r
+ if (bMinX >= aMaxX || tx >= 0 && !(bMaxY < aMinY || aMaxY < bMinY)) {\r
+ if (ty != 0.0) {\r
+ /* ______ ______\r
+ * | | | |\r
+ * | |----\ | |\r
+ * | | \--->| |\r
+ * |______| |______|\r
+ */\r
+ double mx = 0.5 * (aMaxX + bMinX);\r
+ point(mx, 0.0);\r
+ point(mx, ty);\r
+ } else\r
+ ; // Just a straight line\r
+ } else {\r
+ double x0 = bMinX;\r
+ double x1 = aMaxX;\r
+ double my;\r
+ /* ______\r
+ * | |\r
+ * | |\r
+ * /->| |\r
+ * | |______|\r
+ * |\r
+ * \-------------\\r
+ * ______ |\r
+ * | | |\r
+ * | |-/\r
+ * | |\r
+ * |______|\r
+ * \r
+ * If the elements are separated in Y-direction,\r
+ * route between the elements (this is always the shortest path).\r
+ */\r
+ if (bMaxY < aMinY)\r
+ my = 0.5 * (aMinY + bMaxY);\r
+ else if (aMaxY < bMinY)\r
+ my = 0.5 * (aMaxY + bMinY);\r
+ else {\r
+ /*\r
+ * /------------------------\\r
+ * | ______ ______ |\r
+ * | | | | | |\r
+ * | | | | |--+\r
+ * +->| | | | |\r
+ * | |______| |______| |\r
+ * | |\r
+ * \------------------------/\r
+ * \r
+ * or\r
+ * \r
+ * /-----------\\r
+ * | ______ |\r
+ * | | | |\r
+ * | | | |\r
+ * /--+->| | |\r
+ * | ___|______| |\r
+ * | | | |\r
+ * | | |-+---/\r
+ * | | | |\r
+ * | |______| |\r
+ * | |\r
+ * \----------/\r
+ * \r
+ * We may choose either lower or upper path.\r
+ */\r
+ double upperX0 = bMinX;\r
+ double upperX1 = aMaxX;\r
+ double lowerX0 = bMinX;\r
+ double lowerX1 = aMaxX;\r
+ double upperY = Math.min(aMinY, bMinY);\r
+ double lowerY = Math.max(aMaxY, bMaxY);\r
+\r
+ if (aMinX < bMinX) {\r
+ if (ty < 0.5 * (aMinY + aMaxY))\r
+ lowerX0 = aMinX;\r
+ else\r
+ upperX0 = aMinX;\r
+ }\r
+\r
+ if (bMaxX > aMaxX) {\r
+ if (ty < 0.5 * (aMinY + aMaxY))\r
+ upperX1 = bMaxX;\r
+ else\r
+ lowerX1 = bMaxX;\r
+ }\r
+\r
+ double upperLength = upperX1 - upperY + (upperX1 - upperX0)\r
+ + (ty - upperY) + (tx - upperX0);\r
+ double lowerLength = lowerX1 + lowerY + (lowerX1 - lowerX0)\r
+ + (lowerY - ty) + (tx - lowerX0);\r
+\r
+ if (upperLength < lowerLength) {\r
+ x0 = upperX0;\r
+ x1 = upperX1;\r
+ my = upperY;\r
+ } else {\r
+ x0 = lowerX0;\r
+ x1 = lowerX1;\r
+ my = lowerY;\r
+ }\r
+ }\r
+ point(x1, 0.0);\r
+ point(x1, my);\r
+ point(x0, my);\r
+ point(x0, ty);\r
+ }\r
+ }\r
+\r
+ void routeWest() {\r
+ if (tx >= 0.0) {\r
+ double fx = Math.max(aMaxX, bMaxX);\r
+ double mx = 0.5 * (aMaxX + bMinX);\r
+ if (bMinY >= 0.0 || bMaxY <= 0.0 || mx < 0.0) {\r
+ /* ______\r
+ * | |\r
+ * | |\r
+ * | |<-\\r
+ * ______ |______| |\r
+ * | | |\r
+ * | |-------------------/\r
+ * | |\r
+ * |______|\r
+ */\r
+ point(fx, 0.0);\r
+ }\r
+ else {\r
+ /* /-------------\\r
+ * | ______ |\r
+ * | | | |\r
+ * ______ | | | |\r
+ * | | | | |<-+\r
+ * | |----+ |______| |\r
+ * | | | |\r
+ * |______| \-------------/\r
+ * \r
+ * We may choose either upper or lower path\r
+ * by the path length.\r
+ */\r
+ double my = Math.abs(bMinY) + Math.abs(ty - bMinY) < Math\r
+ .abs(bMaxY) + Math.abs(ty - bMaxY) ? bMinY : bMaxY;\r
+ point(mx, 0.0);\r
+ point(mx, my);\r
+ point(fx, my);\r
+ }\r
+ point(fx, ty);\r
+ } else {\r
+ double fx = Math.max(aMaxX, bMaxX);\r
+ double mx = 0.5 * (aMinX + bMaxX);\r
+ point(fx, 0.0);\r
+ if (ty <= aMinY || ty >= aMaxY\r
+ || (tx >= mx && ty >= aMinY && ty <= aMaxY)) {\r
+ /* ______\r
+ * | |\r
+ * | |\r
+ * | |--\\r
+ * ______ |______| |\r
+ * | | |\r
+ * | |<------------------/\r
+ * | |\r
+ * |______|\r
+ */\r
+ point(fx, ty);\r
+ }\r
+ else {\r
+ /* /-------------\\r
+ * | ______ |\r
+ * | | | |\r
+ * ______ | | | |\r
+ * | | | | |--+\r
+ * | |<---+ |______| |\r
+ * | | | |\r
+ * |______| \-------------/\r
+ * \r
+ * We may choose either upper or lower path\r
+ * by the path length.\r
+ */\r
+ double my = Math.abs(aMinY) + Math.abs(ty - aMinY) < Math\r
+ .abs(aMaxY) + Math.abs(ty - aMaxY) ? aMinY : aMaxY;\r
+ point(fx, my);\r
+ point(mx, my);\r
+ point(mx, ty);\r
+ }\r
+ }\r
+ }\r
+\r
+ void routeSouth() {\r
+ if (tx > 0.0 && (bMinY >= 0.0 || (ty > 0.0 && bMinX <= aMaxX)))\r
+ point(tx, 0.0);\r
+ else if (bMinX > aMaxX) {\r
+ double mx = 0.5 * (aMaxX + bMinX);\r
+ point(mx, 0.0);\r
+ point(mx, bMinY);\r
+ point(tx, bMinY);\r
+ } else {\r
+ double fx = aMaxX;\r
+ double my = 0.5 * (aMaxY + bMinY);\r
+ if (my < aMaxY && (tx < aMinX || ty < aMinY)) {\r
+ my = Math.min(aMinY, bMinY);\r
+ if (bMaxX > aMaxX)\r
+ fx = bMaxX;\r
+ }\r
+ point(fx, 0.0);\r
+ point(fx, my);\r
+ point(tx, my);\r
+ }\r
+ }\r
+\r
+ double xx, xy, yx, yy;\r
+\r
+ void point(double x, double y) {\r
+ lineTo(x * xx + y * yx + sx, x * xy + y * yy + sy);\r
+ }\r
+\r
+ /*\r
+ * should draw only horizontal or vertical lines. Determine the offset and\r
+ * draw both lines.\r
+ */\r
+ void lineTo(double x, double y) {\r
+ double cx = path.getCurrentPoint().getX();\r
+ double cy = path.getCurrentPoint().getY();\r
+ \r
+ if (Math.abs(cx - x) < 1e-5) {\r
+ // Vertical line\r
+ if(points.size() % 2 == 0) {\r
+ points.add(points.get(points.size()-2));\r
+ }\r
+ points.add(y);\r
+ } else if (Math.abs(cy - y) < 1e-5) {\r
+ // Horizontal line\r
+ if(points.size() % 2 != 0) {\r
+ points.add(cy);\r
+ }\r
+ points.add(x);\r
+ }\r
+ path.lineTo(x, y);\r
+ }\r
+ void rotate() {\r
+ double temp;\r
+\r
+ temp = tx;\r
+ tx = ty;\r
+ ty = -temp;\r
+\r
+ temp = aMinX;\r
+ aMinX = aMinY;\r
+ aMinY = -aMaxX;\r
+ aMaxX = aMaxY;\r
+ aMaxY = -temp;\r
+\r
+ temp = bMinX;\r
+ bMinX = bMinY;\r
+ bMinY = -bMaxX;\r
+ bMaxX = bMaxY;\r
+ bMaxY = -temp;\r
+\r
+ temp = xx;\r
+ xx = -xy;\r
+ xy = temp;\r
+\r
+ temp = yx;\r
+ yx = -yy;\r
+ yy = temp;\r
+\r
+ --targetDirection;\r
+ if (targetDirection < 0)\r
+ targetDirection += 4;\r
+ --sourceDirection;\r
+ }\r
+\r
+ void flip() {\r
+ double temp;\r
+\r
+ ty = -ty;\r
+\r
+ temp = aMinY;\r
+ aMinY = -aMaxY;\r
+ aMaxY = -temp;\r
+\r
+ temp = bMinY;\r
+ bMinY = -bMaxY;\r
+ bMaxY = -temp;\r
+\r
+ yx = -yx;\r
+ yy = -yy;\r
+\r
+ targetDirection = (targetDirection + 2) % 4;\r
+ }\r
+\r
+ /*\r
+ * Puts source terminal to origo and rotates the situation so that the\r
+ * connection leaves to east. Finally, the case where target direction is to\r
+ * south is eliminated by optionally flipping the situation.\r
+ */\r
+ void canonicalize() {\r
+ aMinX -= sx;\r
+ aMinY -= sy;\r
+ aMaxX -= sx;\r
+ aMaxY -= sy;\r
+ bMinX -= sx;\r
+ bMinY -= sy;\r
+ bMaxX -= sx;\r
+ bMaxY -= sy;\r
+ tx -= sx;\r
+ ty -= sy;\r
+ xx = yy = 1.0;\r
+ xy = yx = 0.0;\r
+ while (sourceDirection > 0)\r
+ rotate();\r
+\r
+ if (targetDirection == Constants.SOUTH)\r
+ flip();\r
+ }\r
+\r
+ public void route() {\r
+ /*\r
+ * Three cases: 1. Obstacles share X-axis at some point 2. Obstacles\r
+ * share Y-Axis at some point 3. Obstacles don't share axis => Have to\r
+ * make corners.\r
+ */\r
+ if (aMinX < bMinX && aMaxX > bMinX || aMinX < bMaxX && aMaxX > bMaxX) {\r
+ // Obstacles share x-axis => no corner\r
+ double minX = aMinX > bMinX ? aMinX : bMinX;\r
+ double maxX = aMaxX < bMaxX ? aMaxX : bMaxX;\r
+ double middle = minX + (maxX - minX) / 2;\r
+ sx = middle;\r
+ tx = middle;\r
+ if (sy > ty) {\r
+ sy = aMinY;\r
+ ty = bMaxY;\r
+ } else {\r
+ sy = aMaxY;\r
+ ty = bMinY;\r
+ }\r
+ } else if (aMinY < bMinY && aMaxY > bMinY || aMinY < bMaxY\r
+ && aMaxY > bMaxY) {\r
+ // Obstacles share y-axis => no corner\r
+ double minY = aMinY > bMinY ? aMinY : bMinY;\r
+ double maxY = aMaxY < bMaxY ? aMaxY : bMaxY;\r
+ double middle = minY + (maxY - minY) / 2;\r
+ sy = middle;\r
+ ty = middle;\r
+ if (sx > tx) {\r
+ sx = aMinX;\r
+ tx = bMaxX;\r
+ } else {\r
+ sx = aMaxX;\r
+ tx = bMinX;\r
+ }\r
+ } else {\r
+ // Move starting point to the edge of the start element\r
+ switch (sourceDirection) {\r
+ case Constants.WEST:\r
+ sy = ty > aMaxY ? aMaxY : ty < aMinY ? aMinY : ty;\r
+ sx = aMinX;\r
+ break;\r
+ case Constants.EAST:\r
+ sy = ty > aMaxY ? aMaxY : ty < aMinY ? aMinY : ty;\r
+ sx = aMaxX;\r
+ break;\r
+ case Constants.NORTH:\r
+ sy = aMaxY;\r
+ sx = tx > aMaxX ? aMaxX : tx < aMinX ? aMinX : tx;\r
+ break;\r
+ case Constants.SOUTH:\r
+ sy = aMinY;\r
+ sx = tx > aMaxX ? aMaxX : tx < aMinX ? aMinX : tx;\r
+ break;\r
+ }\r
+\r
+ // Move target point to the edge of the ending element\r
+ switch (targetDirection) {\r
+ case Constants.EAST:\r
+ ty = sy > bMaxY ? bMaxY : sy < bMinY ? bMinY : sy;\r
+ tx = bMaxX;\r
+ break;\r
+ case Constants.WEST:\r
+ ty = sy > bMaxY ? bMaxY : sy < bMinY ? bMinY : sy;\r
+ tx = bMinX;\r
+ break;\r
+ case Constants.NORTH:\r
+ ty = bMinY;\r
+ tx = sx > bMaxX ? bMaxX : sx < bMinX ? bMinX : sx;\r
+ break;\r
+ case Constants.SOUTH:\r
+ ty = bMaxY;\r
+ tx = sx > bMaxX ? bMaxX : sx < bMinX ? bMinX : sx;\r
+ break;\r
+ }\r
+ }\r
+\r
+ path = new Path2D.Double();\r
+ points = new ArrayList<Double>();\r
+\r
+ path.moveTo(sx, sy);\r
+ points.add(sx);\r
+ points.add(sy);\r
+\r
+ // Vertical and horizontal cases\r
+ if ((Math.abs(sx - tx) < 1e-5 && isVertical())\r
+ || (Math.abs(sy - ty) < 1e-5 && isHorizontal())) {\r
+ lineTo(tx, ty);\r
+ return;\r
+ }\r
+ \r
+\r
+ canonicalize();\r
+ switch (targetDirection) {\r
+ case Constants.EAST:\r
+ routeWest();\r
+ break;\r
+ case Constants.WEST:\r
+ routeEast();\r
+ break;\r
+ case Constants.NORTH:\r
+ routeSouth();\r
+ break;\r
+ }\r
+\r
+ point(tx, ty);\r
+\r
+ }\r
+ \r
+ private boolean isVertical() {\r
+ return\r
+ (sourceDirection == Constants.SOUTH && targetDirection == Constants.SOUTH)\r
+ ||\r
+ (sourceDirection == Constants.NORTH && targetDirection == Constants.NORTH);\r
+ }\r
+ \r
+ private boolean isHorizontal() {\r
+ return\r
+ (sourceDirection == Constants.EAST && targetDirection == Constants.EAST)\r
+ ||\r
+ (sourceDirection == Constants.WEST && targetDirection == Constants.WEST);\r
+ }\r
+}\r
+++ /dev/null
-/*******************************************************************************\r
- * Copyright (c) 2010 Association for Decentralized Information Management in\r
- * Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.sysdyn.ui.elements2.connections;\r
-\r
-import java.awt.geom.Arc2D;\r
-import java.awt.geom.Path2D;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-\r
-import org.simantics.utils.datastructures.Pair;\r
-\r
-public class Dependencies {\r
- \r
- /*\r
- * Total length of the arrow is ARROW_LENGTH1 + ARROW_LENGTH2\r
- */\r
- public static double ARROW_LENGTH1 = 0.2;\r
- public static double ARROW_LENGTH2 = 1.0;\r
- public static double ARROW_WIDTH = 0.5;\r
- \r
-//\r
-// // Auxiliary \r
-// double angle0;\r
-// double angle1;\r
-// double cx;\r
-// double cy;\r
-// double r;\r
-// \r
-// // Scene graph\r
-// ShapeNode arcNode;\r
-// FilledShapeNode arrowNode;\r
-// \r
-// public Dependencies() { \r
-// }\r
-// \r
-// public Dependencies(Connectable tail, Connectable head) {\r
-// super();\r
-// this.tail = tail;\r
-// this.head = head;\r
-// }\r
-// \r
-// @Override\r
-// public void elementUpdated(IElement element) {\r
-// update(); \r
-// } \r
-//\r
-// @Override\r
-// public void remove() {\r
-// arcNode.remove();\r
-// arrowNode.remove();\r
-// tail.removeListener(this);\r
-// head.removeListener(this);\r
-// super.remove();\r
-// }\r
-//\r
-// @Override\r
-// public void init(G2DParentNode parent) {\r
-// tail.addListener(this);\r
-// head.addListener(this);\r
-// \r
-// arcNode = parent.addNode(ShapeNode.class);\r
-// arcNode.setScaleStroke(true);\r
-// arcNode.setStroke(new BasicStroke(1));\r
-// arrowNode = parent.addNode(FilledShapeNode.class);\r
-// update();\r
-// }\r
-// \r
- \r
- private static Path2D createArrow(Path2D shape, double x, double y, double dx, double dy) {\r
- if(shape == null)\r
- shape = new Path2D.Double();\r
- else\r
- shape.reset();\r
- \r
- shape.moveTo(x+ARROW_LENGTH1*dx, y+ARROW_LENGTH1*dy);\r
- x -= ARROW_LENGTH2*dx;\r
- y -= ARROW_LENGTH2*dy;\r
- shape.lineTo(x-ARROW_WIDTH*dy, y+ARROW_WIDTH*dx);\r
- shape.lineTo(x+ARROW_WIDTH*dy, y-ARROW_WIDTH*dx);\r
- shape.closePath();\r
- return shape;\r
- }\r
- \r
- public static Arc2D createArc(Arc2D arc, Rectangle2D tail, Rectangle2D head, double angle) {\r
- double x0 = tail.getCenterX();\r
- double y0 = tail.getCenterY();\r
- double x1 = head.getCenterX();\r
- double y1 = head.getCenterY();\r
- \r
-// System.out.println("createArrowShape " + x0 + " " + y0 + " " + x1 + " " + y1);\r
- \r
- double offset = \r
- Math.abs(angle) < 1.0e-6\r
- ? 1e3 * Math.signum(angle)\r
- : Math.tan(Math.PI*0.5-angle)*0.5;\r
- \r
- double cx = 0.5*(x0+x1) + offset * (y1-y0);\r
- double cy = 0.5*(y0+y1) + offset * (x0-x1);\r
- double dx0 = x0 - cx;\r
- double dy0 = y0 - cy;\r
- double dx1 = x1 - cx;\r
- double dy1 = y1 - cy;\r
- \r
- double r = Math.sqrt(dx0*dx0 + dy0*dy0);\r
- \r
-// Rectangle2D bounds = new Rectangle2D.Double();\r
-// tail.getBounds(bounds);\r
- double angle0 = Arcs.nextIntersectingAngle(cx, cy, r, \r
- Math.atan2(-dy0, dx0), tail, angle < 0.0);\r
-// head.getBounds(bounds);\r
- double angle1 = Arcs.nextIntersectingAngle(cx, cy, r, \r
- Math.atan2(-dy1, dx1), head, angle > 0.0);\r
- double extent = angle1-angle0;\r
- //double arcAngle = angle0;\r
- if(angle < 0.0) {\r
- double temp = angle0; \r
- angle0 = angle1;\r
- angle1 = temp;\r
- extent = -extent;\r
- } \r
- if(extent < 0)\r
- extent += Math.PI*2.0;\r
- else if(extent >= 360.0)\r
- extent -= Math.PI*2.0;\r
- if(arc == null)\r
- arc = new Arc2D.Double();\r
- arc.setArc(cx-r, cy-r, 2*r, 2*r, \r
- Math.toDegrees(angle0), \r
- Math.toDegrees(extent), \r
- Arc2D.OPEN);\r
-// \r
- return arc;\r
- }\r
- \r
- public static Point2D computeCenter(Rectangle2D tail, Rectangle2D head, double angle) {\r
-\r
- double x0 = tail.getCenterX();\r
- double y0 = tail.getCenterY();\r
- double x1 = head.getCenterX();\r
- double y1 = head.getCenterY();\r
- \r
-// System.out.println("createArrowShape " + x0 + " " + y0 + " " + x1 + " " + y1);\r
- \r
- double offset = \r
- Math.abs(angle) < 1.0e-6\r
- ? 1e3 * Math.signum(angle)\r
- : Math.tan(Math.PI*0.5-angle)*0.5;\r
- \r
- double cx = 0.5*(x0+x1) + offset * (y1-y0);\r
- double cy = 0.5*(y0+y1) + offset * (x0-x1);\r
- \r
- return new Point2D.Double(cx, cy);\r
- \r
- }\r
- \r
- public static Pair<Arc2D, Path2D> createArrowShape(Pair<Arc2D, Path2D> shapes, Rectangle2D tail, Rectangle2D head, double angle) {\r
- if(shapes == null || shapes.first == null || shapes.second == null) {\r
- shapes = new Pair<Arc2D, Path2D>(new Arc2D.Double(), new Path2D.Double());\r
- }\r
-\r
- createArc(shapes.first, tail, head, angle);\r
- \r
- double angle0 = Math.toRadians(shapes.first.getAngleStart());\r
- double angle1 = Math.toRadians(shapes.first.getAngleStart() + shapes.first.getAngleExtent());\r
- double x = Math.cos(angle > 0.0 ? angle1 : angle0);\r
- double y = -Math.sin(angle > 0.0 ? angle1 : angle0);\r
- double r = shapes.first.getHeight() / 2;\r
- \r
- createArrow(shapes.second, shapes.first.getCenterX() + r*x, shapes.first.getCenterY() + r*y, \r
- angle < 0.0 ? -y : y, \r
- angle > 0.0 ? -x : x);\r
-\r
- return shapes;\r
- \r
- }\r
-// \r
-// public void update() {\r
-// if(arcNode != null)\r
-// updateSceneGraph();\r
-// fireElementUpdated();\r
-// }\r
-// \r
-// @Override\r
-// public void getBounds(Rectangle2D bounds) {\r
-// bounds.setFrame(arcNode.getBounds());\r
-// }\r
-//\r
-// @Override\r
-// public boolean hitTest(double x, double y, double tolerance) {\r
-// double dx = x-cx;\r
-// double dy = y-cy;\r
-// double dist = dx*dx + dy*dy;\r
-// if(dist < (r+tolerance)*(r+tolerance) &&\r
-// dist > (r-tolerance)*(r-tolerance)) {\r
-// double angle = Arcs.normalizeAngle(Math.atan2(-dy, dx));\r
-// if(Arcs.areClockwiseOrdered(angle0, angle, angle1))\r
-// return true;\r
-// }\r
-// return false;\r
-// }\r
-// \r
-// class EventHandler extends DragEventHandler {\r
-// @Override\r
-// protected boolean begin(IDiagramEditor editor, DragEvent event) {\r
-// return event.startModifiers.equals("left");\r
-// }\r
-// \r
-// @Override\r
-// protected void update(IDiagramEditor editor, DragEvent event) {\r
-// if(event == null)\r
-// return;\r
-// angle = Arcs.angleOfArc(\r
-// tail.getOrigo().getX(), tail.getOrigo().getY(), \r
-// event.current.getX(), event.current.getY(), \r
-// head.getOrigo().getX(), head.getOrigo().getY()\r
-// );\r
-// Dependencies.this.update();\r
-// editor.requestRepaint();\r
-// }\r
-// }\r
-//\r
-// @SuppressWarnings("unchecked")\r
-// @Override\r
-// public <T> T getInterface(Class<T> clazz) {\r
-// if(clazz == IEventHandler.class) \r
-// return (T)new EventHandler();\r
-// return super.getInterface(clazz);\r
-// }\r
-//\r
-// @Override\r
-// public void elementRemoved(IElement element) {\r
-// remove(); \r
-// }\r
- \r
-}\r
import org.simantics.g2d.element.ElementClass;\r
import org.simantics.g2d.element.IElement;\r
import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;\r
-import org.simantics.g2d.routing.RouterFactory;\r
import org.simantics.layer0.Layer0;\r
+import org.simantics.sysdyn.ui.editor.routing.DependencyRouter;\r
import org.simantics.utils.datastructures.Pair;\r
\r
/**\r
final AtomicInteger ready = new AtomicInteger(1);\r
final ConcurrentSkipListMap<String, Pair<Resource, Object>> properties = new ConcurrentSkipListMap<String, Pair<Resource, Object>>();\r
\r
- element.setHint(DiagramHints.ROUTE_ALGORITHM, RouterFactory.create(false, false));\r
+ element.setHint(DiagramHints.ROUTE_ALGORITHM, new DependencyRouter());\r
\r
graph.forEachPredicate(elementResource, new AsyncMultiProcedure<Resource>() {\r
\r
import org.simantics.g2d.elementclass.connection.EdgeClass.FixedTransform;\r
import org.simantics.scenegraph.g2d.G2DParentNode;\r
import org.simantics.scenegraph.utils.NodeUtil;\r
+import org.simantics.sysdyn.ui.editor.routing.DependencyRouter;\r
import org.simantics.utils.datastructures.Callback;\r
import org.simantics.utils.datastructures.Pair;\r
import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
System.out.println("pickTest no node!");\r
return false;\r
}\r
- \r
return Arcs.hitTest(node.getBeginBounds(), node.getEndBounds(), node.getAngle(), pickRect.getCenterX(), pickRect.getCenterY(), 3.0);\r
\r
}\r
\r
@Override\r
public void init(IElement e, G2DParentNode parent) {\r
- ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE, "edge_" + e.hashCode(), DependencyNode.class);\r
+ DependencyNode node = ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE, "edge_" + e.hashCode(), DependencyNode.class);\r
+ node.setAngle(0.1);\r
+ \r
update(e);\r
}\r
\r
\r
DependencyNode node = e.getHint(KEY_SG_NODE);\r
if(node == null) return;\r
- \r
final IDiagram diagram = ElementUtils.peekDiagram(e);\r
\r
node.setFieldListener(new PropertyChangeListener() {\r
node.setEndBounds(endTerminalShape.getBounds2D());\r
node.setStroke(stroke);\r
node.setColor(c);\r
- node.setAngle(0.1);\r
+ node.setShapes(DependencyRouter.createArrowShape(node.getShapes(), node.getBeginBounds(), node.getEndBounds(), node.getAngle()));\r
\r
Map<String, Pair<Resource, Object>> properties = e.getHint(DiagramHints.PROPERTIES);\r
if(properties != null) {\r
path = new Path2D.Double();\r
else\r
path.reset();\r
- path.append(node.getArc(), false);\r
+ path.append(node.getShapes().first, false);\r
eh.setPath(e, path);\r
\r
}\r
import org.simantics.scenegraph.g2d.G2DNode;\r
import org.simantics.scenegraph.g2d.events.ISGMouseEvent;\r
import org.simantics.scenegraph.utils.NodeUtil;\r
+import org.simantics.sysdyn.ui.editor.routing.DependencyRouter;\r
import org.simantics.utils.datastructures.Pair;\r
\r
public class DependencyNode extends G2DNode implements ISelectionPainterNode, MouseListener, MouseMotionListener {\r
@SyncField("angle")\r
public void setAngle(Double angle) {\r
this.angle = angle.doubleValue();\r
+ if(this.beginBounds != null && this.endBounds != null)\r
+ this.shapes = DependencyRouter.createArrowShape(this.shapes, this.beginBounds, this.endBounds, this.angle);\r
+ }\r
+ \r
+ @PropertySetter("shapes")\r
+ @SyncField("shapes")\r
+ public void setShapes(Pair<Arc2D, Path2D> shapes) {\r
+ this.shapes = shapes;\r
}\r
\r
public Color getColor() {\r
return angle;\r
}\r
\r
- public Arc2D getArc() {\r
- return shapes.first;\r
+ public Pair<Arc2D, Path2D> getShapes() {\r
+ return shapes;\r
}\r
\r
+\r
+ \r
@Override\r
public void render(Graphics2D g) {\r
if(beginBounds == null || endBounds == null) return;\r
// Removed to let the global control handle rendering quality issues.\r
//g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);\r
\r
- Dependencies.createArrowShape(shapes, beginBounds, endBounds, angle);\r
-\r
boolean selected = NodeUtil.isSelected(this, 2);\r
if(selected) {\r
g.setColor(Color.PINK);\r
import org.simantics.g2d.element.IElement;\r
import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;\r
import org.simantics.g2d.elementclass.connection.ConnectionClass;\r
-import org.simantics.g2d.routing.RouterFactory;\r
+import org.simantics.sysdyn.ui.editor.routing.FlowRouter;\r
\r
/**\r
* An element class for single connection entity elements. A connection entity\r
@Override\r
public void load(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementResource,\r
final IElement element, final AsyncProcedure<IElement> procedure) {\r
- element.setHint(DiagramHints.ROUTE_ALGORITHM, RouterFactory.create(true, true));\r
+ element.setHint(DiagramHints.ROUTE_ALGORITHM, new FlowRouter(false));\r
procedure.execute(graph, element);\r
}\r
\r
*******************************************************************************/\r
package org.simantics.sysdyn.ui.elements2.connections;\r
\r
-import java.awt.BasicStroke;\r
import java.awt.Color;\r
-import java.awt.Shape;\r
import java.awt.Stroke;\r
-import java.util.Map;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.GeneralPath;\r
+import java.awt.geom.Path2D;\r
+import java.awt.geom.PathIterator;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Iterator;\r
+import java.util.List;\r
\r
-import org.simantics.db.Resource;\r
import org.simantics.g2d.diagram.DiagramHints;\r
import org.simantics.g2d.diagram.IDiagram;\r
import org.simantics.g2d.diagram.handler.Topology;\r
import org.simantics.g2d.diagram.handler.Topology.Connection;\r
+import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil;\r
import org.simantics.g2d.element.ElementClass;\r
import org.simantics.g2d.element.ElementUtils;\r
import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.SceneGraphNodeKey;\r
+import org.simantics.g2d.element.handler.BendsHandler;\r
+import org.simantics.g2d.element.handler.EdgeVisuals;\r
+import org.simantics.g2d.element.handler.EdgeVisuals.ArrowType;\r
import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
-import org.simantics.g2d.element.handler.SceneGraph;\r
-import org.simantics.g2d.element.handler.TerminalLayout;\r
-import org.simantics.g2d.element.handler.Transform;\r
import org.simantics.g2d.element.handler.impl.ConfigurableEdgeVisuals;\r
import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;\r
import org.simantics.g2d.element.handler.impl.FillColorImpl;\r
import org.simantics.g2d.element.handler.impl.ParentImpl;\r
import org.simantics.g2d.element.handler.impl.ShapePick;\r
import org.simantics.g2d.element.handler.impl.SimpleElementLayers;\r
+import org.simantics.g2d.elementclass.BranchPoint;\r
import org.simantics.g2d.elementclass.connection.EdgeClass.EdgeHandler;\r
import org.simantics.g2d.elementclass.connection.EdgeClass.FixedTransform;\r
+import org.simantics.g2d.elementclass.connection.EdgeSceneGraph;\r
+import org.simantics.g2d.routing.ConnectionDirectionUtil;\r
+import org.simantics.g2d.routing.Constants;\r
+import org.simantics.g2d.routing.IRouter2;\r
+import org.simantics.g2d.utils.PathUtils;\r
import org.simantics.scenegraph.g2d.G2DParentNode;\r
-import org.simantics.scenegraph.utils.NodeUtil;\r
+import org.simantics.scenegraph.g2d.nodes.EdgeNode;\r
+import org.simantics.sysdyn.ui.editor.participant.SysdynConnectTool.SysdynConnection;\r
+import org.simantics.sysdyn.ui.editor.routing.FlowRouter;\r
import org.simantics.sysdyn.ui.elements2.ValveFactory.ValveSceneGraph;\r
-import org.simantics.utils.datastructures.Pair;\r
-import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
\r
-/**\r
- * @author Toni Kalajainen\r
- */\r
public class FlowEdgeClass {\r
\r
- // TODO scale, rotate, move, transform\r
- public static final ElementClass CLASS =\r
- ElementClass.compile(\r
- SysdynEdgeSceneGraph.INSTANCE,\r
- EdgeHandler.INSTANCE,\r
- ConfigurableEdgeVisuals.DEFAULT,\r
- FillColorImpl.BLACK,\r
- FixedTransform.INSTANCE,\r
- ShapePick.INSTANCE,\r
- ConnectionSelectionOutline.INSTANCE,\r
- SimpleElementLayers.INSTANCE,\r
- ParentImpl.INSTANCE\r
- ).setId("EdgeClass.STRAIGHT");\r
-\r
- public static class SysdynEdgeSceneGraph implements SceneGraph {\r
-\r
- private static final long serialVersionUID = 2914383071126238996L;\r
-\r
- public static final SysdynEdgeSceneGraph INSTANCE = new SysdynEdgeSceneGraph();\r
-\r
- public static final Stroke ARROW_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);\r
-\r
- public static final Key KEY_SG_NODE = new SceneGraphNodeKey(FlowNode.class, "EDGE_NODE");\r
-\r
- @Override\r
- public void init(IElement e, G2DParentNode parent) {\r
- ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE, "edge_" + e.hashCode(), FlowNode.class);\r
- update(e);\r
- }\r
-\r
- @Override\r
- public void cleanup(IElement e) {\r
- ElementUtils.removePossibleNode(e, KEY_SG_NODE);\r
- }\r
-\r
- public void update(final IElement e) {\r
- \r
- FlowNode node = e.getHint(KEY_SG_NODE);\r
- if(node == null) return;\r
-\r
- final IDiagram diagram = ElementUtils.peekDiagram(e);\r
- \r
- // Flownode does not change any property, hence listener is not needed\r
-// node.setFieldListener(new PropertyChangeListener() {\r
-//\r
-// @Override\r
-// public void propertyChange(final PropertyChangeEvent event) {\r
-// \r
-// DiagramUtils.mutateDiagram(diagram, new Callback<DiagramMutator>() {\r
-// \r
-// @Override\r
-// public void run(DiagramMutator mutator) {\r
-//\r
-// String field = event.getPropertyName();\r
-// Map<String, Pair<Resource, Object>> properties = e.getHint(DiagramHints.PROPERTIES);\r
-// Pair<Resource, Object> property = properties.get(field);\r
-// \r
-// mutator.modifyProperty(e, property.first, event.getNewValue());\r
-// \r
-// }\r
-// \r
-// });\r
-// \r
-// }\r
-// \r
-// });\r
- \r
- Stroke stroke = new BasicStroke(0.1f);\r
- Color c = ElementUtils.getFillColor(e, Color.BLACK);\r
-\r
- Shape beginTerminalShape = null;\r
- Shape endTerminalShape = null;\r
- boolean toValve = false;\r
- if (diagram != null) {\r
- Topology topology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class);\r
- if (topology != null) {\r
- Connection beginConnection = topology.getConnection(e, EdgeEnd.Begin);\r
- Connection endConnection = topology.getConnection(e, EdgeEnd.End);\r
- beginTerminalShape = getCanvasTerminalShape(beginConnection);\r
- endTerminalShape = getCanvasTerminalShape(endConnection);\r
- toValve = endConnection.node.getElementClass().containsClass(ValveSceneGraph.class);\r
-// System.out.println("end connection class = " + endConnection.node.getElementClass());\r
- }\r
- }\r
-\r
- if(beginTerminalShape == null || endTerminalShape == null) return;\r
- \r
- node.setBeginBounds(beginTerminalShape.getBounds2D());\r
- node.setEndBounds(endTerminalShape.getBounds2D());\r
- node.setStroke(stroke);\r
- node.setColor(c);\r
- node.setToValve(toValve);\r
-// System.out.println("set toValve = " + toValve);\r
-\r
- Map<String, Pair<Resource, Object>> properties = e.getHint(DiagramHints.PROPERTIES);\r
- if(properties != null) {\r
- for(Map.Entry<String, Pair<Resource, Object>> entry : properties.entrySet()) {\r
- NodeUtil.setPropertyIfSupported(entry.getKey(), entry.getValue().second, node); \r
-// node.setProperty(entry.getKey(), entry.getValue().second);\r
- }\r
- }\r
- \r
- }\r
-\r
- private static Shape getCanvasTerminalShape(Connection connection) {\r
- if (connection != null && connection.node != null && connection.terminal != null) {\r
- TerminalLayout layout = connection.node.getElementClass().getAtMostOneItemOfClass(TerminalLayout.class);\r
- if (layout != null) {\r
- //return layout.getTerminalShape(connection.node, connection.terminal);\r
- Shape shp = layout.getTerminalShape(connection.node, connection.terminal);\r
- Transform tr = connection.node.getElementClass().getAtMostOneItemOfClass(Transform.class);\r
- if (tr == null)\r
- return shp;\r
-\r
- return tr.getTransform(connection.node).createTransformedShape(shp);\r
- \r
- }\r
- }\r
- return null;\r
- }\r
-\r
- }\r
+ // TODO scale, rotate, move, transform\r
+ public static final ElementClass CLASS = ElementClass.compile(\r
+ FlowEdgeSceneGraph.INSTANCE, EdgeHandler.INSTANCE,\r
+ ConfigurableEdgeVisuals.DEFAULT, FillColorImpl.BLACK,\r
+ FixedTransform.INSTANCE, ShapePick.INSTANCE,\r
+ ConnectionSelectionOutline.INSTANCE, SimpleElementLayers.INSTANCE,\r
+ ParentImpl.INSTANCE).setId("FlowEdgeClass");\r
+\r
+ public static class FlowEdgeSceneGraph extends EdgeSceneGraph {\r
+\r
+ private static final long serialVersionUID = -8737581995034992604L;\r
+\r
+ public static final EdgeSceneGraph INSTANCE = new FlowEdgeSceneGraph();\r
+\r
+ @Override\r
+ public void init(IElement e, G2DParentNode parent) {\r
+ ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE,\r
+ "edge_" + e.hashCode(), FlowEdgeNode.class);\r
+ final IDiagram diagram = ElementUtils.peekDiagram(e);\r
+\r
+ boolean toValve = false;\r
+ if (diagram != null) {\r
+ Topology topology = diagram.getDiagramClass()\r
+ .getAtMostOneItemOfClass(Topology.class);\r
+ if (topology != null) {\r
+ Connection endConnection = topology.getConnection(e,\r
+ EdgeEnd.End);\r
+ toValve = endConnection.node.getElementClass()\r
+ .containsClass(ValveSceneGraph.class);\r
+ }\r
+ }\r
+\r
+ ConfigurableEdgeVisuals cev = e.getElementClass()\r
+ .getAtMostOneItemOfClass(ConfigurableEdgeVisuals.class);\r
+ if (cev != null) {\r
+ if (toValve)\r
+ cev.setArrowType(e, EdgeEnd.End, ArrowType.None);\r
+ else {\r
+ cev.setArrowType(e, EdgeEnd.End, ArrowType.Fill);\r
+ cev.setArrowSize(e, EdgeEnd.End, 2);\r
+ }\r
+ }\r
+\r
+ updateRoute(e);\r
+ update(e);\r
+\r
+ }\r
+\r
+ private static Path2D trimLineToArrows(Path2D line,\r
+ ArrowType endArrowType, double endArrowSize) {\r
+ Path2D result = new Path2D.Double();\r
+ PathIterator pi = line.getPathIterator(null);\r
+\r
+ double lineTo[] = new double[2];\r
+ double prevLine[] = new double[2];\r
+ double dummy[] = new double[2];\r
+\r
+ while (!pi.isDone()) {\r
+ int type = pi.currentSegment(lineTo);\r
+ pi.next();\r
+ int nextType = pi.currentSegment(dummy);\r
+\r
+ if (type == PathIterator.SEG_LINETO\r
+ && nextType == PathIterator.SEG_MOVETO) {\r
+ // this is the end of one line\r
+ if (endArrowType == ArrowType.Fill) {\r
+ double dx = (lineTo[0] - prevLine[0]);\r
+ double dy = (lineTo[1] - prevLine[1]);\r
+ double x2 = dx * dx;\r
+ double y2 = dy * dy;\r
+ double len = Math.sqrt(x2 + y2);\r
+ if (len > endArrowSize) {\r
+ double scale = endArrowSize / len;\r
+ lineTo[0] -= dx * scale;\r
+ lineTo[1] -= dy * scale;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (type == 0) {\r
+ result.moveTo(lineTo[0], lineTo[1]);\r
+ } else if (type == 1) {\r
+ result.lineTo(lineTo[0], lineTo[1]);\r
+ } else {\r
+ throw new UnsupportedOperationException(\r
+ "invalid path segment type: " + type);\r
+ }\r
+ prevLine[0] = lineTo[0];\r
+ prevLine[1] = lineTo[1];\r
+\r
+ }\r
+\r
+ result.setWindingRule(line.getWindingRule());\r
+ return result;\r
+ }\r
+\r
+ private void updateRoute(final IElement e) {\r
+\r
+ final List<IElement> segments = new ArrayList<IElement>();\r
+ segments.add(e);\r
+\r
+ IRouter2 router = ElementUtils.getHintOrDefault(e,\r
+ DiagramHints.ROUTE_ALGORITHM, new FlowRouter());\r
+\r
+ router.route(new SysdynConnection() {\r
+\r
+ IDiagram diagram = ElementUtils.peekDiagram(e);\r
+\r
+ final Topology topology = diagram.getDiagramClass()\r
+ .getSingleItem(Topology.class);\r
+\r
+ @Override\r
+ public Connector getBegin(Object seg) {\r
+ IElement e = (IElement) seg;\r
+ Connection begin = topology.getConnection(e, EdgeEnd.Begin);\r
+ return getConnector(begin);\r
+ }\r
+\r
+ @Override\r
+ public Connector getEnd(Object seg) {\r
+ IElement e = (IElement) seg;\r
+ Connection begin = topology.getConnection(e, EdgeEnd.End);\r
+ return getConnector(begin);\r
+ }\r
+\r
+ private Connector getConnector(Connection connection) {\r
+ Connector c = new Connector();\r
+\r
+ AffineTransform at = TerminalUtil.getTerminalPosOnDiagram(\r
+ connection.node, connection.terminal);\r
+ c.x = at.getTranslateX();\r
+ c.y = at.getTranslateY();\r
+\r
+ if (connection.node.getElementClass().containsClass(\r
+ ValveSceneGraph.class)) {\r
+ Rectangle2D bounds = ElementUtils\r
+ .getElementBoundsOnDiagram(connection.node)\r
+ .getBounds2D();\r
+ c.parentObstacle = new Rectangle2D.Double(bounds\r
+ .getCenterX() - FlowRouter.OFFSET, bounds\r
+ .getCenterY() - FlowRouter.OFFSET,\r
+ FlowRouter.OFFSET * 2, FlowRouter.OFFSET * 2);\r
+ } else {\r
+ c.parentObstacle = ElementUtils\r
+ .getElementBoundsOnDiagram(connection.node)\r
+ .getBounds2D();\r
+ }\r
+ ConnectionDirectionUtil.determineAllowedDirections(c);\r
+\r
+ return c;\r
+ }\r
+\r
+ private int toAllowedDirections(BranchPoint.Direction direction) {\r
+ switch (direction) {\r
+ case Any:\r
+ return 0xf;\r
+ case Horizontal:\r
+ return Constants.EAST_FLAG | Constants.WEST_FLAG;\r
+ case Vertical:\r
+ return Constants.NORTH_FLAG | Constants.SOUTH_FLAG;\r
+ default:\r
+ throw new IllegalArgumentException(\r
+ "unrecognized direction: " + direction);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public Collection<? extends Object> getSegments() {\r
+ return segments;\r
+ }\r
+\r
+ @Override\r
+ public void setPath(Object seg, Path2D path) {\r
+ IElement e = (IElement) seg;\r
+ BendsHandler bends = e.getElementClass()\r
+ .getAtMostOneItemOfClass(BendsHandler.class);\r
+ AffineTransform elementTransform = ElementUtils\r
+ .getInvTransform(e);\r
+ path = (Path2D) path.clone();\r
+ path.transform(elementTransform);\r
+ bends.setPath(e, path);\r
+ }\r
+ });\r
+\r
+ }\r
+\r
+ public void update(final IElement e) {\r
+ EdgeNode node = e.getHint(KEY_SG_NODE);\r
+ if (node == null)\r
+ return;\r
+\r
+ EdgeVisuals vh = e.getElementClass().getSingleItem(\r
+ EdgeVisuals.class);\r
+ ArrowType at1 = vh.getArrowType(e, EdgeEnd.Begin);\r
+ ArrowType at2 = vh.getArrowType(e, EdgeEnd.End);\r
+ Stroke stroke = vh.getStroke(e);\r
+ // StrokeType strokeType = vh.getStrokeType(e);\r
+ double as1 = vh.getArrowSize(e, EdgeEnd.Begin);\r
+ double as2 = vh.getArrowSize(e, EdgeEnd.End);\r
+\r
+ Color c = ElementUtils.getFillColor(e, Color.BLACK);\r
+\r
+ // Get terminal shape for clipping the painted edge to its bounds.\r
+ IDiagram diagram = ElementUtils.peekDiagram(e);\r
+ if (diagram != null) {\r
+ Topology topology = diagram.getDiagramClass()\r
+ .getAtMostOneItemOfClass(Topology.class);\r
+ if (topology != null) {\r
+ Connection beginConnection = topology.getConnection(e,\r
+ EdgeEnd.Begin);\r
+ Connection endConnection = topology.getConnection(e,\r
+ EdgeEnd.End);\r
+ int beginBranchDegree = getBranchPointDegree(\r
+ beginConnection, topology);\r
+ int endBranchDegree = getBranchPointDegree(endConnection,\r
+ topology);\r
+ if (beginBranchDegree > 0 && beginBranchDegree < 3) {\r
+ at1 = ArrowType.None;\r
+ }\r
+ if (endBranchDegree > 0 && endBranchDegree < 3) {\r
+ at2 = ArrowType.None;\r
+ }\r
+ }\r
+ }\r
+\r
+ // Read bends\r
+ BendsHandler bh = e.getElementClass().getSingleItem(\r
+ BendsHandler.class);\r
+ Path2D line = bh.getPath(e);\r
+\r
+ boolean drawArrows = at1 != ArrowType.None || at2 != ArrowType.None;\r
+ // line = clipLineEnds(line, beginTerminalShape, endTerminalShape);\r
+\r
+ Point2D first = new Point2D.Double();\r
+ Point2D dir1 = new Point2D.Double();\r
+ Point2D last = new Point2D.Double();\r
+ Point2D dir2 = new Point2D.Double();\r
+ PathIterator pi = line.getPathIterator(null);\r
+ drawArrows &= getPathArrows(pi, first, dir1, last, dir2);\r
+\r
+ if (drawArrows) {\r
+ line = trimLineToArrows(line, at2, as2);\r
+ }\r
+\r
+ EdgeNode.ArrowType pat1 = convert(at1);\r
+ EdgeNode.ArrowType pat2 = convert(at2);\r
+\r
+ node.init(new GeneralPath(line), stroke, c, dir1, dir2, first,\r
+ last, as1, as2, pat1, pat2, null, null);\r
+ }\r
+\r
+ private boolean getPathArrows(PathIterator pi, Point2D begin,\r
+ Point2D beginDirection, Point2D end, Point2D endDirection) {\r
+\r
+ Iterator<double[]> i = PathUtils.toLineIterator(pi);\r
+\r
+ double first1[] = null, last1[] = null;\r
+ double first2[] = null, last2[] = null;\r
+\r
+ // double current[] = new double[2];\r
+\r
+ double[] previous = null;\r
+\r
+ while (i.hasNext()) {\r
+ double[] current = i.next();\r
+\r
+ // Start of the first path\r
+ if (first1 == null) {\r
+ first1 = current;\r
+ last1 = current;\r
+ }\r
+\r
+ // Command was moveTo => start of the second path\r
+ else if (previous != null\r
+ && (previous[2] != current[0] || previous[3] != current[1])) {\r
+ first2 = current;\r
+ last2 = current;\r
+ }\r
+\r
+ // first2 == null => still in the first line\r
+ else if (first2 == null) {\r
+ last1 = current;\r
+ }\r
+\r
+ // second path\r
+ else if (!i.hasNext()) {\r
+ last2 = current;\r
+ }\r
+\r
+ previous = current;\r
+ }\r
+\r
+ if (first1 == null || last1 == null || first2 == null\r
+ || last2 == null)\r
+ return false;\r
+\r
+ double[] first = { mean(first1[0], first2[0]),\r
+ mean(first1[1], first2[1]), mean(first1[2], first2[2]),\r
+ mean(first1[3], first2[3]) };\r
+ double[] last = { mean(last1[0], last2[0]),\r
+ mean(last1[1], last2[1]), mean(last1[2], last2[2]),\r
+ mean(last1[3], last2[3]) };\r
+\r
+ begin.setLocation(PathUtils.getLinePos(first, 0));\r
+ beginDirection.setLocation(PathUtils.getLineTangent(first, 0));\r
+ end.setLocation(PathUtils.getLinePos(last, 1));\r
+ Point2D endTangent = PathUtils.getLineTangent(last, 1);\r
+ endDirection.setLocation(-endTangent.getX(), -endTangent.getY());\r
+\r
+ return true;\r
+ }\r
+\r
+ private double mean(double c1, double c2) {\r
+ return c1 + (c2 - c1) / 2;\r
+ }\r
+\r
+ private static EdgeNode.ArrowType convert(ArrowType at) {\r
+ switch (at) {\r
+ case None:\r
+ return EdgeNode.ArrowType.None;\r
+ case Stroke:\r
+ return EdgeNode.ArrowType.Stroke;\r
+ case Fill:\r
+ return EdgeNode.ArrowType.Fill;\r
+ default:\r
+ throw new IllegalArgumentException("unsupported arrow type: "\r
+ + at);\r
+ }\r
+ }\r
+\r
+ private final Collection<Connection> connectionsTemp = new ArrayList<Connection>();\r
+\r
+ private int getBranchPointDegree(Connection connection,\r
+ Topology topology) {\r
+ if (connection != null && connection.node != null) {\r
+ if (connection.node.getElementClass().containsClass(\r
+ BranchPoint.class)) {\r
+ connectionsTemp.clear();\r
+ topology.getConnections(connection.node,\r
+ connection.terminal, connectionsTemp);\r
+ int degree = connectionsTemp.size();\r
+ connectionsTemp.clear();\r
+ return degree;\r
+ }\r
+ }\r
+ return -1;\r
+ }\r
+\r
+ }\r
\r
}\r
--- /dev/null
+package org.simantics.sysdyn.ui.elements2.connections;\r
+\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.Graphics2D;\r
+import java.awt.Stroke;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.GeneralPath;\r
+\r
+import org.simantics.scenegraph.ISelectionPainterNode;\r
+import org.simantics.scenegraph.g2d.nodes.EdgeNode;\r
+import org.simantics.scenegraph.utils.NodeUtil;\r
+\r
+public class FlowEdgeNode extends EdgeNode implements ISelectionPainterNode {\r
+ \r
+ private static final long serialVersionUID = -6774653631527343539L;\r
+\r
+ private static final BasicStroke SELECTION_STROKE = new BasicStroke(1.0f);\r
+ \r
+ @Override\r
+ public void render(Graphics2D g) {\r
+ if(color != null) g.setColor(color);\r
+ if(stroke == null || shape == null) return;\r
+\r
+ if(alphaComposite != null) {\r
+ g.setComposite(alphaComposite);\r
+ }\r
+\r
+ Stroke effectiveStroke = stroke;\r
+ if(dynamicStroke != null) {\r
+ effectiveStroke = dynamicStroke;\r
+ }\r
+\r
+ Color effectiveColor = color;\r
+ if(dynamicColor != null) {\r
+ effectiveColor = dynamicColor;\r
+ }\r
+\r
+ g.setStroke(effectiveStroke);\r
+\r
+ // Draw line\r
+ boolean selected = NodeUtil.isSelected(this, 2);\r
+ if(selected) {\r
+ g.setColor(Color.PINK);\r
+ g.setStroke(SELECTION_STROKE);\r
+ g.draw(shape);\r
+ }\r
+ g.setColor(effectiveColor);\r
+ g.setStroke(effectiveStroke);\r
+ g.draw(shape);\r
+\r
+ // Draw the ending arrow if necessary\r
+ if(last_at == ArrowType.Fill) {\r
+ AffineTransform at = g.getTransform();\r
+\r
+ g.setStroke(ARROW_STROKE);\r
+\r
+ double theta = Math.atan2(lastdir.getY(), lastdir.getX()) - Math.PI/2;\r
+ g.translate(last.getX(), last.getY());\r
+ g.rotate(theta);\r
+ g.scale(lastsize, lastsize);\r
+ g.fill(FLOW_ARROW);\r
+ g.setTransform(at);\r
+ }\r
+\r
+ }\r
+ \r
+ public transient final static GeneralPath FLOW_ARROW;\r
+\r
+ static {\r
+ FLOW_ARROW = new GeneralPath();\r
+ FLOW_ARROW.moveTo(-1f, 1.6f);\r
+ FLOW_ARROW.lineTo( 0f, 0f);\r
+ FLOW_ARROW.lineTo( 1f, 1.6f);\r
+ FLOW_ARROW.closePath();\r
+ }\r
+\r
+}\r
private Stroke stroke;\r
private Rectangle2D beginBounds;\r
private Rectangle2D endBounds;\r
- private Boolean toValve;\r
- private transient Path2D lines;\r
- private transient Path2D arrow;\r
+ private Path2D lines;\r
+ private Path2D arrow;\r
\r
@PropertySetter("color")\r
@SyncField("color")\r
public void setEndBounds(Rectangle2D endBounds) {\r
this.endBounds = endBounds;\r
}\r
-\r
- @PropertySetter("toValve")\r
- @SyncField("toValve")\r
- public void setToValve(Boolean toValve) {\r
- this.toValve = toValve;\r
+ \r
+ @PropertySetter("lines")\r
+ @SyncField("lines")\r
+ public void setLines(Path2D lines) {\r
+ this.lines = lines;\r
+ }\r
+ \r
+ @PropertySetter("arrow")\r
+ @SyncField("arrow")\r
+ public void setArrow(Path2D arrow) {\r
+ this.arrow = arrow;\r
}\r
\r
public Color getColor() {\r
return endBounds;\r
}\r
\r
- public Boolean getBoolean() {\r
- return toValve;\r
+ public Path2D getLines() {\r
+ return lines;\r
}\r
-\r
+ \r
+ public Path2D getArrow() {\r
+ return arrow;\r
+ }\r
+ \r
@Override\r
public void render(Graphics2D g) {\r
// Removed to let the global control handle rendering quality issues.\r
* -In the second case there is an arrow and valve is beginBounds\r
*/\r
\r
-// System.out.println("FlowNode.render toValve = " + toValve);\r
-\r
- if(toValve) {\r
- lines = Flows.createLines(lines, false, endBounds, beginBounds);\r
- } else {\r
- lines = Flows.createLines(lines, true, beginBounds, endBounds);\r
- arrow = Flows.createArrow(arrow, beginBounds, endBounds);\r
- }\r
-\r
boolean selected = NodeUtil.isSelected(this, 2);\r
if(selected) {\r
g.setColor(Color.PINK);\r
g.draw(lines);\r
if(color != null) g.setColor(color);\r
g.setStroke(stroke);\r
- g.draw(lines);\r
+ if(lines!= null) g.draw(lines);\r
if(arrow != null) g.fill(arrow);\r
} else {\r
if(color != null) g.setColor(color);\r
if(stroke != null) g.setStroke(stroke);\r
- g.draw(lines);\r
+ if(lines!= null) g.draw(lines);\r
if(arrow != null) g.fill(arrow);\r
}\r
\r