1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45:
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51:
52:
65: public class BasicStroke implements Stroke
66: {
67:
71: public static final int JOIN_MITER = 0;
72:
73:
77: public static final int JOIN_ROUND = 1;
78:
79:
83: public static final int JOIN_BEVEL = 2;
84:
85:
89: public static final int CAP_BUTT = 0;
90:
91:
95: public static final int CAP_ROUND = 1;
96:
97:
101: public static final int CAP_SQUARE = 2;
102:
103:
104: private final float width;
105:
106:
107: private final int cap;
108:
109:
110: private final int join;
111:
112:
113: private final float limit;
114:
115:
116: private final float[] dash;
117:
118:
119: private final float phase;
120:
121: private Segment start, end;
122:
123:
140: public BasicStroke(float width, int cap, int join, float miterlimit,
141: float[] dash, float dashPhase)
142: {
143: if (width < 0.0f )
144: throw new IllegalArgumentException("width " + width + " < 0");
145: else if (cap < CAP_BUTT || cap > CAP_SQUARE)
146: throw new IllegalArgumentException("cap " + cap + " out of range ["
147: + CAP_BUTT + ".." + CAP_SQUARE + "]");
148: else if (miterlimit < 1.0f && join == JOIN_MITER)
149: throw new IllegalArgumentException("miterlimit " + miterlimit
150: + " < 1.0f while join == JOIN_MITER");
151: else if (join < JOIN_MITER || join > JOIN_BEVEL)
152: throw new IllegalArgumentException("join " + join + " out of range ["
153: + JOIN_MITER + ".." + JOIN_BEVEL
154: + "]");
155: else if (dashPhase < 0.0f && dash != null)
156: throw new IllegalArgumentException("dashPhase " + dashPhase
157: + " < 0.0f while dash != null");
158: else if (dash != null)
159: if (dash.length == 0)
160: throw new IllegalArgumentException("dash.length is 0");
161: else
162: {
163: boolean allZero = true;
164:
165: for ( int i = 0; i < dash.length; ++i)
166: {
167: if (dash[i] != 0.0f)
168: {
169: allZero = false;
170: break;
171: }
172: }
173:
174: if (allZero)
175: throw new IllegalArgumentException("all dashes are 0.0f");
176: }
177:
178: this.width = width;
179: this.cap = cap;
180: this.join = join;
181: limit = miterlimit;
182: this.dash = dash == null ? null : (float[]) dash.clone();
183: phase = dashPhase;
184: }
185:
186:
200: public BasicStroke(float width, int cap, int join, float miterlimit)
201: {
202: this(width, cap, join, miterlimit, null, 0);
203: }
204:
205:
218: public BasicStroke(float width, int cap, int join)
219: {
220: this(width, cap, join, 10, null, 0);
221: }
222:
223:
236: public BasicStroke(float width)
237: {
238: this(width, CAP_SQUARE, JOIN_MITER, 10, null, 0);
239: }
240:
241:
250: public BasicStroke()
251: {
252: this(1, CAP_SQUARE, JOIN_MITER, 10, null, 0);
253: }
254:
255:
261: public Shape createStrokedShape(Shape s)
262: {
263: PathIterator pi = s.getPathIterator( new AffineTransform() );
264:
265: if( dash == null )
266: return solidStroke( pi );
267:
268: return dashedStroke( pi );
269: }
270:
271:
276: public float getLineWidth()
277: {
278: return width;
279: }
280:
281:
287: public int getEndCap()
288: {
289: return cap;
290: }
291:
292:
298: public int getLineJoin()
299: {
300: return join;
301: }
302:
303:
308: public float getMiterLimit()
309: {
310: return limit;
311: }
312:
313:
320: public float[] getDashArray()
321: {
322: return dash;
323: }
324:
325:
332: public float getDashPhase()
333: {
334: return phase;
335: }
336:
337:
346: public int hashCode()
347: {
348: int hash = Float.floatToIntBits(width);
349: hash ^= cap;
350: hash ^= join;
351: hash ^= Float.floatToIntBits(limit);
352:
353: if (dash != null)
354: for (int i = 0; i < dash.length; i++)
355: hash ^= Float.floatToIntBits(dash[i]);
356:
357: hash ^= Float.floatToIntBits(phase);
358:
359: return hash;
360: }
361:
362:
376: public boolean equals(Object o)
377: {
378: if (! (o instanceof BasicStroke))
379: return false;
380: BasicStroke s = (BasicStroke) o;
381: return width == s.width && cap == s.cap && join == s.join
382: && limit == s.limit && Arrays.equals(dash, s.dash) && phase == s.phase;
383: }
384:
385: private Shape solidStroke(PathIterator pi)
386: {
387: double[] coords = new double[6];
388: double x, y, x0, y0;
389: boolean pathOpen = false;
390: GeneralPath output = new GeneralPath( );
391: Segment[] p;
392: x = x0 = y = y0 = 0;
393:
394: while( !pi.isDone() )
395: {
396: switch( pi.currentSegment(coords) )
397: {
398: case PathIterator.SEG_MOVETO:
399: x0 = x = coords[0];
400: y0 = y = coords[1];
401: if( pathOpen )
402: {
403: capEnds();
404: convertPath(output, start);
405: start = end = null;
406: pathOpen = false;
407: }
408: break;
409:
410: case PathIterator.SEG_LINETO:
411: p = (new LineSegment(x, y, coords[0], coords[1])).
412: getDisplacedSegments(width/2.0);
413: if( !pathOpen )
414: {
415: start = p[0];
416: end = p[1];
417: pathOpen = true;
418: }
419: else
420: addSegments(p);
421:
422: x = coords[0];
423: y = coords[1];
424: break;
425:
426: case PathIterator.SEG_QUADTO:
427: p = (new QuadSegment(x, y, coords[0], coords[1], coords[2],
428: coords[3])).getDisplacedSegments(width/2.0);
429: if( !pathOpen )
430: {
431: start = p[0];
432: end = p[1];
433: pathOpen = true;
434: }
435: else
436: addSegments(p);
437:
438: x = coords[0];
439: y = coords[1];
440: break;
441:
442: case PathIterator.SEG_CUBICTO:
443: p = new CubicSegment(x, y, coords[0], coords[1],
444: coords[2], coords[3],
445: coords[4], coords[5]).getDisplacedSegments(width/2.0);
446: if( !pathOpen )
447: {
448: start = p[0];
449: end = p[1];
450: pathOpen = true;
451: }
452: else
453: addSegments(p);
454:
455: x = coords[0];
456: y = coords[1];
457: break;
458:
459: case PathIterator.SEG_CLOSE:
460: p = (new LineSegment(x, y, x0, y0)).getDisplacedSegments(width/2.0);
461: addSegments(p);
462: convertPath(output, start);
463: convertPath(output, end);
464: start = end = null;
465: pathOpen = false;
466: break;
467: }
468: pi.next();
469: }
470:
471: if( pathOpen )
472: {
473: capEnds();
474: convertPath(output, start);
475: }
476: return output;
477: }
478:
479: private Shape dashedStroke(PathIterator pi)
480: {
481: GeneralPath out = new GeneralPath();
482: return out;
483: }
484:
485:
488: private void capEnds()
489: {
490: Segment returnPath = end.last;
491:
492: end.reverseAll();
493: end = null;
494: capEnd(start, returnPath);
495: start.last = returnPath.last;
496: end = null;
497:
498: capEnd(start, start);
499: }
500:
501:
504: private void convertPath(GeneralPath p, Segment s)
505: {
506: Segment v = s;
507: p.moveTo((float)s.P1.getX(), (float)s.P1.getY());
508:
509: do
510: {
511: if(v instanceof LineSegment)
512: p.lineTo((float)v.P2.getX(), (float)v.P2.getY());
513: else if(v instanceof QuadSegment)
514: p.quadTo((float)((QuadSegment)v).cp.getX(),
515: (float)((QuadSegment)v).cp.getY(),
516: (float)v.P2.getX(),
517: (float)v.P2.getY());
518: else if(v instanceof CubicSegment)
519: p.curveTo((float)((CubicSegment)v).cp1.getX(),
520: (float)((CubicSegment)v).cp1.getY(),
521: (float)((CubicSegment)v).cp2.getX(),
522: (float)((CubicSegment)v).cp2.getY(),
523: (float)v.P2.getX(),
524: (float)v.P2.getY());
525: v = v.next;
526: } while(v != s && v != null);
527:
528: p.closePath();
529: }
530:
531:
534: private void addSegments(Segment[] segments)
535: {
536: double[] p0 = start.last.last();
537: double[] p1 = new double[]{start.last.P2.getX(), start.last.P2.getY()};
538: double[] p2 = new double[]{segments[0].P1.getX(), segments[0].P1.getY()};
539: double[] p3 = segments[0].first();
540: Point2D p;
541:
542: double det = (p1[0] - p0[0])*(p3[1] - p2[1]) -
543: (p3[0] - p2[0])*(p1[1] - p0[1]);
544:
545: if( det > 0 )
546: {
547:
548:
549: p = lineIntersection(p0[0],p0[1],p1[0],p1[1],p2[0],p2[1],p3[0],p3[1], false);
550: if( p == null )
551: {
552:
553: start.add(new LineSegment(start.last.P2, segments[0].P1));
554: p = new Point2D.Double((segments[0].P1.getX()+ start.last.P2.getX())/2.0,
555: (segments[0].P1.getY()+ start.last.P2.getY())/2.0);
556: }
557: else
558: segments[0].P1 = start.last.P2 = p;
559:
560: start.add( segments[0] );
561: joinSegments(end, segments[1], p);
562: }
563: else
564: {
565:
566: p0 = end.last.last();
567: p1 = new double[]{end.last.P2.getX(), end.last.P2.getY()};
568: p2 = new double[]{segments[1].P1.getX(), segments[1].P1.getY()};
569: p3 = segments[1].first();
570:
571: p = lineIntersection(p0[0],p0[1],p1[0],p1[1],
572: p2[0],p2[1],p3[0],p3[1], false);
573: if( p == null )
574: {
575:
576: end.add(new LineSegment(end.last.P2, segments[1].P1));
577: p = new Point2D.Double((segments[1].P1.getX()+ end.last.P2.getX())/2.0,
578: (segments[1].P1.getY()+ end.last.P2.getY())/2.0);
579: }
580: else
581: segments[1].P1 = end.last.P2 = p;
582:
583: end.add( segments[1] );
584: joinSegments(start, segments[0], p);
585: }
586: }
587:
588:
592: private void capEnd(Segment a, Segment b)
593: {
594: double[] p0, p1;
595: double dx, dy, l;
596: Point2D c1,c2;
597:
598: switch( cap )
599: {
600: case CAP_BUTT:
601: a.add(new LineSegment(a.last.P2, b.P1));
602: break;
603:
604: case CAP_SQUARE:
605: p0 = a.last.last();
606: p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()};
607: dx = p1[0] - p0[0];
608: dy = p1[1] - p0[1];
609: l = Math.sqrt(dx * dx + dy * dy);
610: dx = 0.5*width*dx/l;
611: dy = 0.5*width*dy/l;
612: c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy);
613: c2 = new Point2D.Double(b.P1.getX() + dx, b.P1.getY() + dy);
614: a.add(new LineSegment(a.last.P2, c1));
615: a.add(new LineSegment(c1, c2));
616: a.add(new LineSegment(c2, b.P1));
617: break;
618:
619: case CAP_ROUND:
620: p0 = a.last.last();
621: p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()};
622: dx = p1[0] - p0[0];
623: dy = p1[1] - p0[1];
624: l = Math.sqrt(dx * dx + dy * dy);
625: dx = (2.0/3.0)*width*dx/l;
626: dy = (2.0/3.0)*width*dy/l;
627: c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy);
628: c2 = new Point2D.Double(b.P1.getX() + dx, b.P1.getY() + dy);
629: a.add(new CubicSegment(a.last.P2, c1, c2, b.P1));
630: break;
631: }
632: a.add(b);
633: }
634:
635:
641: private Point2D lineIntersection(double X1, double Y1,
642: double X2, double Y2,
643: double X3, double Y3,
644: double X4, double Y4,
645: boolean infinite)
646: {
647: double x1 = X1;
648: double y1 = Y1;
649: double rx = X2 - x1;
650: double ry = Y2 - y1;
651:
652: double x2 = X3;
653: double y2 = Y3;
654: double sx = X4 - x2;
655: double sy = Y4 - y2;
656:
657: double determinant = sx * ry - sy * rx;
658: double nom = (sx * (y2 - y1) + sy * (x1 - x2));
659:
660:
661: if (Math.abs(determinant) < 1E-6)
662: return null;
663:
664: nom = nom / determinant;
665:
666:
667: if(!infinite && (nom > 1.0 || nom < 0.0))
668: return null;
669:
670: return new Point2D.Double(x1 + nom * rx, y1 + nom * ry);
671: }
672:
673:
679: private void joinSegments(Segment a, Segment b, Point2D insideP)
680: {
681: double[] p0, p1;
682: double dx, dy, l;
683: Point2D c1,c2;
684:
685: switch( join )
686: {
687: case JOIN_MITER:
688: p0 = a.last.last();
689: p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()};
690: double[] p2 = new double[]{b.P1.getX(), b.P1.getY()};
691: double[] p3 = b.first();
692: Point2D p = lineIntersection(p0[0],p0[1],p1[0],p1[1],p2[0],p2[1],p3[0],p3[1], true);
693: if( p == null || insideP == null )
694: a.add(new LineSegment(a.last.P2, b.P1));
695: else if((p.distance(insideP)/width) < limit)
696: {
697: a.add(new LineSegment(a.last.P2, p));
698: a.add(new LineSegment(p, b.P1));
699: }
700: else
701: {
702:
703: a.add(new LineSegment(a.last.P2, b.P1));
704: }
705: break;
706:
707: case JOIN_ROUND:
708: p0 = a.last.last();
709: p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()};
710: dx = p1[0] - p0[0];
711: dy = p1[1] - p0[1];
712: l = Math.sqrt(dx * dx + dy * dy);
713: dx = 0.5*width*dx/l;
714: dy = 0.5*width*dy/l;
715: c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy);
716:
717: p0 = new double[]{b.P1.getX(), b.P1.getY()};
718: p1 = b.first();
719:
720: dx = p0[0] - p1[0];
721: dy = p0[1] - p1[1];
722: l = Math.sqrt(dx * dx + dy * dy);
723: dx = 0.5*width*dx/l;
724: dy = 0.5*width*dy/l;
725: c2 = new Point2D.Double(p0[0] + dx, p0[1] + dy);
726: a.add(new CubicSegment(a.last.P2, c1, c2, b.P1));
727: break;
728:
729: case JOIN_BEVEL:
730: a.add(new LineSegment(a.last.P2, b.P1));
731: break;
732: }
733: a.add(b);
734: }
735: }