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