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 }