View Javadoc
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 &lt; {@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 }