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;
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;
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.
RouteGraphModification modis = new RouteGraphModification(ss, rg);
TObjectIntHashMap<RouteNode> 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();
idMap.get(srg.splitLine)
));
}
-
- ArrayList<Resource> interfaceNodes1Resources = new ArrayList<Resource>(srg.interfaceNodes1.size());
- for(RouteNode n : srg.interfaceNodes1)
- interfaceNodes1Resources.add(ss.getResource((Long)n.getData()));
- ArrayList<Resource> interfaceNodes2Resources = new ArrayList<Resource>(srg.interfaceNodes2.size());
- for(RouteNode n : srg.interfaceNodes2)
- interfaceNodes2Resources.add(ss.getResource((Long)n.getData()));
-
- ArrayList<Resource> lines2Resources = new ArrayList<Resource>(srg.lines2.size());
- for(RouteLine n : srg.lines2)
- lines2Resources.add(ss.getResource((Long)n.getData()));
-
- ArrayList<Resource> terminals1Resources = new ArrayList<Resource>(srg.terminals1.size());
- for(RouteTerminal n : srg.terminals1)
- terminals1Resources.add(ss.getResource((Long)n.getData()));
- ArrayList<Resource> terminals2Resources = new ArrayList<Resource>(srg.terminals2.size());
- for(RouteTerminal n : srg.terminals2)
- terminals2Resources.add(ss.getResource((Long)n.getData()));
+ ArrayList<Resource> terminals1Resources = toResources(srg.terminals1);
+ ArrayList<Resource> terminals2Resources = toResources(srg.terminals2);
+
+ boolean mustFlip = analyzePartInputs(graph, terminals1Resources, terminals2Resources);
+
+ ArrayList<Resource> interfaceNodes1 = toResources(mustFlip ? srg.interfaceNodes2 : srg.interfaceNodes1);
+ ArrayList<Resource> interfaceNodes2 = toResources(mustFlip ? srg.interfaceNodes1 : srg.interfaceNodes2);
+
+ ArrayList<Resource> lines2 = toResources(mustFlip ? srg.lines1 : srg.lines2);
+ ArrayList<Resource> terminals1 = mustFlip ? terminals2Resources : terminals1Resources;
+ ArrayList<Resource> 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<Resource> toResources(Collection<? extends RouteNode> nodes) throws DatabaseException {
+ ArrayList<Resource> 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 <code>true</code> 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<Resource> terminals1, List<Resource> 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<Resource> interfaceNodes1Resources,
ArrayList<Resource> lines2Resources,
ArrayList<Resource> terminals1Resources,
ArrayList<Resource> 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);
}
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.
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);
}
/**
}
}
- 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<Resource> interfaceNodes)
throws DatabaseException {
if(interfaceNodes.size() > 1) {
}
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));
}