]> 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 a43e8350a15b2aeff8c2f155558d4cd93b5aa72b..8d9d814d8eeb3c5599a08a4113e380752a1863b6 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * 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
@@ -8,18 +8,25 @@
  *
  * 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;
@@ -27,8 +34,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;
 
 /**
  *
@@ -50,6 +60,12 @@ public class PickRequest {
     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);
@@ -67,6 +83,11 @@ public class PickRequest {
         pickArea = GeometryUtils.transformShape(shape, transform);
     }
 
+    public PickRequest context(ICanvasContext ctx) {
+        this.pickContext = ctx;
+        return this;
+    }
+
     public static interface PickFilter {
         boolean accept(IElement e);
 
@@ -123,8 +144,33 @@ public class PickRequest {
     }
 
     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
@@ -160,6 +206,74 @@ 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;
+                }
+
+                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);
+                }
+            }; 
+        }
     }
 
 }