import java.util.Collection;\r
import java.util.Collections;\r
import java.util.Deque;\r
-import java.util.HashSet;\r
import java.util.Iterator;\r
import java.util.List;\r
\r
import org.simantics.db.exception.DatabaseException;\r
import org.simantics.diagram.connection.RouteGraph;\r
import org.simantics.diagram.connection.RouteGraphConnectionClass;\r
+import org.simantics.diagram.connection.RouteLine;\r
import org.simantics.diagram.connection.RouteTerminal;\r
+import org.simantics.diagram.connection.delta.RouteGraphDelta;\r
import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle;\r
import org.simantics.diagram.content.ResourceTerminal;\r
import org.simantics.diagram.stubs.DiagramResource;\r
import org.simantics.diagram.synchronization.ISynchronizationContext;\r
import org.simantics.diagram.synchronization.SynchronizationHints;\r
+import org.simantics.diagram.synchronization.graph.RouteGraphConnection;\r
import org.simantics.g2d.canvas.ICanvasContext;\r
import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;\r
import org.simantics.g2d.canvas.impl.DependencyReflection.Reference;\r
import org.simantics.g2d.diagram.DiagramHints;\r
import org.simantics.g2d.diagram.DiagramUtils;\r
import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.diagram.handler.PickContext;\r
import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
import org.simantics.g2d.diagram.participant.ElementPainter;\r
import org.simantics.g2d.diagram.participant.TerminalPainter;\r
import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
import org.simantics.g2d.element.handler.SceneGraph;\r
import org.simantics.g2d.element.handler.TerminalTopology;\r
+import org.simantics.g2d.element.handler.impl.BranchPointTerminal;\r
import org.simantics.g2d.element.impl.Element;\r
import org.simantics.g2d.elementclass.BranchPoint;\r
import org.simantics.g2d.elementclass.BranchPoint.Direction;\r
import org.simantics.g2d.elementclass.FlagHandler;\r
import org.simantics.g2d.participant.RenderingQualityInteractor;\r
import org.simantics.g2d.participant.TransformUtil;\r
+import org.simantics.g2d.utils.geom.DirectionSet;\r
import org.simantics.modeling.ModelingResources;\r
-import org.simantics.scenegraph.INode;\r
import org.simantics.scenegraph.g2d.G2DParentNode;\r
import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
import org.simantics.scenegraph.g2d.events.KeyEvent;\r
import org.simantics.utils.ui.ErrorLogger;\r
import org.simantics.utils.ui.ExceptionUtils;\r
\r
+import gnu.trove.map.hash.THashMap;\r
+\r
/**\r
* A basic tool for making connection on diagrams.\r
* \r
@Dependency\r
protected PointerInteractor pi;\r
\r
+ @Dependency\r
+ protected PickContext pickContext;\r
+\r
/**\r
* Start element terminal of the connection. <code>null</code> if connection\r
* was started from a flag or a branch point.\r
\r
protected G2DParentNode endFlagNode;\r
\r
+ private RouteGraphTarget lastRouteGraphTarget;\r
+\r
/**\r
* @param startTerminal\r
* @param mouseId\r
\r
ShapeNode pathNode = ghostNode.getOrCreateNode("path", ShapeNode.class);\r
pathNode.setColor(new Color(160, 0, 0));\r
- pathNode.setStroke(new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10,\r
- new float[] { 5f, 2f }, 0));\r
- pathNode.setScaleStroke(true);\r
+ pathNode.setStroke(new BasicStroke(0.1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10,\r
+ new float[] { 0.5f, 0.2f }, 0));\r
+ pathNode.setScaleStroke(false);\r
pathNode.setZIndex(0);\r
\r
G2DParentNode points = ghostNode.getOrCreateNode("points", G2DParentNode.class);\r
\r
ControlPoint begin = controlPoints.getFirst();\r
ControlPoint end = controlPoints.getLast();\r
- \r
+\r
RouteGraph routeGraph = new RouteGraph();\r
RouteTerminal a = addControlPoint(routeGraph, begin);\r
RouteTerminal b = addControlPoint(routeGraph, end);\r
routeGraph.link(a, b);\r
- \r
- // Route connection segments separately\r
- /*Router2 router = ElementUtils.getHintOrDefault(diagram, DiagramHints.ROUTE_ALGORITHM, TrivialRouter2.INSTANCE);\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
- @Override\r
- public Collection<? extends Object> getSegments() {\r
- return segments;\r
- }\r
-\r
- @Override\r
- public Connector getBegin(Object seg) {\r
- return getConnector(((Segment) seg).begin);\r
- }\r
-\r
- @Override\r
- public Connector getEnd(Object seg) {\r
- return getConnector(((Segment) seg).end);\r
- }\r
-\r
- private Connector getConnector(ControlPoint cp) {\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
- 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
- }\r
-\r
- return c;\r
- }\r
\r
- @Override\r
- public void setPath(Object seg, Path2D path) {\r
- ((Segment) seg).path = (Path2D) path.clone();\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("unrecognized direction: " + direction);\r
- }\r
- }\r
- });\r
-\r
- // Combine the routed paths\r
- Path2D path = new Path2D.Double();\r
- for (Segment seg : segments) {\r
- //System.out.println("SEG: " + seg);\r
- if (seg.path != null)\r
- path.append(seg.path.getPathIterator(null), true);\r
- }*/\r
- \r
Path2D path = routeGraph.getPath2D();\r
\r
// Create scene graph to visualize the connection.\r
ShapeNode pathNode = ghostNode.getOrCreateNode("path", ShapeNode.class);\r
pathNode.setShape(path);\r
\r
- G2DParentNode points = ghostNode.getOrCreateNode("points", G2DParentNode.class);\r
- HashSet<INode> unusedChildren = new HashSet<INode>(points.getNodes());\r
- int i = 0;\r
- for (ControlPoint cp : controlPoints) {\r
- if (cp.isAttachedToTerminal())\r
- continue;\r
-\r
- String id = String.valueOf(i);\r
- BranchPointNode bpn = points.getOrCreateNode(id, BranchPointNode.class);\r
- bpn.setDegree(2);\r
- bpn.setDirection((byte) cp.getDirection().ordinal());\r
- bpn.setTransform(AffineTransform.getTranslateInstance(cp.getPosition().getX(), cp.getPosition().getY()));\r
-\r
- ++i;\r
- unusedChildren.remove(bpn);\r
- }\r
- for (INode unusedChild : unusedChildren)\r
- points.removeNode(unusedChild);\r
-\r
setDirty();\r
}\r
\r
\r
// Make sure that we are ending with a flag if ALT is pressed\r
// and no end terminal is defined.\r
- endWithoutTerminal(lastMouseCanvasPos, shouldEndWithFlag(me));\r
-\r
- updateSG();\r
+ if (!endWithoutTerminal(lastMouseCanvasPos, shouldEndWithFlag(me)))\r
+ updateSG();\r
return false;\r
}\r
}\r
+ } else {\r
+ RouteGraphTarget cp = RouteGraphConnectTool.pickRouteGraphConnection(\r
+ diagram,\r
+ pi.getCanvasPickShape(me.controlPosition),\r
+ pi.getPickDistance());\r
+ if (cp != null) {\r
+ // Remove branch point highlight from previously picked route graph.\r
+ if (lastRouteGraphTarget != null && cp.getNode() != lastRouteGraphTarget.getNode())\r
+ cp.getNode().showBranchPoint(null);\r
+ lastRouteGraphTarget = cp;\r
+\r
+ // Validate connection before visualizing connectability\r
+ Point2D isectPos = cp.getIntersectionPosition();\r
+ TerminalInfo ti = TerminalInfo.create(\r
+ isectPos,\r
+ cp.getElement(),\r
+ BranchPointTerminal.existingTerminal(\r
+ isectPos,\r
+ DirectionSet.ANY,\r
+ BranchPointNode.SHAPE),\r
+ BranchPointNode.SHAPE);\r
+ Pair<ConnectionJudgement, TerminalInfo> canConnect = canConnect(ti.e, ti.t);\r
+ if (canConnect != null) {\r
+ connectionJudgment = canConnect.first;\r
+ controlPoints.getLast().setPosition(ti.posDia).setAttachedToTerminal(ti);\r
+ endTerminal = ti;\r
+ cp.getNode().showBranchPoint(isectPos);\r
+ if (!endWithoutTerminal(lastMouseCanvasPos, shouldEndWithFlag(me)))\r
+ updateSG();\r
+ return false;\r
+ }\r
+ } else {\r
+ if (lastRouteGraphTarget != null) {\r
+ lastRouteGraphTarget.getNode().showBranchPoint(null);\r
+ lastRouteGraphTarget = null;\r
+ }\r
+ }\r
}\r
\r
connectionJudgment = null;\r
\r
// Make sure that we are ending with a flag if ALT is pressed and no end\r
// terminal is defined.\r
- endWithoutTerminal(lastMouseCanvasPos, shouldEndWithFlag(me));\r
-\r
- updateSG();\r
+ if (!endWithoutTerminal(lastMouseCanvasPos, shouldEndWithFlag(me)))\r
+ updateSG();\r
\r
return false;\r
}\r
if (snapAdvisor != null)\r
snapAdvisor.snap(mouseCanvasPos);\r
\r
- // Clicked on an allowed end terminal. End connection & end mode.\r
- if (isEndTerminalDefined()) {\r
+ if (lastRouteGraphTarget != null) {\r
+ lastRouteGraphTarget.getNode().showBranchPoint(null);\r
+ attachToConnection();\r
+ remove();\r
+ return true;\r
+ } else if (isEndTerminalDefined()) {\r
+ // Clicked on an allowed end terminal. End connection & end mode.\r
createConnection();\r
remove();\r
return true;\r
return false;\r
}\r
\r
+ private void attachToConnection() {\r
+ ConnectionJudgement judgment = this.connectionJudgment;\r
+ if (judgment == null) {\r
+ ErrorLogger.defaultLogError("Cannot attach to connection, no judgment available on connection validity", null);\r
+ return;\r
+ }\r
+\r
+ ConnectionBuilder builder = new ConnectionBuilder(this.diagram);\r
+ RouteGraph before = lastRouteGraphTarget.getNode().getRouteGraph();\r
+ THashMap<Object, Object> copyMap = new THashMap<>();\r
+ RouteGraph after = before.copy(copyMap);\r
+\r
+ RouteLine attachTo = (RouteLine) copyMap.get(lastRouteGraphTarget.getLine());\r
+ after.makePersistent(attachTo);\r
+ for (RouteLine line : after.getAllLines()) {\r
+ if (!line.isTransient() && line.isHorizontal() == attachTo.isHorizontal()\r
+ && line.getPosition() == attachTo.getPosition()) {\r
+ attachTo = line;\r
+ break;\r
+ }\r
+ }\r
+ RouteLine attachToLine = attachTo;\r
+ RouteGraphDelta delta = new RouteGraphDelta(before, after);\r
+\r
+ Simantics.getSession().asyncRequest(new WriteRequest() {\r
+ @Override\r
+ public void perform(WriteGraph graph) throws DatabaseException {\r
+ graph.markUndoPoint();\r
+ Resource connection = ElementUtils.getObject(endTerminal.e);\r
+ if (!delta.isEmpty()) {\r
+ new RouteGraphConnection(graph, connection).synchronize(graph, before, after, delta);\r
+ }\r
+ Resource line = RouteGraphConnection.deserialize(graph, attachToLine.getData());\r
+ Deque<ControlPoint> cps = new ArrayDeque<>();\r
+ for (Iterator<ControlPoint> iterator = controlPoints.descendingIterator(); iterator.hasNext();)\r
+ cps.add(iterator.next());\r
+ builder.attachToRouteGraph(graph, judgment, connection, line, cps, startTerminal, FlagClass.Type.In);\r
+ }\r
+ }, new Callback<DatabaseException>() {\r
+ @Override\r
+ public void run(DatabaseException parameter) {\r
+ if (parameter != null)\r
+ ExceptionUtils.logAndShowError(parameter);\r
+ }\r
+ });\r
+ }\r
+\r
protected boolean cancelPreviousBend() {\r
if (!routePointsAllowed())\r
return false;\r
return endFlag != null;\r
}\r
\r
- protected void endWithoutTerminal(Point2D mousePos, boolean altDown) {\r
+ /**\r
+ * @param mousePos\r
+ * @param altDown\r
+ * @return <code>true</code> if updateSG was executed, <code>false</code>\r
+ * otherwise\r
+ */\r
+ protected boolean endWithoutTerminal(Point2D mousePos, boolean altDown) {\r
// Just go with branch points if flags are not allowed.\r
if (!createFlags)\r
- return;\r
+ return false;\r
\r
boolean endTerminalDefined = isEndTerminalDefined();\r
\r
setHint(TerminalPainter.TERMINAL_HOVER_STRATEGY, terminalHoverStrategy);\r
\r
updateSG();\r
+ return true;\r
}\r
} else {\r
if (isEndingInFlag()) {\r
setHint(TerminalPainter.TERMINAL_HOVER_STRATEGY, terminalHoverStrategy);\r
\r
updateSG();\r
+ return true;\r
}\r
}\r
+ return false;\r
}\r
\r
protected void createConnection() {\r