]> gerrit.simantics Code Review - simantics/platform.git/commitdiff
Take zoom level into account when picking connections 34/3334/3
authorJussi Koskela <jussi.koskela@semantum.fi>
Tue, 29 Oct 2019 12:04:28 +0000 (14:04 +0200)
committerJussi Koskela <jussi.koskela@semantum.fi>
Tue, 29 Oct 2019 12:04:28 +0000 (14:04 +0200)
Also fixed RouteGraphConnectTool.pickNearestRouteGraphConnection which
gave flawed results. Visual hints are fixed to match the performed
actions. This versions always picks the closest connection if there are
multiple hits.

gitlab #396

Change-Id: Ib5d8be3b2c7301dae5545166c07e11c03f92a50d

bundles/org.simantics.diagram.connection/src/org/simantics/diagram/connection/RouteLine.java
bundles/org.simantics.diagram/src/org/simantics/diagram/participant/RouteGraphConnectTool.java
bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java
bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/pointertool/PointerInteractor.java
bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/RouteGraphConnectionClass.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DSceneGraph.java
bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/connection/RouteGraphNode.java

index c32b0ee240ec0291a21354b365d6317f61664b97..8a73ead7aec28b406bf3acd0a5ec9786d17f61ec 100644 (file)
@@ -97,11 +97,11 @@ public class RouteLine implements RouteNode, Serializable {
     public boolean isNear(double x2, double y2, double tolerance) {
         return isHorizontal 
                 ? Math.abs(y2-position) <= tolerance 
-                    && points.get(0).x <= x2 
-                    && x2 <= points.get(points.size()-1).x
+                    && points.get(0).x <= x2 - tolerance
+                    && x2 <= points.get(points.size()-1).x + tolerance
                 : Math.abs(x2-position) <= tolerance 
-                    && points.get(0).y <= y2 
-                    && y2 <= points.get(points.size()-1).y;
+                    && points.get(0).y <= y2 - tolerance
+                    && y2 <= points.get(points.size()-1).y + tolerance;
     }
 
     public void print(PrintStream out) {
index f4add638742601fd3737cdee33fd44776e8a7f1b..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;
@@ -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;
@@ -863,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;
-
-            pd = (lo + hi) * .5;
+        Segment nearestSegment = null;
+        RouteLine nearestLine = null;
+        IElement nearestConnection = null;
 
-            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) {
index 265caa805b604210e12612d49b22a2126ada9c35..912e79cd9ce3e8caec89606ba61c495e797c0923 100644 (file)
@@ -14,12 +14,17 @@ package org.simantics.g2d.diagram.handler;
 import java.awt.Shape;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Area;
+import java.awt.geom.Line2D;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
+import org.simantics.diagram.connection.RouteLine;
+import org.simantics.diagram.connection.RoutePoint;
+import org.simantics.diagram.connection.segments.Segment;
 import org.simantics.g2d.canvas.ICanvasContext;
 import org.simantics.g2d.connection.handler.ConnectionHandler;
 import org.simantics.g2d.element.IElement;
@@ -28,8 +33,11 @@ import org.simantics.g2d.element.handler.TerminalTopology;
 import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
 import org.simantics.g2d.elementclass.BranchPoint;
 import org.simantics.g2d.elementclass.MonitorHandler;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
 import org.simantics.g2d.utils.GeometryUtils;
+import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
 import org.simantics.scenegraph.utils.TransformedRectangle;
+import org.simantics.utils.datastructures.Pair;
 
 /**
  *
@@ -172,6 +180,64 @@ public class PickRequest {
                 });
             }
         };
+
+        /*
+         * First use the default sorting if available, then sort successive connections by their distance to cursor.
+         */
+        public static PickSorter connectionSorter(PickSorter sorter, double x, double y) {
+            return new PickSorter() {
+
+                private double getDistanceSqToRouteGraphConnection(RouteGraphNode rgn, double x, double y) {
+                    double minDistanceSq = Double.MAX_VALUE;
+                    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) {
+                                minDistanceSq = distanceSq;
+                            }
+                        }
+                    }
+                    return minDistanceSq;
+                }
+                
+                @Override
+                public void sort(List<IElement> elements) {
+                    if (sorter != null)
+                        sorter.sort(elements);
+
+                    List<Pair<Double, IElement>> connections = new ArrayList<>(elements.size());
+                    int tail = 0;
+                    for (int i = 0; i < elements.size(); i++) {
+                        IElement element = elements.get(i);
+                        RouteGraphNode rgn = element.getHint(RouteGraphConnectionClass.KEY_RG_NODE);
+                        if (rgn != null) {
+                            double distanceSq = getDistanceSqToRouteGraphConnection(rgn, x, y);
+                            connections.add(new Pair<Double, IElement>(distanceSq, element));
+                        }
+                        
+                        if (rgn == null || i == elements.size() - 1) {
+                            Collections.sort(connections, new Comparator<Pair<Double, IElement>>() {
+                                @Override
+                                public int compare(Pair<Double, IElement> connection1, Pair<Double, IElement> connection2) {
+                                    return Double.compare(connection2.first, connection1.first);
+                                }
+                            });
+                            for (Pair<Double, IElement> connection : connections) {
+                                elements.set(tail, connection.second);
+                                tail++;
+                            }
+                            connections.clear();
+                            tail = i + 1;
+                        }
+                    }
+                }
+            }; 
+        }
     }
 
 }
