]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/participant/RouteGraphConnectTool.java
Fix NPE from flagTransform
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / participant / RouteGraphConnectTool.java
index 4cccea938270b6b8ac0b50baf6ac91d2b190528a..7454a2f3986cfff025242c5d9557876eaa53f809 100644 (file)
@@ -16,6 +16,7 @@ import java.awt.AlphaComposite;
 import java.awt.Composite;
 import java.awt.Shape;
 import java.awt.geom.AffineTransform;
+import java.awt.geom.Line2D;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.util.ArrayDeque;
@@ -24,6 +25,7 @@ import java.util.Collection;
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 
 import org.simantics.Simantics;
@@ -32,7 +34,6 @@ import org.simantics.db.WriteGraph;
 import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.diagram.connection.RouteGraph;
-import org.simantics.diagram.connection.RouteGraphConnectionClass;
 import org.simantics.diagram.connection.RouteLine;
 import org.simantics.diagram.connection.RoutePoint;
 import org.simantics.diagram.connection.RouteTerminal;
@@ -41,6 +42,7 @@ import org.simantics.diagram.connection.rendering.IRouteGraphRenderer;
 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.connection.segments.Segment;
 import org.simantics.diagram.synchronization.ISynchronizationContext;
 import org.simantics.diagram.synchronization.SynchronizationHints;
 import org.simantics.diagram.synchronization.graph.RouteGraphConnection;
@@ -76,6 +78,7 @@ import org.simantics.g2d.element.handler.impl.BranchPointTerminal;
 import org.simantics.g2d.element.impl.Element;
 import org.simantics.g2d.elementclass.FlagClass;
 import org.simantics.g2d.elementclass.FlagHandler;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
 import org.simantics.g2d.participant.TransformUtil;
 import org.simantics.g2d.utils.geom.DirectionSet;
 import org.simantics.scenegraph.g2d.G2DParentNode;
@@ -85,6 +88,7 @@ import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
 import org.simantics.scenegraph.g2d.events.MouseEvent;
 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonEvent;
 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
 import org.simantics.scenegraph.g2d.events.command.Commands;
@@ -477,6 +481,10 @@ public class RouteGraphConnectTool extends AbstractMode {
         if (me instanceof MouseButtonPressedEvent)
             return processMouseButtonPress((MouseButtonPressedEvent) me);
 
+        // #7653: Support creating connections between terminals without lifting mouse button in between.
+        if (me instanceof MouseButtonReleasedEvent)
+            return processMouseButtonRelease((MouseButtonReleasedEvent) me);
+
         return false;
     }
 
@@ -497,29 +505,31 @@ public class RouteGraphConnectTool extends AbstractMode {
             endFlagNode.setTransform(AffineTransform.getTranslateInstance(mouseCanvasPos.getX(), mouseCanvasPos.getY()));
         }
 
