]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteGraph.java
Fixed two problems in connecting picking
[simantics/platform.git] / bundles / org.simantics.diagram.connection / src / org / simantics / diagram / connection / RouteGraph.java
index 1905cfe2b2c172969e2883a5659a0d807a106761..249399d0a8b5b7b6c875c68d8464fda198961160 100644 (file)
  *******************************************************************************/
 package org.simantics.diagram.connection;
 
  *******************************************************************************/
 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.Line2D;
 import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.io.PrintStream;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.awt.geom.Rectangle2D;
 import java.io.PrintStream;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
 
 import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;
 import org.simantics.diagram.connection.rendering.arrows.PlainLineEndStyle;
 import org.simantics.diagram.connection.segments.Segment;
 import org.simantics.diagram.connection.splitting.SplittedRouteGraph;
 
 
 import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;
 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;
 public class RouteGraph implements Serializable {
 
     private static final long serialVersionUID = 2004022454972623908L;
@@ -45,6 +49,13 @@ public class RouteGraph implements Serializable {
     boolean isSimpleConnection;
     boolean needsUpdate = false;
 
     boolean isSimpleConnection;
     boolean needsUpdate = false;
 
+    public void updateTerminals() {
+        boolean changed = false;
+        for(RouteTerminal terminal : terminals)
+            changed |= terminal.updateDynamicPosition();
+        if(changed) update();
+    }
+
     /**
      * Adds a route line to the graph.
      * @param isHorizontal true, if the line is horizontal
     /**
      * Adds a route line to the graph.
      * @param isHorizontal true, if the line is horizontal
@@ -78,21 +89,31 @@ public class RouteGraph implements Serializable {
      * </pre>
      * @param style Tells what kind of line end is drawn 
      *        to the connection.
      * </pre>
      * @param style Tells what kind of line end is drawn 
      *        to the connection.
+     * @param position a provider for a dynamic position for the terminal or
+     *        <code>null</code> if terminal position is not dynamic
      * @return The new terminal.
      */
      * @return The new terminal.
      */
+    public RouteTerminal addTerminal(double x, double y, 
+            double minX, double minY,
+            double maxX, double maxY, 
+            int allowedDirections,
+            ILineEndStyle style, RouteTerminalPosition position) {
+        return addTerminal(x, y, minX, minY, maxX, maxY, allowedDirections, style, null, position);
+    }
+
     public RouteTerminal addTerminal(double x, double y, 
             double minX, double minY,
             double maxX, double maxY, 
             int allowedDirections,
             ILineEndStyle style) {
     public RouteTerminal addTerminal(double x, double y, 
             double minX, double minY,
             double maxX, double maxY, 
             int allowedDirections,
             ILineEndStyle style) {
-       return addTerminal(x, y, minX, minY, maxX, maxY, allowedDirections, style, null);
-       
+        return addTerminal(x, y, minX, minY, maxX, maxY, allowedDirections, style, null, null);
     }
     }
+
     public RouteTerminal addTerminal(double x, double y, 
             double minX, double minY,
             double maxX, double maxY, 
             int allowedDirections,
     public RouteTerminal addTerminal(double x, double y, 
             double minX, double minY,
             double maxX, double maxY, 
             int allowedDirections,
-            ILineEndStyle style, ILineEndStyle dynamicStyle) {
+            ILineEndStyle style, ILineEndStyle dynamicStyle, RouteTerminalPosition position) {
         if(CHECK_PARAMERS) {
             if(allowedDirections > 0x1f)
                 throw new IllegalArgumentException("Illegal allowedDirection flags.");
         if(CHECK_PARAMERS) {
             if(allowedDirections > 0x1f)
                 throw new IllegalArgumentException("Illegal allowedDirection flags.");
@@ -102,7 +123,7 @@ public class RouteGraph implements Serializable {
         if(style == null)
             style = PlainLineEndStyle.INSTANCE;
         RouteTerminal terminal = new RouteTerminal(x, y, minX, minY,
         if(style == null)
             style = PlainLineEndStyle.INSTANCE;
         RouteTerminal terminal = new RouteTerminal(x, y, minX, minY,
-                maxX, maxY, allowedDirections, false, style); 
+                maxX, maxY, allowedDirections, false, style, position); 
         terminal.setDynamicStyle(dynamicStyle);
         terminals.add(terminal);
         return terminal;
         terminal.setDynamicStyle(dynamicStyle);
         terminals.add(terminal);
         return terminal;
@@ -112,7 +133,7 @@ public class RouteGraph implements Serializable {
             double minX, double minY,
             double maxX, double maxY,
             ILineEndStyle style) {
             double minX, double minY,
             double maxX, double maxY,
             ILineEndStyle style) {
-       return addBigTerminal(minX, minY, maxX, maxY, style, null);
+        return addBigTerminal(minX, minY, maxX, maxY, style, null);
     }
     public RouteTerminal addBigTerminal( 
             double minX, double minY,
     }
     public RouteTerminal addBigTerminal( 
             double minX, double minY,
@@ -124,7 +145,7 @@ public class RouteGraph implements Serializable {
                 0.5*(minX+maxX), 0.5*(minY+maxY),
                 minX, minY,
                 maxX, maxY, 
                 0.5*(minX+maxX), 0.5*(minY+maxY),
                 minX, minY,
                 maxX, maxY, 
-                0xf, true, style); 
+                0xf, true, style, null); 
         terminal.setDynamicStyle(dynamicStyle);
         
         terminals.add(terminal);
         terminal.setDynamicStyle(dynamicStyle);
         
         terminals.add(terminal);
@@ -142,7 +163,7 @@ public class RouteGraph implements Serializable {
             ILineEndStyle style) {
         return addTerminal(x, y, 
                 bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY(), 
             ILineEndStyle style) {
         return addTerminal(x, y, 
                 bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY(), 
-                allowedDirections, style);
+                allowedDirections, style, null);
     }
 
     /**
     }
 
     /**
@@ -152,7 +173,7 @@ public class RouteGraph implements Serializable {
         RouteTerminal newTerminal = addTerminal(terminal.x, terminal.y,
                 terminal.getMinX(), terminal.getMinY(),
                 terminal.getMaxX(), terminal.getMaxY(),
         RouteTerminal newTerminal = addTerminal(terminal.x, terminal.y,
                 terminal.getMinX(), terminal.getMinY(),
                 terminal.getMaxX(), terminal.getMaxY(),
-                terminal.getAllowedDirections(), terminal.getStyle(), terminal.getDynamicStyle());
+                terminal.getAllowedDirections(), terminal.getStyle(), terminal.getDynamicStyle(), terminal.getDynamicPosition());
         newTerminal.setData(terminal.getData());
         return terminal;
     }
         newTerminal.setData(terminal.getData());
         return terminal;
     }
@@ -166,7 +187,7 @@ public class RouteGraph implements Serializable {
             double maxX, double maxY, 
             int allowedDirections) {
         return addTerminal(x, y, minX, minY, maxX, maxY, allowedDirections, 
             double maxX, double maxY, 
             int allowedDirections) {
         return addTerminal(x, y, minX, minY, maxX, maxY, allowedDirections, 
-                PlainLineEndStyle.INSTANCE);
+                PlainLineEndStyle.INSTANCE, null);
     }
     
     /**
     }
     
     /**
@@ -492,9 +513,9 @@ public class RouteGraph implements Serializable {
         for(RouteTerminal terminal : terminals)
             if((terminal.getAllowedDirections()&0x10) != 0) {     
                 try {
         for(RouteTerminal terminal : terminals)
             if((terminal.getAllowedDirections()&0x10) != 0) {     
                 try {
-                       RouteLine line = terminal.getLine();
-                       if(line == null)
-                               continue;
+                    RouteLine line = terminal.getLine();
+                    if(line == null)
+                        continue;
                     RoutePoint b = line.getBegin();
                     double distSq = Line2D.ptSegDistSq(terminal.x, terminal.y, b.x, b.y, x, y); 
                     if(distSq <= toleranceSq) {
                     RoutePoint b = line.getBegin();
                     double distSq = Line2D.ptSegDistSq(terminal.x, terminal.y, b.x, b.y, x, y); 
                     if(distSq <= toleranceSq) {
@@ -625,7 +646,7 @@ public class RouteGraph implements Serializable {
         if((mask & PICK_POINTS) != 0) {
             Object point = pickPoint(x, y, tolerance, mask);
             if(point != null)
         if((mask & PICK_POINTS) != 0) {
             Object point = pickPoint(x, y, tolerance, mask);
             if(point != null)
-               return point;
+                return point;
         }
         /*if((mask & PICK_DIRECT_LINES) != 0) {
             RouteTerminal terminal = pickDirectLine(x, y, tolerance);
         }
         /*if((mask & PICK_DIRECT_LINES) != 0) {
             RouteTerminal terminal = pickDirectLine(x, y, tolerance);
@@ -900,12 +921,12 @@ public class RouteGraph implements Serializable {
              * 
              * Update 14.2.2014: With Sulca, there is a constant issue when line.terminal == null but point is RouteTerminal. 
              */
              * 
              * Update 14.2.2014: With Sulca, there is a constant issue when line.terminal == null but point is RouteTerminal. 
              */
-               if (point instanceof RouteLink) {
-                   RouteLine l = ((RouteLink)point).getOther(line);
-                   l.points.remove(point);
-                   if(!l.isTransient())
-                       nLines.add(l);
-               }
+            if (point instanceof RouteLink) {
+                RouteLine l = ((RouteLink)point).getOther(line);
+                l.points.remove(point);
+                if(!l.isTransient())
+                    nLines.add(l);
+            }
         }
         
         if(nLines.isEmpty())
         }
         
         if(nLines.isEmpty())
@@ -919,10 +940,10 @@ public class RouteGraph implements Serializable {
                  * 
                  * Update 16.10.2014: With Sulca, there is a constant issue when line.terminal == null but point is RouteTerminal. 
                  */
                  * 
                  * Update 16.10.2014: With Sulca, there is a constant issue when line.terminal == null but point is RouteTerminal. 
                  */
-               if (point instanceof RouteLink) {
-                       RouteLink link = (RouteLink)point;
-                       link.replace(l, merged);
-               }
+                if (point instanceof RouteLink) {
+                    RouteLink link = (RouteLink)point;
+                    link.replace(l, merged);
+                }
             }
         }
         THashSet<RouteLine> removedLines = new THashSet<RouteLine>();
             }
         }
         THashSet<RouteLine> removedLines = new THashSet<RouteLine>();
@@ -1328,6 +1349,18 @@ public class RouteGraph implements Serializable {
         path.moveTo(x, y);
     }
     
         path.moveTo(x, y);
     }
     
+    private static final Comparator<RoutePoint> RG_COMP = (o1, o2) -> {
+        if (o1.getX() < o2.getX())
+            return -1;
+        else if (o1.getX() > o2.getX())
+            return 1;
+        if (o1.getY() < o2.getY())
+            return -1;
+        else if (o1.getY() > o2.getY())
+            return 1;
+        return 0;
+    };
+    
     private static void addPathEnd(Path2D path, RoutePoint cur, RouteLine line) {
         double x = cur.x, y = cur.y;
         if(cur instanceof RouteTerminal) {
     private static void addPathEnd(Path2D path, RoutePoint cur, RouteLine line) {
         double x = cur.x, y = cur.y;
         if(cur instanceof RouteTerminal) {
@@ -1365,8 +1398,9 @@ public class RouteGraph implements Serializable {
         }
 
         // Analyze graph
         }
 
         // Analyze graph
-        THashMap<RoutePoint, RouteLine> begins = 
-                new THashMap<RoutePoint, RouteLine>();
+        Map<RoutePoint, RouteLine> begins = 
+//                new THashMap<RoutePoint, RouteLine>(); //The ordering of the coordinates in the path should be deterministic between scenegraphs
+                new TreeMap<RoutePoint, RouteLine>(RG_COMP);
         for(RouteLine line : lines) {
             add(begins, line);
         }
         for(RouteLine line : lines) {
             add(begins, line);
         }
@@ -1386,7 +1420,7 @@ public class RouteGraph implements Serializable {
         }
     }
     
         }
     }
     
-    private void drawContinuousPath(Path2D path, THashMap<RoutePoint, RouteLine> begins,
+    private void drawContinuousPath(Path2D path, Map<RoutePoint, RouteLine> begins,
             RoutePoint cur, RouteLine curLine) {
         if(curLine == null)
             return;
             RoutePoint cur, RouteLine curLine) {
         if(curLine == null)
             return;
@@ -1415,7 +1449,7 @@ public class RouteGraph implements Serializable {
         }
     }
 
         }
     }
 
-    private static void add(THashMap<RoutePoint, RouteLine> begins,
+    private static void add(Map<RoutePoint, RouteLine> begins,
             RouteLine line) { 
         if(line.points.size() > 1) {
             {
             RouteLine line) { 
         if(line.points.size() > 1) {
             {
@@ -1441,6 +1475,46 @@ public class RouteGraph implements Serializable {
             routeLine.collectSegments(segments);
         return segments;
     }
             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();
 
     public Path2D getPath2D() {
         Path2D result = new Path2D.Double();
@@ -1541,8 +1615,8 @@ public class RouteGraph implements Serializable {
     }
     
     public void reclaimTransientMemory() {
     }
     
     public void reclaimTransientMemory() {
-       removeTransientRouteLines();
-       needsUpdate = true;
+        removeTransientRouteLines();
+        needsUpdate = true;
     }
 
 }
     }
 
 }