index 4f9df238f4b602ec6e95798570ad2cbbb154f04a..940579ebe82e17ad0f961d8622b57a666869a2a3 100644 (file)
@@ -45,12 +45,14 @@ import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalIn
 import org.simantics.g2d.element.ElementClassProviders;
 import org.simantics.g2d.element.IElement;
 import org.simantics.g2d.element.IElementClassProvider;
+import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
 import org.simantics.g2d.participant.KeyUtil;
 import org.simantics.g2d.participant.MouseUtil;
 import org.simantics.g2d.participant.TransformUtil;
 import org.simantics.g2d.scenegraph.SceneGraphConstants;
 import org.simantics.g2d.utils.CanvasUtils;
 import org.simantics.g2d.utils.GeometryUtils;
+import org.simantics.scenegraph.g2d.G2DSceneGraph;
 import org.simantics.scenegraph.g2d.events.Event;
 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
 import org.simantics.scenegraph.g2d.events.KeyEvent;
@@ -60,12 +62,15 @@ import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
 import org.simantics.scenegraph.g2d.events.command.Commands;
+import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
 import org.simantics.utils.ObjectUtils;
 import org.simantics.utils.datastructures.context.IContext;
 import org.simantics.utils.datastructures.context.IContextListener;
+import org.simantics.utils.datastructures.hints.HintListenerAdapter;
 import org.simantics.utils.datastructures.hints.IHintContext.Key;
 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
