KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > prefuse > render > EdgeRenderer


1 package prefuse.render;
2
3 import java.awt.BasicStroke JavaDoc;
4 import java.awt.Graphics2D JavaDoc;
5 import java.awt.Polygon JavaDoc;
6 import java.awt.Shape JavaDoc;
7 import java.awt.geom.AffineTransform JavaDoc;
8 import java.awt.geom.CubicCurve2D JavaDoc;
9 import java.awt.geom.Line2D JavaDoc;
10 import java.awt.geom.Point2D JavaDoc;
11 import java.awt.geom.Rectangle2D JavaDoc;
12
13 import prefuse.Constants;
14 import prefuse.util.ColorLib;
15 import prefuse.util.GraphicsLib;
16 import prefuse.util.StrokeLib;
17 import prefuse.visual.EdgeItem;
18 import prefuse.visual.VisualItem;
19
20
21 /**
22  * <p>Renderer that draws edges as lines connecting nodes. Both
23  * straight and curved lines are supported. Curved lines are drawn using
24  * cubic Bezier curves. Subclasses can override the
25  * {@link #getCurveControlPoints(EdgeItem, Point2D[], double, double, double, double)}
26  * method to provide custom control point assignment for such curves.</p>
27  *
28  * <p>This class also supports arrows for directed edges. See the
29  * {@link #setArrowType(int)} method for more.</p>
30  *
31  * @version 1.0
32  * @author <a HREF="http://jheer.org">jeffrey heer</a>
33  */

