]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java
Allow PickSorter access to the PickRequest used for pick operation
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / diagram / handler / PickRequest.java
index 4d5d76094ec1e118f20130cf0fefa06405c6fee2..8d9d814d8eeb3c5599a08a4113e380752a1863b6 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.g2d.diagram.handler;\r
-\r
-import java.awt.Shape;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Area;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.List;\r
-\r
-import org.simantics.g2d.connection.handler.ConnectionHandler;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.handler.BendsHandler;\r
-import org.simantics.g2d.element.handler.TerminalTopology;\r
-import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;\r
-import org.simantics.g2d.elementclass.BranchPoint;\r
-import org.simantics.g2d.elementclass.MonitorHandler;\r
-import org.simantics.g2d.utils.GeometryUtils;\r
-import org.simantics.scenegraph.utils.TransformedRectangle;\r
-\r
-/**\r
- *\r
- * @See {@link GeometryUtils} for intersect and contains tests\r
- * @See {@link TransformedRectangle}\r
- * @See {@link Area} intersects operations for complex shapes\r
- * @author Toni Kalajainen\r
- */\r
-public class PickRequest {\r
-\r
-    public static enum PickPolicy {\r
-        PICK_INTERSECTING_OBJECTS,\r
-        PICK_CONTAINED_OBJECTS\r
-    }\r
-\r
-    public Shape      pickArea;\r
-    public PickPolicy pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;\r
-    /** Pick filter (null == everything is accepted) */\r
-    public PickFilter pickFilter = null;\r
-    public PickSorter pickSorter = null;\r
-\r
-    public PickRequest(double x, double y)\r
-    {\r
-        pickArea = new Rectangle2D.Double(x, y, 1, 1);\r
-    }\r
-    public PickRequest(Point2D p)\r
-    {\r
-        pickArea = new Rectangle2D.Double(p.getX(), p.getY(), 0.0001, 0.0001);\r
-    }\r
-    public PickRequest(Shape shape)\r
-    {\r
-        pickArea = shape;\r
-    }\r
-    public PickRequest(Shape shape, AffineTransform transform)\r
-    {\r
-        pickArea = GeometryUtils.transformShape(shape, transform);\r
-    }\r
-\r
-    public static interface PickFilter {\r
-        boolean accept(IElement e);\r
-\r
-        public static final PickFilter FILTER_ALL = new PickFilter() {\r
-            @Override\r
-            public boolean accept(IElement e) {\r
-                return true;\r
-            }\r
-        };\r
-        // Connections\r
-        public static final PickFilter FILTER_CONNECTIONS = new PickFilter() {\r
-            @Override\r
-            public boolean accept(IElement e) {\r
-                return e.getElementClass().containsClass(ConnectionHandler.class);\r
-            }\r
-            @Override\r
-            public String toString() { return "FILTER_CONNECTIONS"; }\r
-        };\r
-        // Connection edges\r
-        public static final PickFilter FILTER_CONNECTION_EDGES = new PickFilter() {\r
-            @Override\r
-            public boolean accept(IElement e) {\r
-                return e.getElementClass().containsClass(BendsHandler.class) || e.getElementClass().containsClass(ConnectionSelectionOutline.class);\r
-            }\r
-            @Override\r
-            public String toString() { return "FILTER_CONNECTION_EDGES"; }\r
-        };\r
-        // Connections\r
-        public static final PickFilter FILTER_BRANCH_POINT = new PickFilter() {\r
-            @Override\r
-            public boolean accept(IElement e) {\r
-                return e.getElementClass().containsClass(BranchPoint.class);\r
-            }\r
-            @Override\r
-            public String toString() { return "FILTER_BRANCH_POINTS"; }\r
-        };\r
-        // Anything that has terminals\r
-        public static final PickFilter FILTER_NODES = new PickFilter() {\r
-            @Override\r
-            public boolean accept(IElement e) {\r
-                return e.getElementClass().containsClass(TerminalTopology.class);\r
-            }\r
-            @Override\r
-            public String toString() { return "FILTER_NODES"; }\r
-        };\r
-        public static final PickFilter FILTER_MONITORS = new PickFilter() {\r
-            @Override\r
-            public boolean accept(IElement e) {\r
-                return e.getElementClass().containsClass(MonitorHandler.class);\r
-            }\r
-            @Override\r
-            public String toString() { return "FILTER_MONITORS"; }\r
-        };\r
-    }\r
-\r
-    public static interface PickSorter {\r
-        void sort(List<IElement> elements);\r
-\r
-        //\r
-        public static final PickSorter CONNECTIONS_LAST = new PickSorter() {\r
-            @Override\r
-            public void sort(List<IElement> elements) {\r
-                Collections.sort(elements, new Comparator<IElement>() {\r
-                    @Override\r
-                    public int compare(IElement e1, IElement e2) {\r
-                        boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);\r
-                        boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);\r
-                        if (!isConn1 && isConn2)\r
-                            return -1;\r
-                        if (isConn1 && !isConn2)\r
-                            return 1;\r
-                        return 0;\r
-                    }\r
-                });\r
-            }\r
-        };\r
-        public static final PickSorter CONNECTIONS_FIRST = new PickSorter() {\r
-            @Override\r
-            public void sort(List<IElement> elements) {\r
-                Collections.sort(elements, new Comparator<IElement>() {\r
-                    @Override\r
-                    public int compare(IElement e1, IElement e2) {\r
-                        boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);\r
-                        boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);\r
-                        if (!isConn1 && isConn2)\r
-                            return 1;\r
-                        if (isConn1 && !isConn2)\r
-                            return -1;\r
-                        return 0;\r
-                    }\r
-                });\r
-            }\r
-        };\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2020 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *     Semantum Oy - gitlab #454
+ *******************************************************************************/
+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;
+import org.simantics.g2d.element.handler.BendsHandler;
+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;
+
+/**
+ *
+ * @See {@link GeometryUtils} for intersect and contains tests
+ * @See {@link TransformedRectangle}
+ * @See {@link Area} intersects operations for complex shapes
+ * @author Toni Kalajainen
+ */
+public class PickRequest {
+
+    public static enum PickPolicy {
+        PICK_INTERSECTING_OBJECTS,
+        PICK_CONTAINED_OBJECTS
+    }
+
+    public Shape      pickArea;
+    public PickPolicy pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
+    /** Pick filter (null == everything is accepted) */
+    public PickFilter pickFilter = null;
+    public PickSorter pickSorter = null;
+
+    /**
+     * Used to optimize picking if provided via R-tree traversal to find
+     * intersecting elements, not everything.
+     */
+    public ICanvasContext pickContext;
+
+    public PickRequest(double x, double y)
+    {
+        pickArea = new Rectangle2D.Double(x, y, 1, 1);
+    }
+    public PickRequest(Point2D p)
+    {
+        pickArea = new Rectangle2D.Double(p.getX(), p.getY(), 0.0001, 0.0001);
+    }
+    public PickRequest(Shape shape)
+    {
+        pickArea = shape;
+    }
+    public PickRequest(Shape shape, AffineTransform transform)
+    {
+        pickArea = GeometryUtils.transformShape(shape, transform);
+    }
+
+    public PickRequest context(ICanvasContext ctx) {
+        this.pickContext = ctx;
+        return this;
+    }
+
+    public static interface PickFilter {
+        boolean accept(IElement e);
+
+        public static final PickFilter FILTER_ALL = new PickFilter() {
+            @Override
+            public boolean accept(IElement e) {
+                return true;
+            }
+        };
+        // Connections
+        public static final PickFilter FILTER_CONNECTIONS = new PickFilter() {
+            @Override
+            public boolean accept(IElement e) {
+                return e.getElementClass().containsClass(ConnectionHandler.class);
+            }
+            @Override
+            public String toString() { return "FILTER_CONNECTIONS"; }
+        };
+        // Connection edges
+        public static final PickFilter FILTER_CONNECTION_EDGES = new PickFilter() {
+            @Override
+            public boolean accept(IElement e) {
+                return e.getElementClass().containsClass(BendsHandler.class) || e.getElementClass().containsClass(ConnectionSelectionOutline.class);
+            }
+            @Override
+            public String toString() { return "FILTER_CONNECTION_EDGES"; }
+        };
+        // Connections
+        public static final PickFilter FILTER_BRANCH_POINT = new PickFilter() {
+            @Override
+            public boolean accept(IElement e) {
+                return e.getElementClass().containsClass(BranchPoint.class);
+            }
+            @Override
+            public String toString() { return "FILTER_BRANCH_POINTS"; }
+        };
+        // Anything that has terminals
+        public static final PickFilter FILTER_NODES = new PickFilter() {
+            @Override
+            public boolean accept(IElement e) {
+                return e.getElementClass().containsClass(TerminalTopology.class);
+            }
+            @Override
+            public String toString() { return "FILTER_NODES"; }
+        };
+        public static final PickFilter FILTER_MONITORS = new PickFilter() {
+            @Override
+            public boolean accept(IElement e) {
+                return e.getElementClass().containsClass(MonitorHandler.class);
+            }
+            @Override
+            public String toString() { return "FILTER_MONITORS"; }
+        };
+    }
+
+    public static interface PickSorter {
+        /**
+         * Sorts the specified element list.
+         * 
+         * @param elements the element list to sort
+         */
+        void sort(List<IElement> elements);
+
+        /**
+         * Extended interface-method that receives the pick request in addition to the
+         * picked elements to be sorted. Allows e.g. looking at the pick area in the
+         * sorter.
+         * 
+         * <p>
+         * The default implementation just invokes {@link #sort(List)} ignoring the pick
+         * request. The default implementation also keeps PickSorter API/ABI-compatible.
+         * 
+         * @param request  the original pick request that produced the hits listed in
+         *                 <code>elements</code>
+         * @param elements the element list to sort
+         * 
+         * @author Tuukka Lehtonen
+         * @since 1.43.0, 1.35.3
+         */
+        default void sort(PickRequest request, List<IElement> elements) {
+            sort(elements);
+        }
+
+        //
+        public static final PickSorter CONNECTIONS_LAST = new PickSorter() {
+            @Override
+            public void sort(List<IElement> elements) {
+                Collections.sort(elements, new Comparator<IElement>() {
+                    @Override
+                    public int compare(IElement e1, IElement e2) {
+                        boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);
+                        boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);
+                        if (!isConn1 && isConn2)
+                            return -1;
+                        if (isConn1 && !isConn2)
+                            return 1;
+                        return 0;
+                    }
+                });
+            }
+        };
+        public static final PickSorter CONNECTIONS_FIRST = new PickSorter() {
+            @Override
+            public void sort(List<IElement> elements) {
+                Collections.sort(elements, new Comparator<IElement>() {
+                    @Override
+                    public int compare(IElement e1, IElement e2) {
+                        boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);
+                        boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);
+                        if (!isConn1 && isConn2)
+                            return 1;
+                        if (isConn1 && !isConn2)
+                            return -1;
+                        return 0;
+                    }
+                });
+            }
+        };
+
+        /*
+         * 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;
+                }
+
+                private void sortConnections(List<IElement> 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(Pair.make(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;
+                        }
+                    }
+                }
+
+                @Override
+                public void sort(PickRequest request, List<IElement> elements) {
+                    if (sorter != null)
+                        sorter.sort(request, elements);
+                    sortConnections(elements);
+                }
+
+                @Override
+                public void sort(List<IElement> elements) {
+                    if (sorter != null)
+                        sorter.sort(elements);
+                    sortConnections(elements);
+                }
+            }; 
+        }
+    }
+
+}