X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=inline;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fflag%2FSplitter.java;h=a5220f99c8a1ce7678d6b7ead492de954127427d;hb=13a80a14b87f94af7daa6cd5ab5b9b5f5ae379c1;hp=6303f80c1be21d565431fcf3914f2199a0ff4afa;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Splitter.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Splitter.java
index 6303f80c1..a5220f99c 100644
--- a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Splitter.java
+++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Splitter.java
@@ -1,257 +1,248 @@
-package org.simantics.diagram.flag;
-
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Line2D;
-import java.awt.geom.Point2D;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.vecmath.Tuple2d;
-import javax.vecmath.Vector2d;
-
-import org.simantics.databoard.Bindings;
-import org.simantics.db.ReadGraph;
-import org.simantics.db.Resource;
-import org.simantics.db.Statement;
-import org.simantics.db.WriteGraph;
-import org.simantics.db.common.utils.OrderedSetUtils;
-import org.simantics.db.exception.DatabaseException;
-import org.simantics.db.exception.ServiceException;
-import org.simantics.diagram.content.ConnectionUtil;
-import org.simantics.diagram.content.EdgeResource;
-import org.simantics.diagram.stubs.DiagramResource;
-import org.simantics.diagram.synchronization.graph.AddElement;
-import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
-import org.simantics.g2d.element.IElement;
-import org.simantics.g2d.elementclass.FlagClass;
-import org.simantics.g2d.elementclass.FlagClass.Type;
-import org.simantics.layer0.Layer0;
-import org.simantics.modeling.ModelingResources;
-import org.simantics.structural.stubs.StructuralResource2;
-
-/**
- * A class that handles splitting a connection in two with diagram local flags.
- *
- * The connection splitting process consists of the following steps:
- *
- * - Disconnect the end points of the selected connection edge segment (SEG).
- * An end point is either :DIA.BranchPoint or (terminal) DIA:Connector. This
- * operation will always split a valid connection into two separate parts.
- * - Calculate the contents of the two separated connection parts (branch
- * points and connectors) and decide which part to leave with the existing
- * connection (=P1) and which part to move into a new connection (=P2). The
- * current strategy is to move the parts that
- * do not contain output terminal connectors into a new connection.
- * This works well with diagram to composite mappings where output terminals
- * generate modules behind connections.
- * - Create new connection C' with the same type and STR.HasConnectionType as
- * the original connection C.
- * - Copy connection routing settings from C to C'.
- * - Move P2 into C'.
- * - Create two new flag elements into the same diagram, set their type and
- * interpolate a proper transformation for both along the existing connection
- * line.
- * - Connect the new flags to begin(SEG) and end(SEG) connectors.
- * - Join the flags together.
- *
- *
- * @author Tuukka Lehtonen
- */
-public class Splitter {
-
- Layer0 L0;
- DiagramResource DIA;
- StructuralResource2 STR;
- ModelingResources MOD;
-
- public Splitter(ReadGraph graph) {
- this.L0 = Layer0.getInstance(graph);
- this.DIA = DiagramResource.getInstance(graph);
- this.STR = StructuralResource2.getInstance(graph);
- this.MOD = ModelingResources.getInstance(graph);
- }
-
- public void split(WriteGraph graph, IElement edgeElement, EdgeResource edge, Point2D splitCanvasPos) throws DatabaseException {
- ConnectionUtil cu = new ConnectionUtil(graph);
-
- Resource connection = ConnectionUtil.getConnection(graph, edge);
- Resource diagram = OrderedSetUtils.getSingleOwnerList(graph, connection, DIA.Diagram);
-
- // Disconnect the edge to calculate the two parts that remain.
- cu.disconnect(edge);
-
- Splitter.Parts parts1 = Parts.calculate(graph, edge.first());
- Splitter.Parts parts2 = Parts.calculate(graph, edge.second());
-
- // Resolve which part contains the "output" and which contains
- // "input" to properly position the created flags.
- Splitter.Parts moveToNewConnection = parts2;
- Resource keepConnector = edge.first();
- Resource moveToNewConnectionConnector = edge.second();
-
- boolean inputsOnly1 = parts1.hasInputsOnly(graph);
- boolean inputsOnly2 = parts2.hasInputsOnly(graph);
-
- if (inputsOnly1 && inputsOnly2) {
- // Let it be, can't really do better with this information.
- } else if (inputsOnly1) {
- // Parts1 has input connectors only, therefore we should
- // move those to the new connection instead of parts2.
- moveToNewConnection = parts1;
- keepConnector = edge.second();
- moveToNewConnectionConnector = edge.first();
- }
-
- Resource connectionType = graph.getSingleType(connection, DIA.Connection);
- Resource hasConnectionType = graph.getPossibleObject(connection, STR.HasConnectionType);
- Resource newConnection = cu.newConnection(diagram, connectionType);
- if (hasConnectionType != null)
- graph.claim(newConnection, STR.HasConnectionType, null, hasConnectionType);
-
- // Copy connection routing
- for (Statement routing : graph.getStatements(connection, DIA.Routing))
- graph.claim(newConnection, routing.getPredicate(), newConnection);
-
- for (Statement stm : moveToNewConnection.parts) {
- graph.deny(stm);
- graph.claim(stm.getSubject(), stm.getPredicate(), newConnection);
- }
-
- // 1 = output, 2 = input
- Type type1 = FlagClass.Type.Out;
- Type type2 = FlagClass.Type.In;
-
- // Resolve the "positive" direction of the clicked edge to be split.
- Line2D nearestEdge= ConnectionUtil.resolveNearestEdgeLineSegment(splitCanvasPos, edgeElement);
-
- // Calculate split position and edge line nearest intersection point
- // ab = normalize( vec(a -> b) )
- // ap = vec(a -> split pos)
- Vector2d ab = new Vector2d(nearestEdge.getX2() - nearestEdge.getX1(), nearestEdge.getY2() - nearestEdge.getY1());
- Vector2d ap = new Vector2d(splitCanvasPos.getX() - nearestEdge.getX1(), splitCanvasPos.getY() - nearestEdge.getY1());
- double theta = Math.atan2(ab.y, ab.x);
- ab.normalize();
-
- // intersection = a + ab*(ap.ab)
- Vector2d intersection = new Vector2d(ab);
- intersection.scale(ap.dot(ab));
- intersection.add(new Vector2d(nearestEdge.getX1(), nearestEdge.getY1()));
-
- // Offset flag positions from the intersection point.
- Vector2d pos1 = new Vector2d(intersection);
- Vector2d pos2 = new Vector2d(intersection);
-
- // TODO: improve logic for flag positioning, flags just move on the nearest line without boundaries
- ab.normalize();
- ab.scale(5);
- pos2.add(ab);
- ab.negate();
- pos1.add(ab);
-
- FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
- String commonLabel = scheme.generateLabel(graph, diagram);
-
- // Create flags and connect both disconnected ends to them.
- Resource flag1 = createFlag(graph, diagram, getFlagTransform(pos1, theta), type1, commonLabel);
- Resource flag2 = createFlag(graph, diagram, getFlagTransform(pos2, theta), type2, commonLabel);
-
- Resource flagConnector1 = cu.newConnector(connection, DIA.HasArrowConnector);
- Resource flagConnector2 = cu.newConnector(newConnection, DIA.HasPlainConnector);
- graph.claim(flag1, DIA.Flag_ConnectionPoint, flagConnector1);
- graph.claim(flag2, DIA.Flag_ConnectionPoint, flagConnector2);
-
- graph.claim(flagConnector1, DIA.AreConnected, keepConnector);
- graph.claim(flagConnector2, DIA.AreConnected, moveToNewConnectionConnector);
-
- FlagUtil.join(graph, flag1, flag2);
- }
-
- private AffineTransform getFlagTransform(Tuple2d pos, double theta) {
- AffineTransform at = AffineTransform.getTranslateInstance(pos.x, pos.y);
- at.rotate(theta);
- return at;
- }
-
- private Resource createFlag(WriteGraph graph, Resource diagram, AffineTransform tr, FlagClass.Type type, String label) throws DatabaseException {
- DiagramResource DIA = DiagramResource.getInstance(graph);
-
- Resource flag = graph.newResource();
- graph.claim(flag, L0.InstanceOf, null, DIA.Flag);
-
- AddElement.claimFreshElementName(graph, diagram, flag);
- graph.claim(flag, L0.PartOf, L0.ConsistsOf, diagram);
-
- DiagramGraphUtil.setTransform(graph, flag, tr);
- if (type != null)
- FlagUtil.setFlagType(graph, flag, type);
-
- if (label != null)
- graph.claimLiteral(flag, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);
-
- OrderedSetUtils.add(graph, diagram, flag);
- return flag;
- }
-
- static class Parts {
- DiagramResource DIA;
- Set parts = new HashSet();
-
- private Parts(ReadGraph graph) {
- this.DIA = DiagramResource.getInstance(graph);
- }
-
- public int size() {
- return parts.size();
- }
-
- public static Splitter.Parts calculate(ReadGraph graph, Resource routeNode) throws DatabaseException {
- DiagramResource DIA = DiagramResource.getInstance(graph);
-
- Splitter.Parts p = new Parts(graph);
- Deque todo = new ArrayDeque();
- Set visited = new HashSet();
-
- todo.add(routeNode);
- while (!todo.isEmpty()) {
- Resource part = todo.poll();
- if (!visited.add(part))
- continue;
-
- todo.addAll(graph.getObjects(part, DIA.AreConnected));
-
- Statement toConnection = graph.getPossibleStatement(part, DIA.IsConnectorOf);
- if (toConnection == null)
- toConnection = graph.getPossibleStatement(part, DIA.HasInteriorRouteNode_Inverse);
- if (toConnection == null)
- continue;
-
- p.parts.add(toConnection);
- }
-
- return p;
- }
-
- public boolean hasInputsOnly(ReadGraph graph) throws ServiceException {
- return hasInputs(graph) && !hasOutputs(graph);
- }
-
- public boolean hasInputs(ReadGraph graph) throws ServiceException {
- return hasRelations(graph, DIA.IsArrowConnectorOf);
- }
-
- public boolean hasOutputs(ReadGraph graph) throws ServiceException {
- return hasRelations(graph, DIA.IsPlainConnectorOf);
- }
-
- public boolean hasRelations(ReadGraph graph, Resource relation) throws ServiceException {
- for (Statement stm : parts)
- if (graph.isSubrelationOf(stm.getPredicate(), relation))
- return true;
- return false;
- }
-
- }
-
+package org.simantics.diagram.flag;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
+import org.simantics.databoard.Bindings;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Statement;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.utils.OrderedSetUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.ServiceException;
+import org.simantics.diagram.content.ConnectionUtil;
+import org.simantics.diagram.content.EdgeResource;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.diagram.synchronization.graph.AddElement;
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.elementclass.FlagClass;
+import org.simantics.g2d.elementclass.FlagClass.Type;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.structural.stubs.StructuralResource2;
+
+/**
+ * A class that handles splitting a connection in two with diagram local flags.
+ *
+ * The connection splitting process consists of the following steps:
+ *
+ * - Disconnect the end points of the selected connection edge segment (SEG).
+ * An end point is either :DIA.BranchPoint or (terminal) DIA:Connector. This
+ * operation will always split a valid connection into two separate parts.
+ * - Calculate the contents of the two separated connection parts (branch
+ * points and connectors) and decide which part to leave with the existing
+ * connection (=P1) and which part to move into a new connection (=P2). The
+ * current strategy is to move the parts that
+ * do not contain output terminal connectors into a new connection.
+ * This works well with diagram to composite mappings where output terminals
+ * generate modules behind connections.
+ * - Create new connection C' with the same type and STR.HasConnectionType as
+ * the original connection C.
+ * - Copy connection routing settings from C to C'.
+ * - Move P2 into C'.
+ * - Create two new flag elements into the same diagram, set their type and
+ * interpolate a proper transformation for both along the existing connection
+ * line.
+ * - Connect the new flags to begin(SEG) and end(SEG) connectors.
+ * - Join the flags together.
+ *
+ *
+ * @author Tuukka Lehtonen
+ */
+public class Splitter {
+
+ Layer0 L0;
+ DiagramResource DIA;
+ StructuralResource2 STR;
+ ModelingResources MOD;
+
+ public Splitter(ReadGraph graph) {
+ this.L0 = Layer0.getInstance(graph);
+ this.DIA = DiagramResource.getInstance(graph);
+ this.STR = StructuralResource2.getInstance(graph);
+ this.MOD = ModelingResources.getInstance(graph);
+ }
+
+ public void split(WriteGraph graph, IElement edgeElement, EdgeResource edge, Point2D splitCanvasPos) throws DatabaseException {
+ ConnectionUtil cu = new ConnectionUtil(graph);
+
+ Resource connection = ConnectionUtil.getConnection(graph, edge);
+ Resource diagram = OrderedSetUtils.getSingleOwnerList(graph, connection, DIA.Diagram);
+
+ // Disconnect the edge to calculate the two parts that remain.
+ cu.disconnect(edge);
+
+ Splitter.Parts parts1 = Parts.calculate(graph, edge.first());
+ Splitter.Parts parts2 = Parts.calculate(graph, edge.second());
+
+ // Resolve which part contains the "output" and which contains
+ // "input" to properly position the created flags.
+ Splitter.Parts moveToNewConnection = parts2;
+ Resource keepConnector = edge.first();
+ Resource moveToNewConnectionConnector = edge.second();
+
+ boolean inputsOnly1 = parts1.hasInputsOnly(graph);
+ boolean inputsOnly2 = parts2.hasInputsOnly(graph);
+
+ if (inputsOnly1 && inputsOnly2) {
+ // Let it be, can't really do better with this information.
+ } else if (inputsOnly1) {
+ // Parts1 has input connectors only, therefore we should
+ // move those to the new connection instead of parts2.
+ moveToNewConnection = parts1;
+ keepConnector = edge.second();
+ moveToNewConnectionConnector = edge.first();
+ }
+
+ Resource connectionType = graph.getSingleType(connection, DIA.Connection);
+ Resource hasConnectionType = graph.getPossibleObject(connection, STR.HasConnectionType);
+ Resource newConnection = cu.newConnection(diagram, connectionType);
+ if (hasConnectionType != null)
+ graph.claim(newConnection, STR.HasConnectionType, null, hasConnectionType);
+
+ // Copy connection routing
+ for (Statement routing : graph.getStatements(connection, DIA.Routing))
+ graph.claim(newConnection, routing.getPredicate(), newConnection);
+
+ for (Statement stm : moveToNewConnection.parts) {
+ graph.deny(stm);
+ graph.claim(stm.getSubject(), stm.getPredicate(), newConnection);
+ }
+
+ // 1 = output, 2 = input
+ Type type1 = FlagClass.Type.Out;
+ Type type2 = FlagClass.Type.In;
+
+ // Resolve the "positive" direction of the clicked edge to be split.
+ Line2D nearestEdge= ConnectionUtil.resolveNearestEdgeLineSegment(splitCanvasPos, edgeElement);
+
+ // Calculate split position and edge line nearest intersection point
+ // ab = normalize( vec(a -> b) )
+ // ap = vec(a -> split pos)
+ Vector2D a = new Vector2D(nearestEdge.getX1(), nearestEdge.getY1());
+ Vector2D ab = new Vector2D(nearestEdge.getX2() - nearestEdge.getX1(), nearestEdge.getY2() - nearestEdge.getY1());
+ Vector2D ap = new Vector2D(splitCanvasPos.getX() - nearestEdge.getX1(), splitCanvasPos.getY() - nearestEdge.getY1());
+ double theta = Math.atan2(ab.getY(), ab.getX());
+ ab = ab.normalize();
+
+ // intersection = a + ab*(ap.ab)
+ Vector2D intersection = a.add( ab.scalarMultiply(ap.dotProduct(ab)) );
+
+ // Offset flag positions from the intersection point.
+ // TODO: improve logic for flag positioning, flags just move on the nearest line without boundaries
+ Vector2D pos1 = intersection.subtract(5, ab);
+ Vector2D pos2 = intersection.add(5, ab);
+
+ FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
+ String commonLabel = scheme.generateLabel(graph, diagram);
+
+ // Create flags and connect both disconnected ends to them.
+ Resource flag1 = createFlag(graph, diagram, getFlagTransform(pos1, theta), type1, commonLabel);
+ Resource flag2 = createFlag(graph, diagram, getFlagTransform(pos2, theta), type2, commonLabel);
+
+ Resource flagConnector1 = cu.newConnector(connection, DIA.HasArrowConnector);
+ Resource flagConnector2 = cu.newConnector(newConnection, DIA.HasPlainConnector);
+ graph.claim(flag1, DIA.Flag_ConnectionPoint, flagConnector1);
+ graph.claim(flag2, DIA.Flag_ConnectionPoint, flagConnector2);
+
+ graph.claim(flagConnector1, DIA.AreConnected, keepConnector);
+ graph.claim(flagConnector2, DIA.AreConnected, moveToNewConnectionConnector);
+
+ FlagUtil.join(graph, flag1, flag2);
+ }
+
+ private AffineTransform getFlagTransform(Vector2D pos, double theta) {
+ AffineTransform at = AffineTransform.getTranslateInstance(pos.getX(), pos.getY());
+ at.rotate(theta);
+ return at;
+ }
+
+ private Resource createFlag(WriteGraph graph, Resource diagram, AffineTransform tr, FlagClass.Type type, String label) throws DatabaseException {
+ DiagramResource DIA = DiagramResource.getInstance(graph);
+
+ Resource flag = graph.newResource();
+ graph.claim(flag, L0.InstanceOf, null, DIA.Flag);
+
+ AddElement.claimFreshElementName(graph, diagram, flag);
+ graph.claim(flag, L0.PartOf, L0.ConsistsOf, diagram);
+
+ DiagramGraphUtil.setTransform(graph, flag, tr);
+ if (type != null)
+ FlagUtil.setFlagType(graph, flag, type);
+
+ if (label != null)
+ graph.claimLiteral(flag, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);
+
+ OrderedSetUtils.add(graph, diagram, flag);
+ return flag;
+ }
+
+ static class Parts {
+ DiagramResource DIA;
+ Set parts = new HashSet();
+
+ private Parts(ReadGraph graph) {
+ this.DIA = DiagramResource.getInstance(graph);
+ }
+
+ public int size() {
+ return parts.size();
+ }
+
+ public static Splitter.Parts calculate(ReadGraph graph, Resource routeNode) throws DatabaseException {
+ DiagramResource DIA = DiagramResource.getInstance(graph);
+
+ Splitter.Parts p = new Parts(graph);
+ Deque todo = new ArrayDeque();
+ Set visited = new HashSet();
+
+ todo.add(routeNode);
+ while (!todo.isEmpty()) {
+ Resource part = todo.poll();
+ if (!visited.add(part))
+ continue;
+
+ todo.addAll(graph.getObjects(part, DIA.AreConnected));
+
+ Statement toConnection = graph.getPossibleStatement(part, DIA.IsConnectorOf);
+ if (toConnection == null)
+ toConnection = graph.getPossibleStatement(part, DIA.HasInteriorRouteNode_Inverse);
+ if (toConnection == null)
+ continue;
+
+ p.parts.add(toConnection);
+ }
+
+ return p;
+ }
+
+ public boolean hasInputsOnly(ReadGraph graph) throws ServiceException {
+ return hasInputs(graph) && !hasOutputs(graph);
+ }
+
+ public boolean hasInputs(ReadGraph graph) throws ServiceException {
+ return hasRelations(graph, DIA.IsArrowConnectorOf);
+ }
+
+ public boolean hasOutputs(ReadGraph graph) throws ServiceException {
+ return hasRelations(graph, DIA.IsPlainConnectorOf);
+ }
+
+ public boolean hasRelations(ReadGraph graph, Resource relation) throws ServiceException {
+ for (Statement stm : parts)
+ if (graph.isSubrelationOf(stm.getPredicate(), relation))
+ return true;
+ return false;
+ }
+
+ }
+
}
\ No newline at end of file