View Javadoc
1   /**
2    * Copyright (c) 2008, The JUNG Authors 
3    *
4    * All rights reserved.
5    *
6    * This software is open-source under the BSD license; see either
7    * "license.txt" or
8    * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
9    * Created on Apr 24, 2008
10   *  
11   */
12  package edu.uci.ics.jung.visualization.picking;
13  
14  import java.awt.Shape;
15  import java.awt.geom.Point2D;
16  import java.util.Collection;
17  import java.util.ConcurrentModificationException;
18  
19  import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
20  import edu.uci.ics.jung.algorithms.layout.Layout;
21  import edu.uci.ics.jung.visualization.Layer;
22  import edu.uci.ics.jung.visualization.VisualizationServer;
23  
24  /**
25   * A <code>GraphElementAccessor</code> that finds the closest element to 
26   * the pick point, and returns it if it is within the element's shape.
27   * This is best suited to elements with convex shapes that do not overlap.
28   * It differs from <code>ShapePickSupport</code> in that it only checks
29   * the closest element to see whether it contains the pick point.
30   * Possible unexpected odd behaviors:
31   * <ul>
32   * <li>If the elements overlap, this mechanism may pick another element than the one that's 
33   * "on top" (rendered last) if the pick point is closer to the center of an obscured vertex.
34   * <li>If element shapes are not convex, then this mechanism may return <code>null</code>
35   * even if the pick point is inside some element's shape, if the pick point is closer
36   * to the center of another element.
37   * </ul>
38   * Users who want to avoid either of these should use <code>ShapePickSupport</code>
39   * instead, which is slower but more flexible.  If neither of the above conditions
40   * (overlapping elements or non-convex shapes) is true, then <code>ShapePickSupport</code>
41   * and this class should have the same behavior.
42   */
43  public class ClosestShapePickSupport<V,E> implements GraphElementAccessor<V,E> {
44  	
45  	protected VisualizationServer<V,E> vv;
46  	protected float pickSize;
47  
48  	/**
49       * Creates a <code>ShapePickSupport</code> for the <code>vv</code>
50       * VisualizationServer, with the specified pick footprint.
51       * The <code>VisualizationServer</code> is used to fetch the current
52       * <code>Layout</code>. 
53       * @param vv source of the current <code>Layout</code>.
54       * @param pickSize the size of the pick footprint for line edges
55  	 */
56  	public ClosestShapePickSupport(VisualizationServer<V,E> vv, float pickSize)
57  	{
58  		this.vv = vv;
59  		this.pickSize = pickSize;
60  	}
61  	
62  	/**
63       * Create a <code>ShapePickSupport</code> with the <code>vv</code>
64       * VisualizationServer and default pick footprint.
65       * The footprint defaults to 2.
66       * @param vv source of the current <code>Layout</code>.
67  	 */
68  	public ClosestShapePickSupport(VisualizationServer<V,E> vv) 
69  	{
70  		this.vv = vv;
71  	}
72  
73  	/**
74  	 * @see edu.uci.ics.jung.algorithms.layout.GraphElementAccessor#getEdge(edu.uci.ics.jung.algorithms.layout.Layout, double, double)
75  	 */
76  	public E getEdge(Layout<V,E> layout, double x, double y) 
77  	{
78  		return null;
79  	}
80  
81  	/**
82  	 * @see edu.uci.ics.jung.algorithms.layout.GraphElementAccessor#getVertex(edu.uci.ics.jung.algorithms.layout.Layout, double, double)
83  	 */
84  	public V getVertex(Layout<V,E> layout, double x, double y) 
85  	{
86  		// first, find the closest vertex to (x,y)
87  		double minDistance = Double.MAX_VALUE;
88          V closest = null;
89  		while(true) 
90  		{
91  		    try 
92  		    {
93                  for(V v : layout.getGraph().getVertices()) 
94                  {
95  		            Point2D p = layout.apply(v);
96  		            double dx = p.getX() - x;
97  		            double dy = p.getY() - y;
98  		            double dist = dx * dx + dy * dy;
99  		            if (dist < minDistance) 
100 		            {
101 		                minDistance = dist;
102 		                closest = v;
103 		            }
104 		        }
105 		        break;
106 		    } 
107 		    catch(ConcurrentModificationException cme) {}
108 		}
109 		
110 		// now check to see whether (x,y) is in the shape for this vertex.
111 		
112 		// get the vertex shape
113         Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(closest);
114         // get the vertex location
115         Point2D p = layout.apply(closest);
116         // transform the vertex location to screen coords
117         p = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p);
118         
119         double ox = x - p.getX();
120         double oy = y - p.getY();
121 
122         if (shape.contains(ox, oy))
123         	return closest;
124         else
125         	return null;
126 	}
127 
128 	/**
129 	 * @see edu.uci.ics.jung.algorithms.layout.GraphElementAccessor#getVertices(edu.uci.ics.jung.algorithms.layout.Layout, java.awt.Shape)
130 	 */
131 	public Collection<V> getVertices(Layout<V,E> layout, Shape rectangle) 
132 	{
133 		// FIXME: RadiusPickSupport and ShapePickSupport are not using the same mechanism!
134 		// talk to Tom and make sure I understand which should be used.
135 		// in particular, there are some transformations that the latter uses; the latter is also 
136 		// doing a couple of kinds of filtering.  (well, only one--just predicate-based.)
137 		// looks to me like the VV could (should) be doing this filtering.  (maybe.)
138 		// 
139 		return null;
140 	}
141 }