X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fadapter%2FRouteGraphUtils.java;h=83a4f1b441e44f0011690d9db5c61c50b42e7d3e;hb=09414975498828d06ac41a95a1e3ee5c2531b934;hp=bba694a5e90a4d7beaaa45bb7e968b59614bb48e;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java index bba694a5e..83a4f1b44 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/adapter/RouteGraphUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Association for Decentralized Information Management + * Copyright (c) 2012, 2016 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -8,13 +8,14 @@ * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation + * Semantum Oy - refactoring *******************************************************************************/ package org.simantics.diagram.adapter; -import gnu.trove.map.hash.THashMap; -import gnu.trove.set.hash.THashSet; - +import java.awt.BasicStroke; +import java.awt.Color; import java.awt.Shape; +import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.Collection; @@ -25,17 +26,22 @@ import java.util.Set; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; +import org.simantics.db.Session; import org.simantics.db.Statement; import org.simantics.db.common.procedure.adapter.TransientCacheListener; +import org.simantics.db.common.request.ResourceRead2; import org.simantics.db.common.request.UnaryRead; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; -import org.simantics.diagram.adapter.RouteGraphConnectionClassFactory.BackendConnection; +import org.simantics.diagram.connection.ConnectionVisuals; import org.simantics.diagram.connection.RouteGraph; import org.simantics.diagram.connection.RouteGraphConnectionClass; import org.simantics.diagram.connection.RouteLine; import org.simantics.diagram.connection.RouteNode; import org.simantics.diagram.connection.RouteTerminal; +import org.simantics.diagram.connection.rendering.BasicConnectionStyle; +import org.simantics.diagram.connection.rendering.ConnectionStyle; +import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer; import org.simantics.diagram.connection.rendering.arrows.ArrowLineEndStyle; import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle; import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle; @@ -43,17 +49,20 @@ import org.simantics.diagram.content.EdgeResource; import org.simantics.diagram.content.TerminalMap; import org.simantics.diagram.query.DiagramRequests; import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.diagram.stubs.G2DResource; import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; import org.simantics.diagram.synchronization.graph.RouteGraphConnection; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.impl.CanvasContext; import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.diagram.handler.Topology.Connection; import org.simantics.g2d.diagram.impl.ElementDiagram; import org.simantics.g2d.element.ElementUtils; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd; import org.simantics.g2d.elementclass.FlagClass.Type; import org.simantics.layer0.Layer0; +import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent; import org.simantics.scenegraph.utils.GeometryUtils; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.structural2.modelingRules.CPTerminal; @@ -61,126 +70,33 @@ import org.simantics.structural2.modelingRules.IAttachmentRelationMap; import org.simantics.structural2.modelingRules.IModelingRules; import org.simantics.utils.threads.CurrentThread; +import gnu.trove.map.hash.THashMap; +import gnu.trove.set.hash.THashSet; + public class RouteGraphUtils { - - public static boolean DEBUG = false; - + + public static boolean DEBUG = false; + public static final ILineEndStyle HEAD = new ArrowLineEndStyle("fill 2 1 0"); public static final ILineEndStyle TAIL = PlainLineEndStyle.INSTANCE; - private static EdgeEnd toEdgeEnd(ReadGraph graph, Resource attachmentRelation, EdgeEnd defaultValue) - throws DatabaseException { - DiagramResource DIA = DiagramResource.getInstance(graph); - if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector)) - return EdgeEnd.Begin; - if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) - return EdgeEnd.End; - return defaultValue; - } - - private static Resource resolveFlagAttachment(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules) throws DatabaseException { - DiagramResource DIA = DiagramResource.getInstance(graph); - Type type = resolveFlagType(graph, connection, flag, modelingRules); - if (type != null) { - switch (type) { - case In: return DIA.HasPlainConnector; - case Out: return DIA.HasArrowConnector; - } - } - return null; - } - - private static Type resolveFlagType(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules) throws DatabaseException { - return readFlagType(graph, flag); + public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection) throws DatabaseException { + ICanvasContext canvas = new CanvasContext(CurrentThread.getThreadAccess()); + IDiagram diagram = new ElementDiagram(canvas); + return load(graph, diagramRuntime, connection, canvas, diagram); } - private static Type readFlagType(ReadGraph graph, Resource flag) throws DatabaseException { - DiagramResource DIA = DiagramResource.getInstance(graph); - Resource flagType = graph.getPossibleObject(flag, DIA.HasFlagType); - Type type = DiagramGraphUtil.toFlagType(DIA, flagType); - return type; + public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection, ICanvasContext canvas, IDiagram diagram) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + Resource diagramResource = graph.getPossibleObject(connection, L0.PartOf); + IModelingRules modelingRules = graph.syncRequest(DiagramRequests.getModelingRules(diagramResource, null), TransientCacheListener.instance()); + return load(graph, diagramRuntime, connection, canvas, diagram, modelingRules, null); } -// public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, ILineEndStyle defaultValue) -// throws DatabaseException { -// ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation), -// TransientCacheListener.instance()); -// return style != null ? style : defaultValue; -// } + public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection, ICanvasContext canvas, IDiagram diagram, IModelingRules modelingRules, Set backendConnections) throws DatabaseException { - /** - * A request for caching ILineEndStyle results. - */ - public static class LineEndStyle extends UnaryRead { - public LineEndStyle(Resource attachmentRelation) { - super(attachmentRelation); - } - @Override - public ILineEndStyle perform(ReadGraph graph) throws DatabaseException { - return loadLineEndStyle0(graph, parameter); - } - } - - public static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation) - throws DatabaseException { - ILineEndStyle style = graph.getPossibleAdapter(attachmentRelation, ILineEndStyle.class); - if (style != null) - return style; DiagramResource DIA = DiagramResource.getInstance(graph); - if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) - return HEAD; - if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector)) - return TAIL; - return null; - } - - private static Statement findTerminalStatement(ReadGraph graph, Resource connection, Resource connector) - throws DatabaseException { - - StructuralResource2 STR = StructuralResource2.getInstance(graph); - - for (Statement stm : graph.getStatements(connector, STR.Connects)) { - if (connection.equals(stm.getObject())) - continue; - return stm; - } - return null; - - } - - private static Resource getInverseAttachment(ReadGraph graph, Resource attachmentRelation) - throws DatabaseException { - DiagramResource DIA = DiagramResource.getInstance(graph); - Resource inverse = graph.getPossibleObject(attachmentRelation, DIA.HasInverseAttachment); - if (inverse != null) - return inverse; - if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) - return DIA.HasPlainConnector; - if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector)) - return DIA.HasArrowConnector; - return null; - } - - public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection) throws DatabaseException { - - ICanvasContext canvas = new CanvasContext(CurrentThread.getThreadAccess()); - IDiagram diagram = new ElementDiagram(canvas); - return load(graph, diagramRuntime, connection, canvas, diagram); - - } - - public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection, ICanvasContext canvas, IDiagram diagram) throws DatabaseException { - - Layer0 L0 = Layer0.getInstance(graph); - DiagramResource DIA = DiagramResource.getInstance(graph); - StructuralResource2 STR = StructuralResource2.getInstance(graph); - - Resource diagramResource = graph.getPossibleObject(connection, L0.PartOf); - - IModelingRules modelingRules = graph.syncRequest(DiagramRequests.getModelingRules(diagramResource, null), TransientCacheListener.instance()); - -// IModelingRules modelingRules = diagram.getHint(DiagramModelHints.KEY_MODELING_RULES); -// Resource diagramRuntime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE); + StructuralResource2 STR = StructuralResource2.getInstance(graph); RouteGraph rg = new RouteGraph(); @@ -232,7 +148,7 @@ public class RouteGraphUtils { for (Statement toConnector : toConnectorStatements) { Resource connector = toConnector.getObject(); - Statement terminalStm = findTerminalStatement(graph, connection, connector); + Statement terminalStm = findTerminalStatement(graph, connection, connector, STR); if (terminalStm == null) // Ignore broken connector: attached to the connection but not to any terminal. continue; @@ -255,7 +171,7 @@ public class RouteGraphUtils { System.out.println("modeling rules decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")"); } else if (graph.isInstanceOf(terminalElement, DIA.Flag)) { // Secondary: believe flag type - attachment = resolveFlagAttachment(graph, connection, terminalElement, modelingRules); + attachment = resolveFlagAttachment(graph, connection, terminalElement, modelingRules, DIA); if (attachment != null) { if (connectorToModeledAttachment == null) connectorToModeledAttachment = new THashMap(toConnectorStatements.size()); @@ -272,16 +188,13 @@ public class RouteGraphUtils { Resource forcedAttachmentRelation = null; if (terminalCount == 2 && connectorToModeledAttachment.size() == 1) { - forcedAttachmentRelation = getInverseAttachment(graph, connectorToModeledAttachment.values().iterator().next()); + forcedAttachmentRelation = getInverseAttachment(graph, connectorToModeledAttachment.values().iterator().next(), DIA); if (DEBUG) System.out.println("set forced attachment: " + NameUtils.getSafeLabel(graph, forcedAttachmentRelation)); } Resource connectionType = graph.getPossibleObject(connection, STR.HasConnectionType); - // Needed to support ConnectionEntity#getTerminalConnections - Set backendConnections = new THashSet(toConnectorStatements.size(), 0.75f); - // Load all node terminal connections as RouteTerminals for (Statement toConnector : toConnectorStatements) { Resource connector = toConnector.getObject(); @@ -289,7 +202,7 @@ public class RouteGraphUtils { if (DEBUG) System.out.println("original attachment relation: " + NameUtils.getSafeLabel(graph, attachmentRelation)); - Statement terminalStm = findTerminalStatement(graph, connection, connector); + Statement terminalStm = findTerminalStatement(graph, connection, connector, STR); if (terminalStm == null) // Ignore broken connector: attached to the connection but not to any terminal. continue; @@ -326,9 +239,9 @@ public class RouteGraphUtils { System.out.println("terminal: " + graph.getURI(terminalStm.getPredicate())); } AffineTransform terminalElementTr = diagramRuntime != null ? - DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement) : - DiagramGraphUtil.getWorldTransform(graph, terminalElement); - + DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement) : + DiagramGraphUtil.getWorldTransform(graph, terminalElement); + if (DEBUG) System.out.println("terminalElementTr: " + terminalElementTr); @@ -352,7 +265,7 @@ public class RouteGraphUtils { if (DEBUG) System.out.println("decided attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation)); -// // Get element bounds to decide allowed terminal direction(s) + // Get element bounds to decide allowed terminal direction(s) IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null)); ElementUtils.getElementBounds(te, bounds); { @@ -392,12 +305,14 @@ public class RouteGraphUtils { } //System.out.println("DIR(" + x + ", " + y + ", " + bounds + "): " + Integer.toHexString(direction)); - backendConnections.add( - new BackendConnection( - toEdgeEnd(graph, attachmentRelation, EdgeEnd.Begin), - terminalElement, - terminal) - ); + if (backendConnections != null) { + backendConnections.add( + new BackendConnection( + toEdgeEnd(graph, attachmentRelation, EdgeEnd.Begin, DIA), + terminalElement, + terminal) + ); + } if (direction == 0) // Accept any horizontal/vertical direction if nothing is defined @@ -409,7 +324,7 @@ public class RouteGraphUtils { if (DEBUG) System.out.println("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation)); - ILineEndStyle endStyle = RouteGraphConnectionClassFactory.loadLineEndStyle(graph, attachmentRelation, connectionType, RouteGraphConnectionClassFactory.TAIL); + ILineEndStyle endStyle = loadLineEndStyle(graph, attachmentRelation, connectionType, TAIL); RouteTerminal routeTerminal = rg.addTerminal(x, y, minx, miny, maxx, maxy, direction, endStyle); routeTerminal.setData( RouteGraphConnection.serialize(graph, connector) ); @@ -432,30 +347,241 @@ public class RouteGraphUtils { rg.link(n1, n2); } -// // Load connection line style. -// ConnectionStyle style = readConnectionStyle(graph, canvas, modelingRules, connection); -// StyledRouteGraphRenderer renderer = getRenderer(graph, canvas, style); -// -// // Finish element load -// element.setHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH, rg); -// element.setHint(RouteGraphConnectionClass.KEY_RENDERER, renderer); -// element.setHint(RouteGraphConnectionClass.KEY_PICK_TOLERANCE, 0.5); -// -// // Initialize ConnectionEntity in element -// element.setHint(ElementHints.KEY_CONNECTION_ENTITY, new CE(diagram, connection, element, backendConnections)); -// -// // Setup graph writeback support for route graph modifications -// final Session session = graph.getSession(); -// element.setHint(RouteGraphConnectionClass.KEY_RG_LISTENER, new IRouteGraphListener() { -// @Override -// public void routeGraphChanged(RouteGraphChangeEvent event) { -// scheduleSynchronize(session, connection, event); -// } -// }); - return rg; - } + } + + public static EdgeEnd toEdgeEnd(ReadGraph graph, Resource attachmentRelation, EdgeEnd defaultValue, DiagramResource DIA) + throws DatabaseException { + if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector)) + return EdgeEnd.Begin; + if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) + return EdgeEnd.End; + return defaultValue; + } + + public static Resource resolveFlagAttachment(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules, DiagramResource DIA) throws DatabaseException { + Type type = resolveFlagType(graph, connection, flag, modelingRules, DIA); + if (type != null) { + switch (type) { + case In: return DIA.HasPlainConnector; + case Out: return DIA.HasArrowConnector; + } + } + return null; + } + + private static Type resolveFlagType(ReadGraph graph, Resource connection, Resource flag, IModelingRules modelingRules, DiagramResource DIA) throws DatabaseException { + return readFlagType(graph, flag, DIA); + } + + private static Type readFlagType(ReadGraph graph, Resource flag, DiagramResource DIA) throws DatabaseException { + Resource flagType = graph.getPossibleObject(flag, DIA.HasFlagType); + Type type = DiagramGraphUtil.toFlagType(DIA, flagType); + return type; + } + + public static Statement findTerminalStatement(ReadGraph graph, Resource connection, Resource connector, StructuralResource2 STR) + throws DatabaseException { + for (Statement stm : graph.getStatements(connector, STR.Connects)) { + if (connection.equals(stm.getObject())) + continue; + return stm; + } + return null; + } + + public static Resource getInverseAttachment(ReadGraph graph, Resource attachmentRelation, DiagramResource DIA) + throws DatabaseException { + Resource inverse = graph.getPossibleObject(attachmentRelation, DIA.HasInverseAttachment); + if (inverse != null) + return inverse; + if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) + return DIA.HasPlainConnector; + if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector)) + return DIA.HasArrowConnector; + return null; + } + + public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, ILineEndStyle defaultValue) + throws DatabaseException { + ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation), + TransientCacheListener.instance()); + return style != null ? style : defaultValue; + } + + public static ILineEndStyle loadLineEndStyle(ReadGraph graph, Resource attachmentRelation, Resource connectionType, ILineEndStyle defaultValue) + throws DatabaseException { + if(connectionType != null) { + ILineEndStyle style = graph.syncRequest(new LineEndStyleWithType(attachmentRelation, connectionType), + TransientCacheListener.instance()); + return style != null ? style : defaultValue; + } else { + ILineEndStyle style = graph.syncRequest(new LineEndStyle(attachmentRelation), + TransientCacheListener.instance()); + return style != null ? style : defaultValue; + } + } + + /** + * A request for caching ILineEndStyle results. + */ + public static class LineEndStyle extends UnaryRead { + public LineEndStyle(Resource attachmentRelation) { + super(attachmentRelation); + } + @Override + public ILineEndStyle perform(ReadGraph graph) throws DatabaseException { + return loadLineEndStyle0(graph, parameter); + } + } + + public static class LineEndStyleWithType extends ResourceRead2 { + public LineEndStyleWithType(Resource attachmentRelation, Resource connectionType) { + super(attachmentRelation, connectionType); + } + @Override + public ILineEndStyle perform(ReadGraph graph) throws DatabaseException { + return loadLineEndStyle0(graph, resource, resource2); + } + } + + private static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation) + throws DatabaseException { + ILineEndStyle style = graph.getPossibleAdapter(attachmentRelation, ILineEndStyle.class); + if (style != null) + return style; + DiagramResource DIA = DiagramResource.getInstance(graph); + if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) + return HEAD; + if (graph.isSubrelationOf(attachmentRelation, DIA.HasTailConnector)) + return TAIL; + return null; + } + + private static ILineEndStyle loadLineEndStyle0(ReadGraph graph, Resource attachmentRelation, Resource connectionType) + throws DatabaseException { + DiagramResource DIA = DiagramResource.getInstance(graph); + if (graph.isSubrelationOf(attachmentRelation, DIA.HasHeadConnector)) { + if(connectionType != null) { + G2DResource G2D = G2DResource.getInstance(graph); + Resource end = graph.getPossibleObject(connectionType, G2D.HasEndArrow); + if(end != null) { + Double size = graph.getPossibleRelatedValue(end, G2D.HasSize, Bindings.DOUBLE); + if(size == null) size = 0.0; + Double widthRatio = graph.getPossibleRelatedValue(end, G2D.HasWidthRatio, Bindings.DOUBLE); + if(widthRatio == null) widthRatio = 1.0; + Double space = graph.getPossibleRelatedValue(end, G2D.HasSpace, Bindings.DOUBLE); + if(space == null) space = 0.0; + + Resource c = graph.getPossibleObject(end, G2D.HasColor); + Color color = null; + if (c != null) { + float[] col = graph.getPossibleValue(c, Bindings.FLOAT_ARRAY); + if (col != null && col.length >= 3) { + color = new Color(col[0], col[1], col[2]); + } + } + + return new ArrowLineEndStyle(size, widthRatio*size, space, color); + } + } + } + return loadLineEndStyle0(graph, attachmentRelation); + } + + /** + * @param graph + * @param canvas + * @param style + * @return + * @throws DatabaseException + */ + public static StyledRouteGraphRenderer getRenderer(ReadGraph graph, ConnectionStyle style) + throws DatabaseException { + return graph.syncRequest(new Renderer(style), + TransientCacheListener.instance()); + } + + /** + * A request for caching StyledRouteGraphRenderer results. + */ + public static class Renderer extends UnaryRead { + public Renderer(ConnectionStyle style) { + super(style); + } + @Override + public StyledRouteGraphRenderer perform(ReadGraph graph) throws DatabaseException { + return new StyledRouteGraphRenderer(parameter); + } + } + + /** + * @param graph + * @param canvas + * @param modelingRules + * @param connection + * @return + * @throws DatabaseException + */ + protected static ConnectionStyle readConnectionStyle(ReadGraph graph, IModelingRules modelingRules, Resource connection, StructuralResource2 STR) throws DatabaseException { + Resource connectionType = null; + if (modelingRules != null) + connectionType = modelingRules.getConnectionType(graph, connection); + if (connectionType == null) + connectionType = graph.getPossibleObject(connection, STR.HasConnectionType); + return readConnectionStyleFromConnectionType(graph, connectionType); + } + + protected static ConnectionStyle readConnectionStyleFromConnectionType(ReadGraph graph, Resource connectionType) throws DatabaseException { + return graph.syncRequest(new ReadConnectionStyleFromConnectionType(connectionType), + TransientCacheListener.instance()); + } + + /** + * A request for caching ConnectionStyle results. + */ + public static class ReadConnectionStyleFromConnectionType extends UnaryRead { + public ReadConnectionStyleFromConnectionType(Resource connectionType) { + super(connectionType); + } + @Override + public ConnectionStyle perform(ReadGraph graph) throws DatabaseException { + return readConnectionStyleFromConnectionType0(graph, parameter); + } + } + + protected static ConnectionStyle readConnectionStyleFromConnectionType0(ReadGraph graph, Resource connectionType) throws DatabaseException { + ConnectionVisuals cv = null; + if (connectionType != null) + cv = graph.syncRequest(DiagramRequests.getConnectionVisuals(connectionType), + TransientCacheListener.instance()); + + // Fixed style settings + Color branchPointColor = Color.BLACK; + double branchPointRadius = 0.5; + double degenerateLineLength = 0.8; + + Color lineColor = cv != null ? cv.toColor() : null; + if (lineColor == null) + lineColor = Color.DARK_GRAY; + Stroke lineStroke = cv != null ? cv.stroke : null; + if (lineStroke == null) + lineStroke = new BasicStroke(0.1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10, null, 0); + Stroke routeLineStroke = GeometryUtils.scaleStrokeWidth(lineStroke, 2); + + return new BasicConnectionStyle( + lineColor, + branchPointColor, + branchPointRadius, + lineStroke, + routeLineStroke, + degenerateLineLength); + } + + public static void scheduleSynchronize(Session session, Resource connection, RouteGraphChangeEvent event) { + session.asyncRequest(RouteGraphConnection.synchronizer(connection, event)); + } // ------------------------------------------------------------------------ // RouteGraph RouteTerminal allowed direction rotation support @@ -531,4 +657,47 @@ public class RouteGraphUtils { private static final double DEG_45 = Math.PI/4.; private static final double DEG_135 = Math.PI*3./4.; + public static class BackendConnection { + public final Resource node; + public final Resource terminal; + public final EdgeEnd end; + public final int hash; + public BackendConnection(EdgeEnd end, Resource node, Resource terminal) { + assert end != null; + assert node != null; + assert terminal != null; + this.end = end; + this.node = node; + this.terminal = terminal; + this.hash = makeHash(); + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof Connection)) + return false; + Connection other = (Connection) obj; + return other.terminal == terminal + && other.node == node + && other.end == end; + } + private int makeHash() { + final int prime = 31; + int result = 1; + result = prime * result + end.hashCode(); + result = prime * result + ((node == null) ? 0 : node.hashCode()); + result = prime * result + ((terminal == null) ? 0 : terminal.hashCode()); + return result; + } + @Override + public int hashCode() { + return hash; + } + @Override + public String toString() { + return "BackendConnection[node=" + node + ", terminal=" + terminal + ", end=" + end + "]"; + } + } + }