import org.simantics.diagram.connection.RouteLine;
import org.simantics.diagram.connection.RouteNode;
import org.simantics.diagram.connection.RouteTerminal;
+import org.simantics.diagram.connection.RouteTerminalPosition;
import org.simantics.diagram.connection.rendering.BasicConnectionStyle;
import org.simantics.diagram.connection.rendering.ConnectionStyle;
+import org.simantics.diagram.connection.rendering.ExampleConnectionStyle;
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;
import org.simantics.diagram.content.EdgeResource;
+import org.simantics.diagram.content.ResourceTerminal;
import org.simantics.diagram.content.TerminalMap;
import org.simantics.diagram.query.DiagramRequests;
import org.simantics.diagram.stubs.DiagramResource;
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.DataElementMap;
import org.simantics.g2d.diagram.handler.Topology.Connection;
+import org.simantics.g2d.diagram.handler.Topology.Terminal;
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.element.handler.TerminalLayout;
import org.simantics.g2d.elementclass.FlagClass.Type;
import org.simantics.layer0.Layer0;
import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphChangeEvent;
import org.simantics.structural2.modelingRules.IAttachmentRelationMap;
import org.simantics.structural2.modelingRules.IModelingRules;
import org.simantics.utils.threads.CurrentThread;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import gnu.trove.map.hash.THashMap;
import gnu.trove.set.hash.THashSet;
public class RouteGraphUtils {
+ private static final Logger LOGGER = LoggerFactory.getLogger(RouteGraph.class);
public static boolean DEBUG = false;
public static final ILineEndStyle HEAD = new ArrowLineEndStyle("fill 2 1 0");
Layer0 L0 = Layer0.getInstance(graph);
Resource diagramResource = graph.getPossibleObject(connection, L0.PartOf);
IModelingRules modelingRules = graph.syncRequest(DiagramRequests.getModelingRules(diagramResource, null), TransientCacheListener.<IModelingRules>instance());
- return load(graph, diagramRuntime, connection, canvas, diagram, modelingRules, null);
+ return load(graph, diagramRuntime, connection, canvas, diagram, null, modelingRules, null);
}
- public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection, ICanvasContext canvas, IDiagram diagram, IModelingRules modelingRules, Set<BackendConnection> backendConnections) throws DatabaseException {
+ public static RouteGraph load(ReadGraph graph, Resource diagramRuntime, Resource connection, ICanvasContext canvas, IDiagram diagram, IElement element, IModelingRules modelingRules, Set<BackendConnection> backendConnections) throws DatabaseException {
DiagramResource DIA = DiagramResource.getInstance(graph);
StructuralResource2 STR = StructuralResource2.getInstance(graph);
RouteGraph rg = new RouteGraph();
// Default capacity should be enough for common cases.
- Set<EdgeResource> links = new THashSet<EdgeResource>();
- Map<Object, RouteNode> nodeByData = new THashMap<Object, RouteNode>();
+ Set<EdgeResource> links = new THashSet<>();
+ Map<Object, RouteNode> nodeByData = new THashMap<>();
// Load all route graph interior RouteNodes: route lines and points
for (Resource interiorNode : graph.getObjects(connection, DIA.HasInteriorRouteNode)) {
if (graph.isInstanceOf(interiorNode, DIA.RouteLine)) {
+ Collection<Resource> areConnected = graph.getObjects(interiorNode, DIA.AreConnected);
+ if (areConnected.size() < 2) {
+ // Degenerated route line encountered, most likely due to a bug somewhere else.
+ // Ignoring them because adding them to the RouteGraph structure would cause
+ // problems during rendering.
+ LOGGER.warn("Stray RouteLine found: " + NameUtils.getSafeName(graph, interiorNode));
+ continue;
+ }
+
Boolean isHorizontal = graph.getRelatedValue(interiorNode, DIA.IsHorizontal, Bindings.BOOLEAN);
Double position = graph.getRelatedValue(interiorNode, DIA.HasPosition, Bindings.DOUBLE);
RouteLine line = rg.addLine(isHorizontal, position);
nodeByData.put( interiorNode, line );
- for (Resource connectedTo : graph.getObjects(interiorNode, DIA.AreConnected)) {
+ for (Resource connectedTo : areConnected) {
links.add( new EdgeResource(interiorNode, connectedTo) );
}
} else if (graph.isInstanceOf(interiorNode, DIA.RoutePoint)) {
connectorToModeledAttachment = new THashMap<Resource, Resource>(toConnectorStatements.size());
connectorToModeledAttachment.put(connector, attachment);
if (DEBUG)
- System.out.println("modeling rules decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");
+ LOGGER.debug("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, DIA);
connectorToModeledAttachment = new THashMap<Resource, Resource>(toConnectorStatements.size());
connectorToModeledAttachment.put(connector, attachment);
if (DEBUG)
- System.out.println("flag type decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");
+ LOGGER.debug("flag type decided attachment: " + NameUtils.getSafeName(graph, attachment, true) + " for (" + NameUtils.toString(graph, toConnector, true) + ") & (" + NameUtils.toString(graph, terminalStm, true) + ")");
}
}
}
if (terminalCount == 2 && connectorToModeledAttachment.size() == 1) {
forcedAttachmentRelation = getInverseAttachment(graph, connectorToModeledAttachment.values().iterator().next(), DIA);
if (DEBUG)
- System.out.println("set forced attachment: " + NameUtils.getSafeLabel(graph, forcedAttachmentRelation));
+ LOGGER.debug("set forced attachment: " + NameUtils.getSafeLabel(graph, forcedAttachmentRelation));
}
Resource connectionType = graph.getPossibleObject(connection, STR.HasConnectionType);
+ DataElementMap diagramDataElementMap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
// Load all node terminal connections as RouteTerminals
for (Statement toConnector : toConnectorStatements) {
Resource connector = toConnector.getObject();
Resource attachmentRelation = toConnector.getPredicate();
if (DEBUG)
- System.out.println("original attachment relation: " + NameUtils.getSafeLabel(graph, attachmentRelation));
+ LOGGER.debug("original attachment relation: " + NameUtils.getSafeLabel(graph, attachmentRelation));
Statement terminalStm = findTerminalStatement(graph, connection, connector, STR);
if (terminalStm == null)
if (position.length != 2)
position = new double[] { 0, 0 };
+ AffineTransform terminalTr = DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement);
+ final AffineTransform terminalElementTransform = new AffineTransform(terminalTr);
+
if (DEBUG) {
- System.out.println("terminalStm: " + NameUtils.toString(graph, terminalStm));
- System.out.println("terminal: " + graph.getURI(terminalStm.getPredicate()));
+ LOGGER.debug("terminalStm: " + NameUtils.toString(graph, terminalStm));
+ LOGGER.debug("terminal: " + NameUtils.getURIOrSafeNameInternal(graph, terminalStm.getPredicate()));
+ LOGGER.debug("terminalElement: " + NameUtils.getURIOrSafeNameInternal(graph, terminalElement) + " : " + NameUtils.getURIOrSafeNameInternal(graph, terminalElementType));
+ LOGGER.debug("terminalElementTr: " + terminalTr);
}
- AffineTransform terminalElementTr = diagramRuntime != null ?
- DiagramGraphUtil.getDynamicWorldTransform(graph, diagramRuntime, terminalElement) :
- DiagramGraphUtil.getWorldTransform(graph, terminalElement);
-
- if (DEBUG)
- System.out.println("terminalElementTr: " + terminalElementTr);
- double x = terminalElementTr.getTranslateX();
- double y = terminalElementTr.getTranslateY();
+ double x = terminalTr.getTranslateX();
+ double y = terminalTr.getTranslateY();
double minx = x-1, miny = y-1, maxx = x+1, maxy = y+1;
int direction = 0x0;
if (att != null) {
attachmentRelation = att;
if (DEBUG)
- System.out.println("modeling rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
+ LOGGER.debug("modeling rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
} else if (forcedAttachmentRelation != null) {
attachmentRelation = forcedAttachmentRelation;
if (DEBUG)
- System.out.println("forced rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
+ LOGGER.debug("forced rules attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
}
if (DEBUG)
- System.out.println("decided attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
+ LOGGER.debug("decided attachment: " + NameUtils.getSafeLabel(graph, attachmentRelation));
// Get element bounds to decide allowed terminal direction(s)
IElement te = graph.syncRequest(DiagramRequests.getElement(canvas, diagram, terminalElement, null));
ElementUtils.getElementBounds(te, bounds);
{
- Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(bounds, terminalElementTr);
+ Shape shp = org.simantics.g2d.utils.GeometryUtils.transformShape(bounds, terminalTr);
bounds.setFrame(shp.getBounds2D());
}
maxx = bounds.getMaxX();
maxy = bounds.getMaxY();
- AffineTransform terminalPos = DiagramGraphUtil.getDynamicAffineTransform(graph, terminalElement, terminal);
- //AffineTransform terminalPos2 = DiagramGraphUtil.getAffineTransform(graph, terminal);
+ final ResourceTerminal rt = new ResourceTerminal(terminal);
+ final TerminalLayout tl = te.getElementClass().getSingleItem(TerminalLayout.class);
+ AffineTransform terminalPos = tl.getTerminalPosition(element, rt);
+
if (terminalPos != null) {
- if (DEBUG) {
- System.out.println("terminalPos: " + terminalPos);
- //System.out.println("terminalPos2: " + terminalPos2);
- }
- terminalElementTr.concatenate(terminalPos);
+ terminalTr.concatenate(terminalPos);
+ x = terminalTr.getTranslateX();
+ y = terminalTr.getTranslateY();
if (DEBUG)
- System.out.println("terminalElementTr: " + terminalElementTr);
- x = terminalElementTr.getTranslateX();
- y = terminalElementTr.getTranslateY();
+ LOGGER.debug("terminalPos/Tr: " + terminalPos + ", " + terminalTr);
}
Integer allowedDirections = graph.getPossibleRelatedValue(terminal, DIA.Terminal_AllowedDirections, Bindings.INTEGER);
if (allowedDirections != null) {
direction |= allowedDirections;
- direction = rotateDirection(direction, terminalElementTr);
+ direction = rotateDirection(direction, terminalTr);
} else {
direction |= RouteGraphConnectionClass.shortestDirectionOutOfBounds(x, y, bounds);
}
- //System.out.println("DIR(" + x + ", " + y + ", " + bounds + "): " + Integer.toHexString(direction));
+ //LOGGER.debug("DIR(" + x + ", " + y + ", " + bounds + "): " + Integer.toHexString(direction));
if (backendConnections != null) {
backendConnections.add(
// FIXME: routegraph crashes if this is done for all terminals regardless of the amount of terminals.
if (DEBUG)
- System.out.println("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation));
+ LOGGER.debug("load line style: " + NameUtils.getSafeLabel(graph, attachmentRelation));
ILineEndStyle endStyle = loadLineEndStyle(graph, attachmentRelation, connectionType, TAIL);
- RouteTerminal routeTerminal = rg.addTerminal(x, y, minx, miny, maxx, maxy, direction, endStyle);
+ RouteTerminal routeTerminal = rg.addTerminal(x, y, minx, miny, maxx, maxy, direction, endStyle,
+ new RouteTerminalPositionImpl(diagram, diagramDataElementMap, terminalElement, terminalElementTransform, tl, rt));
routeTerminal.setData( RouteGraphConnection.serialize(graph, connector) );
nodeByData.put( connector, routeTerminal );
RouteNode n1 = nodeByData.get(link.first());
RouteNode n2 = nodeByData.get(link.second());
if (n1 == null || n2 == null) {
- System.err.println("Stray connection link found: " + link.toString(graph));
+ LOGGER.warn("Stray connection link found: " + link.toString(graph));
continue;
}
rg.link(n1, n2);
}
}
+ private static final ConnectionStyle DEFAULT_CONNECTION_STYLE = new BasicConnectionStyle(Color.BLACK, Color.BLACK, 3, ExampleConnectionStyle.SOLID, ExampleConnectionStyle.SOLID, 8);
+
/**
* @param graph
* @param canvas
connectionType = modelingRules.getConnectionType(graph, connection);
if (connectionType == null)
connectionType = graph.getPossibleObject(connection, STR.HasConnectionType);
- return readConnectionStyleFromConnectionType(graph, connectionType);
+ return connectionType != null ? readConnectionStyleFromConnectionType(graph, connectionType) : DEFAULT_CONNECTION_STYLE;
}
protected static ConnectionStyle readConnectionStyleFromConnectionType(ReadGraph graph, Resource connectionType) throws DatabaseException {
// Fixed style settings
Color branchPointColor = Color.BLACK;
- double branchPointRadius = 0.5;
+ double branchPointRadius = cv != null && cv.branchPointRadius != null ? cv.branchPointRadius : 0.5;
double degenerateLineLength = 0.8;
-
+
Color lineColor = cv != null ? cv.toColor() : null;
if (lineColor == null)
lineColor = Color.DARK_GRAY;
if (lineStroke == null)
lineStroke = new BasicStroke(0.1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10, null, 0);
Stroke routeLineStroke = GeometryUtils.scaleStrokeWidth(lineStroke, 2);
+ double rounding = cv.rounding == null ? 0.0 : cv.rounding;
return new BasicConnectionStyle(
lineColor,
branchPointRadius,
lineStroke,
routeLineStroke,
- degenerateLineLength);
+ degenerateLineLength,
+ rounding);
}
public static void scheduleSynchronize(Session session, Resource connection, RouteGraphChangeEvent event) {
}
}
+ private static class RouteTerminalPositionImpl implements RouteTerminalPosition {
+
+ private IDiagram diagram;
+ private DataElementMap dataElementMap;
+ private Resource element;
+ private AffineTransform elementTransform;
+ private TerminalLayout terminalLayout;
+ private Terminal elementTerminal;
+
+ private transient AffineTransform lastTerminalTr;
+ private transient AffineTransform transform;
+
+ public RouteTerminalPositionImpl(IDiagram diagram, DataElementMap dem, Resource element, AffineTransform elementTransform, TerminalLayout terminalLayout, Terminal terminal) {
+ this.diagram = diagram;
+ this.dataElementMap = dem;
+ this.element = element;
+ this.elementTransform = elementTransform;
+ this.terminalLayout = terminalLayout;
+ this.elementTerminal = terminal;
+ }
+
+ @Override
+ public AffineTransform getTransform() {
+ IElement actualElement = dataElementMap.getElement(diagram, element);
+ AffineTransform terminalTr = actualElement != null ? terminalLayout.getTerminalPosition(actualElement, elementTerminal) : null;
+ if (terminalTr == null)
+ return elementTransform;
+
+ // Return cached transform if terminal transform has not changed.
+ AffineTransform result = this.transform;
+ AffineTransform lastTerminalTr = this.lastTerminalTr;
+ if (lastTerminalTr != null) {
+ if (terminalTr.equals(lastTerminalTr))
+ return result;
+ lastTerminalTr.setTransform(terminalTr);
+ } else {
+ lastTerminalTr = this.lastTerminalTr = new AffineTransform(terminalTr);
+ result = this.transform = new AffineTransform();
+ }
+
+ result.setTransform(elementTransform);
+ result.concatenate(terminalTr);
+ return result;
+ }
+
+ }
}