1
2
3
4
5
6
7
8
9
10 package edu.uci.ics.jung.visualization.decorators;
11
12 import static com.google.common.base.Preconditions.checkNotNull;
13
14 import java.awt.Shape;
15 import java.awt.geom.AffineTransform;
16 import java.awt.geom.CubicCurve2D;
17 import java.awt.geom.Ellipse2D;
18 import java.awt.geom.GeneralPath;
19 import java.awt.geom.Line2D;
20 import java.awt.geom.QuadCurve2D;
21 import java.awt.geom.Rectangle2D;
22 import java.awt.geom.RectangularShape;
23
24 import com.google.common.base.Function;
25
26 import edu.uci.ics.jung.graph.Graph;
27 import edu.uci.ics.jung.graph.util.EdgeIndexFunction;
28 import edu.uci.ics.jung.graph.util.EdgeType;
29 import edu.uci.ics.jung.graph.util.Pair;
30 import edu.uci.ics.jung.visualization.util.ArrowFactory;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 public class EdgeShape<V,E> {
46 private static final Line2D LINE = new Line2D.Float(0.0f, 0.0f, 1.0f, 0.0f);
47 private static final GeneralPath BENT_LINE = new GeneralPath();
48 private static final QuadCurve2D QUAD_CURVE = new QuadCurve2D.Float();
49 private static final CubicCurve2D CUBIC_CURVE = new CubicCurve2D.Float();
50 private static final Ellipse2D ELLIPSE = new Ellipse2D.Float(-.5f, -.5f, 1, 1);
51 private static Rectangle2D BOX = new Rectangle2D.Float();
52
53 private static GeneralPath triangle;
54 private static GeneralPath bowtie;
55
56 protected final Graph<V, E> graph;
57
58
59
60
61
62 protected final Loop loop;
63
64
65
66
67
68 protected final SimpleLoop simpleLoop;
69
70 protected final Box box;
71
72 public EdgeShape(Graph<V, E> g) {
73 this.graph = g;
74 this.box = new Box();
75 this.loop = new Loop();
76 this.simpleLoop = new SimpleLoop();
77 }
78
79 private Shape getLoopOrNull(E e) {
80 return getLoopOrNull(e, loop);
81 }
82
83 private Shape getLoopOrNull(E e, Function<? super E, Shape> loop) {
84 Pair<V> endpoints = graph.getEndpoints(e);
85 checkNotNull(endpoints);
86 boolean isLoop = endpoints.getFirst().equals(endpoints.getSecond());
87 if (isLoop) {
88 return loop.apply(e);
89 }
90 return null;
91 }
92
93 public static <V, E> EdgeShape<V, E>.Line line(Graph<V, E> graph) {
94 return new EdgeShape<V, E>(graph).new Line();
95 }
96
97 public static <V, E> EdgeShape<V, E>.QuadCurve quadCurve(Graph<V, E> graph) {
98 return new EdgeShape<V, E>(graph).new QuadCurve();
99 }
100
101 public static <V, E> EdgeShape<V, E>.QuadCurve cubicCurve(Graph<V, E> graph) {
102 return new EdgeShape<V, E>(graph).new QuadCurve();
103 }
104
105 public static <V, E> EdgeShape<V, E>.Orthogonal orthogonal(Graph<V, E> graph) {
106 return new EdgeShape<V, E>(graph).new Orthogonal();
107 }
108
109 public static <V, E> EdgeShape<V, E>.Wedge wedge(Graph<V, E> graph, int width) {
110 return new EdgeShape<V, E>(graph).new Wedge(width);
111 }
112
113
114
115
116
117 public class Line implements Function<E, Shape> {
118
119
120
121
122
123 public Shape apply(E e) {
124 Shape loop = getLoopOrNull(e);
125 return loop == null
126 ? LINE
127 : loop;
128 }
129 }
130
131 private int getIndex(E e, EdgeIndexFunction<V, E> edgeIndexFunction) {
132 return edgeIndexFunction == null
133 ? 1
134 : edgeIndexFunction.getIndex(graph, e);
135 }
136
137
138
139
140
141 public class BentLine extends ParallelEdgeShapeTransformer<V,E> {
142 public void setEdgeIndexFunction(EdgeIndexFunction<V,E> edgeIndexFunction) {
143 this.edgeIndexFunction = edgeIndexFunction;
144 loop.setEdgeIndexFunction(edgeIndexFunction);
145 }
146
147
148
149
150
151
152 public Shape apply(E e) {
153 Shape edgeShape = getLoopOrNull(e);
154 if (edgeShape != null) {
155 return edgeShape;
156 }
157
158 int index = getIndex(e, edgeIndexFunction);
159 float controlY = control_offset_increment + control_offset_increment * index;
160 BENT_LINE.reset();
161 BENT_LINE.moveTo(0.0f, 0.0f);
162 BENT_LINE.lineTo(0.5f, controlY);
163 BENT_LINE.lineTo(1.0f, 1.0f);
164 return BENT_LINE;
165 }
166
167 }
168
169
170
171
172
173 public class QuadCurve extends ParallelEdgeShapeTransformer<V,E> {
174 @Override
175 public void setEdgeIndexFunction(EdgeIndexFunction<V,E> parallelEdgeIndexFunction) {
176 this.edgeIndexFunction = parallelEdgeIndexFunction;
177 loop.setEdgeIndexFunction(parallelEdgeIndexFunction);
178 }
179
180
181
182
183
184
185 public Shape apply(E e) {
186 Shape edgeShape = getLoopOrNull(e);
187 if (edgeShape != null) {
188 return edgeShape;
189 }
190
191 int index = getIndex(e, edgeIndexFunction);
192
193 float controlY = control_offset_increment +
194 control_offset_increment * index;
195 QUAD_CURVE.setCurve(0.0f, 0.0f, 0.5f, controlY, 1.0f, 0.0f);
196 return QUAD_CURVE;
197 }
198 }
199
200
201
202
203
204
205
206 public class CubicCurve extends ParallelEdgeShapeTransformer<V,E> {
207 public void setEdgeIndexFunction(EdgeIndexFunction<V,E> edgeIndexFunction) {
208 this.edgeIndexFunction = edgeIndexFunction;
209 loop.setEdgeIndexFunction(edgeIndexFunction);
210 }
211
212
213
214
215
216
217 public Shape apply(E e) {
218 Shape edgeShape = getLoopOrNull(e);
219 if (edgeShape != null) {
220 return edgeShape;
221 }
222
223 int index = getIndex(e, edgeIndexFunction);
224
225 float controlY = control_offset_increment
226 + control_offset_increment * index;
227 CUBIC_CURVE.setCurve(0.0f, 0.0f, 0.33f, 2 * controlY, .66f, -controlY,
228 1.0f, 0.0f);
229 return CUBIC_CURVE;
230 }
231 }
232
233
234
235
236
237
238
239 public class SimpleLoop extends ParallelEdgeShapeTransformer<V,E> {
240 public Shape apply(E e) {
241 return ELLIPSE;
242 }
243 }
244
245 private Shape buildFrame(RectangularShape shape, int index) {
246 float x = -.5f;
247 float y = -.5f;
248 float diam = 1.f;
249 diam += diam * index/2;
250 x += x * index/2;
251 y += y * index/2;
252
253 shape.setFrame(x, y, diam, diam);
254
255 return shape;
256 }
257
258
259
260
261
262 public class Loop extends ParallelEdgeShapeTransformer<V,E> {
263 public Shape apply(E e) {
264 return buildFrame(ELLIPSE, getIndex(e, edgeIndexFunction));
265 }
266 }
267
268
269
270
271
272
273
274 public class Wedge extends ParallelEdgeShapeTransformer<V,E> {
275 public Wedge(int width) {
276 triangle = ArrowFactory.getWedgeArrow(width, 1);
277 triangle.transform(AffineTransform.getTranslateInstance(1,0));
278 bowtie = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
279 bowtie.moveTo(0, width/2);
280 bowtie.lineTo(1, -width/2);
281 bowtie.lineTo(1, width/2);
282 bowtie.lineTo(0, -width/2);
283 bowtie.closePath();
284 }
285
286 public Shape apply(E e) {
287 Shape edgeShape = getLoopOrNull(e);
288 if (edgeShape != null) {
289 return edgeShape;
290 }
291 return (graph.getEdgeType(e) == EdgeType.DIRECTED)
292 ? triangle
293 : bowtie;
294 }
295 }
296
297
298
299
300
301 public class Box extends ParallelEdgeShapeTransformer<V,E> {
302 public Shape apply(E e) {
303 return buildFrame(BOX, getIndex(e, edgeIndexFunction));
304 }
305 }
306
307
308
309
310
311 public class Orthogonal extends ParallelEdgeShapeTransformer<V,E> {
312 public Shape apply(E e) {
313 Shape loop = getLoopOrNull(e, box);
314 return loop == null
315 ? LINE
316 : loop;
317 }
318 }
319 }
320
321