From b643293df21959d6416d0ba42637419c33a1dbad Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Fri, 21 Aug 2020 16:06:50 +0300 Subject: [PATCH] Fixed route graph splitting and diagram mapping race condition problem RouteGraphConnectionSplitter.doSplit now always splits the connection so that part #1 is always the part that stays with the existing diagram connection and part #2 contains the entities that are moved to the newly created route graph connection resource. Part #1 is the part where the "output terminal" attached to the connection lies. This simplifies and clarifies the implementation of doSplit and does not require moving diagram mapping statements around. Also, more importantly RouteGraphConnectionSplitter and FlagUtil changes ensure that diagram mapping is *not* executed in FlagUtil.join, which was the main cause of the previous corruption. Diagram mapping is only activated once for the affected diagram(s) after everything else is done. gitlab #549 gitlab #586 Change-Id: Icf7479e8e111bf663f637d6909419267cfa4eec4 (cherry picked from commit cc78488e2230fea07726acb86db3c7dd245b39ce) --- .../diagram/connection/RouteLine.java | 7 + .../diagram/connection/segments/Segment.java | 5 + .../splitting/SplittedRouteGraph.java | 78 ++++- .../org/simantics/diagram/flag/FlagUtil.java | 34 +- .../flag/RouteGraphConnectionSplitter.java | 308 ++++++++++-------- .../graph/RouteGraphModification.java | 9 +- 6 files changed, 286 insertions(+), 155 deletions(-) diff --git a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java index 2011bfa28..225c4abfe 100644 --- a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java +++ b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java @@ -113,10 +113,17 @@ public class RouteLine implements RouteNode, Serializable { out.print(" HOR"); else out.print(" VER"); + if (hidden) + out.print(" HIDDEN"); + out.print(" @ " + position); for(RoutePoint point : points) { out.print(" ("+point.x+","+point.y+")"); } out.print(" (data=" + data + ")"); + if (nextTransient != null) + out.print(" (next transient line=" + nextTransient.getData() + ")"); + if (terminal != null) + out.print(" (terminal=" + terminal.getData() + ")"); out.println(); } diff --git a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/segments/Segment.java b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/segments/Segment.java index b283cb4ed..4e4280c0a 100644 --- a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/segments/Segment.java +++ b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/segments/Segment.java @@ -28,4 +28,9 @@ public class Segment { public boolean isDegenerated() { return p1.getX() == p2.getX() && p1.getY() == p2.getY(); } + + @Override + public String toString() { + return String.format("(%f, %f) (%f, %f)", p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } } diff --git a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/splitting/SplittedRouteGraph.java b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/splitting/SplittedRouteGraph.java index ae79dc019..8f7f71a29 100644 --- a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/splitting/SplittedRouteGraph.java +++ b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/splitting/SplittedRouteGraph.java @@ -1,13 +1,17 @@ package org.simantics.diagram.connection.splitting; -import gnu.trove.set.hash.THashSet; - +import java.awt.geom.Line2D; import java.awt.geom.Point2D; +import java.util.ArrayList; import org.simantics.diagram.connection.RouteGraph; import org.simantics.diagram.connection.RouteLine; import org.simantics.diagram.connection.RouteNode; +import org.simantics.diagram.connection.RoutePoint; import org.simantics.diagram.connection.RouteTerminal; +import org.simantics.diagram.connection.segments.Segment; + +import gnu.trove.set.hash.THashSet; public class SplittedRouteGraph { public final RouteLine splitLine; @@ -91,7 +95,75 @@ public class SplittedRouteGraph { } } - /** + public static final class PickResult { + /** + * The connection route line nearest to {@link #pickPoint}. + */ + public final RouteLine nearestLine; + /** + * Original pick point in canvas coordinates. + */ + public final Point2D pickPoint; + /** + * Intersection point in canvas coordinates of {@link #nearestLine} and + * perpendicular line from {@link #pickPoint} to {@link #nearestLine}. + */ + public final Point2D intersectionPoint; + + public PickResult(RouteLine nearestLine, Point2D pickPoint, Point2D intersectionPoint) { + this.nearestLine = nearestLine; + this.pickPoint = pickPoint; + this.intersectionPoint = intersectionPoint; + } + } + + public static PickResult pickNearestLine(RouteGraph rg, double x, double y) { + Segment nearestSegment = null; + RouteLine nearestLine = null; + + ArrayList segments = new ArrayList<>(); + double minDistanceSq = Double.MAX_VALUE; + for (RouteLine line : rg.getAllLines()) { + segments.clear(); + line.collectSegments(segments); + for (Segment segment : segments) { + RoutePoint p1 = segment.p1; + RoutePoint p2 = segment.p2; + double distanceSq = Line2D.ptSegDistSq(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y); + if (distanceSq < minDistanceSq) { + minDistanceSq = distanceSq; + nearestSegment = segment; + nearestLine = line; + } + } + } + + if (nearestSegment == null) + return null; + + RoutePoint p1 = nearestSegment.p1; + RoutePoint p2 = nearestSegment.p2; + Point2D p = pointToLineIntersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y); + return new PickResult(nearestLine, new Point2D.Double(x, y), p); + } + + private static Point2D pointToLineIntersection(double x1, double y1, double x2, double y2, double px, double py) { + double d = Math.pow(x2 - x1, 2.0) + Math.pow(y2 - y1, 2.0); + if (d == 0) { + return new Point2D.Double(x1, y1); + } else { + double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / d; + if (u > 1.0) { + return new Point2D.Double(x2, y2); + } else if (u <= 0.0) { + return new Point2D.Double(x1, y1); + } else { + return new Point2D.Double(x2 * u + x1 * (1.0-u), (y2 * u + y1 * (1.0- u))); + } + } + } + + /** * @param point * @param line * @return the specified point instance snapped to the specified line diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/FlagUtil.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/FlagUtil.java index d38a301da..e53f4b636 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/FlagUtil.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/FlagUtil.java @@ -176,6 +176,17 @@ public final class FlagUtil { * @throws DatabaseException */ public static Resource join(WriteGraph g, Resource flag, Resource otherFlag) throws DatabaseException { + return join(g, flag, otherFlag, true); + } + + /** + * @param g + * @param flag + * @param otherFlag + * @return the created DIA.ConnectionJoin instance + * @throws DatabaseException + */ + public static Resource join(WriteGraph g, Resource flag, Resource otherFlag, boolean activateDiagramMapping) throws DatabaseException { DiagramResource DIA = DiagramResource.getInstance(g); StructuralResource2 STR = StructuralResource2.getInstance(g); Resource connectionJoin = g.newResource(); @@ -184,14 +195,24 @@ public final class FlagUtil { g.claim(connectionJoin, DIA.JoinsFlag, flag); g.claim(connectionJoin, DIA.JoinsFlag, otherFlag); - IActivationManager manager = g.getService(IActivationManager.class); - for(Resource diagram : OrderedSetUtils.getSubjects(g, flag)) - manager.activateOnce(g, diagram); - for(Resource diagram : OrderedSetUtils.getSubjects(g, otherFlag)) - manager.activateOnce(g, diagram); + if (activateDiagramMapping) { + activateMappingForParentDiagramsOf(g, flag, otherFlag); + } + return connectionJoin; } + public static void activateMappingForParentDiagramsOf(WriteGraph graph, Resource... elements) throws DatabaseException { + IActivationManager manager = graph.getService(IActivationManager.class); + Set diagrams = new HashSet<>(elements.length); + for (Resource e : elements) { + diagrams.addAll(OrderedSetUtils.getSubjects(graph, e)); + } + for (Resource diagram : diagrams) { + manager.activateOnce(graph, diagram); + } + } + public static void disconnectFlag(WriteGraph graph, Resource flag) throws DatabaseException { // Remove any :ConnectionJoin's this flag is joined by // if there's less than two flags joined by the join. @@ -531,5 +552,4 @@ public final class FlagUtil { return flags; } - -} +} \ No newline at end of file diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/RouteGraphConnectionSplitter.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/RouteGraphConnectionSplitter.java index 3f1076b5d..5c85a9944 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/RouteGraphConnectionSplitter.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/RouteGraphConnectionSplitter.java @@ -1,11 +1,10 @@ package org.simantics.diagram.flag; -import gnu.trove.map.hash.TObjectIntHashMap; - import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; @@ -24,9 +23,11 @@ import org.simantics.diagram.connection.RouteNode; import org.simantics.diagram.connection.RoutePoint; import org.simantics.diagram.connection.RouteTerminal; import org.simantics.diagram.connection.splitting.SplittedRouteGraph; +import org.simantics.diagram.connection.splitting.SplittedRouteGraph.PickResult; import org.simantics.diagram.content.ConnectionUtil; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.diagram.synchronization.graph.AddElement; +import org.simantics.diagram.synchronization.graph.BasicResources; import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; import org.simantics.diagram.synchronization.graph.RouteGraphModification; import org.simantics.g2d.elementclass.FlagClass; @@ -34,6 +35,8 @@ import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.structural.stubs.StructuralResource2; +import gnu.trove.map.hash.TObjectIntHashMap; + /** * A class that handles splitting a route graph connection in two with diagram * local flags. @@ -87,34 +90,33 @@ public class RouteGraphConnectionSplitter { RouteGraphModification modis = new RouteGraphModification(ss, rg); TObjectIntHashMap idMap = modis.getIdMap(); + if (DEBUG) { + System.out.println("Split canvas position: " + splitCanvasPos); + rg.print(); + } + // Find the edge to disconnect in the graph. // Bisect the nearest route line. - RouteLine line = SplittedRouteGraph.findNearestLine(rg, splitCanvasPos); - if (DEBUG) - rg.print(); - if (line == null) + PickResult picked = SplittedRouteGraph.pickNearestLine(rg, splitCanvasPos.getX(), splitCanvasPos.getY()); + if (picked == null) return; + + RouteLine line = picked.nearestLine; + if (DEBUG) { + System.out.println("picked nearest line:"); line.print(System.out); for (RoutePoint rp : line.getPoints()) System.out.println("RP: " + rp.getX() + ", " + rp.getY()); } // Get exact intersection point on the line - double isectX = splitCanvasPos.getX(); - double isectY = splitCanvasPos.getY(); - SplittedRouteGraph srg; - if (line.isHorizontal()) { - isectY = line.getPosition(); - srg = rg.splitGraph(line, isectX); - } - else { - isectX = line.getPosition(); - srg = rg.splitGraph(line, isectY); - } + double isectX = picked.intersectionPoint.getX(); + double isectY = picked.intersectionPoint.getY(); + SplittedRouteGraph srg = rg.splitGraph(line, line.isHorizontal() ? isectX : isectY); if (DEBUG) System.out.println(srg); - + // Disconnect if(rg.isSimpleConnection()) { RouteNode na = srg.terminals1.iterator().next(); @@ -141,44 +143,106 @@ public class RouteGraphConnectionSplitter { idMap.get(srg.splitLine) )); } - - ArrayList interfaceNodes1Resources = new ArrayList(srg.interfaceNodes1.size()); - for(RouteNode n : srg.interfaceNodes1) - interfaceNodes1Resources.add(ss.getResource((Long)n.getData())); - ArrayList interfaceNodes2Resources = new ArrayList(srg.interfaceNodes2.size()); - for(RouteNode n : srg.interfaceNodes2) - interfaceNodes2Resources.add(ss.getResource((Long)n.getData())); - - ArrayList lines2Resources = new ArrayList(srg.lines2.size()); - for(RouteLine n : srg.lines2) - lines2Resources.add(ss.getResource((Long)n.getData())); - - ArrayList terminals1Resources = new ArrayList(srg.terminals1.size()); - for(RouteTerminal n : srg.terminals1) - terminals1Resources.add(ss.getResource((Long)n.getData())); - ArrayList terminals2Resources = new ArrayList(srg.terminals2.size()); - for(RouteTerminal n : srg.terminals2) - terminals2Resources.add(ss.getResource((Long)n.getData())); + ArrayList terminals1Resources = toResources(srg.terminals1); + ArrayList terminals2Resources = toResources(srg.terminals2); + + boolean mustFlip = analyzePartInputs(graph, terminals1Resources, terminals2Resources); + + ArrayList interfaceNodes1 = toResources(mustFlip ? srg.interfaceNodes2 : srg.interfaceNodes1); + ArrayList interfaceNodes2 = toResources(mustFlip ? srg.interfaceNodes1 : srg.interfaceNodes2); + + ArrayList lines2 = toResources(mustFlip ? srg.lines1 : srg.lines2); + ArrayList terminals1 = mustFlip ? terminals2Resources : terminals1Resources; + ArrayList terminals2 = mustFlip ? terminals1Resources : terminals2Resources; + doSplit(graph, connection, - interfaceNodes1Resources, - interfaceNodes2Resources, - lines2Resources, - terminals1Resources, - terminals2Resources, + interfaceNodes1, + interfaceNodes2, + lines2, + terminals1, + terminals2, line.isHorizontal(), + mustFlip, isectX, isectY); modis.addModi(new RouteGraphModification.Split( - modis.toIds(interfaceNodes1Resources), - modis.toIds(interfaceNodes2Resources), - modis.toIds(lines2Resources), - modis.toIds(terminals1Resources), - modis.toIds(terminals2Resources), + modis.toIds(interfaceNodes1), + modis.toIds(interfaceNodes2), + modis.toIds(lines2), + modis.toIds(terminals1), + modis.toIds(terminals2), line.isHorizontal(), + mustFlip, isectX, isectY )); - } - + + private ArrayList toResources(Collection nodes) throws DatabaseException { + ArrayList result = new ArrayList<>(nodes.size()); + for (RouteNode n : nodes) + result.add(ss.getResource((Long)n.getData())); + return result; + } + + /** + * @param graph + * @param terminals1 + * @param terminals2 + * @return true if inputs need to be flipped, i.e. if terminals2 + * contains the output terminals and terminals1 doesn't. + * @throws DatabaseException + */ + private boolean analyzePartInputs(ReadGraph graph, List terminals1, List terminals2) throws DatabaseException { + @SuppressWarnings("unused") + int inputs1 = 0, outputs1 = 0; + for(Resource connector : terminals1) { + if(graph.hasStatement(connector, DIA.IsHeadConnectorOf)) + ++inputs1; + else + ++outputs1; + } + @SuppressWarnings("unused") + int inputs2 = 0, outputs2 = 0; + for(Resource connector : terminals2) { + if(graph.hasStatement(connector, DIA.IsHeadConnectorOf)) + ++inputs2; + else + ++outputs2; + } + + boolean mustFlip = outputs1 == 0; + + if (DEBUG) { + System.out.println("inputs1: " + inputs1); + System.out.println("outputs1: " + outputs1); + System.out.println("inputs2: " + inputs2); + System.out.println("outputs2: " + outputs2); + System.out.println("=> type1: " + (mustFlip ? FlagClass.Type.In : FlagClass.Type.Out)); + System.out.println("=> type2: " + (mustFlip ? FlagClass.Type.Out : FlagClass.Type.In)); + System.out.println("=> must flip route graph parts to split: " + mustFlip); + } + + return mustFlip; + } + + private static String routeNodeDebugInfo(ReadGraph graph, Resource c) throws DatabaseException { + BasicResources BR = BasicResources.getInstance(graph); + String ctr = NameUtils.getSafeName(graph, c, true); + for (Resource e : graph.getObjects(c, BR.STR.Connects)) { + ctr += " --> " + NameUtils.getSafeName(graph, e); + } + for (Resource e : graph.getObjects(c, BR.DIA.AreConnected)) { + ctr += " <-> " + NameUtils.getSafeName(graph, e); + } + return ctr; + } + + /** + * Internal routine that is only public because + * {@link RouteGraphModification#runUpdates(WriteGraph)} needs to invoke it. + * + * Assumes that #1 parameters will stay with the existing connection and #2 + * parameters will go to the newly created connection. + */ public void doSplit(WriteGraph graph, Resource connection, ArrayList interfaceNodes1Resources, @@ -186,22 +250,28 @@ public class RouteGraphConnectionSplitter { ArrayList lines2Resources, ArrayList terminals1Resources, ArrayList terminals2Resources, - boolean isHorizontal, + boolean isHorizontal, + boolean invertFlagRotation, double isectX, double isectY) throws DatabaseException { + // 1 = output, 2 = input + FlagClass.Type + type1 = FlagClass.Type.Out, + type2 = FlagClass.Type.In; + if (DEBUG) { System.out.println("doSplit:"); System.out.println(NameUtils.getSafeName(graph, connection, true)); for (Resource i : interfaceNodes1Resources) - System.out.println("i1: " + NameUtils.getSafeName(graph, i, true)); + System.out.println("i1: " + routeNodeDebugInfo(graph, i)); for (Resource i : interfaceNodes2Resources) - System.out.println("i2: " + NameUtils.getSafeName(graph, i, true)); + System.out.println("i2: " + routeNodeDebugInfo(graph, i)); for (Resource l : lines2Resources) - System.out.println("l2r: " + NameUtils.getSafeName(graph, l, true)); + System.out.println("l2r: " + routeNodeDebugInfo(graph, l)); for (Resource t : terminals1Resources) - System.out.println("t1: " + NameUtils.getSafeName(graph, t, true)); + System.out.println("t1: " + routeNodeDebugInfo(graph, t)); for (Resource t : terminals2Resources) - System.out.println("t2: " + NameUtils.getSafeName(graph, t, true)); + System.out.println("t2: " + routeNodeDebugInfo(graph, t)); System.out.println("is horizontal: " + isHorizontal); System.out.println("@(x,y): " + isectX + ", " + isectY); } @@ -209,15 +279,39 @@ public class RouteGraphConnectionSplitter { ConnectionUtil cu = new ConnectionUtil(graph); Resource diagram = OrderedSetUtils.getSingleOwnerList(graph, connection, DIA.Diagram); - Resource connectionType = graph.getSingleType(connection, DIA.Connection); + Resource diagramConnectionType = graph.getSingleType(connection, DIA.Connection); Resource hasConnectionType = graph.getPossibleObject(connection, STR.HasConnectionType); - Resource newConnection = cu.newConnection(diagram, connectionType); + Resource newConnection = cu.newConnection(diagram, diagramConnectionType); if (hasConnectionType != null) graph.claim(newConnection, STR.HasConnectionType, null, hasConnectionType); // Give running name to connection increment the counter attached to the diagram. AddElement.claimFreshElementName(graph, diagram, newConnection); + String commonLabel = DiagramFlagPreferences + .getActiveFlagLabelingScheme(graph) + .generateLabel(graph, diagram); + + Point2D pos1, pos2; + double theta; + double flagDist = 3.0; + if(isHorizontal) { + theta = 0.0; + pos1 = new Point2D.Double(isectX-flagDist, isectY); + pos2 = new Point2D.Double(isectX+flagDist, isectY); + } else { + theta = Math.PI*0.5; + pos1 = new Point2D.Double(isectX, isectY-flagDist); + pos2 = new Point2D.Double(isectX, isectY+flagDist); + } + + if (invertFlagRotation) { + theta += Math.PI; + Point2D p = pos1; + pos1 = pos2; + pos2 = p; + } + // WORKAROUND for mapping problems: // If any terminal of the split connection contains a flag, make sure their STR.Joins relations are all removed // to give mapping a chance to fix them properly. @@ -237,97 +331,31 @@ public class RouteGraphConnectionSplitter { graph.claim(rn, predicate, newConnection); } - // 1 = output, 2 = input - FlagClass.Type type1, type2; - - FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph); - String commonLabel = scheme.generateLabel(graph, diagram); - // Create flags and connect both disconnected ends to them. - Point2D pos1, pos2; - double theta; - double flagDist = 3.0; - if(isHorizontal) { - theta = 0.0; - pos1 = new Point2D.Double(isectX-flagDist, isectY); - pos2 = new Point2D.Double(isectX+flagDist, isectY); - } - else { - theta = Math.PI*0.5; - pos1 = new Point2D.Double(isectX, isectY-flagDist); - pos2 = new Point2D.Double(isectX, isectY+flagDist); - } - - // Chooses flag directions - { - @SuppressWarnings("unused") - int inputs1 = 0, outputs1 = 0; - for(Resource connector : terminals1Resources) { - if(graph.hasStatement(connector, DIA.IsHeadConnectorOf)) - ++inputs1; - else - ++outputs1; - } - @SuppressWarnings("unused") - int inputs2 = 0, outputs2 = 0; - for(Resource connector : terminals2Resources) { - if(graph.hasStatement(connector, DIA.IsHeadConnectorOf)) - ++inputs2; - else - ++outputs2; - } - - if(outputs1 == 0) { - type1 = FlagClass.Type.In; - type2 = FlagClass.Type.Out; - theta += Math.PI; - } - else { - type1 = FlagClass.Type.Out; - type2 = FlagClass.Type.In; - } - if (DEBUG) { - System.out.println("inputs1: " + inputs1); - System.out.println("outputs1: " + outputs1); - System.out.println("=> type1: " + type1); - System.out.println("inputs2: " + inputs2); - System.out.println("outputs2: " + outputs2); - System.out.println("=> type2: " + type2); - } - } Resource flag1 = createFlag(graph, diagram, getFlagTransform(pos1, theta), type1, commonLabel); Resource flag2 = createFlag(graph, diagram, getFlagTransform(pos2, theta), type2, commonLabel); + if (DEBUG) { + System.out.println("LABEL FOR NEW FLAGS: " + commonLabel); System.out.println("FLAG1: " + NameUtils.getSafeName(graph, flag1, true)); System.out.println("FLAG2: " + NameUtils.getSafeName(graph, flag2, true)); } -// System.out.println("conn1: " + NameUtils.getSafeLabel(graph, type1 == FlagClass.Type.In ? DIA.HasPlainConnector : DIA.HasArrowConnector)); -// System.out.println("conn2: " + NameUtils.getSafeLabel(graph, type2 == FlagClass.Type.In ? DIA.HasPlainConnector : DIA.HasArrowConnector)); - Resource flagConnector1 = cu.newConnector(connection, - type1 == FlagClass.Type.In ? DIA.HasPlainConnector : DIA.HasArrowConnector); - Resource flagConnector2 = cu.newConnector(newConnection, - type2 == FlagClass.Type.In ? DIA.HasPlainConnector : DIA.HasArrowConnector); + 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); double position = isHorizontal ? isectY : isectX; - connectFlag(graph, isHorizontal, position, connection, flagConnector1, - interfaceNodes1Resources); - connectFlag(graph, isHorizontal, position, newConnection, flagConnector2, - interfaceNodes2Resources); - - FlagUtil.join(graph, flag1, flag2); - - // Move mapping relations to new connection if necessary - if(type1 == FlagClass.Type.In) { - moveStatements(graph, connection, newConnection, MOD.ElementToComponent); - moveStatements(graph, connection, newConnection, MOD.DiagramConnectionToConnection); - moveStatements(graph, connection, newConnection, MOD.DiagramConnectionToConnectionSpecial); - FlagUtil.fixBindsStatements(graph, graph.getPossibleObject(newConnection, MOD.DiagramConnectionToConnection)); - } - else - FlagUtil.fixBindsStatements(graph, graph.getPossibleObject(connection, MOD.DiagramConnectionToConnection)); + connectFlag(graph, isHorizontal, position, connection, flagConnector1, interfaceNodes1Resources); + connectFlag(graph, isHorizontal, position, newConnection, flagConnector2, interfaceNodes2Resources); + + // Join the flags without activatingn diagram mapping at this point + FlagUtil.join(graph, flag1, flag2, false); + FlagUtil.fixBindsStatements(graph, graph.getPossibleObject(connection, MOD.DiagramConnectionToConnection)); + + // Finally ensure that all the diagrams related to the operation are mapped properly in one go + FlagUtil.activateMappingForParentDiagramsOf(graph, flag1, flag2); } /** @@ -370,15 +398,6 @@ public class RouteGraphConnectionSplitter { } } - private static void moveStatements(WriteGraph graph, Resource from, Resource to, Resource relation) throws DatabaseException { - if(from.equals(to)) - return; - for(Statement stat : graph.getStatements(from, relation)) - if(stat.getSubject().equals(from)) - graph.claim(to, stat.getPredicate(), stat.getObject()); - graph.deny(from, relation); - } - private void connectFlag(WriteGraph graph, boolean isHorizontal, double position, Resource connection, Resource flagConnector, Collection interfaceNodes) throws DatabaseException { if(interfaceNodes.size() > 1) { @@ -421,6 +440,7 @@ public class RouteGraphConnectionSplitter { } public static void splitConnection(WriteGraph graph, Resource connection, double x, double y) throws DatabaseException { + // TODO: provide a proper runtimeDiagram parameter to load to support also connections attached to flags attached to diagram template flag tables RouteGraph rg = RouteGraphUtils.load(graph, null, connection); new RouteGraphConnectionSplitter(graph).split(graph, connection, rg, new Point2D.Double(x, y)); } diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java index a15b99bc4..41f4c00e6 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/RouteGraphModification.java @@ -162,12 +162,14 @@ public class RouteGraphModification { int[] lines2; int[] terminals1; int[] terminals2; - boolean isHorizontal; + boolean isHorizontal; + boolean invertFlagRotation; double isectX; double isectY; public Split(int[] interface1, int[] interface2, int[] lines2, int[] terminals1, int[] terminals2, boolean isHorizontal, + boolean invertFlagRotation, double isectX, double isectY) { this.interface1 = interface1; this.interface2 = interface2; @@ -175,6 +177,7 @@ public class RouteGraphModification { this.terminals1 = terminals1; this.terminals2 = terminals2; this.isHorizontal = isHorizontal; + this.invertFlagRotation = invertFlagRotation; this.isectX = isectX; this.isectY = isectY; } @@ -188,6 +191,7 @@ public class RouteGraphModification { this.terminals1 = readInts(it); this.terminals2 = readInts(it); this.isHorizontal = Boolean.parseBoolean(it.next()); + this.invertFlagRotation = Boolean.parseBoolean(it.next()); this.isectX = Double.parseDouble(it.next()); this.isectY = Double.parseDouble(it.next()); } @@ -208,6 +212,8 @@ public class RouteGraphModification { b.append("$"); b.append(isHorizontal); b.append("$"); + b.append(invertFlagRotation); + b.append("$"); b.append(isectX); b.append("$"); b.append(isectY); @@ -531,6 +537,7 @@ public class RouteGraphModification { toResources(modi.terminals1), toResources(modi.terminals2), modi.isHorizontal, + modi.invertFlagRotation, modi.isectX, modi.isectY ); -- 2.43.2