34 public class EdgeRenderer extends AbstractShapeRenderer {
35     
36     public static final String JavaDoc EDGE_TYPE = "edgeType";
37     
38     protected static final double HALF_PI = Math.PI / 2;
39     
40     protected Line2D JavaDoc m_line = new Line2D.Float JavaDoc();
41     protected CubicCurve2D JavaDoc m_cubic = new CubicCurve2D.Float JavaDoc();
42
43     protected int m_edgeType = Constants.EDGE_TYPE_LINE;
44     protected int m_xAlign1 = Constants.CENTER;
45     protected int m_yAlign1 = Constants.CENTER;
46     protected int m_xAlign2 = Constants.CENTER;
47     protected int m_yAlign2 = Constants.CENTER;
48     protected double m_width = 1;
49     protected float m_curWidth = 1;
50     protected Point2D JavaDoc m_tmpPoints[] = new Point2D JavaDoc[2];
51     protected Point2D JavaDoc m_ctrlPoints[] = new Point2D JavaDoc[2];
52     protected Point2D JavaDoc m_isctPoints[] = new Point2D JavaDoc[2];
53     
54     // arrow head handling
55
protected int m_edgeArrow = Constants.EDGE_ARROW_FORWARD;
56     protected int m_arrowWidth = 8;
57     protected int m_arrowHeight = 12;
58     protected Polygon JavaDoc m_arrowHead = updateArrowHead(
59                                         m_arrowWidth, m_arrowHeight);
60     protected AffineTransform JavaDoc m_arrowTrans = new AffineTransform JavaDoc();
61     protected Shape JavaDoc m_curArrow;
62
63     /**
64      * Create a new EdgeRenderer.
65      */

66     public EdgeRenderer() {
67         m_tmpPoints[0] = new Point2D.Float JavaDoc();
68         m_tmpPoints[1] = new Point2D.Float JavaDoc();
69         m_ctrlPoints[0] = new Point2D.Float JavaDoc();
70         m_ctrlPoints[1] = new Point2D.Float JavaDoc();
71         m_isctPoints[0] = new Point2D.Float JavaDoc();
72         m_isctPoints[1] = new Point2D.Float JavaDoc();
73     }
74
75     /**
76      * Create a new EdgeRenderer with the given edge type.
77      * @param edgeType the edge type, one of
78      * {@link prefuse.Constants#EDGE_TYPE_LINE} or
79      * {@link prefuse.Constants#EDGE_TYPE_CURVE}.
80      */

81     public EdgeRenderer(int edgeType) {
82         this(edgeType, Constants.EDGE_ARROW_FORWARD);
83     }
84     
85     /**
86      * Create a new EdgeRenderer with the given edge and arrow types.
87      * @param edgeType the edge type, one of
88      * {@link prefuse.Constants#EDGE_TYPE_LINE} or
89      * {@link prefuse.Constants#EDGE_TYPE_CURVE}.
90      * @param arrowType the arrow type, one of
91      * {@link prefuse.Constants#EDGE_ARROW_FORWARD},
92      * {@link prefuse.Constants#EDGE_ARROW_REVERSE}, or
93      * {@link prefuse.Constants#EDGE_ARROW_NONE}.
94      * @see #setArrowType(int)
95      */

96     public EdgeRenderer(int edgeType, int arrowType) {
97         this();
98         setEdgeType(edgeType);
99         setArrowType(arrowType);
100     }
101     
102     /**
103      * @see prefuse.render.AbstractShapeRenderer#getRenderType(prefuse.visual.VisualItem)
104      */

105     public int getRenderType(VisualItem item) {
106         return RENDER_TYPE_DRAW;
107     }
108     
109     /**
110      * @see prefuse.render.AbstractShapeRenderer#getRawShape(prefuse.visual.VisualItem)
111      */

112     protected Shape JavaDoc getRawShape(VisualItem item) {
113         EdgeItem edge = (EdgeItem)item;
114         VisualItem item1 = edge.getSourceItem();
115         VisualItem item2 = edge.getTargetItem();
116         
117         int type = m_edgeType;
118         
119         getAlignedPoint(m_tmpPoints[0], item1.getBounds(),
120                         m_xAlign1, m_yAlign1);
121         getAlignedPoint(m_tmpPoints[1], item2.getBounds(),
122                         m_xAlign2, m_yAlign2);
123         m_curWidth = (float)(m_width * getLineWidth(item));
124         
125         // create the arrow head, if needed
126
EdgeItem e = (EdgeItem)item;
127         if ( e.isDirected() && m_edgeArrow != Constants.EDGE_ARROW_NONE ) {
128             // get starting and ending edge endpoints
129
boolean forward = (m_edgeArrow == Constants.EDGE_ARROW_FORWARD);
130             Point2D JavaDoc start = null, end = null;
131             start = m_tmpPoints[forward?0:1];
132             end = m_tmpPoints[forward?1:0];
133             
134             // compute the intersection with the target bounding box
135
VisualItem dest = forward ? e.getTargetItem() : e.getSourceItem();
136             int i = GraphicsLib.intersectLineRectangle(start, end,
137                     dest.getBounds(), m_isctPoints);
138             if ( i > 0 ) end = m_isctPoints[0];
139             
140             // create the arrow head shape
141
AffineTransform JavaDoc at = getArrowTrans(start, end, m_curWidth);
142             m_curArrow = at.createTransformedShape(m_arrowHead);
143             
144             // update the endpoints for the edge shape
145
// need to bias this by arrow head size
146
Point2D JavaDoc lineEnd = m_tmpPoints[forward?1:0];
147             lineEnd.setLocation(0, -m_arrowHeight);
148             at.transform(lineEnd, lineEnd);
149         } else {
150             m_curArrow = null;
151         }
152         
153         // create the edge shape
154
Shape JavaDoc shape = null;
155         double n1x = m_tmpPoints[0].getX();
156         double n1y = m_tmpPoints[0].getY();
157         double n2x = m_tmpPoints[1].getX();
158         double n2y = m_tmpPoints[1].getY();
159         switch ( type ) {
160             case Constants.EDGE_TYPE_LINE:
161                 m_line.setLine(n1x, n1y, n2x, n2y);
162                 shape = m_line;
163                 break;
164             case Constants.EDGE_TYPE_CURVE:
165                 getCurveControlPoints(edge, m_ctrlPoints,n1x,n1y,n2x,n2y);
166                 m_cubic.setCurve(n1x, n1y,
167                                 m_ctrlPoints[0].getX(), m_ctrlPoints[0].getY(),
168                                 m_ctrlPoints[1].getX(), m_ctrlPoints[1].getY(),
169                                 n2x, n2y);
170                 shape = m_cubic;
171                 break;
172             default:
173                 throw new IllegalStateException JavaDoc("Unknown edge type");
174         }
175         
176         // return the edge shape
177
return shape;
178     }
179
180     /**
181      * @see prefuse.render.Renderer#render(java.awt.Graphics2D, prefuse.visual.VisualItem)
182      */

183     public void render(Graphics2D JavaDoc g, VisualItem item) {
184         // render the edge line
185
super.render(g, item);
186         // render the edge arrow head, if appropriate
187
if ( m_curArrow != null ) {
188             g.setPaint(ColorLib.getColor(item.getFillColor()));
189             g.fill(m_curArrow);
190         }
191     }
192
193     /**
194      * Returns an affine transformation that maps the arrowhead shape
195      * to the position and orientation specified by the provided
196      * line segment end points.
197      */

198     protected AffineTransform JavaDoc getArrowTrans(Point2D JavaDoc p1, Point2D JavaDoc p2,
199                                             double width)
200     {
201         m_arrowTrans.setToTranslation(p2.getX(), p2.getY());
202         m_arrowTrans.rotate(-HALF_PI +
203             Math.atan2(p2.getY()-p1.getY(), p2.getX()-p1.getX()));
204         if ( width > 1 ) {
205             double scalar = width/4;
206             m_arrowTrans.scale(scalar, scalar);
207         }
208         return m_arrowTrans;
209     }
210     
211     /**
212      * Update the dimensions of the arrow head, creating a new
213      * arrow head if necessary. The return value is also set
214      * as the member variable <code>m_arrowHead</code>
215      * @param w the width of the untransformed arrow head base, in pixels
216      * @param h the height of the untransformed arrow head, in pixels
217      * @return the untransformed arrow head shape
218      */

219     protected Polygon JavaDoc updateArrowHead(int w, int h) {
220         if ( m_arrowHead == null ) {
221             m_arrowHead = new Polygon JavaDoc();
222         } else {
223             m_arrowHead.reset();
224         }
225         m_arrowHead.addPoint(0, 0);
226         m_arrowHead.addPoint(-w/2, -h);
227         m_arrowHead.addPoint( w/2, -h);
228         m_arrowHead.addPoint(0, 0);
229         return m_arrowHead;
230     }
231         
232     
233
234     /**
235      * @see prefuse.render.AbstractShapeRenderer#getTransform(prefuse.visual.VisualItem)
236      */

237     protected AffineTransform JavaDoc getTransform(VisualItem item) {
238         return null;
239     }
240     
241     /**
242      * @see prefuse.render.Renderer#locatePoint(java.awt.geom.Point2D, prefuse.visual.VisualItem)
243      */

244     public boolean locatePoint(Point2D JavaDoc p, VisualItem item) {
245         Shape JavaDoc s = getShape(item);
246         if ( s == null ) {
247             return false;
248         } else {
249             double width = Math.max(2, getLineWidth(item));
250             double halfWidth = width/2.0;
251             return s.intersects(p.getX()-halfWidth,
252                                 p.getY()-halfWidth,
253                                 width,width);
254         }
255     }
256     
257     /**
258      * @see prefuse.render.Renderer#setBounds(prefuse.visual.VisualItem)
259      */

260     public void setBounds(VisualItem item) {
261         if ( !m_manageBounds ) return;
262         Shape JavaDoc shape = getShape(item);
263         if ( shape == null ) {
264             item.setBounds(item.getX(), item.getY(), 0, 0);
265             return;
266         }
267         GraphicsLib.setBounds(item, shape, getStroke(item));
268         if ( m_curArrow != null ) {
269             Rectangle2D JavaDoc bbox = (Rectangle2D JavaDoc)item.get(VisualItem.BOUNDS);
270             Rectangle2D.union(bbox, m_curArrow.getBounds2D(), bbox);
271         }
272     }
273
274     /**
275      * Returns the line width to be used for this VisualItem. By default,
276      * returns the base width value set using the {@link #setDefaultLineWidth(double)}
277      * method, scaled by the item size returned by
278      * {@link VisualItem#getSize()}. Subclasses can override this method to
279      * perform custom line width determination, however, the preferred
280      * method is to change the item size value itself.
281      * @param item the VisualItem for which to determine the line width
282      * @return the desired line width, in pixels
283      */

284     protected double getLineWidth(VisualItem item) {
285         return item.getSize();
286     }
287     
288     /**
289      * Returns the stroke value returned by {@link VisualItem#getStroke()},
290      * scaled by the current line width
291      * determined by the {@link #getLineWidth(VisualItem)} method. Subclasses
292      * may override this method to perform custom stroke assignment, but should
293      * respect the line width paremeter stored in the {@link #m_curWidth}
294      * member variable, which caches the result of <code>getLineWidth</code>.
295      * @see prefuse.render.AbstractShapeRenderer#getStroke(prefuse.visual.VisualItem)
296      */

297     protected BasicStroke JavaDoc getStroke(VisualItem item) {
298         return StrokeLib.getDerivedStroke(item.getStroke(), m_curWidth);
299     }
300
301     /**
302      * Determines the control points to use for cubic (Bezier) curve edges.
303      * Override this method to provide custom curve specifications.
304      * To reduce object initialization, the entries of the Point2D array are
305      * already initialized, so use the <tt>Point2D.setLocation()</tt> method rather than
306      * <tt>new Point2D.Double()</tt> to more efficiently set custom control points.
307      * @param eitem the EdgeItem we are determining the control points for
308      * @param cp array of Point2D's (length >= 2) in which to return the control points
309      * @param x1 the x co-ordinate of the first node this edge connects to
310      * @param y1 the y co-ordinate of the first node this edge connects to
311      * @param x2 the x co-ordinate of the second node this edge connects to
312      * @param y2 the y co-ordinate of the second node this edge connects to
313      */

314     protected void getCurveControlPoints(EdgeItem eitem, Point2D JavaDoc[] cp,
315                     double x1, double y1, double x2, double y2)
316     {
317         double dx = x2-x1, dy = y2-y1;
318         cp[0].setLocation(x1+2*dx/3,y1);
319         cp[1].setLocation(x2-dx/8,y2-dy/8);
320     }
321
322     /**
323      * Helper method, which calculates the top-left co-ordinate of a rectangle
324      * given the rectangle's alignment.
325      */

326     protected static void getAlignedPoint(Point2D JavaDoc p, Rectangle2D JavaDoc r, int xAlign, int yAlign) {
327         double x = r.getX(), y = r.getY(), w = r.getWidth(), h = r.getHeight();
328         if ( xAlign == Constants.CENTER ) {
329             x = x+(w/2);
330         } else if ( xAlign == Constants.RIGHT ) {
331             x = x+w;
332         }
333         if ( yAlign == Constants.CENTER ) {
334             y = y+(h/2);
335         } else if ( yAlign == Constants.BOTTOM ) {
336             y = y+h;
337         }
338         p.setLocation(x,y);
339     }
340
341     /**
342      * Returns the type of the drawn edge. This is one of
343      * {@link prefuse.Constants#EDGE_TYPE_LINE} or
344      * {@link prefuse.Constants#EDGE_TYPE_CURVE}.
345      * @return the edge type
346      */

347     public int getEdgeType() {
348         return m_edgeType;
349     }
350     
351     /**
352      * Sets the type of the drawn edge. This must be one of
353     * {@link prefuse.Constants#EDGE_TYPE_LINE} or
354     * {@link prefuse.Constants#EDGE_TYPE_CURVE}.
355      * @param type the new edge type
356      */

357     public void setEdgeType(int type) {
358         if ( type < 0 || type >= Constants.EDGE_TYPE_COUNT )
359             throw new IllegalArgumentException JavaDoc(
360                     "Unrecognized edge curve type: "+type);
361         m_edgeType = type;
362     }
363     
364     /**
365      * Returns the type of the drawn edge. This is one of
366      * {@link prefuse.Constants#EDGE_ARROW_FORWARD},
367      * {@link prefuse.Constants#EDGE_ARROW_REVERSE}, or
368      * {@link prefuse.Constants#EDGE_ARROW_NONE}.
369      * @return the edge type
370      */

371     public int getArrowType() {
372         return m_edgeArrow;
373     }
374     
375     /**
376      * Sets the type of the drawn edge. This is either
377      * {@link prefuse.Constants#EDGE_ARROW_NONE} for no edge arrows,
378      * {@link prefuse.Constants#EDGE_ARROW_FORWARD} for arrows from source to
379      * target on directed edges, or
380      * {@link prefuse.Constants#EDGE_ARROW_REVERSE} for arrows from target to
381      * source on directed edges.
382      * @param type the new arrow type
383      */

384     public void setArrowType(int type) {
385         if ( type < 0 || type >= Constants.EDGE_ARROW_COUNT )
386             throw new IllegalArgumentException JavaDoc(
387                     "Unrecognized edge arrow type: "+type);
388         m_edgeArrow = type;
389     }
390     
391     /**
392      * Sets the dimensions of an arrow head for a directed edge. This specifies
393      * the pixel dimensions when both the zoom level and the size factor
394      * (a combination of item size value and default stroke width) are 1.0.
395      * @param width the untransformed arrow head width, in pixels. This
396      * specifies the span of the base of the arrow head.
397      * @param height the untransformed arrow head height, in pixels. This
398      * specifies the distance from the point of the arrow to its base.
399      */

400     public void setArrowHeadSize(int width, int height) {
401         m_arrowWidth = width;
402         m_arrowHeight = height;
403         m_arrowHead = updateArrowHead(width, height);
404     }
405     
406     /**
407      * Get the height of the untransformed arrow head. This is the distance,
408      * in pixels, from the tip of the arrow to its base.
409      * @return the default arrow head height
410      */

411     public int getArrowHeadHeight() {
412         return m_arrowHeight;
413     }
414
415     /**
416      * Get the width of the untransformed arrow head. This is the length,
417      * in pixels, of the base of the arrow head.
418      * @return the default arrow head width
419      */

420     public int getArrowHeadWidth() {
421         return m_arrowWidth;
422     }
423     
424     /**
425      * Get the horizontal aligment of the edge mount point with the first node.
426      * @return the horizontal alignment, one of {@link prefuse.Constants#LEFT},
427      * {@link prefuse.Constants#RIGHT}, or {@link prefuse.Constants#CENTER}.
428      */

429     public int getHorizontalAlignment1() {
430         return m_xAlign1;
431     }
432     
433     /**
434      * Get the vertical aligment of the edge mount point with the first node.
435      * @return the vertical alignment, one of {@link prefuse.Constants#TOP},
436      * {@link prefuse.Constants#BOTTOM}, or {@link prefuse.Constants#CENTER}.
437      */

438     public int getVerticalAlignment1() {
439         return m_yAlign1;
440     }
441
442     /**
443      * Get the horizontal aligment of the edge mount point with the second
444      * node.
445      * @return the horizontal alignment, one of {@link prefuse.Constants#LEFT},
446      * {@link prefuse.Constants#RIGHT}, or {@link prefuse.Constants#CENTER}.
447      */

448     public int getHorizontalAlignment2() {
449         return m_xAlign2;
450     }
451     
452     /**
453      * Get the vertical aligment of the edge mount point with the second node.
454      * @return the vertical alignment, one of {@link prefuse.Constants#TOP},
455      * {@link prefuse.Constants#BOTTOM}, or {@link prefuse.Constants#CENTER}.
456      */

457     public int getVerticalAlignment2() {
458         return m_yAlign2;
459     }
460     
461     /**
462      * Set the horizontal aligment of the edge mount point with the first node.
463      * @param align the horizontal alignment, one of
464      * {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or
465      * {@link prefuse.Constants#CENTER}.
466      */

467     public void setHorizontalAlignment1(int align) {
468         m_xAlign1 = align;
469     }
470     
471     /**
472      * Set the vertical aligment of the edge mount point with the first node.
473      * @param align the vertical alignment, one of
474      * {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or
475      * {@link prefuse.Constants#CENTER}.
476      */

477     public void setVerticalAlignment1(int align) {
478         m_yAlign1 = align;
479     }
480
481     /**
482      * Set the horizontal aligment of the edge mount point with the second
483      * node.
484      * @param align the horizontal alignment, one of
485      * {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or
486      * {@link prefuse.Constants#CENTER}.
487      */

488     public void setHorizontalAlignment2(int align) {
489         m_xAlign2 = align;
490     }
491     
492     /**
493      * Set the vertical aligment of the edge mount point with the second node.
494      * @param align the vertical alignment, one of
495      * {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or
496      * {@link prefuse.Constants#CENTER}.
497      */

498     public void setVerticalAlignment2(int align) {
499         m_yAlign2 = align;
500     }
501     
502     /**
503      * Sets the default width of lines. This width value will
504      * be scaled by the value of an item's size data field. The default
505      * base width is 1.
506      * @param w the desired default line width, in pixels
507      */

508     public void setDefaultLineWidth(double w) {
509         m_width = w;
510     }
511     
512     /**
513      * Gets the default width of lines. This width value that will
514      * be scaled by the value of an item's size data field. The default
515      * base width is 1.
516      * @return the default line width, in pixels
517      */

518     public double getDefaultLineWidth() {
519         return m_width;
520     }
521
522 } // end of class EdgeRenderer
523
Popular Tags