Line | Hits | Source |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2005, the JUNG Project and the Regents of the University | |
3 | * of California | |
4 | * All rights reserved. | |
5 | * | |
6 | * This software is open-source under the BSD license; see either | |
7 | * "license.txt" or | |
8 | * http://jung.sourceforge.net/license.txt for a description. | |
9 | * Created on Mar 11, 2005 | |
10 | * | |
11 | */ | |
12 | package edu.uci.ics.jung.visualization; | |
13 | ||
14 | import java.awt.Shape; | |
15 | import java.awt.geom.AffineTransform; | |
16 | import java.awt.geom.GeneralPath; | |
17 | import java.awt.geom.PathIterator; | |
18 | import java.awt.geom.Point2D; | |
19 | import java.awt.geom.Rectangle2D; | |
20 | import java.util.ConcurrentModificationException; | |
21 | import java.util.Iterator; | |
22 | ||
23 | import edu.uci.ics.jung.graph.Edge; | |
24 | import edu.uci.ics.jung.graph.Vertex; | |
25 | import edu.uci.ics.jung.utils.Pair; | |
26 | import edu.uci.ics.jung.visualization.transform.LayoutTransformer; | |
27 | import edu.uci.ics.jung.visualization.transform.MutableAffineTransformer; | |
28 | import edu.uci.ics.jung.visualization.transform.Transformer; | |
29 | ||
30 | /** | |
31 | * ShapePickSupport provides access to Vertices and Edges based on | |
32 | * their actual shapes. | |
33 | * | |
34 | * @author Tom Nelson - RABA Technologies | |
35 | * | |
36 | */ | |
37 | public class ShapePickSupport implements PickSupport { | |
38 | ||
39 | protected HasGraphLayout hasGraphLayout; | |
40 | protected HasShapeFunctions hasShapeFunctions; | |
41 | protected float pickSize; | |
42 | protected LayoutTransformer layoutTransformer; | |
43 | ||
44 | /** | |
45 | * Create an instance. | |
46 | * The HasGraphLayout is used as the source of the current | |
47 | * Graph Layout. The HasShapes | |
48 | * is used to access the VertexShapes and the EdgeShapes | |
49 | * @param hasGraphLayout source of the current layout. | |
50 | * @param hasShapeFunctions source of Vertex and Edge shapes. | |
51 | * @param pickSize how large to make the pick footprint for line edges | |
52 | */ | |
53 | public ShapePickSupport(HasGraphLayout hasGraphLayout, | |
54 | LayoutTransformer layoutTransformer, HasShapeFunctions hasShapeFunctions, | |
55 | 0 | float pickSize) { |
56 | 0 | this.hasGraphLayout = hasGraphLayout; |
57 | 0 | this.hasShapeFunctions = hasShapeFunctions; |
58 | 0 | this.layoutTransformer = layoutTransformer; |
59 | 0 | this.pickSize = pickSize; |
60 | 0 | } |
61 | ||
62 | 0 | public ShapePickSupport(float pickSize) { |
63 | 0 | this.pickSize = pickSize; |
64 | 0 | } |
65 | ||
66 | /** | |
67 | * Create an instance. | |
68 | * The pickSize footprint defaults to 2. | |
69 | */ | |
70 | public ShapePickSupport() { | |
71 | 0 | this(2); |
72 | 0 | } |
73 | ||
74 | /** | |
75 | * called by a HasLayout impl (like VisualizationViewer) when this | |
76 | * PickSupport impl is | |
77 | * added to it. This allows the PickSupport to | |
78 | * always get the current Layout and the current Renderer | |
79 | * from thecomponent it supports picking on. | |
80 | */ | |
81 | public void setHasGraphLayout(HasGraphLayout hasGraphLayout) { | |
82 | 0 | this.hasGraphLayout = hasGraphLayout; |
83 | 0 | } |
84 | ||
85 | /** | |
86 | * @param hasShapes The hasShapes to set. | |
87 | */ | |
88 | public void setHasShapes(HasShapeFunctions hasShapes) { | |
89 | 0 | this.hasShapeFunctions = hasShapes; |
90 | 0 | } |
91 | /** | |
92 | * @return Returns the layoutTransformer. | |
93 | */ | |
94 | public LayoutTransformer getLayoutTransformer() { | |
95 | 0 | return layoutTransformer; |
96 | } | |
97 | ||
98 | /** | |
99 | * When this PickSupport is set on a VisualizationViewer, | |
100 | * the VisualizationViewer calls this method to pass its | |
101 | * layout transformer in | |
102 | * | |
103 | * @param layoutTransformer The layoutTransformer to set. | |
104 | */ | |
105 | public void setLayoutTransformer(LayoutTransformer layoutTransformer) { | |
106 | 0 | this.layoutTransformer = layoutTransformer; |
107 | 0 | } |
108 | ||
109 | /** | |
110 | * Iterates over Vertices, checking to see if x,y is contained in the | |
111 | * Vertex's Shape. If (x,y) is contained in more than one vertex, use | |
112 | * the vertex whose center is closest to the pick point. | |
113 | * @see edu.uci.ics.jung.visualization.PickSupport#getVertex(double, double) | |
114 | */ | |
115 | public Vertex getVertex(double x, double y) { | |
116 | 0 | Layout layout = hasGraphLayout.getGraphLayout(); |
117 | ||
118 | 0 | Vertex closest = null; |
119 | 0 | double minDistance = Double.MAX_VALUE; |
120 | while(true) { | |
121 | try { | |
122 | 0 | for (Iterator iter=layout.getGraph().getVertices().iterator(); iter.hasNext(); ) { |
123 | 0 | if(hasShapeFunctions != null) { |
124 | 0 | Vertex v = (Vertex) iter.next(); |
125 | 0 | Shape shape = hasShapeFunctions.getVertexShapeFunction().getShape(v); |
126 | // transform the vertex location to screen coords | |
127 | 0 | Point2D p = layoutTransformer.layoutTransform(layout.getLocation(v)); |
128 | 0 | if(p == null) continue; |
129 | 0 | AffineTransform xform = |
130 | AffineTransform.getTranslateInstance(p.getX(), p.getY()); | |
131 | 0 | shape = xform.createTransformedShape(shape); |
132 | // see if this vertex center is closest to the pick point | |
133 | // among any other containing vertices | |
134 | 0 | if(shape.contains(x, y)) { |
135 | ||
136 | 0 | Rectangle2D bounds = shape.getBounds2D(); |
137 | 0 | double dx = bounds.getCenterX() - x; |
138 | 0 | double dy = bounds.getCenterY() - y; |
139 | 0 | double dist = dx * dx + dy * dy; |
140 | 0 | if (dist < minDistance) { |
141 | 0 | minDistance = dist; |
142 | 0 | closest = v; |
143 | } | |
144 | } | |
145 | } | |
146 | } | |
147 | 0 | break; |
148 | 0 | } catch(ConcurrentModificationException cme) {} |
149 | } | |
150 | 0 | return closest; |
151 | } | |
152 | ||
153 | /** | |
154 | * return an edge whose shape intersects the 'pickArea' footprint of the passed | |
155 | * x,y, coordinates. | |
156 | */ | |
157 | public Edge getEdge(double x, double y) { | |
158 | 0 | Layout layout = hasGraphLayout.getGraphLayout(); |
159 | ||
160 | // as a Line has no area, we can't always use edgeshape.contains(point) so we | |
161 | // make a small rectangular pickArea around the point and check if the | |
162 | // edgeshape.intersects(pickArea) | |
163 | 0 | Rectangle2D pickArea = |
164 | new Rectangle2D.Float((float)x-pickSize/2,(float)y-pickSize/2,pickSize,pickSize); | |
165 | 0 | Edge closest = null; |
166 | 0 | double minDistance = Double.MAX_VALUE; |
167 | while(true) { | |
168 | try { | |
169 | 0 | for (Iterator iter=layout.getGraph().getEdges().iterator(); iter.hasNext(); ) { |
170 | ||
171 | 0 | if(hasShapeFunctions != null) { |
172 | 0 | Edge e = (Edge) iter.next(); |
173 | 0 | Pair pair = e.getEndpoints(); |
174 | 0 | Vertex v1 = (Vertex)pair.getFirst(); |
175 | 0 | Vertex v2 = (Vertex)pair.getSecond(); |
176 | 0 | boolean isLoop = v1.equals(v2); |
177 | 0 | Point2D p1 = layoutTransformer.layoutTransform(layout.getLocation(v1)); |
178 | 0 | Point2D p2 = layoutTransformer.layoutTransform(layout.getLocation(v2)); |
179 | 0 | if(p1 == null || p2 == null) continue; |
180 | 0 | float x1 = (float) p1.getX(); |
181 | 0 | float y1 = (float) p1.getY(); |
182 | 0 | float x2 = (float) p2.getX(); |
183 | 0 | float y2 = (float) p2.getY(); |
184 | ||
185 | // translate the edge to the starting vertex | |
186 | 0 | AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); |
187 | ||
188 | 0 | Shape edgeShape = hasShapeFunctions.getEdgeShapeFunction().getShape(e); |
189 | 0 | if(isLoop) { |
190 | // make the loops proportional to the size of the vertex | |
191 | 0 | Shape s2 = hasShapeFunctions.getVertexShapeFunction().getShape(v2); |
192 | 0 | Rectangle2D s2Bounds = s2.getBounds2D(); |
193 | 0 | xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight()); |
194 | // move the loop so that the nadir is centered in the vertex | |
195 | 0 | xform.translate(0, -edgeShape.getBounds2D().getHeight()/2); |
196 | } else { | |
197 | 0 | float dx = x2 - x1; |
198 | 0 | float dy = y2 - y1; |
199 | // rotate the edge to the angle between the vertices | |
200 | 0 | double theta = Math.atan2(dy,dx); |
201 | 0 | xform.rotate(theta); |
202 | // stretch the edge to span the distance between the vertices | |
203 | 0 | float dist = (float) Math.sqrt(dx*dx + dy*dy); |
204 | 0 | xform.scale(dist, 1.0f); |
205 | } | |
206 | ||
207 | // transform the edge to its location and dimensions | |
208 | 0 | edgeShape = xform.createTransformedShape(edgeShape); |
209 | ||
210 | // because of the transform, the edgeShape is now a GeneralPath | |
211 | // see if this edge is the closest of any that intersect | |
212 | 0 | if(edgeShape.intersects(pickArea)) { |
213 | 0 | float cx=0; |
214 | 0 | float cy=0; |
215 | 0 | float[] f = new float[6]; |
216 | 0 | PathIterator pi = ((GeneralPath)edgeShape).getPathIterator(null); |
217 | 0 | if(pi.isDone()==false) { |
218 | 0 | pi.next(); |
219 | 0 | pi.currentSegment(f); |
220 | 0 | cx = f[0]; |
221 | 0 | cy = f[1]; |
222 | 0 | if(pi.isDone()==false) { |
223 | 0 | pi.currentSegment(f); |
224 | 0 | cx = f[0]; |
225 | 0 | cy = f[1]; |
226 | } | |
227 | } | |
228 | 0 | float dx = (float) (cx - x); |
229 | 0 | float dy = (float) (cy - y); |
230 | 0 | float dist = dx * dx + dy * dy; |
231 | 0 | if (dist < minDistance) { |
232 | 0 | minDistance = dist; |
233 | 0 | closest = e; |
234 | } | |
235 | } | |
236 | } | |
237 | } | |
238 | 0 | break; |
239 | 0 | } catch(ConcurrentModificationException cme) {} |
240 | } | |
241 | 0 | return closest; |
242 | } | |
243 | ||
244 | /** | |
245 | * <code>ShapePickSupport</code> gets its layout from its VisualizationViewer, so this | |
246 | * method currently does nothing. | |
247 | * @see edu.uci.ics.jung.visualization.PickSupport#setLayout(edu.uci.ics.jung.visualization.Layout) | |
248 | */ | |
249 | 0 | public void setLayout(Layout layout) {} |
250 | } |
this report was generated by version 1.0.5 of jcoverage. |
copyright © 2003, jcoverage ltd. all rights reserved. |