]> gerrit.simantics Code Review - simantics/platform.git/blob - 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
1 package org.simantics.diagram.connection.splitting;
2
3 import java.awt.geom.Line2D;
4 import java.awt.geom.Point2D;
5 import java.util.ArrayList;
6
7 import org.simantics.diagram.connection.RouteGraph;
8 import org.simantics.diagram.connection.RouteLine;
9 import org.simantics.diagram.connection.RouteNode;
10 import org.simantics.diagram.connection.RoutePoint;
11 import org.simantics.diagram.connection.RouteTerminal;
12 import org.simantics.diagram.connection.segments.Segment;
13
14 import gnu.trove.set.hash.THashSet;
15
16 public class SplittedRouteGraph {
17     public final RouteLine splitLine;
18     public final THashSet<RouteNode> interfaceNodes1;
19     public final THashSet<RouteLine> lines1; // does not contain splitLine
20     public final THashSet<RouteTerminal> terminals1;
21     public final THashSet<RouteNode> interfaceNodes2;
22     public final THashSet<RouteLine> lines2; // does not contain splitLine
23     public final THashSet<RouteTerminal> terminals2;
24     
25     public SplittedRouteGraph(RouteLine splitLine,
26             THashSet<RouteNode> interfaceNodes1, THashSet<RouteLine> lines1,
27             THashSet<RouteTerminal> terminals1,
28             THashSet<RouteNode> interfaceNodes2, THashSet<RouteLine> lines2,
29             THashSet<RouteTerminal> terminals2) {
30         super();
31         this.splitLine = splitLine;
32         this.interfaceNodes1 = interfaceNodes1;
33         this.lines1 = lines1;
34         this.terminals1 = terminals1;
35         this.interfaceNodes2 = interfaceNodes2;
36         this.lines2 = lines2;
37         this.terminals2 = terminals2;
38     }
39     
40     @Override
41     public String toString() {
42         StringBuilder b = new StringBuilder();
43         
44         b.append("splitLine = " + splitLine.getData() + "\n");
45         
46         b.append("interfaceNodes1 =");
47         for(RouteNode node : interfaceNodes1)
48             b.append(" " + node.getData() + "(" + node.getClass().getSimpleName() + ")");
49         b.append("\n");
50         
51         b.append("lines1 =");
52         for(RouteLine node : lines1)
53             b.append(" " + node.getData());
54         b.append("\n");
55         
56         b.append("terminals1 =");
57         for(RouteTerminal node : terminals1)
58             b.append(" " + node.getData());
59         b.append("\n");
60         
61         b.append("interfaceNodes2 =");
62         for(RouteNode node : interfaceNodes2)
63             b.append(" " + node.getData() + "(" + node.getClass().getSimpleName() + ")");
64         b.append("\n");
65         
66         b.append("lines2 =");
67         for(RouteLine node : lines2)
68             b.append(" " + node.getData());
69         b.append("\n");
70         
71         b.append("terminals2 =");
72         for(RouteTerminal node : terminals2)
73             b.append(" " + node.getData());
74         b.append("\n");
75         
76         return b.toString();
77     }
78
79     public static RouteLine findNearestLine(RouteGraph rg, Point2D splitCanvasPos) {
80         double hi = 1000, lo = 1;
81         RouteLine nearestLine = null;
82         final double LIMIT = 0.5;
83         while (true) {
84             double tolerance = (hi + lo) * 0.5;
85             RouteLine line = rg.pickLine(splitCanvasPos.getX(), splitCanvasPos.getY(), tolerance);
86             double delta = (hi - lo) * 0.5;
87             if (delta < LIMIT)
88                 return nearestLine;
89             if (line == null) {
90                 lo = tolerance;
91             } else {
92                 nearestLine = line;
93                 hi = tolerance;
94             }
95         }
96     }
97
98     public static final class PickResult {
99         /**
100          * The connection route line nearest to {@link #pickPoint}.
101          */
102         public final RouteLine nearestLine;
103         /**
104          * Original pick point in canvas coordinates.
105          */
106         public final Point2D pickPoint;
107         /**
108          * Intersection point in canvas coordinates of {@link #nearestLine} and
109          * perpendicular line from {@link #pickPoint} to {@link #nearestLine}.
110          */
111         public final Point2D intersectionPoint;
112
113         public PickResult(RouteLine nearestLine, Point2D pickPoint, Point2D intersectionPoint) {
114             this.nearestLine = nearestLine;
115             this.pickPoint = pickPoint;
116             this.intersectionPoint = intersectionPoint;
117         }
118     }
119
120     public static PickResult pickNearestLine(RouteGraph rg, double x, double y) {
121         Segment nearestSegment = null;
122         RouteLine nearestLine = null;
123
124         ArrayList<Segment> segments = new ArrayList<>();
125         double minDistanceSq = Double.MAX_VALUE;
126         for (RouteLine line : rg.getAllLines()) {
127             segments.clear();
128             line.collectSegments(segments);
129             for (Segment segment : segments) {
130                 RoutePoint p1 = segment.p1;
131                 RoutePoint p2 = segment.p2;
132                 double distanceSq = Line2D.ptSegDistSq(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y);
133                 if (distanceSq < minDistanceSq) {
134                     minDistanceSq = distanceSq;
135                     nearestSegment = segment;
136                     nearestLine = line;
137                 }
138             }
139         }
140
141         if (nearestSegment == null)
142             return null;
143
144         RoutePoint p1 = nearestSegment.p1;
145         RoutePoint p2 = nearestSegment.p2;
146         Point2D p = pointToLineIntersection(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y);
147         return new PickResult(nearestLine, new Point2D.Double(x, y), p);
148     }
149
150     private static Point2D pointToLineIntersection(double x1, double y1, double x2, double y2, double px, double py) {
151         double d = Math.pow(x2 - x1, 2.0) + Math.pow(y2 - y1, 2.0);
152         if (d == 0) {
153             return new Point2D.Double(x1, y1);
154         } else {
155             double u = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / d;
156             if (u > 1.0) {
157                 return new Point2D.Double(x2, y2);
158             } else if (u <= 0.0) {
159                 return new Point2D.Double(x1, y1);
160             } else {
161                 return new Point2D.Double(x2 * u + x1 * (1.0-u), (y2 * u + y1 * (1.0- u)));
162             }
163         }
164     }
165
166         /**
167      * @param point
168      * @param line
169      * @return the specified point instance snapped to the specified line
170      */
171     public static Point2D snapToLine(Point2D point, RouteLine line) {
172 //        line.print(System.out);
173 //        for (RoutePoint rp : line.getPoints())
174 //            System.out.println("RP: " + rp.getX() + ", " + rp.getY());
175         // Get exact intersection point on the line
176         if (line.isHorizontal()) {
177             point.setLocation( point.getX(), line.getPosition() );
178         } else {
179             point.setLocation( line.getPosition(), point.getY() );
180         }
181         return point;
182     }
183
184     /**
185      * @param point
186      * @param line
187      * @return new {@link Point2D} instance with specified point snapped to specified line
188      */
189     public static Point2D snappedToLine(Point2D point, RouteLine line) {
190         Point2D result = (Point2D) point.clone();
191         return snapToLine(result, line);
192     }
193
194 }