X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fflag%2FJoiner.java;fp=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fflag%2FJoiner.java;h=cc02b763801d2493972ec166cb3215c156a81d4d;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Joiner.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Joiner.java new file mode 100644 index 000000000..cc02b7638 --- /dev/null +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/flag/Joiner.java @@ -0,0 +1,161 @@ +package org.simantics.diagram.flag; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +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.layer0.util.RemoverUtil; +import org.simantics.diagram.content.ConnectionUtil; +import org.simantics.diagram.stubs.DiagramResource; +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 joining of diagram-local flag pairs into a direct + * connections. + * + * @author Tuukka Lehtonen + */ +public class Joiner { + + Layer0 L0; + DiagramResource DIA; + StructuralResource2 STR; + ModelingResources MOD; + + public Joiner(ReadGraph graph) { + this.L0 = Layer0.getInstance(graph); + this.DIA = DiagramResource.getInstance(graph); + this.STR = StructuralResource2.getInstance(graph); + this.MOD = ModelingResources.getInstance(graph); + } + + public void joinLocal(WriteGraph graph, Collection flags) throws DatabaseException { + ConnectionUtil cu = new ConnectionUtil(graph); + Set visited = new HashSet(); + ArrayDeque todo = new ArrayDeque(flags); + while (!todo.isEmpty()) { + Resource flag1 = todo.poll(); + Resource flag2 = FlagUtil.getPossibleCounterpart(graph, flag1); + if (flag2 == null || !visited.add(flag1) || !visited.add(flag2)) + continue; + + Type type1 = FlagUtil.getFlagType(graph, flag1); + Type type2 = FlagUtil.getFlagType(graph, flag2); + + Resource connector1 = null; + Resource connector2 = null; + + Resource connection1 = null; + Resource connection2 = null; + + // #7781: prevent joining of flags where one of them or both are + // have no connections to them, i.e. are disconnected. + // First ensure that both flags are connected to something, + // even considering joining them. + for (Resource connector : graph.getObjects(flag1, STR.IsConnectedTo)) { + connector1 = graph.getPossibleObject(connector, DIA.AreConnected); + connection1 = ConnectionUtil.getConnection(graph, connector1); + } + for (Resource connector : graph.getObjects(flag2, STR.IsConnectedTo)) { + connector2 = graph.getPossibleObject(connector, DIA.AreConnected); + connection2 = ConnectionUtil.getConnection(graph, connector2); + } + if (connection1 == null || connector1 == null || connection2 == null || connector2 == null) + continue; + + // Disconnect flags from their respective edges + // This code relies on the fact that flag terminals are + // functional and can only be connected once. This implies + // that their :DIA.Connectors cannot have more than one + // AreConnected relation. + for (Resource connector : graph.getObjects(flag1, STR.IsConnectedTo)) { + connector1 = graph.getPossibleObject(connector, DIA.AreConnected); + connection1 = ConnectionUtil.getConnection(graph, connector1); + cu.removeConnectionPart(connector); + } + for (Resource connector : graph.getObjects(flag2, STR.IsConnectedTo)) { + connector2 = graph.getPossibleObject(connector, DIA.AreConnected); + connection2 = ConnectionUtil.getConnection(graph, connector2); + cu.removeConnectionPart(connector); + } + + // Decide which connection to remove. The strategy is: + // * always keep the connection that has an ElementToComponent relation. + // * if there are no ElementToComponent relations, keep the connection on the OutputFlag side. + // * if flag type information is not available, keep connection1. + Resource connectionToKeep = connection1; + Resource connectionToRemove = connection2; + Resource hasElementToComponent1 = graph.getPossibleObject(connection1, MOD.ElementToComponent); + Resource hasElementToComponent2 = graph.getPossibleObject(connection2, MOD.ElementToComponent); + if (hasElementToComponent1 != null && hasElementToComponent2 != null) + throw new UnsupportedOperationException("Both flag are connected with connections that have mapped components, can't decide which connection to remove in join operation"); + if (hasElementToComponent2 != null + || (type1 != Type.Out && type2 == Type.Out)) { + connectionToKeep = connection2; + connectionToRemove = connection1; + } + + // Remove connection join and flags + for (Resource diagram : OrderedSetUtils.getOwnerLists(graph, flag1, DIA.Diagram)) + OrderedSetUtils.remove(graph, diagram, flag1); + for (Resource diagram : OrderedSetUtils.getOwnerLists(graph, flag2, DIA.Diagram)) + OrderedSetUtils.remove(graph, diagram, flag2); + FlagUtil.disconnectFlag(graph, flag1); + double[] transform1 = graph.getRelatedValue(flag1, DIA.HasTransform, Bindings.DOUBLE_ARRAY); + double[] transform2 = graph.getRelatedValue(flag2, DIA.HasTransform, Bindings.DOUBLE_ARRAY); + RemoverUtil.remove(graph, flag1); + RemoverUtil.remove(graph, flag2); + + // Move connector from connection to remove to other connection + for(Statement connectorStat : graph.getStatements(connectionToRemove, DIA.HasConnector)) { + graph.deny(connectorStat); + graph.claim(connectionToKeep, connectorStat.getPredicate(), connectorStat.getObject()); + } + for(Resource node : graph.getObjects(connectionToRemove, DIA.HasInteriorRouteNode)) { + graph.deny(node, DIA.HasInteriorRouteNode_Inverse, connectionToRemove); + graph.claim(node, DIA.HasInteriorRouteNode_Inverse, connectionToKeep); + } + + // Remove obsolete connection + cu.removeConnection(connectionToRemove); + + // Reconnect respective edges + if(graph.isInstanceOf(connector1, DIA.RouteLine) && graph.isInstanceOf(connector2, DIA.RouteLine) + && graph.getRelatedValue(connector1, DIA.IsHorizontal) == graph.getRelatedValue(connector2, DIA.IsHorizontal)) { + boolean horizontal = graph.getRelatedValue(connector1, DIA.IsHorizontal); + + double position; + if(horizontal) + position = 0.5 * (transform1[4] + transform2[4]); + else + position = 0.5 * (transform1[5] + transform2[5]); + + Resource intermediateRouteLine = graph.newResource(); + graph.claim(intermediateRouteLine, L0.InstanceOf, DIA.RouteLine); + graph.claimLiteral(intermediateRouteLine, DIA.IsHorizontal, !horizontal); + graph.claimLiteral(intermediateRouteLine, DIA.HasPosition, position); + graph.claim(connectionToKeep, DIA.HasInteriorRouteNode, intermediateRouteLine); + graph.claim(connector1, DIA.AreConnected, intermediateRouteLine); + graph.claim(connector2, DIA.AreConnected, intermediateRouteLine); + } + else + graph.claim(connector1, DIA.AreConnected, connector2); + } + } + + public static void joinFlagsLocal(WriteGraph graph, Collection flags) throws DatabaseException { + new Joiner(graph).joinLocal(graph, flags); + } + +} \ No newline at end of file