]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/handler/PickRequest.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / diagram / handler / PickRequest.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2020 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *     Semantum Oy - gitlab #454
12  *******************************************************************************/
13 package org.simantics.g2d.diagram.handler;
14
15 import java.awt.Shape;
16 import java.awt.geom.AffineTransform;
17 import java.awt.geom.Area;
18 import java.awt.geom.Line2D;
19 import java.awt.geom.Point2D;
20 import java.awt.geom.Rectangle2D;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.Comparator;
24 import java.util.List;
25
26 import org.simantics.diagram.connection.RouteLine;
27 import org.simantics.diagram.connection.RoutePoint;
28 import org.simantics.diagram.connection.segments.Segment;
29 import org.simantics.g2d.canvas.ICanvasContext;
30 import org.simantics.g2d.connection.handler.ConnectionHandler;
31 import org.simantics.g2d.element.IElement;
32 import org.simantics.g2d.element.handler.BendsHandler;
33 import org.simantics.g2d.element.handler.TerminalTopology;
34 import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
35 import org.simantics.g2d.elementclass.BranchPoint;
36 import org.simantics.g2d.elementclass.MonitorHandler;
37 import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
38 import org.simantics.g2d.utils.GeometryUtils;
39 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
40 import org.simantics.scenegraph.utils.TransformedRectangle;
41 import org.simantics.utils.datastructures.Pair;
42
43 /**
44  *
45  * @See {@link GeometryUtils} for intersect and contains tests
46  * @See {@link TransformedRectangle}
47  * @See {@link Area} intersects operations for complex shapes
48  * @author Toni Kalajainen
49  */
50 public class PickRequest {
51
52     public static enum PickPolicy {
53         PICK_INTERSECTING_OBJECTS,
54         PICK_CONTAINED_OBJECTS
55     }
56
57     public Shape      pickArea;
58     public PickPolicy pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
59     /** Pick filter (null == everything is accepted) */
60     public PickFilter pickFilter = null;
61     public PickSorter pickSorter = null;
62
63     /**
64      * Used to optimize picking if provided via R-tree traversal to find
65      * intersecting elements, not everything.
66      */
67     public ICanvasContext pickContext;
68
69     public PickRequest(double x, double y)
70     {
71         pickArea = new Rectangle2D.Double(x, y, 1, 1);
72     }
73     public PickRequest(Point2D p)
74     {
75         pickArea = new Rectangle2D.Double(p.getX(), p.getY(), 0.0001, 0.0001);
76     }
77     public PickRequest(Shape shape)
78     {
79         pickArea = shape;
80     }
81     public PickRequest(Shape shape, AffineTransform transform)
82     {
83         pickArea = GeometryUtils.transformShape(shape, transform);
84     }
85
86     public PickRequest context(ICanvasContext ctx) {
87         this.pickContext = ctx;
88         return this;
89     }
90
91     public static interface PickFilter {
92         boolean accept(IElement e);
93
94         public static final PickFilter FILTER_ALL = new PickFilter() {
95             @Override
96             public boolean accept(IElement e) {
97                 return true;
98             }
99         };
100         // Connections
101         public static final PickFilter FILTER_CONNECTIONS = new PickFilter() {
102             @Override
103             public boolean accept(IElement e) {
104                 return e.getElementClass().containsClass(ConnectionHandler.class);
105             }
106             @Override
107             public String toString() { return "FILTER_CONNECTIONS"; }
108         };
109         // Connection edges
110         public static final PickFilter FILTER_CONNECTION_EDGES = new PickFilter() {
111             @Override
112             public boolean accept(IElement e) {
113                 return e.getElementClass().containsClass(BendsHandler.class) || e.getElementClass().containsClass(ConnectionSelectionOutline.class);
114             }
115             @Override
116             public String toString() { return "FILTER_CONNECTION_EDGES"; }
117         };
118         // Connections
119         public static final PickFilter FILTER_BRANCH_POINT = new PickFilter() {
120             @Override
121             public boolean accept(IElement e) {
122                 return e.getElementClass().containsClass(BranchPoint.class);
123             }
124             @Override
125             public String toString() { return "FILTER_BRANCH_POINTS"; }
126         };
127         // Anything that has terminals
128         public static final PickFilter FILTER_NODES = new PickFilter() {
129             @Override
130             public boolean accept(IElement e) {
131                 return e.getElementClass().containsClass(TerminalTopology.class);
132             }
133             @Override
134             public String toString() { return "FILTER_NODES"; }
135         };
136         public static final PickFilter FILTER_MONITORS = new PickFilter() {
137             @Override
138             public boolean accept(IElement e) {
139                 return e.getElementClass().containsClass(MonitorHandler.class);
140             }
141             @Override
142             public String toString() { return "FILTER_MONITORS"; }
143         };
144     }
145
146     public static interface PickSorter {
147         /**
148          * Sorts the specified element list.
149          * 
150          * @param elements the element list to sort
151          */
152         void sort(List<IElement> elements);
153
154         /**
155          * Extended interface-method that receives the pick request in addition to the
156          * picked elements to be sorted. Allows e.g. looking at the pick area in the
157          * sorter.
158          * 
159          * <p>
160          * The default implementation just invokes {@link #sort(List)} ignoring the pick
161          * request. The default implementation also keeps PickSorter API/ABI-compatible.
162          * 
163          * @param request  the original pick request that produced the hits listed in
164          *                 <code>elements</code>
165          * @param elements the element list to sort
166          * 
167          * @author Tuukka Lehtonen
168          * @since 1.43.0, 1.35.3
169          */
170         default void sort(PickRequest request, List<IElement> elements) {
171             sort(elements);
172         }
173
174         //
175         public static final PickSorter CONNECTIONS_LAST = new PickSorter() {
176             @Override
177             public void sort(List<IElement> elements) {
178                 Collections.sort(elements, new Comparator<IElement>() {
179                     @Override
180                     public int compare(IElement e1, IElement e2) {
181                         boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);
182                         boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);
183                         if (!isConn1 && isConn2)
184                             return -1;
185                         if (isConn1 && !isConn2)
186                             return 1;
187                         return 0;
188                     }
189                 });
190             }
191         };
192         public static final PickSorter CONNECTIONS_FIRST = new PickSorter() {
193             @Override
194             public void sort(List<IElement> elements) {
195                 Collections.sort(elements, new Comparator<IElement>() {
196                     @Override
197                     public int compare(IElement e1, IElement e2) {
198                         boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);
199                         boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);
200                         if (!isConn1 && isConn2)
201                             return 1;
202                         if (isConn1 && !isConn2)
203                             return -1;
204                         return 0;
205                     }
206                 });
207             }
208         };
209
210         /*
211          * First use the default sorting if available, then sort successive connections by their distance to cursor.
212          */
213         public static PickSorter connectionSorter(PickSorter sorter, double x, double y) {
214             return new PickSorter() {
215
216                 private double getDistanceSqToRouteGraphConnection(RouteGraphNode rgn, double x, double y) {
217                     double minDistanceSq = Double.MAX_VALUE;
218                     for (RouteLine line : rgn.getRouteGraph().getAllLines()) {
219                         ArrayList<Segment> segments = new ArrayList<Segment>();
220                         line.collectSegments(segments);
221                         for (Segment segment : segments) {
222                             RoutePoint p1 = segment.p1;
223                             RoutePoint p2 = segment.p2;
224
225                             double distanceSq = Line2D.ptSegDistSq(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y);
226                             if (distanceSq < minDistanceSq) {
227                                 minDistanceSq = distanceSq;
228                             }
229                         }
230                     }
231                     return minDistanceSq;
232                 }
233
234                 private void sortConnections(List<IElement> elements) {
235                     List<Pair<Double, IElement>> connections = new ArrayList<>(elements.size());
236                     int tail = 0;
237                     for (int i = 0; i < elements.size(); i++) {
238                         IElement element = elements.get(i);
239                         RouteGraphNode rgn = element.getHint(RouteGraphConnectionClass.KEY_RG_NODE);
240                         if (rgn != null) {
241                             double distanceSq = getDistanceSqToRouteGraphConnection(rgn, x, y);
242                             connections.add(Pair.make(distanceSq, element));
243                         }
244                         
245                         if (rgn == null || i == elements.size() - 1) {
246                             Collections.sort(connections, new Comparator<Pair<Double, IElement>>() {
247                                 @Override
248                                 public int compare(Pair<Double, IElement> connection1, Pair<Double, IElement> connection2) {
249                                     return Double.compare(connection2.first, connection1.first);
250                                 }
251                             });
252                             for (Pair<Double, IElement> connection : connections) {
253                                 elements.set(tail, connection.second);
254                                 tail++;
255                             }
256                             connections.clear();
257                             tail = i + 1;
258                         }
259                     }
260                 }
261
262                 @Override
263                 public void sort(PickRequest request, List<IElement> elements) {
264                     if (sorter != null)
265                         sorter.sort(request, elements);
266                     sortConnections(elements);
267                 }
268
269                 @Override
270                 public void sort(List<IElement> elements) {
271                     if (sorter != null)
272                         sorter.sort(elements);
273                     sortConnections(elements);
274                 }
275             }; 
276         }
277     }
278
279 }