1:
37:
38:
39: package ;
40:
41: import ;
42: import ;
43:
44:
45:
80: public final class GeneralPath implements Shape, Cloneable
81: {
82: public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
83: public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
84:
85:
86: private static final int INIT_SIZE = 10;
87:
88:
89: private static final double BIG_VALUE = java.lang.Double.MAX_VALUE / 10.0;
90:
91:
94: int rule;
95:
96:
102: byte[] types;
103:
104:
112: float[] xpoints;
113: float[] ypoints;
114:
115:
116: private int subpath = -1;
117:
118:
121: int index;
122:
123:
127: public GeneralPath()
128: {
129: this(WIND_NON_ZERO, INIT_SIZE);
130: }
131:
132:
137: public GeneralPath(int rule)
138: {
139: this(rule, INIT_SIZE);
140: }
141:
142:
149: public GeneralPath(int rule, int capacity)
150: {
151: if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO)
152: throw new IllegalArgumentException();
153: this.rule = rule;
154: if (capacity < INIT_SIZE)
155: capacity = INIT_SIZE;
156: types = new byte[capacity];
157: xpoints = new float[capacity];
158: ypoints = new float[capacity];
159: }
160:
161:
166: public GeneralPath(Shape s)
167: {
168: types = new byte[INIT_SIZE];
169: xpoints = new float[INIT_SIZE];
170: ypoints = new float[INIT_SIZE];
171: PathIterator pi = s.getPathIterator(null);
172: setWindingRule(pi.getWindingRule());
173: append(pi, false);
174: }
175:
176:
179: public void moveTo(float x, float y)
180: {
181: subpath = index;
182: ensureSize(index + 1);
183: types[index] = PathIterator.SEG_MOVETO;
184: xpoints[index] = x;
185: ypoints[index++] = y;
186: }
187:
188:
193: public void lineTo(float x, float y)
194: {
195: ensureSize(index + 1);
196: types[index] = PathIterator.SEG_LINETO;
197: xpoints[index] = x;
198: ypoints[index++] = y;
199: }
200:
201:
208: public void quadTo(float x1, float y1, float x2, float y2)
209: {
210: ensureSize(index + 2);
211: types[index] = PathIterator.SEG_QUADTO;
212: xpoints[index] = x1;
213: ypoints[index++] = y1;
214: xpoints[index] = x2;
215: ypoints[index++] = y2;
216: }
217:
218:
227: public void curveTo(float x1, float y1, float x2, float y2, float x3,
228: float y3)
229: {
230: ensureSize(index + 3);
231: types[index] = PathIterator.SEG_CUBICTO;
232: xpoints[index] = x1;
233: ypoints[index++] = y1;
234: xpoints[index] = x2;
235: ypoints[index++] = y2;
236: xpoints[index] = x3;
237: ypoints[index++] = y3;
238: }
239:
240:
244: public void closePath()
245: {
246: ensureSize(index + 1);
247: types[index] = PathIterator.SEG_CLOSE;
248: xpoints[index] = xpoints[subpath];
249: ypoints[index++] = ypoints[subpath];
250: }
251:
252:
257: public void append(Shape s, boolean connect)
258: {
259: append(s.getPathIterator(null), connect);
260: }
261:
262:
279: public void append(PathIterator iter, boolean connect)
280: {
281:
282: float[] f = new float[6];
283: while (! iter.isDone())
284: {
285: switch (iter.currentSegment(f))
286: {
287: case PathIterator.SEG_MOVETO:
288: if (! connect || (index == 0))
289: {
290: moveTo(f[0], f[1]);
291: break;
292: }
293: if ((index >= 1) && (types[index - 1] == PathIterator.SEG_CLOSE)
294: && (f[0] == xpoints[index - 1])
295: && (f[1] == ypoints[index - 1]))
296: break;
297:
298:
299: case PathIterator.SEG_LINETO:
300: lineTo(f[0], f[1]);
301: break;
302: case PathIterator.SEG_QUADTO:
303: quadTo(f[0], f[1], f[2], f[3]);
304: break;
305: case PathIterator.SEG_CUBICTO:
306: curveTo(f[0], f[1], f[2], f[3], f[4], f[5]);
307: break;
308: case PathIterator.SEG_CLOSE:
309: closePath();
310: break;
311: }
312:
313: connect = false;
314: iter.next();
315: }
316: }
317:
318:
321: public int getWindingRule()
322: {
323: return rule;
324: }
325:
326:
332: public void setWindingRule(int rule)
333: {
334: if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO)
335: throw new IllegalArgumentException();
336: this.rule = rule;
337: }
338:
339:
342: public Point2D getCurrentPoint()
343: {
344: if (subpath < 0)
345: return null;
346: return new Point2D.Float(xpoints[index - 1], ypoints[index - 1]);
347: }
348:
349:
352: public void reset()
353: {
354: subpath = -1;
355: index = 0;
356: }
357:
358:
361: public void transform(AffineTransform xform)
362: {
363: double nx;
364: double ny;
365: double[] m = new double[6];
366: xform.getMatrix(m);
367: for (int i = 0; i < index; i++)
368: {
369: nx = m[0] * xpoints[i] + m[2] * ypoints[i] + m[4];
370: ny = m[1] * xpoints[i] + m[3] * ypoints[i] + m[5];
371: xpoints[i] = (float) nx;
372: ypoints[i] = (float) ny;
373: }
374: }
375:
376:
381: public Shape createTransformedShape(AffineTransform xform)
382: {
383: GeneralPath p = new GeneralPath(this);
384: p.transform(xform);
385: return p;
386: }
387:
388:
391: public Rectangle getBounds()
392: {
393: return getBounds2D().getBounds();
394: }
395:
396:
399: public Rectangle2D getBounds2D()
400: {
401: float x1;
402: float y1;
403: float x2;
404: float y2;
405:
406: if (index > 0)
407: {
408: x1 = x2 = xpoints[0];
409: y1 = y2 = ypoints[0];
410: }
411: else
412: x1 = x2 = y1 = y2 = 0.0f;
413:
414: for (int i = 0; i < index; i++)
415: {
416: x1 = Math.min(xpoints[i], x1);
417: y1 = Math.min(ypoints[i], y1);
418: x2 = Math.max(xpoints[i], x2);
419: y2 = Math.max(ypoints[i], y2);
420: }
421: return (new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1));
422: }
423:
424:
432: public boolean contains(double x, double y)
433: {
434: return (getWindingNumber(x, y) != 0);
435: }
436:
437:
444: public boolean contains(Point2D p)
445: {
446: return contains(p.getX(), p.getY());
447: }
448:
449:
455: public boolean contains(double x, double y, double w, double h)
456: {
457: if (! getBounds2D().intersects(x, y, w, h))
458: return false;
459:
460:
461: if (getAxisIntersections(x, y, false, w) != 0
462: || getAxisIntersections(x, y + h, false, w) != 0
463: || getAxisIntersections(x + w, y, true, h) != 0
464: || getAxisIntersections(x, y, true, h) != 0)
465: return false;
466:
467:
468: if (getWindingNumber(x, y) != 0)
469: return true;
470:
471: return false;
472: }
473:
474:
483: public boolean contains(Rectangle2D r)
484: {
485: return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
486: }
487:
488:
497: public boolean intersects(double x, double y, double w, double h)
498: {
499:
500: if (getAxisIntersections(x, y, false, w) != 0
501: || getAxisIntersections(x, y + h, false, w) != 0
502: || getAxisIntersections(x + w, y, true, h) != 0
503: || getAxisIntersections(x, y, true, h) != 0)
504: return true;
505:
506:
507: if (getWindingNumber(x, y) != 0)
508: return true;
509:
510: return false;
511: }
512:
513:
519: public boolean intersects(Rectangle2D r)
520: {
521: return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
522: }
523:
524:
529: private static class GeneralPathIterator implements PathIterator
530: {
531:
534: private static final int[] NUM_COORDS = {
535: 1,
536: 1,
537: 2,
538: 3,
539: 0};
540:
541:
545: final GeneralPath path;
546:
547:
550: private final AffineTransform transform;
551:
552:
555: private int pos;
556:
557:
565: GeneralPathIterator(GeneralPath path, AffineTransform transform)
566: {
567: this.path = path;
568: this.transform = transform;
569: }
570:
571:
574: public int getWindingRule()
575: {
576: return path.rule;
577: }
578:
579:
583: public boolean isDone()
584: {
585: return pos >= path.index;
586: }
587:
588:
591: public void next()
592: {
593: int seg;
594:
595:
598: seg = path.types[pos];
599: if (seg == SEG_CLOSE)
600: pos++;
601: else
602: pos += NUM_COORDS[seg];
603: }
604:
605:
608: public int currentSegment(float[] coords)
609: {
610: int seg;
611: int numCoords;
612:
613: seg = path.types[pos];
614: numCoords = NUM_COORDS[seg];
615: if (numCoords > 0)
616: {
617: for (int i = 0; i < numCoords; i++)
618: {
619: coords[i << 1] = path.xpoints[pos + i];
620: coords[(i << 1) + 1] = path.ypoints[pos + i];
621: }
622:
623: if (transform != null)
624: transform.transform(
625: coords,
626: 0, coords,
627: 0, numCoords);
628: }
629: return seg;
630: }
631:
632:
635: public int currentSegment(double[] coords)
636: {
637: int seg;
638: int numCoords;
639:
640: seg = path.types[pos];
641: numCoords = NUM_COORDS[seg];
642: if (numCoords > 0)
643: {
644: for (int i = 0; i < numCoords; i++)
645: {
646: coords[i << 1] = (double) path.xpoints[pos + i];
647: coords[(i << 1) + 1] = (double) path.ypoints[pos + i];
648: }
649: if (transform != null)
650: transform.transform(
651: coords,
652: 0, coords,
653: 0, numCoords);
654: }
655: return seg;
656: }
657: }
658:
659:
666: public PathIterator getPathIterator(AffineTransform at)
667: {
668: return new GeneralPathIterator(this, at);
669: }
670:
671:
674: public PathIterator getPathIterator(AffineTransform at, double flatness)
675: {
676: return new FlatteningPathIterator(getPathIterator(at), flatness);
677: }
678:
679:
689: public Object clone()
690: {
691:
692: return new GeneralPath(this);
693: }
694:
695:
699: private void ensureSize(int size)
700: {
701: if (subpath < 0)
702: throw new IllegalPathStateException("need initial moveto");
703: if (size <= xpoints.length)
704: return;
705: byte[] b = new byte[types.length << 1];
706: System.arraycopy(types, 0, b, 0, index);
707: types = b;
708: float[] f = new float[xpoints.length << 1];
709: System.arraycopy(xpoints, 0, f, 0, index);
710: xpoints = f;
711: f = new float[ypoints.length << 1];
712: System.arraycopy(ypoints, 0, f, 0, index);
713: ypoints = f;
714: }
715:
716:
720: private int getAxisIntersections(double x, double y, boolean useYaxis,
721: double distance)
722: {
723: return (evaluateCrossings(x, y, false, useYaxis, distance));
724: }
725:
726:
729: private int getWindingNumber(double x, double y)
730: {
731:
734: return (evaluateCrossings(x, y, true, true, BIG_VALUE));
735: }
736:
737:
748: private int evaluateCrossings(double x, double y, boolean neg,
749: boolean useYaxis, double distance)
750: {
751: float cx = 0.0f;
752: float cy = 0.0f;
753: float firstx = 0.0f;
754: float firsty = 0.0f;
755:
756: int negative = (neg) ? -1 : 1;
757: double x0;
758: double x1;
759: double x2;
760: double x3;
761: double y0;
762: double y1;
763: double y2;
764: double y3;
765: double[] r = new double[4];
766: int nRoots;
767: double epsilon = 0.0;
768: int pos = 0;
769: int windingNumber = 0;
770: boolean pathStarted = false;
771:
772: if (index == 0)
773: return (0);
774: if (useYaxis)
775: {
776: float[] swap1;
777: swap1 = ypoints;
778: ypoints = xpoints;
779: xpoints = swap1;
780: double swap2;
781: swap2 = y;
782: y = x;
783: x = swap2;
784: }
785:
786:
788: epsilon = ypoints[0] * 1E-7;
789:
790: if(epsilon == 0)
791: epsilon = 1E-7;
792:
793: pos = 0;
794: while (pos < index)
795: {
796: switch (types[pos])
797: {
798: case PathIterator.SEG_MOVETO:
799: if (pathStarted)
800: {
801: x0 = cx;
802: y0 = cy;
803: x1 = firstx;
804: y1 = firsty;
805:
806: if (y0 == 0.0)
807: y0 -= epsilon;
808: if (y1 == 0.0)
809: y1 -= epsilon;
810: if (Line2D.linesIntersect(x0, y0, x1, y1,
811: epsilon, 0.0, distance, 0.0))
812: windingNumber += (y1 < y0) ? 1 : negative;
813:
814: cx = firstx;
815: cy = firsty;
816: }
817: cx = firstx = xpoints[pos] - (float) x;
818: cy = firsty = ypoints[pos++] - (float) y;
819: pathStarted = true;
820: break;
821: case PathIterator.SEG_CLOSE:
822: x0 = cx;
823: y0 = cy;
824: x1 = firstx;
825: y1 = firsty;
826:
827: if (y0 == 0.0)
828: y0 -= epsilon;
829: if (y1 == 0.0)
830: y1 -= epsilon;
831: if (Line2D.linesIntersect(x0, y0, x1, y1,
832: epsilon, 0.0, distance, 0.0))
833: windingNumber += (y1 < y0) ? 1 : negative;
834:
835: cx = firstx;
836: cy = firsty;
837: pos++;
838: pathStarted = false;
839: break;
840: case PathIterator.SEG_LINETO:
841: x0 = cx;
842: y0 = cy;
843: x1 = xpoints[pos] - (float) x;
844: y1 = ypoints[pos++] - (float) y;
845:
846: if (y0 == 0.0)
847: y0 -= epsilon;
848: if (y1 == 0.0)
849: y1 -= epsilon;
850: if (Line2D.linesIntersect(x0, y0, x1, y1,
851: epsilon, 0.0, distance, 0.0))
852: windingNumber += (y1 < y0) ? 1 : negative;
853:
854: cx = xpoints[pos - 1] - (float) x;
855: cy = ypoints[pos - 1] - (float) y;
856: break;
857: case PathIterator.SEG_QUADTO:
858: x0 = cx;
859: y0 = cy;
860: x1 = xpoints[pos] - x;
861: y1 = ypoints[pos++] - y;
862: x2 = xpoints[pos] - x;
863: y2 = ypoints[pos++] - y;
864:
865:
866: if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0)
867: && (y0 * y1 <= 0 || y1 * y2 <= 0))
868: {
869: if (y0 == 0.0)
870: y0 -= epsilon;
871: if (y2 == 0.0)
872: y2 -= epsilon;
873:
874: r[0] = y0;
875: r[1] = 2 * (y1 - y0);
876: r[2] = (y2 - 2 * y1 + y0);
877:
878:
880: if ((nRoots = QuadCurve2D.solveQuadratic(r)) == 2)
881: for (int i = 0; i < nRoots; i++)
882: {
883: float t = (float) r[i];
884: if (t > 0.0f && t < 1.0f)
885: {
886: double crossing = t * t * (x2 - 2 * x1 + x0)
887: + 2 * t * (x1 - x0) + x0;
888: if (crossing >= 0.0 && crossing <= distance)
889: windingNumber += (2 * t * (y2 - 2 * y1 + y0)
890: + 2 * (y1 - y0) < 0) ? 1 : negative;
891: }
892: }
893: }
894:
895: cx = xpoints[pos - 1] - (float) x;
896: cy = ypoints[pos - 1] - (float) y;
897: break;
898: case PathIterator.SEG_CUBICTO:
899: x0 = cx;
900: y0 = cy;
901: x1 = xpoints[pos] - x;
902: y1 = ypoints[pos++] - y;
903: x2 = xpoints[pos] - x;
904: y2 = ypoints[pos++] - y;
905: x3 = xpoints[pos] - x;
906: y3 = ypoints[pos++] - y;
907:
908:
909: if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0 || x3 > 0.0)
910: && (y0 * y1 <= 0 || y1 * y2 <= 0 || y2 * y3 <= 0))
911: {
912: if (y0 == 0.0)
913: y0 -= epsilon;
914: if (y3 == 0.0)
915: y3 -= epsilon;
916:
917: r[0] = y0;
918: r[1] = 3 * (y1 - y0);
919: r[2] = 3 * (y2 + y0 - 2 * y1);
920: r[3] = y3 - 3 * y2 + 3 * y1 - y0;
921:
922: if ((nRoots = CubicCurve2D.solveCubic(r)) != 0)
923: for (int i = 0; i < nRoots; i++)
924: {
925: float t = (float) r[i];
926: if (t > 0.0 && t < 1.0)
927: {
928: double crossing = -(t * t * t) * (x0 - 3 * x1
929: + 3 * x2 - x3)
930: + 3 * t * t * (x0 - 2 * x1 + x2)
931: + 3 * t * (x1 - x0) + x0;
932: if (crossing >= 0 && crossing <= distance)
933: windingNumber += (3 * t * t * (y3 + 3 * y1
934: - 3 * y2 - y0)
935: + 6 * t * (y0 - 2 * y1 + y2)
936: + 3 * (y1 - y0) < 0) ? 1 : negative;
937: }
938: }
939: }
940:
941: cx = xpoints[pos - 1] - (float) x;
942: cy = ypoints[pos - 1] - (float) y;
943: break;
944: }
945: }
946:
947:
948: if (useYaxis)
949: {
950: float[] swap;
951: swap = ypoints;
952: ypoints = xpoints;
953: xpoints = swap;
954: }
955: return (windingNumber);
956: }
957: }