1 /*
2 * Copyright (c) 2005, The JUNG Authors
3 * All rights reserved.
4 *
5 * This software is open-source under the BSD license; see either "license.txt"
6 * or https://github.com/jrtom/jung/blob/master/LICENSE for a description.
7 *
8 *
9 * Created on Apr 12, 2005
10 */
11 package edu.uci.ics.jung.algorithms.layout;
12
13 import java.awt.Shape;
14 import java.awt.geom.Point2D;
15 import java.util.Collection;
16 import java.util.ConcurrentModificationException;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.Set;
20
21 import edu.uci.ics.jung.graph.Graph;
22
23
24 /**
25 * Simple implementation of PickSupport that returns the vertex or edge
26 * that is closest to the specified location. This implementation
27 * provides the same picking options that were available in
28 * previous versions of AbstractLayout.
29 *
30 * <p>No element will be returned that is farther away than the specified
31 * maximum distance.
32 *
33 * @author Tom Nelson
34 * @author Joshua O'Madadhain
35 */
36 public class RadiusGraphElementAccessor<V, E> implements GraphElementAccessor<V, E> {
37
38 protected double maxDistance;
39
40 /**
41 * Creates an instance with an effectively infinite default maximum distance.
42 */
43 public RadiusGraphElementAccessor() {
44 this(Math.sqrt(Double.MAX_VALUE - 1000));
45 }
46
47 /**
48 * Creates an instance with the specified default maximum distance.
49 * @param maxDistance the maximum distance at which any element can be from a specified location
50 * and still be returned
51 */
52 public RadiusGraphElementAccessor(double maxDistance) {
53 this.maxDistance = maxDistance;
54 }
55
56 /**
57 * Gets the vertex nearest to the location of the (x,y) location selected,
58 * within a distance of <tt>maxDistance</tt>. Iterates through all
59 * visible vertices and checks their distance from the click. Override this
60 * method to provide a more efficient implementation.
61 *
62 * @param layout the context in which the location is defined
63 * @param x the x coordinate of the location
64 * @param y the y coordinate of the location
65 * @return a vertex which is associated with the location {@code (x,y)}
66 * as given by {@code layout}
67 */
68 public V getVertex(Layout<V,E> layout, double x, double y) {
69 return getVertex(layout, x, y, this.maxDistance);
70 }
71
72 /**
73 * Gets the vertex nearest to the location of the (x,y) location selected,
74 * within a distance of {@code maxDistance}. Iterates through all
75 * visible vertices and checks their distance from the location. Override this
76 * method to provide a more efficient implementation.
77 *
78 * @param layout the context in which the location is defined
79 * @param x the x coordinate of the location
80 * @param y the y coordinate of the location
81 * @param maxDistance the maximum distance at which any element can be from a specified location
82 * and still be returned
83 * @return a vertex which is associated with the location {@code (x,y)}
84 * as given by {@code layout}
85 */
86 public V getVertex(Layout<V,E> layout, double x, double y, double maxDistance) {
87 double minDistance = maxDistance * maxDistance;
88 V closest = null;
89 while(true) {
90 try {
91 for(V v : layout.getGraph().getVertices()) {
92
93 Point2D p = layout.apply(v);
94 double dx = p.getX() - x;
95 double dy = p.getY() - y;
96 double dist = dx * dx + dy * dy;
97 if (dist < minDistance) {
98 minDistance = dist;
99 closest = v;
100 }
101 }
102 break;
103 } catch(ConcurrentModificationException cme) {}
104 }
105 return closest;
106 }
107
108 public Collection<V> getVertices(Layout<V,E> layout, Shape rectangle) {
109 Set<V> pickedVertices = new HashSet<V>();
110 while(true) {
111 try {
112 for(V v : layout.getGraph().getVertices()) {
113
114 Point2D p = layout.apply(v);
115 if(rectangle.contains(p)) {
116 pickedVertices.add(v);
117 }
118 }
119 break;
120 } catch(ConcurrentModificationException cme) {}
121 }
122 return pickedVertices;
123 }
124
125 public E getEdge(Layout<V,E> layout, double x, double y) {
126 return getEdge(layout, x, y, this.maxDistance);
127 }
128
129 /**
130 * Gets the vertex nearest to the location of the (x,y) location selected,
131 * whose endpoints are < {@code maxDistance}. Iterates through all
132 * visible vertices and checks their distance from the location. Override this
133 * method to provide a more efficient implementation.
134 *
135 * @param layout the context in which the location is defined
136 * @param x the x coordinate of the location
137 * @param y the y coordinate of the location
138 * @param maxDistance the maximum distance at which any element can be from a specified location
139 * and still be returned
140 * @return an edge which is associated with the location {@code (x,y)}
141 * as given by {@code layout}
142 */
143 public E getEdge(Layout<V,E> layout, double x, double y, double maxDistance) {
144 double minDistance = maxDistance * maxDistance;
145 E closest = null;
146 while(true) {
147 try {
148 for(E e : layout.getGraph().getEdges()) {
149
150 // Could replace all this set stuff with getFrom_internal() etc.
151 Graph<V, E> graph = layout.getGraph();
152 Collection<V> vertices = graph.getIncidentVertices(e);
153 Iterator<V> vertexIterator = vertices.iterator();
154 V v1 = vertexIterator.next();
155 V v2 = vertexIterator.next();
156 // Get coords
157 Point2D p1 = layout.apply(v1);
158 Point2D p2 = layout.apply(v2);
159 double x1 = p1.getX();
160 double y1 = p1.getY();
161 double x2 = p2.getX();
162 double y2 = p2.getY();
163 // Calculate location on line closest to (x,y)
164 // First, check that v1 and v2 are not coincident.
165 if (x1 == x2 && y1 == y2)
166 continue;
167 double b =
168 ((y - y1) * (y2 - y1) + (x - x1) * (x2 - x1))
169 / ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
170 //
171 double distance2; // square of the distance
172 if (b <= 0)
173 distance2 = (x - x1) * (x - x1) + (y - y1) * (y - y1);
174 else if (b >= 1)
175 distance2 = (x - x2) * (x - x2) + (y - y2) * (y - y2);
176 else {
177 double x3 = x1 + b * (x2 - x1);
178 double y3 = y1 + b * (y2 - y1);
179 distance2 = (x - x3) * (x - x3) + (y - y3) * (y - y3);
180 }
181
182 if (distance2 < minDistance) {
183 minDistance = distance2;
184 closest = e;
185 }
186 }
187 break;
188 } catch(ConcurrentModificationException cme) {}
189 }
190 return closest;
191 }
192 }