From: Jussi Koskela Date: Thu, 5 Sep 2019 10:06:16 +0000 (+0300) Subject: Improved the positioning of issue decoration for connection elements X-Git-Tag: v1.43.0~136^2~79 X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=commitdiff_plain;h=46cdf23c4ea4733a3acbfd5c9895ec9e65965c7d Improved the positioning of issue decoration for connection elements Shows the issue decorator at a point that belongs to the connection and is closest to the center of its bounding box. Also fixed the updating of position. gitlab #372 Change-Id: Icf948997a9a2ce11a6420d83286144328af4e877 --- diff --git a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteGraph.java b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteGraph.java index 62d3de68f..249399d0a 100644 --- a/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteGraph.java +++ b/bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteGraph.java @@ -11,13 +11,9 @@ *******************************************************************************/ package org.simantics.diagram.connection; -import gnu.trove.list.array.TDoubleArrayList; -import gnu.trove.map.hash.THashMap; -import gnu.trove.map.hash.TObjectIntHashMap; -import gnu.trove.set.hash.THashSet; - import java.awt.geom.Line2D; import java.awt.geom.Path2D; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.PrintStream; import java.io.Serializable; @@ -34,6 +30,11 @@ import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle; import org.simantics.diagram.connection.segments.Segment; import org.simantics.diagram.connection.splitting.SplittedRouteGraph; +import gnu.trove.list.array.TDoubleArrayList; +import gnu.trove.map.hash.THashMap; +import gnu.trove.map.hash.TObjectIntHashMap; +import gnu.trove.set.hash.THashSet; + public class RouteGraph implements Serializable { private static final long serialVersionUID = 2004022454972623908L; @@ -1474,6 +1475,46 @@ public class RouteGraph implements Serializable { routeLine.collectSegments(segments); return segments; } + + public Segment findNearestSegment(double x, double y) { + Segment nearest = null; + double minDistanceSq = Double.MAX_VALUE; + + for (Segment segment : getSegments()) { + RoutePoint p1 = segment.p1; + RoutePoint p2 = segment.p2; + + double distanceSq = Line2D.ptSegDistSq(p1.x, p1.y, p2.x, p2.y, x, y); + if (distanceSq < minDistanceSq) { + minDistanceSq = distanceSq; + nearest = segment; + } + } + return nearest; + } + + public Point2D findNearestPoint(double x, double y) { + Segment nearest = findNearestSegment(x, y); + if (nearest == null) return null; + + RoutePoint p1 = nearest.p1; + RoutePoint p2 = nearest.p2; + + double d = Math.pow(p2.x - p1.x, 2.0) + Math.pow(p2.y - p1.y, 2.0); + + if (d == 0) { + return new Point2D.Double(p1.x, p1.y); + } else { + double u = ((x - p1.x) * (p2.x - p1.x) + (y - p1.y) * (p2.y - p1.y)) / d; + if (u > 1.0) { + return new Point2D.Double(p2.x, p2.y); + } else if (u <= 0.0) { + return new Point2D.Double(p1.x, p1.y); + } else { + return new Point2D.Double(p2.x * u + p1.x * (1.0-u), (p2.y * u + p1.y * (1.0- u))); + } + } + } public Path2D getPath2D() { Path2D result = new Path2D.Double(); diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/IssueDecorationStyle.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/IssueDecorationStyle.java index d473ff46d..68210f9e2 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/IssueDecorationStyle.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/IssueDecorationStyle.java @@ -12,9 +12,12 @@ package org.simantics.modeling.ui.diagram.style; import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -25,9 +28,13 @@ import org.simantics.db.common.procedure.adapter.TransientCacheListener; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; +import org.simantics.diagram.connection.RouteGraph; import org.simantics.diagram.elements.DecorationSVGNode; import org.simantics.diagram.elements.SVGNode; +import org.simantics.diagram.handler.Paster; +import org.simantics.diagram.handler.Paster.RouteLine; import org.simantics.diagram.profile.StyleBase; +import org.simantics.diagram.stubs.DiagramResource; import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; import org.simantics.issues.Severity; import org.simantics.issues.common.IssueResourcesContexts; @@ -36,10 +43,13 @@ import org.simantics.modeling.ModelingResources; import org.simantics.modeling.ui.Activator; import org.simantics.modeling.ui.diagram.style.IssueDecorationStyle.IssueResult; import org.simantics.scenegraph.INode; +import org.simantics.scenegraph.g2d.nodes.ConnectionNode; import org.simantics.scenegraph.g2d.nodes.Decoration; +import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode; import org.simantics.scenegraph.profile.EvaluationContext; import org.simantics.scenegraph.profile.common.ProfileVariables; import org.simantics.scenegraph.utils.NodeUtil; +import org.simantics.structural.stubs.StructuralResource2; import org.simantics.utils.datastructures.map.Tuple; @@ -74,7 +84,6 @@ public class IssueDecorationStyle extends StyleBase { return null; List contexts = getContexts(graph, element); - AffineTransform transform = DiagramGraphUtil.getAffineTransform(graph, element); Map> issuesBySeverity = graph.syncRequest(new ListModelIssuesBySeverity(model, true, true, Severity.NOTE), TransientCacheListener.>>instance()); @@ -82,14 +91,43 @@ public class IssueDecorationStyle extends StyleBase { List issues = issuesBySeverity.get(severity); if (issues != null) { Set issueContexts = graph.syncRequest(new IssueResourcesContexts(issues)); - if (!Collections.disjoint(issueContexts, contexts)) - return new IssueResult(severity, transform); + if (!Collections.disjoint(issueContexts, contexts)) { + return new IssueResult(severity, getIdentifier(graph, runtimeDiagram, element)); + } } } return null; } + private static Object getIdentifier(ReadGraph graph, Resource runtimeDiagram, Resource element) throws DatabaseException { + DiagramResource DIA = DiagramResource.getInstance(graph); + StructuralResource2 STR = StructuralResource2.getInstance(graph); + if (graph.isInstanceOf(element, DIA.RouteGraphConnection)) { + Collection connectors = graph.getObjects(element, DIA.HasConnector); + Collection routeNodes = graph.getObjects(element, DIA.HasInteriorRouteNode); + + // This is needed to make this query result change every time the underlying element changes visually. + Set identifier = new HashSet(connectors.size() + routeNodes.size()); + + for (Resource connector : connectors) { + for (Resource connectedTo : graph.getObjects(connector, STR.Connects)) { + if (!connectedTo.equals(element)) { + AffineTransform at = DiagramGraphUtil.getDynamicAffineTransform(graph, runtimeDiagram, connectedTo, DIA.HasDynamicTransform, false); + identifier.add(at); + } + } + } + for (Resource routeLine : routeNodes) { + RouteLine rl = Paster.readRouteLine(graph, routeLine); + identifier.add(rl); + } + return identifier; + } else { + return DiagramGraphUtil.getAffineTransform(graph, element); + } + } + @Override public void applyStyleForNode(EvaluationContext observer, INode node, IssueResult result) { if (result == null) { @@ -117,6 +155,18 @@ public class IssueDecorationStyle extends StyleBase { protected AffineTransform getDecorationPosition(INode node) { Rectangle2D bounds = NodeUtil.getLocalBounds(node, Decoration.class); + if (node instanceof ConnectionNode) { + for (INode child : ((ConnectionNode)node).getSortedNodes()) { + if (child instanceof RouteGraphNode) { + RouteGraphNode rgn = (RouteGraphNode) child; + RouteGraph rg = rgn.getRouteGraph(); + Point2D nearest = rg.findNearestPoint(bounds.getCenterX(), bounds.getCenterY()); + if (nearest != null) { + return AffineTransform.getTranslateInstance(nearest.getX(), nearest.getY()); + } + } + } + } double tx = bounds.getX(); double ty = bounds.getY(); return AffineTransform.getTranslateInstance(tx, ty); @@ -148,8 +198,8 @@ public class IssueDecorationStyle extends StyleBase { * element moves. */ public static class IssueResult extends Tuple { - public IssueResult(Severity severity, AffineTransform transform) { - super(severity, transform); + public IssueResult(Severity severity, Object identifier) { + super(severity, identifier); } public Severity getSeverity() { return (Severity) getField(0);