+import org.simantics.utils.datastructures.hints.IHintObservable;
 import org.simantics.utils.threads.ThreadUtils;
 
 /**
@@ -203,6 +208,14 @@ public class PointerInteractor extends AbstractDiagramParticipant {
         super.addedToContext(ctx);
         hoverStrategy = new DefaultHoverStrategy((TerminalHoverStrategy) getHint(TerminalPainter.TERMINAL_HOVER_STRATEGY));
         setHint(TerminalPainter.TERMINAL_HOVER_STRATEGY, hoverStrategy);
+
+        getContext().getSceneGraph().setGlobalProperty(G2DSceneGraph.PICK_DISTANCE, getPickDistance());
+        getHintStack().addKeyHintListener(KEY_PICK_DISTANCE, new HintListenerAdapter() {
+            @Override
+            public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
+                getContext().getSceneGraph().setGlobalProperty(G2DSceneGraph.PICK_DISTANCE, getPickDistance());
+            }
+        });
     }
 
     @EventHandler(priority = 0)
@@ -297,7 +310,7 @@ public class PointerInteractor extends AbstractDiagramParticipant {
             return null;
 
         double      pd              = getPickDistance();
-        Rectangle2D controlPickRect = new Rectangle2D.Double(controlPos.getX()-pd, controlPos.getY()-pd, pd*2+1, pd*2+1);
+        Rectangle2D controlPickRect = new Rectangle2D.Double(controlPos.getX()-pd, controlPos.getY()-pd, pd*2, pd*2);
         Shape       canvasShape     = GeometryUtils.transformShape(controlPickRect, inverse);
         return canvasShape;
     }
@@ -415,15 +428,13 @@ public class PointerInteractor extends AbstractDiagramParticipant {
 
         assertDependencies();
 
-        // Pick Terminal
-        double pickDist = getPickDistance();
-        Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1);
-        Shape       canvasPickRect  = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());
+        Shape       canvasPickRect  = getCanvasPickShape(me.controlPosition);
         int selectionId = me.mouseId;
 
         PickRequest req = new PickRequest(canvasPickRect).context(getContext());
         req.pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
-        req.pickSorter = pickSorter;
+        req.pickSorter = PickRequest.PickSorter.connectionSorter(pickSorter, req.pickArea.getBounds2D().getCenterX(), req.pickArea.getBounds2D().getCenterY());
+
         //req.pickSorter = PickRequest.PickSorter.CONNECTIONS_LAST;
         List<IElement> pickables = new ArrayList<IElement>();
         pickContext.pick(diagram, req, pickables);
@@ -586,14 +597,13 @@ public class PointerInteractor extends AbstractDiagramParticipant {
         if (getToolMode() != Hints.POINTERTOOL) return false;
         if (me.clickCount < 2) return false;
 
-        // Pick Terminal
-        double pickDist = getPickDistance();
-        Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1);
-        Shape       canvasPickRect  = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());
+        Shape       canvasPickRect  = getCanvasPickShape(me.controlPosition);
         int         selectionId     = me.mouseId;
 
         PickRequest req             = new PickRequest(canvasPickRect).context(getContext());
         req.pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
+
+        req.pickSorter = PickRequest.PickSorter.connectionSorter(pickSorter, req.pickArea.getBounds2D().getCenterX(), req.pickArea.getBounds2D().getCenterY());
         List<IElement> pick         = new ArrayList<IElement>();
         pickContext.pick(diagram, req, pick);
 
@@ -641,23 +651,13 @@ public class PointerInteractor extends AbstractDiagramParticipant {
         assertDependencies();
 
         Point2D         curCanvasPos    = util.controlToCanvas(me.controlPosition, curCanvasDragPos);
-        PickRequest     req             = new PickRequest(me.startCanvasPos).context(getContext());
+        Shape       canvasPickRect  = getCanvasPickShape(me.controlPosition);
+        PickRequest     req             = new PickRequest(canvasPickRect).context(getContext());
         req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS;
+        req.pickSorter = PickRequest.PickSorter.connectionSorter(pickSorter, req.pickArea.getBounds2D().getCenterX(), req.pickArea.getBounds2D().getCenterY()); 
         List<IElement>  picks           = new ArrayList<IElement>();
         pickContext.pick(diagram, req, picks);
 
-        //System.out.println(picks);
-        if (picks.isEmpty()) {
-            // Widen the area of searching if nothing is found with point picking
-               double pickDist = getPickDistance();
-            Rectangle2D     controlPickRect     = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1);
-            Shape           canvasPickRect      = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());
-            req = new PickRequest(canvasPickRect).context(getContext());
-            req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS;
-            pickContext.pick(diagram, req, picks);
-            //System.out.println("2nd try: " + picks);
-        }
-
         Set<IElement> sel            = selection.getSelection(me.mouseId);
         IElement      topMostPick    = picks.isEmpty() ? null : picks.get(picks.size() - 1);
         Set<IElement> elementsToDrag = new HashSet<IElement>();
@@ -686,6 +686,15 @@ public class PointerInteractor extends AbstractDiagramParticipant {
                         getContext().add(tm);
                         return !onlyConnections;
                     }
+                } else {
+                    // forward MouseDragBegin to closest RouteGraphNode
+                    for (int i = picks.size() - 1; i >= 0; i--) {
+                        RouteGraphNode rgn = picks.get(i).getHint(RouteGraphConnectionClass.KEY_RG_NODE);
+                        if (rgn != null) {
+                            rgn.handleDrag(me);
+                            break;
+                        }
+                    }
                 }
             }
         }
index a9015fcfb95c9918f7ebaea362d677c1517abea8..bf4577b51e1666378a10b3b6a46e4412b54dab99 100644 (file)
@@ -171,6 +171,10 @@ public class RouteGraphConnectionClass {
 
         @Override
         public boolean pickTest(IElement e, Shape s, PickPolicy policy) {
+            RouteGraphNode rgn = e.getHint(KEY_RG_NODE);
+            if (rgn == null) {
+                return false;
+            }
             RouteGraph rg = getRouteGraph(e);
             if (rg == null)
                 return false;
@@ -185,7 +189,7 @@ public class RouteGraphConnectionClass {
                        if (e.containsHint(KEY_USE_TOLERANCE_IN_SELECTION))
                                tolerance = getTolerance(e);
                        else
-                               tolerance = (bounds.getHeight()+bounds.getHeight()) * 0.25;
+                               tolerance = Math.max((bounds.getHeight()+bounds.getHeight()) * 0.25, rgn.getSelectionStrokeWidth() / 2);
                        Object node = rg.pickLine(bounds.getCenterX(), bounds.getCenterY(), tolerance);
                     return node != null;
             }
index 9055a8efeca37c1c4822558b77c6ab1c36d242cb..a013dc0419a74ab386d50f5c554fd6190b18e81b 100644 (file)
@@ -48,6 +48,7 @@ public class G2DSceneGraph extends G2DParentNode implements ILookupService, INod
     private static final long          serialVersionUID = -7066146333849901429L;
     
     public static final String        IGNORE_FOCUS = "ignoreFocus";
+    public static final String        PICK_DISTANCE = "pickDistance";
 
     protected transient Container     rootPane         = null;
     // TODO: swing dependency in here might not be a good idea
index 0a2583fc6dd734552999376241b15b509f235086..048514f3a0eaae298b58bdf98149542a8a8fe13b 100644 (file)
@@ -23,7 +23,6 @@ import java.awt.geom.Path2D;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.lang.reflect.Constructor;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -47,6 +46,7 @@ import org.simantics.scenegraph.INode;
 import org.simantics.scenegraph.ISelectionPainterNode;
 import org.simantics.scenegraph.g2d.G2DNode;
 import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.G2DSceneGraph;
 import org.simantics.scenegraph.g2d.IG2DNode;
 import org.simantics.scenegraph.g2d.events.EventTypes;
 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
@@ -61,6 +61,7 @@ import org.simantics.scenegraph.g2d.events.command.CommandEvent;
 import org.simantics.scenegraph.g2d.events.command.Commands;
 import org.simantics.scenegraph.g2d.nodes.GridNode;
 import org.simantics.scenegraph.g2d.nodes.LinkNode;
+import org.simantics.scenegraph.g2d.nodes.NavigationNode;
 import org.simantics.scenegraph.g2d.nodes.SVGNodeAssignment;
 import org.simantics.scenegraph.g2d.nodes.connection.HighlightActionPointsAction.Action;
 import org.simantics.scenegraph.g2d.nodes.connection.HighlightActionPointsAction.Pick;
@@ -69,8 +70,8 @@ import org.simantics.scenegraph.utils.ColorUtil;
 import org.simantics.scenegraph.utils.GeometryUtils;
 import org.simantics.scenegraph.utils.InitValueSupport;
 import org.simantics.scenegraph.utils.NodeUtil;
-import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import gnu.trove.map.hash.THashMap;
 
@@ -486,7 +487,7 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In
         return null;
     }
 
-    private double getSelectionStrokeWidth() {
+    public double getSelectionStrokeWidth() {
         if (selectionStroke instanceof BasicStroke) {
             BasicStroke bs = (BasicStroke) selectionStroke;
             return bs.getLineWidth();
@@ -530,6 +531,12 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In
 
     @Override
     protected boolean mouseDragged(MouseDragBegin e) {
+        // Consume event if drag is possible. 
+        // PointerInteractor will call handleDrag with the MouseDragBegin event for the route line that is closest to the cursor.
+        return currentAction != null;
+    }
+
+    public boolean handleDrag(MouseDragBegin e) {
         if (dragAction != null && !e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) && e.button == MouseEvent.LEFT_BUTTON) {
             currentAction = dragAction;
             dragAction = null;
@@ -549,7 +556,7 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In
             }
         }
         if (newBranchPointPosition != null) {
-            RouteLine line = rg.pickLine(mouseX, mouseY, pickTolerance);
+            RouteLine line = rg.pickLine(mouseX, mouseY, scaledPickTolerance());
             if (line != null) {
                 newBranchPointPosition.setLocation(mouseX, mouseY);
                 SplittedRouteGraph.snapToLine(newBranchPointPosition, line);
@@ -660,7 +667,7 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In
                     return false;
                 }
                 //System.out.println("move action");
-                dragAction = SnappingMoveAction.create(rg, mouseX, mouseY, pickTolerance, moveFilter, getSnapAdvisor());
+                dragAction = SnappingMoveAction.create(rg, mouseX, mouseY, scaledPickTolerance(), moveFilter, getSnapAdvisor());
                 //System.out.println("DRAG ACTION: " + dragAction);
             }
 
@@ -671,6 +678,20 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In
         return false;
     }
 
+    private double scaledPickTolerance() {
+        NavigationNode nn = NodeUtil.findNearestParentNode(this, NavigationNode.class);
+        double scale = 1.0;
+        if (nn != null) {
+            scale = GeometryUtils.getScale(nn.getTransform());
+        }
+        double pickDistance = 0;
+        G2DSceneGraph sg = NodeUtil.getRootNode(nn != null ? nn : this);
+        if (sg != null) {
+            pickDistance = sg.getGlobalProperty(G2DSceneGraph.PICK_DISTANCE, pickDistance);
+        }
+        return Math.max(getSelectionStrokeWidth() / 2.0, pickDistance / scale);
+    }
+    
     /**
      * Checks the selections data node in the scene graph for any links 
      * @return
@@ -755,11 +776,11 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In
             return false;
 
         if (!e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) && e.keyCode == KeyEvent.VK_S) {
-            Object target = rg.pick(mouseX, mouseY, pickTolerance, RouteGraph.PICK_PERSISTENT_LINES | RouteGraph.PICK_TRANSIENT_LINES);
+            Object target = rg.pick(mouseX, mouseY, scaledPickTolerance(), RouteGraph.PICK_PERSISTENT_LINES | RouteGraph.PICK_TRANSIENT_LINES);
             return splitTarget(target);
         }
         else if (!e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK | MouseEvent.CTRL_MASK) && (e.keyCode == KeyEvent.VK_R || e.keyCode == KeyEvent.VK_D)) {
-            Object target = rg.pick(mouseX, mouseY, pickTolerance, RouteGraph.PICK_PERSISTENT_LINES);
+            Object target = rg.pick(mouseX, mouseY, scaledPickTolerance(), RouteGraph.PICK_PERSISTENT_LINES);
             return deleteTarget(target);
         }
         else if (e.keyCode == KeyEvent.VK_ESCAPE) {
@@ -783,7 +804,7 @@ public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, In
         }
         else if (e.keyCode == KeyEvent.VK_ALT) {
             // Begin connection branching visualization.
-            RouteLine line = rg.pickLine(mouseX, mouseY, pickTolerance);
+            RouteLine line = rg.pickLine(mouseX, mouseY, scaledPickTolerance());
             if (branchable && line != null) {
                 newBranchPointPosition = new Point2D.Double(mouseX, mouseY);
                 SplittedRouteGraph.snapToLine(newBranchPointPosition, line);