1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g2d.diagram.handler;
14 import java.awt.Shape;
15 import java.awt.geom.AffineTransform;
16 import java.awt.geom.Area;
17 import java.awt.geom.Line2D;
18 import java.awt.geom.Point2D;
19 import java.awt.geom.Rectangle2D;
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Comparator;
23 import java.util.List;
25 import org.simantics.diagram.connection.RouteLine;
26 import org.simantics.diagram.connection.RoutePoint;
27 import org.simantics.diagram.connection.segments.Segment;
28 import org.simantics.g2d.canvas.ICanvasContext;
29 import org.simantics.g2d.connection.handler.ConnectionHandler;
30 import org.simantics.g2d.element.IElement;
31 import org.simantics.g2d.element.handler.BendsHandler;
32 import org.simantics.g2d.element.handler.TerminalTopology;
33 import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
34 import org.simantics.g2d.elementclass.BranchPoint;
35 import org.simantics.g2d.elementclass.MonitorHandler;
36 import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
37 import org.simantics.g2d.utils.GeometryUtils;
38 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
39 import org.simantics.scenegraph.utils.TransformedRectangle;
40 import org.simantics.utils.datastructures.Pair;
44 * @See {@link GeometryUtils} for intersect and contains tests
45 * @See {@link TransformedRectangle}
46 * @See {@link Area} intersects operations for complex shapes
47 * @author Toni Kalajainen
49 public class PickRequest {
51 public static enum PickPolicy {
52 PICK_INTERSECTING_OBJECTS,
53 PICK_CONTAINED_OBJECTS
56 public Shape pickArea;
57 public PickPolicy pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
58 /** Pick filter (null == everything is accepted) */
59 public PickFilter pickFilter = null;
60 public PickSorter pickSorter = null;
63 * Used to optimize picking if provided via R-tree traversal to find
64 * intersecting elements, not everything.
66 public ICanvasContext pickContext;
68 public PickRequest(double x, double y)
70 pickArea = new Rectangle2D.Double(x, y, 1, 1);
72 public PickRequest(Point2D p)
74 pickArea = new Rectangle2D.Double(p.getX(), p.getY(), 0.0001, 0.0001);
76 public PickRequest(Shape shape)
80 public PickRequest(Shape shape, AffineTransform transform)
82 pickArea = GeometryUtils.transformShape(shape, transform);
85 public PickRequest context(ICanvasContext ctx) {
86 this.pickContext = ctx;
90 public static interface PickFilter {
91 boolean accept(IElement e);
93 public static final PickFilter FILTER_ALL = new PickFilter() {
95 public boolean accept(IElement e) {
100 public static final PickFilter FILTER_CONNECTIONS = new PickFilter() {
102 public boolean accept(IElement e) {
103 return e.getElementClass().containsClass(ConnectionHandler.class);
106 public String toString() { return "FILTER_CONNECTIONS"; }
109 public static final PickFilter FILTER_CONNECTION_EDGES = new PickFilter() {
111 public boolean accept(IElement e) {
112 return e.getElementClass().containsClass(BendsHandler.class) || e.getElementClass().containsClass(ConnectionSelectionOutline.class);
115 public String toString() { return "FILTER_CONNECTION_EDGES"; }
118 public static final PickFilter FILTER_BRANCH_POINT = new PickFilter() {
120 public boolean accept(IElement e) {
121 return e.getElementClass().containsClass(BranchPoint.class);
124 public String toString() { return "FILTER_BRANCH_POINTS"; }
126 // Anything that has terminals
127 public static final PickFilter FILTER_NODES = new PickFilter() {
129 public boolean accept(IElement e) {
130 return e.getElementClass().containsClass(TerminalTopology.class);
133 public String toString() { return "FILTER_NODES"; }
135 public static final PickFilter FILTER_MONITORS = new PickFilter() {
137 public boolean accept(IElement e) {
138 return e.getElementClass().containsClass(MonitorHandler.class);
141 public String toString() { return "FILTER_MONITORS"; }
145 public static interface PickSorter {
146 void sort(List<IElement> elements);
149 public static final PickSorter CONNECTIONS_LAST = new PickSorter() {
151 public void sort(List<IElement> elements) {
152 Collections.sort(elements, new Comparator<IElement>() {
154 public int compare(IElement e1, IElement e2) {
155 boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);
156 boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);
157 if (!isConn1 && isConn2)
159 if (isConn1 && !isConn2)
166 public static final PickSorter CONNECTIONS_FIRST = new PickSorter() {
168 public void sort(List<IElement> elements) {
169 Collections.sort(elements, new Comparator<IElement>() {
171 public int compare(IElement e1, IElement e2) {
172 boolean isConn1 = PickFilter.FILTER_CONNECTION_EDGES.accept(e1);
173 boolean isConn2 = PickFilter.FILTER_CONNECTION_EDGES.accept(e2);
174 if (!isConn1 && isConn2)
176 if (isConn1 && !isConn2)
185 * First use the default sorting if available, then sort successive connections by their distance to cursor.
187 public static PickSorter connectionSorter(PickSorter sorter, double x, double y) {
188 return new PickSorter() {
190 private double getDistanceSqToRouteGraphConnection(RouteGraphNode rgn, double x, double y) {
191 double minDistanceSq = Double.MAX_VALUE;
192 for (RouteLine line : rgn.getRouteGraph().getAllLines()) {
193 ArrayList<Segment> segments = new ArrayList<Segment>();
194 line.collectSegments(segments);
195 for (Segment segment : segments) {
196 RoutePoint p1 = segment.p1;
197 RoutePoint p2 = segment.p2;
199 double distanceSq = Line2D.ptSegDistSq(p1.getX(), p1.getY(), p2.getX(), p2.getY(), x, y);
200 if (distanceSq < minDistanceSq) {
201 minDistanceSq = distanceSq;
205 return minDistanceSq;
209 public void sort(List<IElement> elements) {
211 sorter.sort(elements);
213 List<Pair<Double, IElement>> connections = new ArrayList<>(elements.size());
215 for (int i = 0; i < elements.size(); i++) {
216 IElement element = elements.get(i);
217 RouteGraphNode rgn = element.getHint(RouteGraphConnectionClass.KEY_RG_NODE);
219 double distanceSq = getDistanceSqToRouteGraphConnection(rgn, x, y);
220 connections.add(new Pair<Double, IElement>(distanceSq, element));
223 if (rgn == null || i == elements.size() - 1) {
224 Collections.sort(connections, new Comparator<Pair<Double, IElement>>() {
226 public int compare(Pair<Double, IElement> connection1, Pair<Double, IElement> connection2) {
227 return Double.compare(connection2.first, connection1.first);
230 for (Pair<Double, IElement> connection : connections) {
231 elements.set(tail, connection.second);