1
2
3
4
5
6
7
8
9
10
11
12 package edu.uci.ics.jung.visualization.picking;
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.Collection;
21 import java.util.ConcurrentModificationException;
22 import java.util.HashSet;
23 import java.util.Set;
24
25 import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
26 import edu.uci.ics.jung.algorithms.layout.Layout;
27 import edu.uci.ics.jung.graph.util.Pair;
28 import edu.uci.ics.jung.visualization.Layer;
29 import edu.uci.ics.jung.visualization.VisualizationServer;
30 import edu.uci.ics.jung.visualization.transform.MutableTransformerDecorator;
31
32
33
34
35
36
37
38
39
40
41 public class ViewLensShapePickSupport<V, E> extends ShapePickSupport<V,E>
42 implements GraphElementAccessor<V,E> {
43
44 public ViewLensShapePickSupport(VisualizationServer<V,E> vv, float pickSize) {
45 super(vv, pickSize);
46 }
47
48 public ViewLensShapePickSupport(VisualizationServer<V,E> vv) {
49 this(vv, 2);
50 }
51
52 public V getVertex(Layout<V, E> layout, double x, double y) {
53
54 V closest = null;
55 double minDistance = Double.MAX_VALUE;
56 Point2D ip = ((MutableTransformerDecorator)vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)).getDelegate().inverseTransform(new Point2D.Double(x,y));
57 x = ip.getX();
58 y = ip.getY();
59
60 while(true) {
61 try {
62 for(V v : getFilteredVertices(layout)) {
63
64 Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(v);
65
66 Point2D p = layout.apply(v);
67 if(p == null) continue;
68 AffineTransform xform =
69 AffineTransform.getTranslateInstance(p.getX(), p.getY());
70 shape = xform.createTransformedShape(shape);
71
72
73
74 Point2D lp = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, p);
75 AffineTransform xlate = AffineTransform.getTranslateInstance(
76 lp.getX()-p.getX(),lp.getY()-p.getY());
77 shape = xlate.createTransformedShape(shape);
78
79
80 shape = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.VIEW, shape);
81
82
83
84
85 if(shape.contains(x, y)) {
86
87 if(style == Style.LOWEST) {
88
89 return v;
90 } else if(style == Style.HIGHEST) {
91
92 closest = v;
93 } else {
94 Rectangle2D bounds = shape.getBounds2D();
95 double dx = bounds.getCenterX() - x;
96 double dy = bounds.getCenterY() - y;
97 double dist = dx * dx + dy * dy;
98 if (dist < minDistance) {
99 minDistance = dist;
100 closest = v;
101 }
102 }
103 }
104 }
105 break;
106 } catch(ConcurrentModificationException cme) {}
107 }
108 return closest;
109 }
110
111 public Collection<V> getVertices(Layout<V, E> layout, Shape rectangle) {
112 Set<V> pickedVertices = new HashSet<V>();
113
114
115 rectangle = ((MutableTransformerDecorator)vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)).getDelegate().inverseTransform(rectangle);
116
117 while(true) {
118 try {
119 for(V v : getFilteredVertices(layout)) {
120 Point2D p = layout.apply(v);
121 if(p == null) continue;
122
123 Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(v);
124
125 AffineTransform xform =
126 AffineTransform.getTranslateInstance(p.getX(), p.getY());
127 shape = xform.createTransformedShape(shape);
128
129 shape = vv.getRenderContext().getMultiLayerTransformer().transform(shape);
130 Rectangle2D bounds = shape.getBounds2D();
131 p.setLocation(bounds.getCenterX(),bounds.getCenterY());
132
133 if(rectangle.contains(p)) {
134 pickedVertices.add(v);
135 }
136 }
137 break;
138 } catch(ConcurrentModificationException cme) {}
139 }
140 return pickedVertices;
141 }
142
143 public E getEdge(Layout<V, E> layout, double x, double y) {
144
145 Point2D ip = ((MutableTransformerDecorator)vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)).getDelegate().inverseTransform(new Point2D.Double(x,y));
146 x = ip.getX();
147 y = ip.getY();
148
149
150
151
152 Rectangle2D pickArea =
153 new Rectangle2D.Float((float)x-pickSize/2,(float)y-pickSize/2,pickSize,pickSize);
154 E closest = null;
155 double minDistance = Double.MAX_VALUE;
156 while(true) {
157 try {
158 for(E e : getFilteredEdges(layout)) {
159
160 Pair<V> pair = layout.getGraph().getEndpoints(e);
161 V v1 = pair.getFirst();
162 V v2 = pair.getSecond();
163 boolean isLoop = v1.equals(v2);
164 Point2D p1 = layout.apply(v1);
165
166 Point2D p2 = layout.apply(v2);
167
168 if(p1 == null || p2 == null) continue;
169 float x1 = (float) p1.getX();
170 float y1 = (float) p1.getY();
171 float x2 = (float) p2.getX();
172 float y2 = (float) p2.getY();
173
174
175 AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1);
176
177 Shape edgeShape = vv.getRenderContext().getEdgeShapeTransformer().apply(e);
178 if(isLoop) {
179
180 Shape s2 = vv.getRenderContext().getVertexShapeTransformer().apply(v2);
181 Rectangle2D s2Bounds = s2.getBounds2D();
182 xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight());
183
184 xform.translate(0, -edgeShape.getBounds2D().getHeight()/2);
185 } else {
186 float dx = x2 - x1;
187 float dy = y2 - y1;
188
189 double theta = Math.atan2(dy,dx);
190 xform.rotate(theta);
191
192 float dist = (float) Math.sqrt(dx*dx + dy*dy);
193 xform.scale(dist, 1.0f);
194 }
195
196
197 edgeShape = xform.createTransformedShape(edgeShape);
198
199 edgeShape = vv.getRenderContext().getMultiLayerTransformer().transform(edgeShape);
200
201
202
203 if(edgeShape.intersects(pickArea)) {
204 float cx=0;
205 float cy=0;
206 float[] f = new float[6];
207 PathIterator pi = new GeneralPath(edgeShape).getPathIterator(null);
208 if(pi.isDone()==false) {
209 pi.next();
210 pi.currentSegment(f);
211 cx = f[0];
212 cy = f[1];
213 if(pi.isDone()==false) {
214 pi.currentSegment(f);
215 cx = f[0];
216 cy = f[1];
217 }
218 }
219 float dx = (float) (cx - x);
220 float dy = (float) (cy - y);
221 float dist = dx * dx + dy * dy;
222 if (dist < minDistance) {
223 minDistance = dist;
224 closest = e;
225 }
226 }
227 }
228 break;
229 } catch(ConcurrentModificationException cme) {}
230 }
231 return closest;
232 }
233 }