Line | Hits | Source |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2005, 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 | * Created on Jul 11, 2005 | |
9 | */ | |
10 | ||
11 | package edu.uci.ics.jung.visualization.transform.shape; | |
12 | ||
13 | import java.awt.Component; | |
14 | import java.awt.Dimension; | |
15 | import java.awt.Font; | |
16 | import java.awt.Graphics; | |
17 | import java.awt.Graphics2D; | |
18 | import java.awt.Paint; | |
19 | import java.awt.Rectangle; | |
20 | import java.awt.Shape; | |
21 | import java.awt.Stroke; | |
22 | import java.awt.geom.AffineTransform; | |
23 | import java.awt.geom.Ellipse2D; | |
24 | import java.awt.geom.GeneralPath; | |
25 | import java.awt.geom.Point2D; | |
26 | import java.awt.geom.Rectangle2D; | |
27 | ||
28 | import javax.swing.Icon; | |
29 | ||
30 | import edu.uci.ics.jung.graph.Edge; | |
31 | import edu.uci.ics.jung.graph.UndirectedEdge; | |
32 | import edu.uci.ics.jung.graph.Vertex; | |
33 | import edu.uci.ics.jung.graph.decorators.VertexIconFunction; | |
34 | import edu.uci.ics.jung.utils.Pair; | |
35 | import edu.uci.ics.jung.visualization.PluggableRenderer; | |
36 | import edu.uci.ics.jung.visualization.transform.HyperbolicTransformer; | |
37 | import edu.uci.ics.jung.visualization.transform.MutableAffineTransformer; | |
38 | import edu.uci.ics.jung.visualization.transform.Transformer; | |
39 | ||
40 | /** | |
41 | * a subclass to apply a TransformingGraphics to certain operations | |
42 | * @author Tom Nelson - RABA Technologies | |
43 | * | |
44 | * | |
45 | */ | |
46 | public class TransformingPluggableRenderer extends PluggableRendererDecorator { | |
47 | ||
48 | /** | |
49 | * the transformer | |
50 | */ | |
51 | Transformer transformer; | |
52 | ||
53 | /** | |
54 | * the Graphics wrapper that uses the transformer | |
55 | */ | |
56 | TransformingGraphics tg2d; | |
57 | ||
58 | /** | |
59 | * create an instance | |
60 | * | |
61 | */ | |
62 | public TransformingPluggableRenderer(PluggableRenderer delegate) { | |
63 | 0 | super(delegate); |
64 | 0 | this.transformer = new MutableAffineTransformer(); |
65 | 0 | this.tg2d = new TransformingGraphics(transformer); |
66 | 0 | } |
67 | ||
68 | /** | |
69 | * @return Returns the transformer. | |
70 | */ | |
71 | public Transformer getTransformer() { | |
72 | 0 | return transformer; |
73 | } | |
74 | ||
75 | /** | |
76 | * @param transformer The transformer to set. | |
77 | */ | |
78 | public void setTransformer(Transformer transformer) { | |
79 | 0 | this.transformer = transformer; |
80 | 0 | this.tg2d.setTransformer(transformer); |
81 | 0 | } |
82 | ||
83 | /** (non-Javadoc) | |
84 | * override to wrap the passed Graphics2D in my TransformingGraphics, | |
85 | * then call overloaded drawSimpleEdge | |
86 | */ | |
87 | protected void drawSimpleEdge(Graphics2D g, Edge e, int x1, int y1, int x2, int y2) { | |
88 | 0 | this.tg2d.setDelegate(g); |
89 | 0 | drawSimpleEdge(tg2d, e, x1, y1, x2, y2); |
90 | 0 | } |
91 | /** | |
92 | * overloaded to use TransformingGraphics | |
93 | * @param g | |
94 | * @param e | |
95 | * @param x1 | |
96 | * @param y1 | |
97 | * @param x2 | |
98 | * @param y2 | |
99 | */ | |
100 | protected void drawSimpleEdge(TransformingGraphics g, Edge e, int x1, int y1, int x2, int y2) { | |
101 | 0 | float flatness = 0; |
102 | ||
103 | 0 | if(transformer instanceof HyperbolicTransformer) { |
104 | 0 | HyperbolicTransformer ht = (HyperbolicTransformer)transformer; |
105 | 0 | Ellipse2D hyperEllipse = ht.getEllipse(); |
106 | 0 | if(hyperEllipse.contains(x1,y1) || hyperEllipse.contains(x2,y2)) { |
107 | 0 | flatness = .05f; |
108 | } | |
109 | } | |
110 | 0 | Pair endpoints = e.getEndpoints(); |
111 | 0 | Vertex v1 = (Vertex)endpoints.getFirst(); |
112 | 0 | Vertex v2 = (Vertex)endpoints.getSecond(); |
113 | 0 | boolean isLoop = v1.equals(v2); |
114 | 0 | Shape s2 = getVertexShapeFunction().getShape(v2); |
115 | 0 | Shape edgeShape = getEdgeShapeFunction().getShape(e); |
116 | ||
117 | 0 | boolean edgeHit = true; |
118 | 0 | boolean arrowHit = true; |
119 | 0 | Rectangle deviceRectangle = null; |
120 | 0 | if(getScreenDevice() != null) { |
121 | 0 | Dimension d = getScreenDevice().getSize(); |
122 | 0 | deviceRectangle = new Rectangle(0,0,d.width,d.height); |
123 | } | |
124 | ||
125 | 0 | AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); |
126 | ||
127 | 0 | if(isLoop) { |
128 | // this is a self-loop. scale it is larger than the vertex | |
129 | // it decorates and translate it so that its nadir is | |
130 | // at the center of the vertex. | |
131 | 0 | Rectangle2D s2Bounds = s2.getBounds2D(); |
132 | 0 | xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight()); |
133 | 0 | xform.translate(0, -edgeShape.getBounds2D().getWidth()/2); |
134 | } else { | |
135 | // this is a normal edge. Rotate it to the angle between | |
136 | // vertex endpoints, then scale it to the distance between | |
137 | // the vertices | |
138 | 0 | float dx = x2-x1; |
139 | 0 | float dy = y2-y1; |
140 | 0 | float thetaRadians = (float) Math.atan2(dy, dx); |
141 | 0 | xform.rotate(thetaRadians); |
142 | 0 | float dist = (float) Math.sqrt(dx*dx + dy*dy); |
143 | 0 | xform.scale(dist, 1.0); |
144 | } | |
145 | ||
146 | 0 | edgeShape = xform.createTransformedShape(edgeShape); |
147 | ||
148 | 0 | edgeHit = g.hit(deviceRectangle, edgeShape, true); |
149 | ||
150 | 0 | if(edgeHit == true) { |
151 | ||
152 | 0 | Paint oldPaint = g.getPaint(); |
153 | ||
154 | // get Paints for filling and drawing | |
155 | // (filling is done first so that drawing and label use same Paint) | |
156 | 0 | Paint fill_paint = getEdgePaintFunction().getFillPaint(e); |
157 | 0 | if (fill_paint != null) |
158 | { | |
159 | 0 | g.setPaint(fill_paint); |
160 | 0 | g.fill(edgeShape, flatness); |
161 | } | |
162 | 0 | Paint draw_paint = getEdgePaintFunction().getDrawPaint(e); |
163 | 0 | if (draw_paint != null) |
164 | { | |
165 | 0 | g.setPaint(draw_paint); |
166 | 0 | g.draw(edgeShape, flatness); |
167 | } | |
168 | ||
169 | 0 | float scalex = (float)g.getTransform().getScaleX(); |
170 | 0 | float scaley = (float)g.getTransform().getScaleY(); |
171 | // see if arrows are too small to bother drawing | |
172 | 0 | if(scalex < .3 || scaley < .3) return; |
173 | ||
174 | 0 | if (getEdgeArrowPredicate().evaluate(e)) { |
175 | ||
176 | 0 | Shape destVertexShape = |
177 | getVertexShapeFunction().getShape((Vertex)e.getEndpoints().getSecond()); | |
178 | 0 | AffineTransform xf = AffineTransform.getTranslateInstance(x2, y2); |
179 | 0 | destVertexShape = xf.createTransformedShape(destVertexShape); |
180 | ||
181 | 0 | arrowHit = g.hit(deviceRectangle, destVertexShape, true); |
182 | ||
183 | 0 | if(arrowHit) { |
184 | ||
185 | 0 | AffineTransform at = |
186 | getArrowTransform((GeneralPath)edgeShape, destVertexShape); | |
187 | 0 | if(at == null) return; |
188 | 0 | Shape arrow = getEdgeArrowFunction().getArrow(e); |
189 | 0 | arrow = at.createTransformedShape(arrow); |
190 | // note that arrows implicitly use the edge's draw paint | |
191 | 0 | g.fill(arrow); |
192 | 0 | if (e instanceof UndirectedEdge) { |
193 | 0 | Shape vertexShape = |
194 | getVertexShapeFunction().getShape((Vertex)e.getEndpoints().getFirst()); | |
195 | 0 | xf = AffineTransform.getTranslateInstance(x1, y1); |
196 | 0 | vertexShape = xf.createTransformedShape(vertexShape); |
197 | ||
198 | 0 | arrowHit = g.hit(deviceRectangle, vertexShape, true); |
199 | 0 | if(arrowHit) { |
200 | 0 | at = getReverseArrowTransform((GeneralPath)edgeShape, vertexShape, !isLoop); |
201 | 0 | if(at == null) return; |
202 | 0 | arrow = getEdgeArrowFunction().getArrow(e); |
203 | 0 | arrow = at.createTransformedShape(arrow); |
204 | 0 | g.fill(arrow); |
205 | } | |
206 | } | |
207 | } | |
208 | } | |
209 | // use existing paint for text if no draw paint specified | |
210 | 0 | if (draw_paint == null) |
211 | 0 | g.setPaint(oldPaint); |
212 | 0 | String label = getEdgeStringer().getLabel(e); |
213 | 0 | if (label != null) { |
214 | 0 | labelEdge(g, e, label, x1, x2, y1, y2); |
215 | } | |
216 | ||
217 | ||
218 | // restore old paint | |
219 | 0 | g.setPaint(oldPaint); |
220 | } | |
221 | 0 | } |
222 | ||
223 | ||
224 | /** | |
225 | * overridden to wrap passed Graphics2D in my TransformingGraphics, then call | |
226 | * overloaded labelEdge | |
227 | */ | |
228 | protected void labelEdge(Graphics2D g2d, Edge e, String label, int x1, int x2, int y1, int y2) { | |
229 | 0 | tg2d.setDelegate(g2d); |
230 | 0 | labelEdge(tg2d, e, label, x1, x2, y1, y2); |
231 | 0 | } |
232 | /** | |
233 | * overloaded to use TransformingGraphics | |
234 | * @param g2d | |
235 | * @param e | |
236 | * @param label | |
237 | * @param x1 | |
238 | * @param x2 | |
239 | * @param y1 | |
240 | * @param y2 | |
241 | */ | |
242 | protected void labelEdge(TransformingGraphics g2d, Edge e, String label, int x1, int x2, int y1, int y2) | |
243 | { | |
244 | 0 | Point2D p = g2d.getTransformer().transform(new Point2D.Float(x1, y1)); |
245 | 0 | Point2D q = g2d.getTransformer().transform(new Point2D.Float(x2, y2)); |
246 | 0 | x1 = (int)p.getX(); |
247 | 0 | y1 = (int)p.getY(); |
248 | 0 | x2 = (int)q.getX(); |
249 | 0 | y2 = (int)q.getY(); |
250 | ||
251 | 0 | int distX = x2 - x1; |
252 | 0 | int distY = y2 - y1; |
253 | 0 | double totalLength = Math.sqrt(distX * distX + distY * distY); |
254 | ||
255 | 0 | double closeness = getEdgeLabelClosenessFunction().getNumber(e).doubleValue(); |
256 | ||
257 | 0 | int posX = (int) (x1 + (closeness) * distX); |
258 | 0 | int posY = (int) (y1 + (closeness) * distY); |
259 | ||
260 | 0 | int xDisplacement = (int) (PluggableRenderer.LABEL_OFFSET * (distY / totalLength)); |
261 | 0 | int yDisplacement = (int) (PluggableRenderer.LABEL_OFFSET * (-distX / totalLength)); |
262 | ||
263 | 0 | Component component = prepareRenderer(getGraphLabelRenderer(), label, isPicked(e), e); |
264 | 0 | Dimension d = component.getPreferredSize(); |
265 | ||
266 | 0 | Font font = getEdgeFontFunction().getFont(e); |
267 | 0 | if(font != null) |
268 | 0 | component.setFont(font); |
269 | ||
270 | 0 | Shape edgeShape = getEdgeShapeFunction().getShape(e); |
271 | ||
272 | 0 | double parallelOffset = 1; |
273 | 0 | Integer parallelCount = (Integer)e.getUserDatum("parallelCount"); |
274 | 0 | if(parallelCount != null) { |
275 | 0 | parallelOffset += parallelCount.intValue(); |
276 | } | |
277 | ||
278 | 0 | if(edgeShape instanceof Ellipse2D) { |
279 | 0 | parallelOffset += edgeShape.getBounds().getHeight(); |
280 | 0 | parallelOffset = -parallelOffset; |
281 | } | |
282 | ||
283 | 0 | parallelOffset *= d.height; |
284 | ||
285 | 0 | AffineTransform old = g2d.getTransform(); |
286 | 0 | AffineTransform xform = new AffineTransform(old); |
287 | 0 | xform.translate(posX+xDisplacement, posY+yDisplacement); |
288 | 0 | if(getGraphLabelRenderer().isRotateEdgeLabels()) { |
289 | // float thetaRadians = (float) Math.atan2(dy, dx); | |
290 | 0 | double dx = x2 - x1; |
291 | 0 | double dy = y2 - y1; |
292 | 0 | double theta = Math.atan2(dy, dx); |
293 | 0 | if(dx < 0) { |
294 | 0 | theta += Math.PI; |
295 | 0 | parallelOffset = -parallelOffset; |
296 | } | |
297 | 0 | xform.rotate(theta); |
298 | } | |
299 | ||
300 | 0 | xform.translate(-d.width/2, -(d.height/2-parallelOffset)); |
301 | 0 | g2d.setTransform(xform); |
302 | 0 | getRendererPane().paintComponent(g2d.getDelegate(), component, getScreenDevice(), |
303 | 0, 0, | |
304 | d.width, d.height, true); | |
305 | 0 | g2d.setTransform(old); |
306 | 0 | } |
307 | ||
308 | /** | |
309 | * overridden to wrap passed Graphics in my TransformingGraphics, then | |
310 | * call overloaded labelVertex | |
311 | */ | |
312 | protected void labelVertex(Graphics g, Vertex v, String label, int x, int y) { | |
313 | 0 | tg2d.setDelegate((Graphics2D)g); |
314 | 0 | labelVertex(tg2d, v, label, x, y); |
315 | 0 | } |
316 | /** | |
317 | * overloaded to use TransformingGraphics | |
318 | * @param g | |
319 | * @param v | |
320 | * @param label | |
321 | * @param x | |
322 | * @param y | |
323 | */ | |
324 | protected void labelVertex(TransformingGraphics g, Vertex v, String label, int x, int y) { | |
325 | 0 | Component component = prepareRenderer(getGraphLabelRenderer(), label, isPicked(v), v); |
326 | 0 | Font font = getVertexFontFunction().getFont(v); |
327 | 0 | if (font != null) |
328 | 0 | component.setFont(font); |
329 | ||
330 | 0 | Dimension d = component.getPreferredSize(); |
331 | ||
332 | 0 | Point2D p = g.getTransformer().transform(new Point2D.Float(x, y)); |
333 | 0 | x = (int)p.getX(); |
334 | 0 | y = (int)p.getY(); |
335 | ||
336 | int h_offset; | |
337 | int v_offset; | |
338 | 0 | if (getVertexLabelCentering()) |
339 | { | |
340 | 0 | h_offset = -d.width / 2; |
341 | 0 | v_offset = -d.height / 2; |
342 | ||
343 | } | |
344 | else | |
345 | { | |
346 | 0 | Rectangle2D bounds = getVertexShapeFunction().getShape(v).getBounds2D(); |
347 | 0 | h_offset = (int)(bounds.getWidth() / 2) + 5; |
348 | 0 | v_offset = (int)(bounds.getHeight() / 2) + 5 -d.height; |
349 | } | |
350 | ||
351 | 0 | getRendererPane().paintComponent(g.getDelegate(), component, getScreenDevice(), x+h_offset, y+v_offset, |
352 | d.width, d.height, true); | |
353 | ||
354 | 0 | } |
355 | ||
356 | /** | |
357 | * overridded to wrap passed Graphics in TransformingGraphics then call | |
358 | * overloaded paintEdge | |
359 | */ | |
360 | public void paintEdge(Graphics g, Edge e, int x1, int y1, int x2, int y2) { | |
361 | 0 | this.tg2d.setDelegate((Graphics2D)g); |
362 | 0 | paintEdge(tg2d, e, x1, y1, x2, y2); |
363 | 0 | } |
364 | /** | |
365 | * overloaded to use TransformingGraphics | |
366 | * @param g2d | |
367 | * @param e | |
368 | * @param x1 | |
369 | * @param y1 | |
370 | * @param x2 | |
371 | * @param y2 | |
372 | */ | |
373 | public void paintEdge(TransformingGraphics g2d, Edge e, int x1, int y1, int x2, int y2) { | |
374 | 0 | if (!getEdgeIncludePredicate().evaluate(e)) |
375 | 0 | return; |
376 | ||
377 | // don't draw edge if either incident vertex is not drawn | |
378 | 0 | Pair endpoints = e.getEndpoints(); |
379 | 0 | Vertex v1 = (Vertex)endpoints.getFirst(); |
380 | 0 | Vertex v2 = (Vertex)endpoints.getSecond(); |
381 | 0 | if (!getVertexIncludePredicate().evaluate(v1) || |
382 | !getVertexIncludePredicate().evaluate(v2)) | |
383 | 0 | return; |
384 | ||
385 | 0 | Stroke new_stroke = getEdgeStrokeFunction().getStroke(e); |
386 | 0 | Stroke old_stroke = g2d.getStroke(); |
387 | 0 | if (new_stroke != null) |
388 | 0 | g2d.setStroke(new_stroke); |
389 | ||
390 | 0 | drawSimpleEdge(g2d, e, x1, y1, x2, y2); |
391 | ||
392 | // restore paint and stroke | |
393 | 0 | if (new_stroke != null) |
394 | 0 | g2d.setStroke(old_stroke); |
395 | ||
396 | 0 | } |
397 | ||
398 | /** | |
399 | * overridden to wrap passed Graphics in TransformingGraphics then | |
400 | * call overloaded paintVertex | |
401 | */ | |
402 | public void paintVertex(Graphics g, Vertex v, int x, int y) { | |
403 | 0 | tg2d.setDelegate((Graphics2D)g); |
404 | 0 | paintVertex(tg2d, v, x, y); |
405 | 0 | } |
406 | /** | |
407 | * overloaded to use TransformingGraphics | |
408 | * @param g2d | |
409 | * @param v | |
410 | * @param x | |
411 | * @param y | |
412 | */ | |
413 | public void paintVertex(TransformingGraphics g2d, Vertex v, int x, int y) { | |
414 | 0 | if (!getVertexIncludePredicate().evaluate(v)) |
415 | 0 | return; |
416 | ||
417 | 0 | boolean vertexHit = true; |
418 | 0 | Rectangle deviceRectangle = null; |
419 | 0 | if(getScreenDevice() != null) { |
420 | 0 | Dimension d = getScreenDevice().getSize(); |
421 | 0 | deviceRectangle = new Rectangle(0,0,d.width,d.height); |
422 | } | |
423 | ||
424 | 0 | Stroke old_stroke = g2d.getStroke(); |
425 | 0 | Stroke new_stroke = getVertexStrokeFunction().getStroke(v); |
426 | 0 | if (new_stroke != null) { |
427 | 0 | g2d.setStroke(new_stroke); |
428 | } | |
429 | // get the shape to be rendered | |
430 | 0 | Shape s = getVertexShapeFunction().getShape(v); |
431 | ||
432 | // create a transform that translates to the location of | |
433 | // the vertex to be rendered | |
434 | 0 | AffineTransform xform = AffineTransform.getTranslateInstance(x,y); |
435 | // transform the vertex shape with xtransform | |
436 | 0 | s = xform.createTransformedShape(s); |
437 | ||
438 | ||
439 | 0 | vertexHit = g2d.hit(deviceRectangle, s, true); |
440 | ||
441 | 0 | if (vertexHit) { |
442 | 0 | VertexIconFunction imager = getVertexIconFunction(); |
443 | ||
444 | 0 | Icon icon = null; |
445 | 0 | if (imager != null && (icon = imager.getIcon(v)) != null) { |
446 | 0 | paintIconForVertex(g2d, icon, x, y); |
447 | } else { | |
448 | 0 | paintShapeForVertex(g2d, v, s); |
449 | } | |
450 | ||
451 | 0 | if (new_stroke != null) { |
452 | 0 | g2d.setStroke(old_stroke); |
453 | } | |
454 | 0 | String label = getVertexStringer().getLabel(v); |
455 | 0 | if (label != null) { |
456 | 0 | labelVertex(g2d, v, label, x, y); |
457 | } | |
458 | } | |
459 | 0 | } |
460 | ||
461 | protected void paintShapeForVertex(TransformingGraphics g2d, Vertex v, Shape shape) { | |
462 | 0 | Paint oldPaint = g2d.getPaint(); |
463 | 0 | Paint fillPaint = getVertexPaintFunction().getFillPaint(v); |
464 | 0 | if(fillPaint != null) { |
465 | 0 | g2d.setPaint(fillPaint); |
466 | 0 | g2d.fill(shape); |
467 | 0 | g2d.setPaint(oldPaint); |
468 | } | |
469 | 0 | Paint drawPaint = getVertexPaintFunction().getDrawPaint(v); |
470 | 0 | if(drawPaint != null) { |
471 | 0 | g2d.setPaint(drawPaint); |
472 | 0 | g2d.draw(shape); |
473 | 0 | g2d.setPaint(oldPaint); |
474 | } | |
475 | 0 | } |
476 | ||
477 | public void paintIconForVertex(TransformingGraphics g, Icon icon, int x, int y) { | |
478 | 0 | if(icon != null) { |
479 | 0 | int xLoc = x - icon.getIconWidth()/2; |
480 | 0 | int yLoc = y - icon.getIconHeight()/2; |
481 | 0 | icon.paintIcon(getScreenDevice(), g.getDelegate(), xLoc, yLoc); |
482 | } | |
483 | 0 | } |
484 | } |
this report was generated by version 1.0.5 of jcoverage. |
copyright © 2003, jcoverage ltd. all rights reserved. |