1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43:
44:
45:
80: public final class GeneralPath implements Shape, Cloneable
81: {
82:
83:
84:
85:
86: public static final int WIND_EVEN_ODD
87: = java.awt.geom.PathIterator.WIND_EVEN_ODD;
88:
89:
90: public static final int WIND_NON_ZERO
91: = java.awt.geom.PathIterator.WIND_NON_ZERO;
92:
93:
94: private static final int INIT_SIZE = 10;
95:
96:
97: private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0;
98:
99:
102: int rule;
103:
104:
110: byte[] types;
111:
112:
120: float[] xpoints;
121: float[] ypoints;
122:
123:
124: private int subpath = -1;
125:
126:
129: int index;
130:
131:
135: public GeneralPath()
136: {
137: this(WIND_NON_ZERO, INIT_SIZE);
138: }
139:
140:
145: public GeneralPath(int rule)
146: {
147: this(rule, INIT_SIZE);
148: }
149:
150:
157: public GeneralPath(int rule, int capacity)
158: {
159: if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO)
160: throw new IllegalArgumentException();
161: this.rule = rule;
162: if (capacity < INIT_SIZE)
163: capacity = INIT_SIZE;
164: types = new byte[capacity];
165: xpoints = new float[capacity];
166: ypoints = new float[capacity];
167: }
168:
169:
174: public GeneralPath(Shape s)
175: {
176: types = new byte[INIT_SIZE];
177: xpoints = new float[INIT_SIZE];
178: ypoints = new float[INIT_SIZE];
179: PathIterator pi = s.getPathIterator(null);
180: setWindingRule(pi.getWindingRule());
181: append(pi, false);
182: }
183:
184:
187: public void moveTo(float x, float y)
188: {
189: subpath = index;
190: ensureSize(index + 1);
191: types[index] = PathIterator.SEG_MOVETO;
192: xpoints[index] = x;
193: ypoints[index++] = y;
194: }
195:
196:
201: public void lineTo(float x, float y)
202: {
203: ensureSize(index + 1);
204: types[index] = PathIterator.SEG_LINETO;
205: xpoints[index] = x;
206: ypoints[index++] = y;
207: }
208:
209:
216: public void quadTo(float x1, float y1, float x2, float y2)
217: {
218: ensureSize(index + 2);
219: types[index] = PathIterator.SEG_QUADTO;
220: xpoints[index] = x1;
221: ypoints[index++] = y1;
222: xpoints[index] = x2;
223: ypoints[index++] = y2;
224: }
225:
226:
235: public void curveTo(float x1, float y1, float x2, float y2, float x3,
236: float y3)
237: {
238: ensureSize(index + 3);
239: types[index] = PathIterator.SEG_CUBICTO;
240: xpoints[index] = x1;
241: ypoints[index++] = y1;
242: xpoints[index] = x2;
243: ypoints[index++] = y2;
244: xpoints[index] = x3;
245: ypoints[index++] = y3;
246: }
247:
248:
252: public void closePath()
253: {
254: ensureSize(index + 1);
255: types[index] = PathIterator.SEG_CLOSE;
256: xpoints[index] = xpoints[subpath];
257: ypoints[index++] = ypoints[subpath];
258: }
259:
260:
265: public void append(Shape s, boolean connect)
266: {
267: append(s.getPathIterator(null), connect);
268: }
269:
270:
287: public void append(PathIterator iter, boolean connect)
288: {
289:
290: float[] f = new float[6];
291: while (! iter.isDone())
292: {
293: switch (iter.currentSegment(f))
294: {
295: case PathIterator.SEG_MOVETO:
296: if (! connect || (index == 0))
297: {
298: moveTo(f[0], f[1]);
299: break;
300: }
301: if ((index >= 1) && (types[index - 1] == PathIterator.SEG_CLOSE)
302: && (f[0] == xpoints[index - 1])
303: && (f[1] == ypoints[index - 1]))
304: break;
305:
306:
307: case PathIterator.SEG_LINETO:
308: lineTo(f[0], f[1]);
309: break;
310: case PathIterator.SEG_QUADTO:
311: quadTo(f[0], f[1], f[2], f[3]);
312: break;
313: case PathIterator.SEG_CUBICTO:
314: curveTo(f[0], f[1], f[2], f[3], f[4], f[5]);
315: break;
316: case PathIterator.SEG_CLOSE:
317: closePath();
318: break;
319: }
320:
321: connect = false;
322: iter.next();
323: }
324: }
325:
326:
329: public int getWindingRule()
330: {
331: return rule;
332: }
333:
334:
340: public void setWindingRule(int rule)
341: {
342: if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO)
343: throw new IllegalArgumentException();
344: this.rule = rule;
345: }
346:
347:
350: public Point2D getCurrentPoint()
351: {
352: if (subpath < 0)
353: return null;
354: return new Point2D.Float(xpoints[index - 1], ypoints[index - 1]);
355: }
356:
357:
360: public void reset()
361: {
362: subpath = -1;
363: index = 0;
364: }
365:
366:
369: public void transform(AffineTransform xform)
370: {
371: double nx;
372: double ny;
373: double[] m = new double[6];
374: xform.getMatrix(m);
375: for (int i = 0; i < index; i++)
376: {
377: nx = m[0] * xpoints[i] + m[2] * ypoints[i] + m[4];
378: ny = m[1] * xpoints[i] + m[3] * ypoints[i] + m[5];
379: xpoints[i] = (float) nx;
380: ypoints[i] = (float) ny;
381: }
382: }
383:
384:
389: public Shape createTransformedShape(AffineTransform xform)
390: {
391: GeneralPath p = new GeneralPath(this);
392: p.transform(xform);
393: return p;
394: }
395:
396:
399: public Rectangle getBounds()
400: {
401: return getBounds2D().getBounds();
402: }
403:
404:
407: public Rectangle2D getBounds2D()
408: {
409: float x1;
410: float y1;
411: float x2;
412: float y2;
413:
414: if (index > 0)
415: {
416: x1 = x2 = xpoints[0];
417: y1 = y2 = ypoints[0];
418: }
419: else
420: x1 = x2 = y1 = y2 = 0.0f;
421:
422: for (int i = 0; i < index; i++)
423: {
424: x1 = Math.min(xpoints[i], x1);
425: y1 = Math.min(ypoints[i], y1);
426: x2 = Math.max(xpoints[i], x2);
427: y2 = Math.max(ypoints[i], y2);
428: }
429: return (new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1));
430: }
431:
432:
440: public boolean contains(double x, double y)
441: {
442: return (getWindingNumber(x, y) != 0);
443: }
444:
445:
452: public boolean contains(Point2D p)
453: {
454: return contains(p.getX(), p.getY());
455: }
456:
457:
463: public boolean contains(double x, double y, double w, double h)
464: {
465: if (! getBounds2D().intersects(x, y, w, h))
466: return false;
467:
468:
469: if (getAxisIntersections(x, y, false, w) != 0
470: || getAxisIntersections(x, y + h, false, w) != 0
471: || getAxisIntersections(x + w, y, true, h) != 0
472: || getAxisIntersections(x, y, true, h) != 0)
473: return false;
474:
475:
476: if (getWindingNumber(x, y) != 0)
477: return true;
478:
479: return false;
480: }
481:
482:
491: public boolean contains(Rectangle2D r)
492: {
493: return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
494: }
495:
496:
505: public boolean intersects(double x, double y, double w, double h)
506: {
507:
508: if (getAxisIntersections(x, y, false, w) != 0
509: || getAxisIntersections(x, y + h, false, w) != 0
510: || getAxisIntersections(x + w, y, true, h) != 0
511: || getAxisIntersections(x, y, true, h) != 0)
512: return true;
513:
514:
515: if (getWindingNumber(x, y) != 0)
516: return true;
517:
518: return false;
519: }
520:
521:
527: public boolean intersects(Rectangle2D r)
528: {
529: return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
530: }
531:
532:
537: private static class GeneralPathIterator implements PathIterator
538: {
539:
542: private static final int[] NUM_COORDS = {
543: 1,
544: 1,
545: 2,
546: 3,
547: 0};
548:
549:
553: final GeneralPath path;
554:
555:
558: private final AffineTransform transform;
559:
560:
563: private int pos;
564:
565:
574: GeneralPathIterator(GeneralPath path, AffineTransform transform)
575: {
576: this.path = path;
577: this.transform = transform;
578: }
579:
580:
583: public int getWindingRule()
584: {
585: return path.rule;
586: }
587:
588:
592: public boolean isDone()
593: {
594: return pos >= path.index;
595: }
596:
597:
600: public void next()
601: {
602: int seg;
603:
604:
607: seg = path.types[pos];
608: if (seg == SEG_CLOSE)
609: pos++;
610: else
611: pos += NUM_COORDS[seg];
612: }
613:
614:
617: public int currentSegment(float[] coords)
618: {
619: int seg;
620: int numCoords;
621:
622: seg = path.types[pos];
623: numCoords = NUM_COORDS[seg];
624: if (numCoords > 0)
625: {
626: for (int i = 0; i < numCoords; i++)
627: {
628: coords[i << 1] = path.xpoints[pos + i];
629: coords[(i << 1) + 1] = path.ypoints[pos + i];
630: }
631:
632: if (transform != null)
633: transform.transform(
634: coords,
635: 0, coords,
636: 0, numCoords);
637: }
638: return seg;
639: }
640:
641:
644: public int currentSegment(double[] coords)
645: {
646: int seg;
647: int numCoords;
648:
649: seg = path.types[pos];
650: numCoords = NUM_COORDS[seg];
651: if (numCoords > 0)
652: {
653: for (int i = 0; i < numCoords; i++)
654: {
655: coords[i << 1] = (double) path.xpoints[pos + i];
656: coords[(i << 1) + 1] = (double) path.ypoints[pos + i];
657: }
658: if (transform != null)
659: transform.transform(
660: coords,
661: 0, coords,
662: 0, numCoords);
663: }
664: return seg;
665: }
666: }
667:
668:
675: public PathIterator getPathIterator(AffineTransform at)
676: {
677: return new GeneralPathIterator(this, at);
678: }
679:
680:
683: public PathIterator getPathIterator(AffineTransform at, double flatness)
684: {
685: return new FlatteningPathIterator(getPathIterator(at), flatness);
686: }
687:
688:
698: public Object clone()
699: {
700:
701: return new GeneralPath(this);
702: }
703:
704:
708: private void ensureSize(int size)
709: {
710: if (subpath < 0)
711: throw new IllegalPathStateException("need initial moveto");
712: if (size <= xpoints.length)
713: return;
714: byte[] b = new byte[types.length << 1];
715: System.arraycopy(types, 0, b, 0, index);
716: types = b;
717: float[] f = new float[xpoints.length << 1];
718: System.arraycopy(xpoints, 0, f, 0, index);
719: xpoints = f;
720: f = new float[ypoints.length << 1];
721: System.arraycopy(ypoints, 0, f, 0, index);
722: ypoints = f;
723: }
724:
725:
729: private int getAxisIntersections(double x, double y, boolean useYaxis,
730: double distance)
731: {
732: return (evaluateCrossings(x, y, false, useYaxis, distance));
733: }
734:
735:
738: private int getWindingNumber(double x, double y)
739: {
740:
743: return (evaluateCrossings(x, y, true, true, BIG_VALUE));
744: }
745:
746:
757: private int evaluateCrossings(double x, double y, boolean neg,
758: boolean useYaxis, double distance)
759: {
760: float cx = 0.0f;
761: float cy = 0.0f;
762: float firstx = 0.0f;
763: float firsty = 0.0f;
764:
765: int negative = (neg) ? -1 : 1;
766: double x0;
767: double x1;
768: double x2;
769: double x3;
770: double y0;
771: double y1;
772: double y2;
773: double y3;
774: double[] r = new double[4];
775: int nRoots;
776: double epsilon = 0.0;
777: int pos = 0;
778: int windingNumber = 0;
779: boolean pathStarted = false;
780:
781: if (index == 0)
782: return (0);
783: if (useYaxis)
784: {
785: float[] swap1;
786: swap1 = ypoints;
787: ypoints = xpoints;
788: xpoints = swap1;
789: double swap2;
790: swap2 = y;
791: y = x;
792: x = swap2;
793: }
794:
795:
797: epsilon = ypoints[0] * 1E-7;
798:
799: if(epsilon == 0)
800: epsilon = 1E-7;
801:
802: pos = 0;
803: while (pos < index)
804: {
805: switch (types[pos])
806: {
807: case PathIterator.SEG_MOVETO:
808: if (pathStarted)
809: {
810: x0 = cx;
811: y0 = cy;
812: x1 = firstx;
813: y1 = firsty;
814:
815: if (y0 == 0.0)
816: y0 -= epsilon;
817: if (y1 == 0.0)
818: y1 -= epsilon;
819: if (Line2D.linesIntersect(x0, y0, x1, y1,
820: epsilon, 0.0, distance, 0.0))
821: windingNumber += (y1 < y0) ? 1 : negative;
822:
823: cx = firstx;
824: cy = firsty;
825: }
826: cx = firstx = xpoints[pos] - (float) x;
827: cy = firsty = ypoints[pos++] - (float) y;
828: pathStarted = true;
829: break;
830: case PathIterator.SEG_CLOSE:
831: x0 = cx;
832: y0 = cy;
833: x1 = firstx;
834: y1 = firsty;
835:
836: if (y0 == 0.0)
837: y0 -= epsilon;
838: if (y1 == 0.0)
839: y1 -= epsilon;
840: if (Line2D.linesIntersect(x0, y0, x1, y1,
841: epsilon, 0.0, distance, 0.0))
842: windingNumber += (y1 < y0) ? 1 : negative;
843:
844: cx = firstx;
845: cy = firsty;
846: pos++;
847: pathStarted = false;
848: break;
849: case PathIterator.SEG_LINETO:
850: x0 = cx;
851: y0 = cy;
852: x1 = xpoints[pos] - (float) x;
853: y1 = ypoints[pos++] - (float) y;
854:
855: if (y0 == 0.0)
856: y0 -= epsilon;
857: if (y1 == 0.0)
858: y1 -= epsilon;
859: if (Line2D.linesIntersect(x0, y0, x1, y1,
860: epsilon, 0.0, distance, 0.0))
861: windingNumber += (y1 < y0) ? 1 : negative;
862:
863: cx = xpoints[pos - 1] - (float) x;
864: cy = ypoints[pos - 1] - (float) y;
865: break;
866: case PathIterator.SEG_QUADTO:
867: x0 = cx;
868: y0 = cy;
869: x1 = xpoints[pos] - x;
870: y1 = ypoints[pos++] - y;
871: x2 = xpoints[pos] - x;
872: y2 = ypoints[pos++] - y;
873:
874:
875: if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0)
876: && (y0 * y1 <= 0 || y1 * y2 <= 0))
877: {
878: if (y0 == 0.0)
879: y0 -= epsilon;
880: if (y2 == 0.0)
881: y2 -= epsilon;
882:
883: r[0] = y0;
884: r[1] = 2 * (y1 - y0);
885: r[2] = (y2 - 2 * y1 + y0);
886:
887:
889: if ((nRoots = QuadCurve2D.solveQuadratic(r)) == 2)
890: for (int i = 0; i < nRoots; i++)
891: {
892: float t = (float) r[i];
893: if (t > 0.0f && t < 1.0f)
894: {
895: double crossing = t * t * (x2 - 2 * x1 + x0)
896: + 2 * t * (x1 - x0) + x0;
897: if (crossing >= 0.0 && crossing <= distance)
898: windingNumber += (2 * t * (y2 - 2 * y1 + y0)
899: + 2 * (y1 - y0) < 0) ? 1 : negative;
900: }
901: }
902: }
903:
904: cx = xpoints[pos - 1] - (float) x;
905: cy = ypoints[pos - 1] - (float) y;
906: break;
907: case PathIterator.SEG_CUBICTO:
908: x0 = cx;
909: y0 = cy;
910: x1 = xpoints[pos] - x;
911: y1 = ypoints[pos++] - y;
912: x2 = xpoints[pos] - x;
913: y2 = ypoints[pos++] - y;
914: x3 = xpoints[pos] - x;
915: y3 = ypoints[pos++] - y;
916:
917:
918: if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0 || x3 > 0.0)
919: && (y0 * y1 <= 0 || y1 * y2 <= 0 || y2 * y3 <= 0))
920: {
921: if (y0 == 0.0)
922: y0 -= epsilon;
923: if (y3 == 0.0)
924: y3 -= epsilon;
925:
926: r[0] = y0;
927: r[1] = 3 * (y1 - y0);
928: r[2] = 3 * (y2 + y0 - 2 * y1);
929: r[3] = y3 - 3 * y2 + 3 * y1 - y0;
930:
931: if ((nRoots = CubicCurve2D.solveCubic(r)) != 0)
932: for (int i = 0; i < nRoots; i++)
933: {
934: float t = (float) r[i];
935: if (t > 0.0 && t < 1.0)
936: {
937: double crossing = -(t * t * t) * (x0 - 3 * x1
938: + 3 * x2 - x3)
939: + 3 * t * t * (x0 - 2 * x1 + x2)
940: + 3 * t * (x1 - x0) + x0;
941: if (crossing >= 0 && crossing <= distance)
942: windingNumber += (3 * t * t * (y3 + 3 * y1
943: - 3 * y2 - y0)
944: + 6 * t * (y0 - 2 * y1 + y2)
945: + 3 * (y1 - y0) < 0) ? 1 : negative;
946: }
947: }
948: }
949:
950: cx = xpoints[pos - 1] - (float) x;
951: cy = ypoints[pos - 1] - (float) y;
952: break;
953: }
954: }
955:
956:
957: if (useYaxis)
958: {
959: float[] swap;
960: swap = ypoints;
961: ypoints = xpoints;
962: xpoints = swap;
963: }
964: return (windingNumber);
965: }
966: }