-        TerminalInfo ti = pi.pickTerminal(me.controlPosition);
-        if (ti != null) {
-            Object canConnect = canConnect(ti.e, ti.t);
-            if (canConnect != null) {
-                connectionJudgment = (ConnectionJudgement) canConnect;
+        List<TerminalInfo> tis = pi.pickTerminals(me.controlPosition);
+        tis = TerminalUtil.findNearestOverlappingTerminals(tis);
+        if (!tis.isEmpty()) {
+            for (TerminalInfo ti : tis) {
+                Object canConnect = canConnect(ti.e, ti.t);
 
-                if (!isEndingInFlag() || !TerminalUtil.isSameTerminal(ti, endTerminal)) {
-                    controlPoints.getLast()
-                    .setPosition(ti.posDia)
-                    .setAttachedToTerminal(ti);
+                if (canConnect != null) {
+                    connectionJudgment = (ConnectionJudgement) canConnect;
 
-                    endTerminal = ti;
+                    if (!isEndingInFlag() || !TerminalUtil.isSameTerminal(ti, endTerminal)) {
+                        controlPoints.getLast().setPosition(ti.posDia).setAttachedToTerminal(ti);
 
-                    connect(ti);
-                    
-                }
+                        endTerminal = ti;
+
+                        connect(ti);
 
-                // Make sure that we are ending with a flag if ALT is pressed
-                // and no end terminal is defined.
-                endWithoutTerminal(lastMouseCanvasPos, shouldEndWithFlag(me));
+                    }
 
-                updateSG(new Point2D.Double(ti.posDia.getTranslateX(), ti.posDia.getTranslateY()));
-                return false;
+                    // Make sure that we are ending with a flag if ALT is pressed
+                    // and no end terminal is defined.
+                    endWithoutTerminal(lastMouseCanvasPos, shouldEndWithFlag(me));
+
+                    updateSG(new Point2D.Double(ti.posDia.getTranslateX(), ti.posDia.getTranslateY()));
+                    return false;
+                }
             }
         }
 
@@ -566,10 +576,8 @@ public class RouteGraphConnectTool extends AbstractMode {
     protected void disconnect(Point2D mouseCanvasPos) {
         setEndTerminal(mouseCanvasPos.getX(), mouseCanvasPos.getY(), null, 0xf);
     }
-    
-    protected boolean processMouseButtonPress(MouseButtonPressedEvent e) {
-        MouseButtonEvent me = e;
 
+    protected boolean processMouseButtonPress(MouseButtonEvent me) {
         // Do nothing before the mouse has moved at least a little.
         // This prevents the user from ending the connection right where
         // it started.
@@ -585,9 +593,7 @@ public class RouteGraphConnectTool extends AbstractMode {
                 snapAdvisor.snap(mouseCanvasPos);
 
             // Clicked on an allowed end terminal. End connection & end mode.
-            if (isEndTerminalDefined()) {
-                createConnection();
-                remove();
+            if (tryEndConnection()) {
                 return true;
             } else {
                 // Finish connection in thin air only if the
@@ -617,6 +623,28 @@ public class RouteGraphConnectTool extends AbstractMode {
         return false;
     }
 
+    private int mouseLeftReleaseCount = 0;
+
+    protected boolean processMouseButtonRelease(MouseButtonReleasedEvent me) {
+        if (me.button == MouseEvent.LEFT_BUTTON
+                && ++mouseLeftReleaseCount == 1) {
+            return tryEndConnection();
+        }
+        return false;
+    }
+
+    /**
+     * @return <code>true</code> if connection was successfully ended
+     */
+    private boolean tryEndConnection() {
+        if (isEndTerminalDefined()) {
+            createConnection();
+            remove();
+            return true;
+        }
+        return false;
+    }
+
     protected boolean cancelPreviousBend() {
         if (!routePointsAllowed())
             return false;
@@ -816,9 +844,9 @@ public class RouteGraphConnectTool extends AbstractMode {
 
     // ------------------------------------------------------------------------
 
-    static RouteGraphTarget pickRouteGraphConnection(IDiagram diagram, Shape pickShape, double pickDistance) {
+    static RouteGraphTarget pickRouteGraphConnection(ICanvasContext ctx, IDiagram diagram, Shape pickShape, double pickDistance) {
         ArrayList<IElement> elements = new ArrayList<IElement>();
-        PickRequest req = new PickRequest(pickShape);
+        PickRequest req = new PickRequest(pickShape).context(ctx);
         DiagramUtils.pick(diagram, req, elements);
         for (Iterator<IElement> it = elements.iterator(); it.hasNext();) {
             IElement e = it.next();
@@ -837,56 +865,53 @@ public class RouteGraphConnectTool extends AbstractMode {
     }
 
     private static RouteGraphTarget pickNearestRouteGraphConnection(ArrayList<IElement> elements, double x, double y, double pd) {
-        // Find the nearest distance at which we get hits.
-        double hi = pd + 1;
-        double lo = hi * .01;
-        double limit = 0.5;
-        while (true) {
-            double delta = (hi - lo);
-            if (delta <= limit)
-                break;
+        Segment nearestSegment = null;
+        RouteLine nearestLine = null;
+        IElement nearestConnection = null;
 
-            pd = (lo + hi) * .5;
-
-            boolean hit = false;
-            for (IElement connection : elements) {
-                RouteGraphNode rgn = connection.getHint(RouteGraphConnectionClass.KEY_RG_NODE);
-                RouteLine line = rgn.getRouteGraph().pickLine(x, y, pd);
-                if (line != null) {
-                    hit = true;
-                    break;
+        double minDistanceSq = Double.MAX_VALUE;
+        
+        for (IElement connection : elements) {
+            RouteGraphNode rgn = connection.getHint(RouteGraphConnectionClass.KEY_RG_NODE);
+            for (RouteLine line : rgn.getRouteGraph().getAllLines()) {
+               ArrayList<Segment> segments = new ArrayList<Segment>();
+                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 && distanceSq < Math.pow(pd + rgn.getSelectionStrokeWidth() / 2, 2)) {
+                        minDistanceSq = distanceSq;
+                        nearestSegment = segment;
+                        nearestLine = line;
+                        nearestConnection = connection;
+                    }
                 }
             }
-
-            if (hit)
-                hi = pd;
-            else
-                lo = pd;
         }
 
-        // Now that the nearest hitting distance is found, find the nearest intersection.
-        RouteGraphTarget nearestTarget = null;
-        double nearest = Double.MAX_VALUE;
-        for (IElement connection : elements) {
-            RouteGraphNode rgn = connection.getHint(RouteGraphConnectionClass.KEY_RG_NODE);
-            RouteLine line = rgn.getRouteGraph().pickLine(x, y, pd);
-            if (line == null)
-                continue;
-
-            Point2D intersection = intersectionPoint(x, y, line);
-            if (intersection == null)
-                continue;
-
-            double dx = intersection.getX() - x;
-            double dy = intersection.getY() - y;
-            double dist = Math.sqrt(dx*dx + dy*dy);
-            if (dist < nearest) {
-                nearest = dist;
-                nearestTarget = new RouteGraphTarget(connection, rgn, line, new Point2D.Double(x, y), intersection);
+        if (nearestSegment != null) {
+            RoutePoint p1 = nearestSegment.p1;
+            RoutePoint p2 = nearestSegment.p2;
+            double d = Math.pow(p2.getX() - p1.getX(), 2.0) + Math.pow(p2.getY() - p1.getY(), 2.0);
+            Point2D p;
+            if (d == 0) {
+                p = new Point2D.Double(p1.getX(), p1.getY());
+            } else {
+                double u = ((x - p1.getX()) * (p2.getX() - p1.getX()) + (y - p1.getY()) * (p2.getY() - p1.getY())) / d;
+                if (u > 1.0) {
+                    p = new Point2D.Double(p2.getX(), p2.getY());
+                } else if (u <= 0.0) {
+                    p = new Point2D.Double(p1.getX(), p1.getY());
+                } else {
+                    p = new Point2D.Double(p2.getX() * u + p1.getX() * (1.0-u), (p2.getY() * u + p1.getY() * (1.0- u)));
+                }
             }
+            return new RouteGraphTarget(nearestConnection, nearestConnection.getHint(RouteGraphConnectionClass.KEY_RG_NODE), nearestLine, new Point2D.Double(x, y), p);
+        } else {
+            return null;
         }
-
-        return nearestTarget;
     }
 
     static Point2D intersectionPoint(double x, double y, RouteLine line) {