KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > prefuse > util > GraphicsLib


1 package prefuse.util;
2
3 import java.awt.BasicStroke JavaDoc;
4 import java.awt.Color JavaDoc;
5 import java.awt.Graphics2D JavaDoc;
6 import java.awt.Shape JavaDoc;
7 import java.awt.Stroke JavaDoc;
8 import java.awt.geom.AffineTransform JavaDoc;
9 import java.awt.geom.Ellipse2D JavaDoc;
10 import java.awt.geom.GeneralPath JavaDoc;
11 import java.awt.geom.Line2D JavaDoc;
12 import java.awt.geom.Point2D JavaDoc;
13 import java.awt.geom.Rectangle2D JavaDoc;
14 import java.awt.geom.RectangularShape JavaDoc;
15 import java.awt.geom.RoundRectangle2D JavaDoc;
16
17 import prefuse.render.AbstractShapeRenderer;
18 import prefuse.visual.VisualItem;
19
20 /**
21  * Library of useful computer graphics routines such as geometry routines
22  * for computing the intersection of different shapes and rendering methods
23  * for computing bounds and performing optimized drawing.
24  *
25  * @author <a HREF="http://jheer.org">jeffrey heer</a>
26  */

27 public class GraphicsLib {
28
29     /** Indicates no intersection between shapes */
30     public static final int NO_INTERSECTION = 0;
31     /** Indicates intersection between shapes */
32     public static final int COINCIDENT = -1;
33     /** Indicates two lines are parallel */
34     public static final int PARALLEL = -2;
35     
36     /**
37      * Compute the intersection of two line segments.
38      * @param a the first line segment
39      * @param b the second line segment
40      * @param intersect a Point in which to store the intersection point
41      * @return the intersection code. One of {@link #NO_INTERSECTION},
42      * {@link #COINCIDENT}, or {@link #PARALLEL}.
43      */

44     public static int intersectLineLine(Line2D JavaDoc a, Line2D JavaDoc b, Point2D JavaDoc intersect) {
45         double a1x = a.getX1(), a1y = a.getY1();
46         double a2x = a.getX2(), a2y = a.getY2();
47         double b1x = b.getX1(), b1y = b.getY1();
48         double b2x = b.getX2(), b2y = b.getY2();
49         return intersectLineLine(a1x,a1y,a2x,a2y,b1x,b1y,b2x,b2y,intersect);
50     }
51     
52     /**
53      * Compute the intersection of two line segments.
54      * @param a1x the x-coordinate of the first endpoint of the first line
55      * @param a1y the y-coordinate of the first endpoint of the first line
56      * @param a2x the x-coordinate of the second endpoint of the first line
57      * @param a2y the y-coordinate of the second endpoint of the first line
58      * @param b1x the x-coordinate of the first endpoint of the second line
59      * @param b1y the y-coordinate of the first endpoint of the second line
60      * @param b2x the x-coordinate of the second endpoint of the second line
61      * @param b2y the y-coordinate of the second endpoint of the second line
62      * @param intersect a Point in which to store the intersection point
63      * @return the intersection code. One of {@link #NO_INTERSECTION},
64      * {@link #COINCIDENT}, or {@link #PARALLEL}.
65      */

66     public static int intersectLineLine(double a1x, double a1y, double a2x,
67         double a2y, double b1x, double b1y, double b2x, double b2y,
68         Point2D JavaDoc intersect)
69     {
70         double ua_t = (b2x-b1x)*(a1y-b1y)-(b2y-b1y)*(a1x-b1x);
71         double ub_t = (a2x-a1x)*(a1y-b1y)-(a2y-a1y)*(a1x-b1x);
72         double u_b = (b2y-b1y)*(a2x-a1x)-(b2x-b1x)*(a2y-a1y);
73
74         if ( u_b != 0 ) {
75             double ua = ua_t / u_b;
76             double ub = ub_t / u_b;
77
78             if ( 0 <= ua && ua <= 1 && 0 <= ub && ub <= 1 ) {
79                 intersect.setLocation(a1x+ua*(a2x-a1x), a1y+ua*(a2y-a1y));
80                 return 1;
81             } else {
82                 return NO_INTERSECTION;
83             }
84         } else {
85             return ( ua_t == 0 || ub_t == 0 ? COINCIDENT : PARALLEL );
86         }
87     }
88
89     /**
90      * Compute the intersection of a line and a rectangle.
91      * @param a1 the first endpoint of the line
92      * @param a2 the second endpoint of the line
93      * @param r the rectangle
94      * @param pts a length 2 or greater array of points in which to store
95      * the results
96      * @return the intersection code. One of {@link #NO_INTERSECTION},
97      * {@link #COINCIDENT}, or {@link #PARALLEL}.
98      */

99     public static int intersectLineRectangle(Point2D JavaDoc a1, Point2D JavaDoc a2, Rectangle2D JavaDoc r, Point2D JavaDoc[] pts) {
100         double a1x = a1.getX(), a1y = a1.getY();
101         double a2x = a2.getX(), a2y = a2.getY();
102         double mxx = r.getMaxX(), mxy = r.getMaxY();
103         double mnx = r.getMinX(), mny = r.getMinY();
104         
105         if ( pts[0] == null ) pts[0] = new Point2D.Double JavaDoc();
106         if ( pts[1] == null ) pts[1] = new Point2D.Double JavaDoc();
107         
108         int i = 0;
109         if ( intersectLineLine(mnx,mny,mxx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
110         if ( intersectLineLine(mxx,mny,mxx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
111         if ( i == 2 ) return i;
112         if ( intersectLineLine(mxx,mxy,mnx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
113         if ( i == 2 ) return i;
114         if ( intersectLineLine(mnx,mxy,mnx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
115         return i;
116     }
117
118     /**
119      * Compute the intersection of a line and a rectangle.
120      * @param l the line
121      * @param r the rectangle
122      * @param pts a length 2 or greater array of points in which to store
123      * the results
124      * @return the intersection code. One of {@link #NO_INTERSECTION},
125      * {@link #COINCIDENT}, or {@link #PARALLEL}.
126      */

127     public static int intersectLineRectangle(Line2D JavaDoc l, Rectangle2D JavaDoc r, Point2D JavaDoc[] pts) {
128         double a1x = l.getX1(), a1y = l.getY1();
129         double a2x = l.getX2(), a2y = l.getY2();
130         double mxx = r.getMaxX(), mxy = r.getMaxY();
131         double mnx = r.getMinX(), mny = r.getMinY();
132         
133         if ( pts[0] == null ) pts[0] = new Point2D.Double JavaDoc();
134         if ( pts[1] == null ) pts[1] = new Point2D.Double JavaDoc();
135         
136         int i = 0;
137         if ( intersectLineLine(mnx,mny,mxx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
138         if ( intersectLineLine(mxx,mny,mxx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
139         if ( i == 2 ) return i;
140         if ( intersectLineLine(mxx,mxy,mnx,mxy,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
141         if ( i == 2 ) return i;
142         if ( intersectLineLine(mnx,mxy,mnx,mny,a1x,a1y,a2x,a2y,pts[i]) > 0 ) i++;
143         return i;
144     }
145     
146     /**
147      * Computes the 2D convex hull of a set of points using Graham's
148      * scanning algorithm. The algorithm has been implemented as described
149      * in Cormen, Leiserson, and Rivest's Introduction to Algorithms.
150      *
151      * The running time of this algorithm is O(n log n), where n is
152      * the number of input points.
153      *
154      * @param pts the input points in [x0,y0,x1,y1,...] order
155      * @param len the length of the pts array to consider (2 * #points)
156      * @return the convex hull of the input points
157      */

158     public static double[] convexHull(double[] pts, int len) {
159         if (len < 6) {
160             throw new IllegalArgumentException JavaDoc(
161                     "Input must have at least 3 points");
162         }
163         int plen = len/2-1;
164         float[] angles = new float[plen];
165         int[] idx = new int[plen];
166         int[] stack = new int[len/2];
167         return convexHull(pts, len, angles, idx, stack);
168     }
169     
170     /**
171      * Computes the 2D convex hull of a set of points using Graham's
172      * scanning algorithm. The algorithm has been implemented as described
173      * in Cormen, Leiserson, and Rivest's Introduction to Algorithms.
174      *
175      * The running time of this algorithm is O(n log n), where n is
176      * the number of input points.
177      *
178      * @param pts
179      * @return the convex hull of the input points
180      */

181     public static double[] convexHull(double[] pts, int len,
182             float[] angles, int[] idx, int[] stack)
183     {
184         // check arguments
185
int plen = len/2 - 1;
186         if (len < 6) {
187             throw new IllegalArgumentException JavaDoc(
188                     "Input must have at least 3 points");
189         }
190         if (angles.length < plen || idx.length < plen || stack.length < len/2) {
191             throw new IllegalArgumentException JavaDoc(
192                     "Pre-allocated data structure too small");
193         }
194         
195         int i0 = 0;
196         // find the starting ref point: leftmost point with the minimum y coord
197
for ( int i=2; i < len; i += 2 ) {
198             if ( pts[i+1] < pts[i0+1] ) {
199                 i0 = i;
200             } else if ( pts[i+1] == pts[i0+1] ) {
201                 i0 = (pts[i] < pts[i0] ? i : i0);
202             }
203         }
204         
205         // calculate polar angles from ref point and sort
206
for ( int i=0, j=0; i < len; i+=2 ) {
207             if ( i == i0 ) continue;
208             angles[j] = (float)Math.atan2(pts[i+1]-pts[i0+1], pts[i]-pts[i0]);
209             idx[j++] = i;
210         }
211         ArrayLib.sort(angles,idx,plen);
212         
213         // toss out duplicated angles
214
float angle = angles[0];
215         int ti = 0, tj = idx[0];
216         for ( int i=1; i<plen; i++ ) {
217             int j = idx[i];
218             if ( angle == angles[i] ) {
219                 // keep whichever angle corresponds to the most distant
220
// point from the reference point
221
double x1 = pts[tj] - pts[i0];
222                 double y1 = pts[tj+1] - pts[i0+1];
223                 double x2 = pts[j] - pts[i0];
224                 double y2 = pts[j+1] - pts[i0+1];
225                 double d1 = x1*x1 + y1*y1;
226                 double d2 = x2*x2 + y2*y2;
227                 if ( d1 >= d2 ) {
228                     idx[i] = -1;
229                 } else {
230                     idx[ti] = -1;
231                     angle = angles[i];
232                     ti = i;
233                     tj = j;
234                 }
235             } else {
236                 angle = angles[i];
237                 ti = i;
238                 tj = j;
239             }
240         }
241         
242         // initialize our stack
243
int sp = 0;
244         stack[sp++] = i0;
245         int j = 0;
246         for ( int k=0; k<2; j++ ) {
247             if ( idx[j] != -1 ) {
248                 stack[sp++] = idx[j];
249                 k++;
250             }
251         }
252         
253         // do graham's scan
254
for ( ; j < plen; j++ ) {
255             if ( idx[j] == -1 ) continue; // skip tossed out points
256
while ( isNonLeft(i0, stack[sp-2], stack[sp-1], idx[j], pts) ) {
257                 sp--;
258             }
259             stack[sp++] = idx[j];
260         }
261
262         // construct the hull
263
double[] hull = new double[2*sp];
264         for ( int i=0; i<sp; i++ ) {
265             hull[2*i] = pts[stack[i]];
266             hull[2*i+1] = pts[stack[i]+1];
267         }
268         
269         return hull;
270     }
271
272     /**
273      * Convex hull helper method for detecting a non left turn about 3 points
274      */

275     private static boolean isNonLeft(int i0, int i1, int i2, int i3, double[] pts) {
276         double l1, l2, l4, l5, l6, angle1, angle2, angle;
277
278         l1 = Math.sqrt(Math.pow(pts[i2+1]-pts[i1+1],2) + Math.pow(pts[i2]-pts[i1],2));
279         l2 = Math.sqrt(Math.pow(pts[i3+1]-pts[i2+1],2) + Math.pow(pts[i3]-pts[i2],2));
280         l4 = Math.sqrt(Math.pow(pts[i3+1]-pts[i0+1],2) + Math.pow(pts[i3]-pts[i0],2));
281         l5 = Math.sqrt(Math.pow(pts[i1+1]-pts[i0+1],2) + Math.pow(pts[i1]-pts[i0],2));
282         l6 = Math.sqrt(Math.pow(pts[i2+1]-pts[i0+1],2) + Math.pow(pts[i2]-pts[i0],2));
283
284         angle1 = Math.acos( ( (l2*l2)+(l6*l6)-(l4*l4) ) / (2*l2*l6) );
285         angle2 = Math.acos( ( (l6*l6)+(l1*l1)-(l5*l5) ) / (2*l6*l1) );
286
287         angle = (Math.PI - angle1) - angle2;
288
289         if (angle <= 0.0) {
290             return(true);
291         } else {
292             return(false);
293         }
294     }
295     
296     /**
297      * Computes the mean, or centroid, of a set of points
298      * @param pts the points array, in x1, y1, x2, y2, ... arrangement.
299      * @param len the length of the array to consider
300      * @return the centroid as a length-2 float array
301      */

302     public static float[] centroid(float pts[], int len) {
303         float[] c = new float[] {0, 0};
304         for ( int i=0; i < len; i+=2 ) {
305             c[0] += pts[i];
306             c[1] += pts[i+1];
307         }
308         c[0] /= len/2;
309         c[1] /= len/2;
310         return c;
311     }
312     
313     /**
314      * Expand a polygon by adding the given distance along the line from
315      * the centroid of the polyong.
316      * @param pts the polygon to expand, a set of points in a float array
317      * @param len the length of the range of the array to consider
318      * @param amt the amount by which to expand the polygon, each point
319      * will be moved this distance along the line from the centroid of the
320      * polygon to the given point.
321      */

322     public static void growPolygon(float pts[], int len, float amt) {
323         float[] c = centroid(pts, len);
324         for ( int i=0; i < len; i+=2 ) {
325             float vx = pts[i]-c[0];
326             float vy = pts[i+1]-c[1];
327             float norm = (float)Math.sqrt(vx*vx+vy*vy);
328             pts[i] += amt*vx/norm;
329             pts[i+1] += amt*vy/norm;
330         }
331     }
332     
333     /**
334      * Compute a cardinal spline, a series of cubic Bezier splines smoothly
335      * connecting a set of points. Cardinal splines maintain C(1)
336      * continuity, ensuring the connected spline segments form a differentiable
337      * curve, ensuring at least a minimum level of smoothness.
338      * @param pts the points to interpolate with a cardinal spline
339      * @param slack a parameter controlling the "tightness" of the spline to
340      * the control points, 0.10 is a typically suitable value
341      * @param closed true if the cardinal spline should be closed (i.e. return
342      * to the starting point), false for an open curve
343      * @return the cardinal spline as a Java2D {@link java.awt.geom.GeneralPath}
344      * instance.
345      */

346     public static GeneralPath JavaDoc cardinalSpline(float pts[], float slack, boolean closed) {
347         GeneralPath JavaDoc path = new GeneralPath JavaDoc();
348         path.moveTo(pts[0], pts[1]);
349         return cardinalSpline(path, pts, slack, closed, 0f, 0f);
350     }
351     
352     /**
353      * Compute a cardinal spline, a series of cubic Bezier splines smoothly
354      * connecting a set of points. Cardinal splines maintain C(1)
355      * continuity, ensuring the connected spline segments form a differentiable
356      * curve, ensuring at least a minimum level of smoothness.
357      * @param pts the points to interpolate with a cardinal spline
358      * @param start the starting index from which to read points
359      * @param npoints the number of points to consider
360      * @param slack a parameter controlling the "tightness" of the spline to
361      * the control points, 0.10 is a typically suitable value
362      * @param closed true if the cardinal spline should be closed (i.e. return
363      * to the starting point), false for an open curve
364      * @return the cardinal spline as a Java2D {@link java.awt.geom.GeneralPath}
365      * instance.
366      */

367     public static GeneralPath JavaDoc cardinalSpline(float pts[], int start, int npoints,
368             float slack, boolean closed)
369     {
370         GeneralPath JavaDoc path = new GeneralPath JavaDoc();
371         path.moveTo(pts[start], pts[start+1]);
372         return cardinalSpline(path, pts, start, npoints, slack, closed, 0f, 0f);
373     }
374     
375     /**
376      * Compute a cardinal spline, a series of cubic Bezier splines smoothly
377      * connecting a set of points. Cardinal splines maintain C(1)
378      * continuity, ensuring the connected spline segments form a differentiable
379      * curve, ensuring at least a minimum level of smoothness.
380      * @param p the GeneralPath instance to use to store the result
381      * @param pts the points to interpolate with a cardinal spline
382      * @param slack a parameter controlling the "tightness" of the spline to
383      * the control points, 0.10 is a typically suitable value
384      * @param closed true if the cardinal spline should be closed (i.e. return
385      * to the starting point), false for an open curve
386      * @param tx a value by which to translate the curve along the x-dimension
387      * @param ty a value by which to translate the curve along the y-dimension
388      * @return the cardinal spline as a Java2D {@link java.awt.geom.GeneralPath}
389      * instance.
390      */

391     public static GeneralPath JavaDoc cardinalSpline(GeneralPath JavaDoc p,
392             float pts[], float slack, boolean closed, float tx, float ty)
393     {
394         int npoints = 0;
395         for ( ; npoints<pts.length; ++npoints )
396             if ( Float.isNaN(pts[npoints]) ) break;
397         return cardinalSpline(p, pts, 0, npoints/2, slack, closed, tx, ty);
398     }
399     
400     /**
401      * Compute a cardinal spline, a series of cubic Bezier splines smoothly
402      * connecting a set of points. Cardinal splines maintain C(1)
403      * continuity, ensuring the connected spline segments form a differentiable
404      * curve, ensuring at least a minimum level of smoothness.
405      * @param p the GeneralPath instance to use to store the result
406      * @param pts the points to interpolate with a cardinal spline
407      * @param start the starting index from which to read points
408      * @param npoints the number of points to consider
409      * @param slack a parameter controlling the "tightness" of the spline to
410      * the control points, 0.10 is a typically suitable value
411      * @param closed true if the cardinal spline should be closed (i.e. return
412      * to the starting point), false for an open curve
413      * @param tx a value by which to translate the curve along the x-dimension
414      * @param ty a value by which to translate the curve along the y-dimension
415      * @return the cardinal spline as a Java2D {@link java.awt.geom.GeneralPath}
416      * instance.
417      */

418     public static GeneralPath JavaDoc cardinalSpline(GeneralPath JavaDoc p,
419             float pts[], int start, int npoints,
420             float slack, boolean closed, float tx, float ty)
421     {
422         // compute the size of the path
423
int len = 2*npoints;
424         int end = start+len;
425         
426         if ( len < 6 ) {
427             throw new IllegalArgumentException JavaDoc(
428                     "To create spline requires at least 3 points");
429         }
430         
431         float dx1, dy1, dx2, dy2;
432         
433         // compute first control point
434
if ( closed ) {
435             dx2 = pts[start+2]-pts[end-2];
436             dy2 = pts[start+3]-pts[end-1];
437         } else {
438             dx2 = pts[start+4]-pts[start];
439             dy2 = pts[start+5]-pts[start+1];
440         }
441         
442         // repeatedly compute next control point and append curve
443
int i;
444         for ( i=start+2; i<end-2; i+=2 ) {
445             dx1 = dx2; dy1 = dy2;
446             dx2 = pts[i+2]-pts[i-2];
447             dy2 = pts[i+3]-pts[i-1];
448             p.curveTo(tx+pts[i-2]+slack*dx1, ty+pts[i-1]+slack*dy1,
449                       tx+pts[i] -slack*dx2, ty+pts[i+1]-slack*dy2,
450                       tx+pts[i], ty+pts[i+1]);
451         }
452         
453         // compute last control point
454
if ( closed ) {
455             dx1 = dx2; dy1 = dy2;
456             dx2 = pts[start]-pts[i-2];
457             dy2 = pts[start+1]-pts[i-1];
458             p.curveTo(tx+pts[i-2]+slack*dx1, ty+pts[i-1]+slack*dy1,
459                       tx+pts[i] -slack*dx2, ty+pts[i+1]-slack*dy2,
460                       tx+pts[i], ty+pts[i+1]);
461             
462             dx1 = dx2; dy1 = dy2;
463             dx2 = pts[start+2]-pts[end-2];
464             dy2 = pts[start+3]-pts[end-1];
465             p.curveTo(tx+pts[end-2]+slack*dx1, ty+pts[end-1]+slack*dy1,
466                       tx+pts[0] -slack*dx2, ty+pts[1] -slack*dy2,
467                       tx+pts[0], ty+pts[1]);
468             p.closePath();
469         } else {
470             p.curveTo(tx+pts[i-2]+slack*dx2, ty+pts[i-1]+slack*dy2,
471                       tx+pts[i] -slack*dx2, ty+pts[i+1]-slack*dy2,
472                       tx+pts[i], ty+pts[i+1]);
473         }
474         return p;
475     }
476     
477     /**
478      * Computes a set of curves using the cardinal spline approach, but
479      * using straight lines for completely horizontal or vertical segments.
480      * @param p the GeneralPath instance to use to store the result
481      * @param pts the points to interpolate with the spline
482      * @param epsilon threshold value under which to treat the difference
483      * between two values to be zero. Used to determine which segments to
484      * treat as lines rather than curves.
485      * @param slack a parameter controlling the "tightness" of the spline to
486      * the control points, 0.10 is a typically suitable value
487      * @param closed true if the spline should be closed (i.e. return
488      * to the starting point), false for an open curve
489      * @param tx a value by which to translate the curve along the x-dimension
490      * @param ty a value by which to translate the curve along the y-dimension
491      * @return the stack spline as a Java2D {@link java.awt.geom.GeneralPath}
492      * instance.
493      */

494     public static GeneralPath JavaDoc stackSpline(GeneralPath JavaDoc p, float[] pts,
495             float epsilon, float slack, boolean closed, float tx, float ty)
496     {
497         int npoints = 0;
498         for ( ; npoints<pts.length; ++npoints )
499             if ( Float.isNaN(pts[npoints]) ) break;
500         return stackSpline(p,pts,0,npoints/2,epsilon,slack,closed,tx,ty);
501     }
502     
503     /**
504      * Computes a set of curves using the cardinal spline approach, but
505      * using straight lines for completely horizontal or vertical segments.
506      * @param p the GeneralPath instance to use to store the result
507      * @param pts the points to interpolate with the spline
508      * @param start the starting index from which to read points
509      * @param npoints the number of points to consider
510      * @param epsilon threshold value under which to treat the difference
511      * between two values to be zero. Used to determine which segments to
512      * treat as lines rather than curves.
513      * @param slack a parameter controlling the "tightness" of the spline to
514      * the control points, 0.10 is a typically suitable value
515      * @param closed true if the spline should be closed (i.e. return
516      * to the starting point), false for an open curve
517      * @param tx a value by which to translate the curve along the x-dimension
518      * @param ty a value by which to translate the curve along the y-dimension
519      * @return the stack spline as a Java2D {@link java.awt.geom.GeneralPath}
520      * instance.
521      */

522     public static GeneralPath JavaDoc stackSpline(GeneralPath JavaDoc p,
523             float pts[], int start, int npoints, float epsilon,
524             float slack, boolean closed, float tx, float ty)
525     {
526         // compute the size of the path
527
int len = 2*npoints;
528         int end = start+len;
529         
530         if ( len < 6 ) {
531             throw new IllegalArgumentException JavaDoc(
532                     "To create spline requires at least 3 points");
533         }
534         
535         float dx1, dy1, dx2, dy2;
536         // compute first control point
537
if ( closed ) {
538             dx2 = pts[start+2]-pts[end-2];
539             dy2 = pts[start+3]-pts[end-1];
540         } else {
541             dx2 = pts[start+4]-pts[start];
542             dy2 = pts[start+5]-pts[start+1];
543         }
544         
545         // repeatedly compute next control point and append curve
546
int i;
547         for ( i=start+2; i<end-2; i+=2 ) {
548             dx1 = dx2; dy1 = dy2;
549             dx2 = pts[i+2]-pts[i-2];
550             dy2 = pts[i+3]-pts[i-1];
551             if ( Math.abs(pts[i] -pts[i-2]) < epsilon ||
552                  Math.abs(pts[i+1]-pts[i-1]) < epsilon )
553             {
554                 p.lineTo(tx+pts[i], ty+pts[i+1]);
555             } else {
556                 p.curveTo(tx+pts[i-2]+slack*dx1, ty+pts[i-1]+slack*dy1,
557                           tx+pts[i] -slack*dx2, ty+pts[i+1]-slack*dy2,
558                           tx+pts[i], ty+pts[i+1]);
559             }
560         }
561         
562         // compute last control point
563
dx1 = dx2; dy1 = dy2;
564         dx2 = pts[start]-pts[i-2];
565         dy2 = pts[start+1]-pts[i-1];
566         if ( Math.abs(pts[i] -pts[i-2]) < epsilon ||
567              Math.abs(pts[i+1]-pts[i-1]) < epsilon )
568         {
569              p.lineTo(tx+pts[i], ty+pts[i+1]);
570         } else {
571             p.curveTo(tx+pts[i-2]+slack*dx1, ty+pts[i-1]+slack*dy1,
572                       tx+pts[i] -slack*dx2, ty+pts[i+1]-slack*dy2,
573                       tx+pts[i], ty+pts[i+1]);
574         }
575         
576         // close the curve if requested
577
if ( closed ) {
578             if ( Math.abs(pts[end-2]-pts[0]) < epsilon ||
579                  Math.abs(pts[end-1]-pts[1]) < epsilon )
580             {
581                 p.lineTo(tx+pts[0], ty+pts[1]);
582             } else {
583                 dx1 = dx2; dy1 = dy2;
584                 dx2 = pts[start+2]-pts[end-2];
585                 dy2 = pts[start+3]-pts[end-1];
586                 p.curveTo(tx+pts[end-2]+slack*dx1, ty+pts[end-1]+slack*dy1,
587                           tx+pts[0] -slack*dx2, ty+pts[1] -slack*dy2,
588                           tx+pts[0], ty+pts[1]);
589             }
590             p.closePath();
591         }
592         return p;
593     }
594     
595     /**
596      * Expand a rectangle by the given amount.
597      * @param r the rectangle to expand
598      * @param amount the amount by which to expand the rectangle
599      */

600     public static void expand(Rectangle2D JavaDoc r, double amount) {
601         r.setRect(r.getX()-amount, r.getY()-amount,
602                   r.getWidth()+2*amount, r.getHeight()+2*amount);
603     }
604     
605     // ------------------------------------------------------------------------
606

607     /**
608      * Sets a VisualItem's bounds based on its shape and stroke type. This
609      * method is optimized to avoid calling .getBounds2D where it can, thus
610      * avoiding object initialization and reducing object churn.
611      * @param item the VisualItem whose bounds are to be set
612      * @param shape a Shape from which to determine the item bounds
613      * @param stroke the stroke type that will be used for drawing the object,
614      * and may affect the final bounds. A null value indicates the
615      * default (line width = 1) stroke is used.
616      */

617     public static void setBounds(VisualItem item,
618                                  Shape JavaDoc shape, BasicStroke JavaDoc stroke)
619     {
620         double x, y, w, h, lw, lw2;
621         
622         if ( shape instanceof RectangularShape JavaDoc ) {
623             // this covers rectangle, rounded rectangle, ellipse, and arcs
624
RectangularShape JavaDoc r = (RectangularShape JavaDoc)shape;
625             x = r.getX();
626             y = r.getY();
627             w = r.getWidth();
628             h = r.getHeight();
629         } else if ( shape instanceof Line2D JavaDoc ) {
630             // this covers straight lines
631
Line2D JavaDoc l = (Line2D JavaDoc)shape;
632             x = l.getX1();
633             y = l.getY1();
634             w = l.getX2();
635             h = l.getY2();
636             if ( w < x ) {
637                 lw = x;
638                 x = w;
639                 w = lw-x;
640             } else {
641                 w = w-x;
642             }
643             if ( h < y ) {
644                 lw = y;
645                 y = h;
646                 h = lw-y;
647             } else {
648                 h = h-y;
649             }
650         } else {
651             // this covers any other arbitrary shapes, but
652
// takes a small object allocation / garbage collection hit
653
Rectangle2D JavaDoc r = shape.getBounds2D();
654             x = r.getX();
655             y = r.getY();
656             w = r.getWidth();
657             h = r.getHeight();
658         }
659         
660         // adjust boundary for stoke length as necessary
661
if ( stroke != null && (lw=stroke.getLineWidth()) > 1 ) {
662             lw2 = lw/2.0;
663             x -= lw2; y -= lw2; w += lw; h += lw;
664         }
665         item.setBounds(x, y, w, h);
666     }
667     
668     /**
669      * Render a shape associated with a VisualItem into a graphics context. This
670      * method uses the {@link java.awt.Graphics} interface methods when it can,
671      * as opposed to the {@link java.awt.Graphics2D} methods such as
672      * {@link java.awt.Graphics2D#draw(java.awt.Shape)} and
673      * {@link java.awt.Graphics2D#fill(java.awt.Shape)}, resulting in a
674      * significant performance increase on the Windows platform, particularly
675      * for rectangle and line drawing calls.
676      * @param g the graphics context to render to
677      * @param item the item being represented by the shape, this instance is
678      * used to get the correct color values for the drawing
679      * @param shape the shape to render
680      * @param stroke the stroke type to use for drawing the object.
681      * @param type the rendering type indicating if the shape should be drawn,
682      * filled, or both. One of
683      * {@link prefuse.render.AbstractShapeRenderer#RENDER_TYPE_DRAW},
684      * {@link prefuse.render.AbstractShapeRenderer#RENDER_TYPE_FILL},
685      * {@link prefuse.render.AbstractShapeRenderer#RENDER_TYPE_DRAW_AND_FILL}, or
686      * {@link prefuse.render.AbstractShapeRenderer#RENDER_TYPE_NONE}.
687      */

688     public static void paint(Graphics2D JavaDoc g, VisualItem item,
689                              Shape JavaDoc shape, BasicStroke JavaDoc stroke, int type)
690     {
691         // if render type is NONE, then there is nothing to do
692
if ( type == AbstractShapeRenderer.RENDER_TYPE_NONE )
693             return;
694         
695         // set up colors
696
Color JavaDoc strokeColor = ColorLib.getColor(item.getStrokeColor());
697         Color JavaDoc fillColor = ColorLib.getColor(item.getFillColor());
698         boolean sdraw = (type == AbstractShapeRenderer.RENDER_TYPE_DRAW ||
699                          type == AbstractShapeRenderer.RENDER_TYPE_DRAW_AND_FILL) &&
700                         strokeColor.getAlpha() != 0;
701         boolean fdraw = (type == AbstractShapeRenderer.RENDER_TYPE_FILL ||
702                          type == AbstractShapeRenderer.RENDER_TYPE_DRAW_AND_FILL) &&
703                         fillColor.getAlpha() != 0;
704         if ( !(sdraw || fdraw) ) return;
705         
706         Stroke JavaDoc origStroke = null;
707         if ( sdraw ) {
708             origStroke = g.getStroke();
709             g.setStroke(stroke);
710         }
711         
712         int x, y, w, h, aw, ah;
713         double xx, yy, ww, hh;
714
715         // see if an optimized (non-shape) rendering call is available for us
716
// these can speed things up significantly on the windows JRE
717
// it is stupid we have to do this, but we do what we must
718
// if we are zoomed in, we have no choice but to use
719
// full precision rendering methods.
720
AffineTransform JavaDoc at = g.getTransform();
721         double scale = Math.max(at.getScaleX(), at.getScaleY());
722         if ( scale > 1.5 ) {
723             if (fdraw) { g.setPaint(fillColor); g.fill(shape); }
724             if (sdraw) { g.setPaint(strokeColor); g.draw(shape); }
725         }
726         else if ( shape instanceof RectangularShape JavaDoc )
727         {
728             RectangularShape JavaDoc r = (RectangularShape JavaDoc)shape;
729             xx = r.getX(); ww = r.getWidth();
730             yy = r.getY(); hh = r.getHeight();
731             
732             x = (int)xx;
733             y = (int)yy;
734             w = (int)(ww+xx-x);
735             h = (int)(hh+yy-y);
736             
737             if ( shape instanceof Rectangle2D JavaDoc ) {
738                 if (fdraw) {
739                     g.setPaint(fillColor);
740                     g.fillRect(x, y, w, h);
741                 }
742                 if (sdraw) {
743                     g.setPaint(strokeColor);
744                     g.drawRect(x, y, w, h);
745                 }
746             } else if ( shape instanceof RoundRectangle2D JavaDoc ) {
747                 RoundRectangle2D JavaDoc rr = (RoundRectangle2D JavaDoc)shape;
748                 aw = (int)rr.getArcWidth();
749                 ah = (int)rr.getArcHeight();
750                 if (fdraw) {
751                     g.setPaint(fillColor);
752                     g.fillRoundRect(x, y, w, h, aw, ah);
753                 }
754                 if (sdraw) {
755                     g.setPaint(strokeColor);
756                     g.drawRoundRect(x, y, w, h, aw, ah);
757                 }
758             } else if ( shape instanceof Ellipse2D JavaDoc ) {
759                 if (fdraw) {
760                     g.setPaint(fillColor);
761                     g.fillOval(x, y, w, h);
762                 }
763                 if (sdraw) {
764                     g.setPaint(strokeColor);
765                     g.drawOval(x, y, w, h);
766                 }
767             } else {
768                 if (fdraw) { g.setPaint(fillColor); g.fill(shape); }
769                 if (sdraw) { g.setPaint(strokeColor); g.draw(shape); }
770             }
771         } else if ( shape instanceof Line2D JavaDoc ) {
772             if (sdraw) {
773                 Line2D JavaDoc l = (Line2D JavaDoc)shape;
774                 x = (int)(l.getX1()+0.5);
775                 y = (int)(l.getY1()+0.5);
776                 w = (int)(l.getX2()+0.5);
777                 h = (int)(l.getY2()+0.5);
778                 g.setPaint(strokeColor);
779                 g.drawLine(x, y, w, h);
780             }
781         } else {
782             if (fdraw) { g.setPaint(fillColor); g.fill(shape); }
783             if (sdraw) { g.setPaint(strokeColor); g.draw(shape); }
784         }
785         if ( sdraw ) {
786             g.setStroke(origStroke);
787         }
788     }
789     
790 } // end of class GraphicsLib
791
Popular Tags