]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/splitting/SplittedRouteGraph.java
Fixed route graph splitting and diagram mapping race condition problem
[simantics/platform.git] / bundles / org.simantics.diagram.connection / src / org / simantics / diagram / connection / splitting / SplittedRouteGraph.java
index fe7dc25250edb5a4ac154cd2cb4a2e8ffa1b099c..8f7f71a299f08f5b08a51cf1adc29db72b623c03 100644 (file)
-package org.simantics.diagram.connection.splitting;\r
-\r
-import gnu.trove.set.hash.THashSet;\r
-\r
-import java.awt.geom.Point2D;\r
-\r
-import org.simantics.diagram.connection.RouteGraph;\r
-import org.simantics.diagram.connection.RouteLine;\r
-import org.simantics.diagram.connection.RouteNode;\r
-import org.simantics.diagram.connection.RouteTerminal;\r
-\r
-public class SplittedRouteGraph {\r
-    public final RouteLine splitLine;\r
-    public final THashSet<RouteNode> interfaceNodes1;\r
-    public final THashSet<RouteLine> lines1; // does not contain splitLine\r
-    public final THashSet<RouteTerminal> terminals1;\r
-    public final THashSet<RouteNode> interfaceNodes2;\r
-    public final THashSet<RouteLine> lines2; // does not contain splitLine\r
-    public final THashSet<RouteTerminal> terminals2;\r
-    \r
-    public SplittedRouteGraph(RouteLine splitLine,\r
-            THashSet<RouteNode> interfaceNodes1, THashSet<RouteLine> lines1,\r
-            THashSet<RouteTerminal> terminals1,\r
-            THashSet<RouteNode> interfaceNodes2, THashSet<RouteLine> lines2,\r
-            THashSet<RouteTerminal> terminals2) {\r
-        super();\r
-        this.splitLine = splitLine;\r
-        this.interfaceNodes1 = interfaceNodes1;\r
-        this.lines1 = lines1;\r
-        this.terminals1 = terminals1;\r
-        this.interfaceNodes2 = interfaceNodes2;\r
-        this.lines2 = lines2;\r
-        this.terminals2 = terminals2;\r
-    }\r
-    \r
-    @Override\r
-    public String toString() {\r
-        StringBuilder b = new StringBuilder();\r
-        \r
-        b.append("splitLine = " + splitLine.getData() + "\n");\r
-        \r
-        b.append("interfaceNodes1 =");\r
-        for(RouteNode node : interfaceNodes1)\r
-            b.append(" " + node.getData() + "(" + node.getClass().getSimpleName() + ")");\r
-        b.append("\n");\r
-        \r
-        b.append("lines1 =");\r
-        for(RouteLine node : lines1)\r
-            b.append(" " + node.getData());\r
-        b.append("\n");\r
-        \r
-        b.append("terminals1 =");\r
-        for(RouteTerminal node : terminals1)\r
-            b.append(" " + node.getData());\r
-        b.append("\n");\r
-        \r
-        b.append("interfaceNodes2 =");\r
-        for(RouteNode node : interfaceNodes2)\r
-            b.append(" " + node.getData() + "(" + node.getClass().getSimpleName() + ")");\r
-        b.append("\n");\r
-        \r
-        b.append("lines2 =");\r
-        for(RouteLine node : lines2)\r
-            b.append(" " + node.getData());\r
-        b.append("\n");\r
-        \r
-        b.append("terminals2 =");\r
-        for(RouteTerminal node : terminals2)\r
-            b.append(" " + node.getData());\r
-        b.append("\n");\r
-        \r
-        return b.toString();\r
-    }\r
-\r
-    public static RouteLine findNearestLine(RouteGraph rg, Point2D splitCanvasPos) {\r
-        double hi = 1000, lo = 1;\r
-        RouteLine nearestLine = null;\r
-        final double LIMIT = 0.5;\r
-        while (true) {\r
-            double tolerance = (hi + lo) * 0.5;\r
-            RouteLine line = rg.pickLine(splitCanvasPos.getX(), splitCanvasPos.getY(), tolerance);\r
-            double delta = (hi - lo) * 0.5;\r
-            if (delta < LIMIT)\r
-                return nearestLine;\r
-            if (line == null) {\r
-                lo = tolerance;\r
-            } else {\r
-                nearestLine = line;\r
-                hi = tolerance;\r
-            }\r
-        }\r
-    }\r
-\r
-    /**\r
-     * @param point\r
-     * @param line\r
-     * @return the specified point instance snapped to the specified line\r
-     */\r
-    public static Point2D snapToLine(Point2D point, RouteLine line) {\r
-//        line.print(System.out);\r
-//        for (RoutePoint rp : line.getPoints())\r
-//            System.out.println("RP: " + rp.getX() + ", " + rp.getY());\r
-        // Get exact intersection point on the line\r
-        if (line.isHorizontal()) {\r
-            point.setLocation( point.getX(), line.getPosition() );\r
-        } else {\r
-            point.setLocation( line.getPosition(), point.getY() );\r
-        }\r
-        return point;\r
-    }\r
-\r
-    /**\r
-     * @param point\r
-     * @param line\r
-     * @return new {@link Point2D} instance with specified point snapped to specified line\r
-     */\r
-    public static Point2D snappedToLine(Point2D point, RouteLine line) {\r
-        Point2D result = (Point2D) point.clone();\r
-        return snapToLine(result, line);\r
-    }\r
-\r
-}\r
+package org.simantics.diagram.connection.splitting;
+
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+
+import org.simantics.diagram.connection.RouteGraph;
+import org.simantics.diagram.connection.RouteLine;
+import org.simantics.diagram.connection.RouteNode;
+import org.simantics.diagram.connection.RoutePoint;
+import org.simantics.diagram.connection.RouteTerminal;
+import org.simantics.diagram.connection.segments.Segment;
+
+import gnu.trove.set.hash.THashSet;
+
+public class SplittedRouteGraph {
+    public final RouteLine splitLine;
+    public final THashSet<RouteNode> interfaceNodes1;
+    public final THashSet<RouteLine> lines1; // does not contain splitLine
+    public final THashSet<RouteTerminal> terminals1;
+    public final THashSet<RouteNode> interfaceNodes2;
+    public final THashSet<RouteLine> lines2; // does not contain splitLine
+    public final THashSet<RouteTerminal> terminals2;
+    
+    public SplittedRouteGraph(RouteLine splitLine,
+            THashSet<RouteNode> interfaceNodes1, THashSet<RouteLine> lines1,
+            THashSet<RouteTerminal> terminals1,
+            THashSet<RouteNode> interfaceNodes2, THashSet<RouteLine> lines2,
+            THashSet<RouteTerminal> terminals2) {
+        super();
+        this.splitLine = splitLine;
+        this.interfaceNodes1 = interfaceNodes1;
+        this.lines1 = lines1;
+        this.terminals1 = terminals1;
+        this.interfaceNodes2 = interfaceNodes2;
+        this.lines2 = lines2;
+        this.terminals2 = terminals2;
+    }
+    
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder();
+        
+        b.append("splitLine = " + splitLine.getData() + "\n");
+        
+        b.append("interfaceNodes1 =");
+        for(RouteNode node : interfaceNodes1)
+            b.append(" " + node.getData() + "(" + node.getClass().getSimpleName() + ")");
+        b.append("\n");
+        
+        b.append("lines1 =");
+        for(RouteLine node : lines1)
+            b.append(" " + node.getData());
+        b.append("\n");
+        
+        b.append("terminals1 =");
+        for(RouteTerminal node : terminals1)
+            b.append(" " + node.getData());
+        b.append("\n");
+        
+        b.append("interfaceNodes2 =");
+        for(RouteNode node : interfaceNodes2)
+            b.append(" " + node.getData() + "(" + node.getClass().getSimpleName() + ")");
+        b.append("\n");
+        
+        b.append("lines2 =");
+        for(RouteLine node : lines2)
+            b.append(" " + node.getData());
+        b.append("\n");
+        
+        b.append("terminals2 =");
+        for(RouteTerminal node : terminals2)
+            b.append(" " + node.getData());
+        b.append("\n");
+        
+        return b.toString();
+    }
+
+    public static RouteLine findNearestLine(RouteGraph rg, Point2D splitCanvasPos) {
+        double hi = 1000, lo = 1;
+        RouteLine nearestLine = null;
+        final double LIMIT = 0.5;
+        while (true) {
+            double tolerance = (hi + lo) * 0.5;
+            RouteLine line = rg.pickLine(splitCanvasPos.getX(), splitCanvasPos.getY(), tolerance);
+            double delta = (hi - lo) * 0.5;
+            if (delta < LIMIT)
+                return nearestLine;
+            if (line == null) {
+                lo = tolerance;
+            } else {
+                nearestLine = line;
+                hi = tolerance;
+            }
+        }
+    }
+
+    public static final class PickResult {
+        /**
+         * The connection route line nearest to {@link #pickPoint}.
+         */
+        public final RouteLine nearestLine;
+        /**
+         * Original pick point in canvas coordinates.
+         */
+        public final Point2D pickPoint;
+        /**
+         * Intersection point in canvas coordinates of {@link #nearestLine} and
+         * perpendicular line from {@link #pickPoint} to {@link #nearestLine}.
+         */
+        public final Point2D intersectionPoint;
+
+        public PickResult(RouteLine nearestLine, Point2D pickPoint, Point2D intersectionPoint) {
+            this.nearestLine = nearestLine;
+            this.pickPoint = pickPoint;
+            this.intersectionPoint = intersectionPoint;
+        }
+    }
+
+    public static PickResult pickNearestLine(RouteGraph rg, double x, double y) {
+        Segment nearestSegment = null;
+        RouteLine nearestLine = null;
+
+        ArrayList<Segment> segments = new ArrayList<>();
+        double minDistanceSq = Double.MAX_VALUE;
+        for (RouteLine line : rg.getAllLines()) {
+            segments.clear();
+            line.collectSegments(segments);
+            for (Segment segment : segments) {
+                RoutePoint p1 = segment.p1;
+                RoutePoint p2 = segment.p2;
+                double distanceSq = Line2D.ptSegDistSq(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y);
+                if (distanceSq < minDistanceSq) {
+                    minDistanceSq = distanceSq;
+                    nearestSegment = segment;
+                    nearestLine = line;
+                }
+            }
+        }
+
+        if (nearestSegment == null)
+            return null;
+
+        RoutePoint p1 = nearestSegment.p1;
+        RoutePoint p2 = nearestSegment.p2;
+        Point2D p = pointToLineIntersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y);
+        return new PickResult(nearestLine, new Point2D.Double(x, y), p);
+    }
+
+    private static Point2D pointToLineIntersection(double x1, double y1, double x2, double y2, double px, double py) {
+        double d = Math.pow(x2 - x1, 2.0) + Math.pow(y2 - y1, 2.0);
+        if (d == 0) {
+            return new Point2D.Double(x1, y1);
+        } else {
+            double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / d;
+            if (u > 1.0) {
+                return new Point2D.Double(x2, y2);
+            } else if (u <= 0.0) {
+                return new Point2D.Double(x1, y1);
+            } else {
+                return new Point2D.Double(x2 * u + x1 * (1.0-u), (y2 * u + y1 * (1.0- u)));
+            }
+        }
+    }
+
+       /**
+     * @param point
+     * @param line
+     * @return the specified point instance snapped to the specified line
+     */
+    public static Point2D snapToLine(Point2D point, RouteLine 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
+        if (line.isHorizontal()) {
+            point.setLocation( point.getX(), line.getPosition() );
+        } else {
+            point.setLocation( line.getPosition(), point.getY() );
+        }
+        return point;
+    }
+
+    /**
+     * @param point
+     * @param line
+     * @return new {@link Point2D} instance with specified point snapped to specified line
+     */
+    public static Point2D snappedToLine(Point2D point, RouteLine line) {
+        Point2D result = (Point2D) point.clone();
+        return snapToLine(result, line);
+    }
+
+}