Line | Hits | Source |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2003, the JUNG Project and the Regents of the University of | |
3 | * California All rights reserved. | |
4 | * | |
5 | * This software is open-source under the BSD license; see either "license.txt" | |
6 | * or http://jung.sourceforge.net/license.txt for a description. | |
7 | */ | |
8 | package edu.uci.ics.jung.visualization.graphdraw; | |
9 | ||
10 | import java.awt.BasicStroke; | |
11 | import java.awt.Color; | |
12 | import java.awt.Graphics; | |
13 | import java.awt.Graphics2D; | |
14 | import java.awt.Stroke; | |
15 | ||
16 | import edu.uci.ics.jung.graph.DirectedEdge; | |
17 | import edu.uci.ics.jung.graph.Edge; | |
18 | import edu.uci.ics.jung.graph.UndirectedEdge; | |
19 | import edu.uci.ics.jung.graph.Vertex; | |
20 | import edu.uci.ics.jung.graph.decorators.EdgeColorFunction; | |
21 | import edu.uci.ics.jung.graph.decorators.EdgeStringer; | |
22 | import edu.uci.ics.jung.graph.decorators.EdgeThicknessFunction; | |
23 | import edu.uci.ics.jung.graph.decorators.StringLabeller; | |
24 | import edu.uci.ics.jung.graph.decorators.VertexColorFunction; | |
25 | import edu.uci.ics.jung.graph.predicates.EdgePredicate; | |
26 | import edu.uci.ics.jung.graph.predicates.SelfLoopEdgePredicate; | |
27 | import edu.uci.ics.jung.visualization.AbstractRenderer; | |
28 | import edu.uci.ics.jung.visualization.contrib.Arrow; | |
29 | ||
30 | /** | |
31 | * A renderer with all sorts of buttons to press and dials to turn. In general, | |
32 | * if a function is available to get an answer to questions of color. Otherwise, | |
33 | * the set fields are used. | |
34 | * <p> | |
35 | * The default is to paint vertices with Black foreground text and Red | |
36 | * backgrounds. Picked vertices are orange. [Whether a vertex is Picked is | |
37 | * resolved with <tt>v.getUserDatum(_VisualizationViewer.VIS_KEY);</tt>] | |
38 | * | |
39 | * <p> | |
40 | * Note that setting a stroke width other than 1 is likely to slow down the | |
41 | * visualization noticably, as is using transparency. | |
42 | * | |
43 | * @deprecated Replaced by PluggableRenderer. | |
44 | * @author danyelf | |
45 | */ | |
46 | public class SettableRenderer extends AbstractRenderer { | |
47 | ||
48 | 0 | protected Color vertexFGColor = Color.BLACK; |
49 | ||
50 | 0 | protected Color vertexPickedColor = Color.ORANGE; |
51 | ||
52 | 0 | protected Color vertexBGColor = Color.RED; |
53 | ||
54 | protected VertexColorFunction vertexColorFunction; | |
55 | ||
56 | protected EdgeThicknessFunction edgeThicknessFunction; | |
57 | ||
58 | protected int edgeThickness; | |
59 | ||
60 | 0 | protected Color edgeColor = Color.BLACK; |
61 | ||
62 | protected EdgeColorFunction edgeColorFunction; | |
63 | ||
64 | protected StringLabeller mStringLabeller; | |
65 | ||
66 | 0 | protected boolean mShouldDrawSelfLoops = false; |
67 | ||
68 | 0 | protected boolean mDrawLightBoxes = true; |
69 | ||
70 | 0 | protected boolean mShouldDrawArrowsUndirected = false; |
71 | ||
72 | 0 | protected boolean mShouldDrawArrowsDirected = true; |
73 | ||
74 | protected Arrow mArrow; | |
75 | ||
76 | protected EdgeStringer mEdgeLabelFunction; | |
77 | ||
78 | protected int mLineHeight; | |
79 | ||
80 | 0 | protected static EdgePredicate self_loop = SelfLoopEdgePredicate.getInstance(); |
81 | ||
82 | /** | |
83 | * This variant simply renders vertices as small empty boxes without labels. | |
84 | */ | |
85 | 0 | public SettableRenderer() { |
86 | 0 | this.mStringLabeller = null; |
87 | 0 | } |
88 | ||
89 | /** | |
90 | * Creates a SettableRenderer that will be drawn in the "heavy" style: a box | |
91 | * around the label | |
92 | * | |
93 | * @param sl | |
94 | */ | |
95 | 0 | public SettableRenderer(StringLabeller sl) { |
96 | 0 | this.mStringLabeller = sl; |
97 | 0 | } |
98 | ||
99 | /** | |
100 | * Creates a SettableRenderer that will label edges with the given EdgeStringer. | |
101 | * (You may want EdgeWeightLabellerStringer, which uses an EdgeWeightLabeller to | |
102 | * label the weights.) | |
103 | * @param sl | |
104 | * @param el | |
105 | */ | |
106 | 0 | public SettableRenderer(StringLabeller sl, EdgeStringer el) { |
107 | 0 | this.mStringLabeller = sl; |
108 | 0 | this.mEdgeLabelFunction = el; |
109 | 0 | } |
110 | ||
111 | /** | |
112 | * Creates a SettableRenderer that will be drawn in the "light" style: a | |
113 | * colored box next to text, instead of text overlaying the box. | |
114 | */ | |
115 | public void setLightDrawing(boolean b) { | |
116 | 0 | this.mDrawLightBoxes = b; |
117 | 0 | } |
118 | ||
119 | public void setStringLabeller(StringLabeller sl) { | |
120 | 0 | this.mStringLabeller = sl; |
121 | 0 | } |
122 | ||
123 | public void setEdgeColor(Color c) { | |
124 | 0 | edgeColor = c; |
125 | 0 | } |
126 | ||
127 | /** | |
128 | * Edges are drawn by calling <tt>EdgeColorFunction</tt> with the edge, to | |
129 | * decide how it is to be drawn. | |
130 | * | |
131 | * @param ecf | |
132 | */ | |
133 | public void setEdgeColorFunction(EdgeColorFunction ecf) { | |
134 | 0 | this.edgeColorFunction = ecf; |
135 | 0 | } |
136 | ||
137 | /** | |
138 | * Forces all edges to draw with this thickness. Sets the edge thickness | |
139 | * function to null. | |
140 | * | |
141 | * @param i | |
142 | */ | |
143 | public void setEdgeThickness(int i) { | |
144 | 0 | this.edgeThicknessFunction = null; |
145 | 0 | this.edgeThickness = i; |
146 | 0 | } |
147 | ||
148 | /** | |
149 | * This version takes a function that dynamically chooses an edge thickness. | |
150 | * | |
151 | * @param etf | |
152 | */ | |
153 | public void setEdgeThicknessFunction(EdgeThicknessFunction etf) { | |
154 | 0 | this.edgeThicknessFunction = etf; |
155 | 0 | this.edgeThickness = 0; |
156 | 0 | } |
157 | ||
158 | /** | |
159 | * Sets whether the system should draw arrows on directed edges. By default, | |
160 | * yes. | |
161 | * | |
162 | * @param b | |
163 | */ | |
164 | public void setShouldDrawDirectedArrows(boolean b) { | |
165 | 0 | this.mShouldDrawArrowsDirected = b; |
166 | 0 | } |
167 | ||
168 | /** | |
169 | * Sets whether the system should draw arrows on directed edges. By default, | |
170 | * yes. | |
171 | * | |
172 | * @param b | |
173 | */ | |
174 | public void setShouldDrawUndirectedArrows(boolean b) { | |
175 | 0 | this.mShouldDrawArrowsUndirected = b; |
176 | 0 | } |
177 | ||
178 | /** | |
179 | * Sets whether the system should draw self-loops. By default, no. | |
180 | * @param b | |
181 | */ | |
182 | public void setShouldDrawSelfLoops( boolean b ) { | |
183 | 0 | this.mShouldDrawSelfLoops = b; |
184 | 0 | } |
185 | ||
186 | /** | |
187 | * Paints the edge in the color specified by the EdgeColorFunction or the | |
188 | * hard-set color, and at the thickness set with an | |
189 | * <tt>EdgeThicknessFunction</tt>. Draws a self-loop if | |
190 | * <tt>shouldDrawSelfLoops()</tt> has been set (by default, no); draws an | |
191 | * arrow on directed edges if <tt>shouldDrawDirectedArrows()</tt> has been | |
192 | * set (by default, yes) and on both ends of undirected edges if | |
193 | * <tt>shouldDrawUndirectedArrows()</tt> has been set (by default, false). | |
194 | * Calls either drawEdge or drawEdgeSimple. Draws one arrow for | |
195 | * self-loops if needed. Note that x1, y1 always correspond to | |
196 | * e.getEndpoints.getFirst() and x2, y2 always correspond to | |
197 | * e.getEndpoints.getSecond() | |
198 | * | |
199 | * @see EdgeThicknessFunction EdgeThicknessFunction | |
200 | * @see EdgeColorFunction EdgeColorFunction | |
201 | */ | |
202 | public void paintEdge(Graphics g, Edge e, int x1, int y1, int x2, int y2) { | |
203 | 0 | Graphics2D g2d = (Graphics2D) g; |
204 | 0 | mLineHeight = g2d.getFontMetrics().getHeight(); |
205 | ||
206 | float edgeWidth; | |
207 | 0 | if (edgeThicknessFunction != null) |
208 | 0 | edgeWidth = edgeThicknessFunction.getEdgeThickness(e); |
209 | else | |
210 | 0 | edgeWidth = edgeThickness; |
211 | ||
212 | 0 | if (edgeColorFunction == null) { |
213 | 0 | g.setColor(edgeColor); |
214 | } else { | |
215 | 0 | g.setColor(edgeColorFunction.getEdgeColor(e)); |
216 | } | |
217 | ||
218 | 0 | if (edgeWidth == 1) |
219 | 0 | drawEdgeSimple(g, e, x1, y1, x2, y2); |
220 | else | |
221 | 0 | drawEdge(edgeWidth, g, e, x1, y1, x2, y2); |
222 | ||
223 | 0 | if (mShouldDrawArrowsDirected && e instanceof DirectedEdge) { |
224 | 0 | drawArrowhead(g2d, e, x1, y1, x2, y2); |
225 | } | |
226 | 0 | if (mShouldDrawArrowsUndirected && e instanceof UndirectedEdge) { |
227 | 0 | drawArrowhead(g2d, e, x1, y1, x2, y2); |
228 | 0 | drawArrowhead(g2d, e, x2, y2, x1, y1); |
229 | } | |
230 | ||
231 | 0 | String label = (mEdgeLabelFunction == null) ? null : mEdgeLabelFunction |
232 | .getLabel(e); | |
233 | 0 | if (label != null) { |
234 | 0 | labelEdge(g2d, e, label, x1, x2, y1, y2); |
235 | } | |
236 | 0 | } |
237 | ||
238 | /** | |
239 | * Labels the edge at the half-way point (if undirected) or three-quarters | |
240 | * if directed or 15 pixels above the vertex if self-loop. | |
241 | * | |
242 | * @param g2d | |
243 | * @param e | |
244 | * @param label | |
245 | * @param x1 | |
246 | * @param x2 | |
247 | * @param y1 | |
248 | * @param y2 | |
249 | */ | |
250 | public void labelEdge(Graphics2D g2d, Edge e, String label, int x1, int x2, | |
251 | int y1, int y2) { | |
252 | ||
253 | 0 | if (self_loop.evaluate(e)) { |
254 | 0 | g2d.drawString(label, x1 - 3 , y1 - 10 - mLineHeight/2); |
255 | 0 | return; |
256 | } | |
257 | ||
258 | 0 | int distX = x2 - x1; |
259 | 0 | int distY = y2 - y1; |
260 | 0 | double totalLength = Math.sqrt(distX * distX + distY * distY); |
261 | ||
262 | //closeness is a double in the range [0,1] that represents | |
263 | //how close to the target vertex the edge weight should be | |
264 | //drawn (0 means "on the source vertex", 1 means "on the target | |
265 | // vertex") | |
266 | //weights of undirected edges should be drawn at 0.5 (in the middle of | |
267 | // the edge) | |
268 | double closeness; | |
269 | 0 | if (e instanceof DirectedEdge) { |
270 | 0 | closeness = 0.73; |
271 | } else { | |
272 | 0 | closeness = 0.5; |
273 | } | |
274 | ||
275 | 0 | int posX = (int) (x1 + (closeness) * distX); |
276 | 0 | int posY = (int) (y1 + (closeness) * distY); |
277 | ||
278 | 0 | int LEN = 10; |
279 | 0 | int xDisplacement = (int) (LEN * (-distY / totalLength)); |
280 | 0 | int yDisplacement = (int) (LEN * (distX / totalLength)); |
281 | 0 | g2d.drawString(label, posX + xDisplacement, posY + yDisplacement + mLineHeight / 2); |
282 | 0 | } |
283 | ||
284 | /** | |
285 | * Draws an arrowhead on this edge in the direction from xsource,ysource to | |
286 | * xend, yend | |
287 | */ | |
288 | protected void drawArrowhead(Graphics2D g2d, Edge e, int xsource, | |
289 | int ysource, int xdest, int ydest) { | |
290 | ||
291 | 0 | if (mArrow == null) { |
292 | 0 | mArrow = new Arrow(Arrow.CLASSIC, 7, 9); |
293 | } | |
294 | ||
295 | 0 | if (mShouldDrawSelfLoops && self_loop.evaluate(e)) { |
296 | 0 | mArrow.drawArrow(g2d, xsource - 10, ysource - 5, xsource, ysource, 15); |
297 | 0 | return; |
298 | } | |
299 | ||
300 | 0 | if (mDrawLightBoxes) { |
301 | 0 | mArrow.drawArrow(g2d, xsource, ysource, xdest, ydest, 12); |
302 | } else { | |
303 | 0 | mArrow.drawArrow(g2d, xsource, ysource, xdest, ydest, 16); |
304 | } | |
305 | ||
306 | 0 | } |
307 | ||
308 | /** | |
309 | * Draws the edge at the given width, then restores the previous stroke. | |
310 | * Calls drawEdgeSimple to accomplish this task. | |
311 | * | |
312 | * @param edgeWidth | |
313 | * the width of the stroke. | |
314 | */ | |
315 | protected void drawEdge(float edgeWidth, Graphics g, Edge e, int x1, | |
316 | int y1, int x2, int y2) { | |
317 | 0 | Graphics2D g2d = (Graphics2D) g; |
318 | ||
319 | 0 | Stroke previous = g2d.getStroke(); |
320 | ||
321 | // if (Math.floor(edgeWidth) == edgeWidth) { | |
322 | // if (strokeTable[(int) edgeWidth] == null) { | |
323 | // Stroke s = new BasicStroke(edgeWidth); | |
324 | // strokeTable[(int) edgeWidth] = s; | |
325 | // g2d.setStroke(strokeTable[(int) edgeWidth]); | |
326 | // } else { | |
327 | 0 | g2d.setStroke(new BasicStroke(edgeWidth)); |
328 | // } | |
329 | // } | |
330 | 0 | drawEdgeSimple(g, e, x1, y1, x2, y2); |
331 | 0 | g2d.setStroke(previous); |
332 | 0 | } |
333 | ||
334 | protected void drawEdgeSimple(Graphics g, Edge e, int x1, int y1, int x2, | |
335 | int y2) { | |
336 | ||
337 | 0 | if (mShouldDrawSelfLoops && self_loop.evaluate(e)) { |
338 | // self-loops | |
339 | 0 | g.drawOval(x1 - 15, y1 - 30, 30, 30); |
340 | } else { | |
341 | 0 | g.drawLine(x1, y1, x2, y2); |
342 | } | |
343 | 0 | } |
344 | ||
345 | /** | |
346 | * Manually sets the color of a Vertex's foreground (i.e. its text). | |
347 | * | |
348 | * @param vertexColor | |
349 | */ | |
350 | public void setVertexForegroundColor(Color vertexColor) { | |
351 | 0 | this.vertexFGColor = vertexColor; |
352 | 0 | } |
353 | ||
354 | /** | |
355 | * Manually sets the color of a picked Vertex's background (i.e. its field). | |
356 | * | |
357 | * @param vertexColor | |
358 | */ | |
359 | public void setVertexPickedColor(Color vertexColor) { | |
360 | 0 | this.vertexPickedColor = vertexColor; |
361 | 0 | } |
362 | ||
363 | /** | |
364 | * Manually sets the color of an unpicked Vertex's background (i.e. its | |
365 | * field). | |
366 | * | |
367 | * @param vertexColor | |
368 | */ | |
369 | public void setVertexBGColor(Color vertexColor) { | |
370 | 0 | this.vertexBGColor = vertexColor; |
371 | 0 | } |
372 | ||
373 | /** | |
374 | * Finds the color of a vertex with a VertexColorFunction. | |
375 | * | |
376 | * @param vcf | |
377 | */ | |
378 | public void setVertexColorFunction(VertexColorFunction vcf) { | |
379 | 0 | this.vertexColorFunction = vcf; |
380 | 0 | } |
381 | ||
382 | /** | |
383 | * Simple label function returns the StringLabeller's notion of v's label. | |
384 | * It may be sometimes useful to override this. | |
385 | * | |
386 | * @param v | |
387 | * a vertex | |
388 | * @return the label on the vertex. | |
389 | */ | |
390 | protected String getLabel(Vertex v) { | |
391 | 0 | if (mStringLabeller == null) return ""; |
392 | 0 | String s = mStringLabeller.getLabel(v); |
393 | 0 | if (s == null) { |
394 | 0 | return "?"; |
395 | } else { | |
396 | 0 | return s; |
397 | } | |
398 | } | |
399 | ||
400 | /** | |
401 | * Paints the vertex, using the settings above (VertexColors, etc). In this | |
402 | * implmenetation, vertices are painted as filled squares with textual | |
403 | * labels over the filled square. | |
404 | */ | |
405 | public void paintVertex(Graphics g, Vertex v, int x, int y) { | |
406 | 0 | String label = getLabel(v); |
407 | 0 | if (mDrawLightBoxes) { |
408 | 0 | paintLightVertex(g, v, x, y, label); |
409 | 0 | return; |
410 | } | |
411 | ||
412 | 0 | Color fg = (vertexColorFunction == null) ? vertexFGColor |
413 | : vertexColorFunction.getForeColor(v); | |
414 | ||
415 | 0 | if (vertexColorFunction == null) { |
416 | 0 | if (isPicked(v)) { |
417 | 0 | g.setColor(vertexPickedColor); |
418 | } else | |
419 | 0 | g.setColor(vertexBGColor); |
420 | } else { | |
421 | 0 | g.setColor(vertexColorFunction.getBackColor(v)); |
422 | } | |
423 | ||
424 | 0 | g.fillRect(x - 8, y - 6, g.getFontMetrics().stringWidth(label) + 8, 16); |
425 | 0 | g.setColor(fg); |
426 | 0 | g.drawString(label, x - 4, y + 6); |
427 | 0 | } |
428 | ||
429 | /** | |
430 | * @param g | |
431 | * @param v | |
432 | * @param x | |
433 | * @param y | |
434 | */ | |
435 | protected void paintLightVertex(Graphics g, Vertex v, int x, int y, | |
436 | String label) { | |
437 | Color bg; | |
438 | 0 | if (vertexColorFunction == null) { |
439 | 0 | if (isPicked(v)) { |
440 | 0 | bg = vertexPickedColor; |
441 | } else | |
442 | 0 | bg = vertexBGColor; |
443 | } else { | |
444 | 0 | bg = vertexColorFunction.getBackColor(v); |
445 | } | |
446 | ||
447 | 0 | Color fg = (vertexColorFunction == null) ? vertexFGColor |
448 | : vertexColorFunction.getForeColor(v); | |
449 | ||
450 | 0 | g.setColor(fg); |
451 | 0 | g.fillRect(x - 7, y - 7, 14, 14); |
452 | 0 | g.setColor(bg); |
453 | 0 | g.fillRect(x - 6, y - 6, 12, 12); |
454 | 0 | if (label.equals("?")) return; |
455 | 0 | g.setColor(fg); |
456 | 0 | g.drawString(label, x + 8, y + 6 ); // + g.getFontMetrics().getHeight()); |
457 | 0 | } |
458 | ||
459 | } |
this report was generated by version 1.0.5 of jcoverage. |
copyright © 2003, jcoverage ltd. all rights reserved. |