1:
37:
38: package ;
39:
40: import ;
41: import ;
42: import ;
43:
44:
45:
73: public class Area implements Shape, Cloneable
74: {
75:
78: private static final double EPSILON = 1E-11;
79:
80:
83: private static final double RS_EPSILON = 1E-13;
84:
85:
88: private static final double PE_EPSILON = 1E-11;
89:
90:
94: Vector solids;
95:
96:
100: Vector holes;
101:
102:
105: private Vector cc_intersections;
106:
107:
111: private int windingRule;
112:
113:
116: public Area()
117: {
118: solids = new Vector();
119: holes = new Vector();
120: }
121:
122:
134: public Area(Shape s)
135: {
136: this();
137:
138: Vector p = makeSegment(s);
139:
140:
141: if (p == null)
142: return;
143:
144:
145: for (int i = 0; i < p.size(); i++)
146: if (((Segment) p.elementAt(i)).getSignedArea() == 0.0)
147: p.remove(i--);
148:
149:
160: Vector paths = new Vector();
161: Segment v;
162:
163: for (int i = 0; i < p.size(); i++)
164: {
165: Segment path = (Segment) p.elementAt(i);
166: createNodesSelf(path);
167: }
168:
169: if (p.size() > 1)
170: {
171: for (int i = 0; i < p.size() - 1; i++)
172: for (int j = i + 1; j < p.size(); j++)
173: {
174: Segment path1 = (Segment) p.elementAt(i);
175: Segment path2 = (Segment) p.elementAt(j);
176: createNodes(path1, path2);
177: }
178: }
179:
180:
181: Vector segments = new Vector();
182:
183: for (int i = 0; i < p.size(); i++)
184: {
185: Segment path = v = (Segment) p.elementAt(i);
186: do
187: {
188: segments.add(v);
189: v = v.next;
190: }
191: while (v != path);
192: }
193:
194: paths = weilerAtherton(segments);
195: deleteRedundantPaths(paths);
196: }
197:
198:
202: public void add(Area area)
203: {
204: if (equals(area))
205: return;
206: if (area.isEmpty())
207: return;
208:
209: Area B = (Area) area.clone();
210:
211: Vector pathA = new Vector();
212: Vector pathB = new Vector();
213: pathA.addAll(solids);
214: pathA.addAll(holes);
215: pathB.addAll(B.solids);
216: pathB.addAll(B.holes);
217:
218: int nNodes = 0;
219:
220: for (int i = 0; i < pathA.size(); i++)
221: {
222: Segment a = (Segment) pathA.elementAt(i);
223: for (int j = 0; j < pathB.size(); j++)
224: {
225: Segment b = (Segment) pathB.elementAt(j);
226: nNodes += createNodes(a, b);
227: }
228: }
229:
230: Vector paths = new Vector();
231: Segment v;
232:
233:
234: Vector segments = new Vector();
235:
236:
237:
238: for (int i = 0; i < pathA.size(); i++)
239: {
240: v = (Segment) pathA.elementAt(i);
241: Segment path = v;
242: do
243: {
244: if (v.isSegmentOutside(area))
245: segments.add(v);
246: v = v.next;
247: }
248: while (v != path);
249: }
250:
251: for (int i = 0; i < pathB.size(); i++)
252: {
253: v = (Segment) pathB.elementAt(i);
254: Segment path = v;
255: do
256: {
257: if (v.isSegmentOutside(this))
258: segments.add(v);
259: v = v.next;
260: }
261: while (v != path);
262: }
263:
264: paths = weilerAtherton(segments);
265: deleteRedundantPaths(paths);
266: }
267:
268:
273: public void subtract(Area area)
274: {
275: if (isEmpty() || area.isEmpty())
276: return;
277:
278: if (equals(area))
279: {
280: reset();
281: return;
282: }
283:
284: Vector pathA = new Vector();
285: Area B = (Area) area.clone();
286: pathA.addAll(solids);
287: pathA.addAll(holes);
288:
289:
290: setDirection(B.holes, true);
291: setDirection(B.solids, false);
292:
293: Vector pathB = new Vector();
294: pathB.addAll(B.solids);
295: pathB.addAll(B.holes);
296:
297: int nNodes = 0;
298:
299:
300: for (int i = 0; i < pathA.size(); i++)
301: {
302: Segment a = (Segment) pathA.elementAt(i);
303: for (int j = 0; j < pathB.size(); j++)
304: {
305: Segment b = (Segment) pathB.elementAt(j);
306: nNodes += createNodes(a, b);
307: }
308: }
309:
310: Vector paths = new Vector();
311:
312:
313: Vector segments = new Vector();
314:
315:
316:
317:
318:
319: for (int i = 0; i < pathA.size(); i++)
320: {
321: Segment v = (Segment) pathA.elementAt(i);
322: Segment path = v;
323: if (v.isSegmentOutside(area) && v.node == null)
324: segments.add(v);
325: boolean node = false;
326: do
327: {
328: if ((v.node != null || node))
329: {
330: node = (v.node != null);
331: if (v.isSegmentOutside(area))
332: segments.add(v);
333: }
334: v = v.next;
335: }
336: while (v != path);
337: }
338:
339: for (int i = 0; i < pathB.size(); i++)
340: {
341: Segment v = (Segment) pathB.elementAt(i);
342: Segment path = v;
343: if (! v.isSegmentOutside(this) && v.node == null)
344: segments.add(v);
345: v = v.next;
346: boolean node = false;
347: do
348: {
349: if ((v.node != null || node))
350: {
351: node = (v.node != null);
352: if (! v.isSegmentOutside(this))
353: segments.add(v);
354: }
355: v = v.next;
356: }
357: while (v != path);
358: }
359:
360: paths = weilerAtherton(segments);
361: deleteRedundantPaths(paths);
362: }
363:
364:
369: public void intersect(Area area)
370: {
371: if (isEmpty() || area.isEmpty())
372: {
373: reset();
374: return;
375: }
376: if (equals(area))
377: return;
378:
379: Vector pathA = new Vector();
380: Area B = (Area) area.clone();
381: pathA.addAll(solids);
382: pathA.addAll(holes);
383:
384: Vector pathB = new Vector();
385: pathB.addAll(B.solids);
386: pathB.addAll(B.holes);
387:
388: int nNodes = 0;
389:
390:
391: for (int i = 0; i < pathA.size(); i++)
392: {
393: Segment a = (Segment) pathA.elementAt(i);
394: for (int j = 0; j < pathB.size(); j++)
395: {
396: Segment b = (Segment) pathB.elementAt(j);
397: nNodes += createNodes(a, b);
398: }
399: }
400:
401: Vector paths = new Vector();
402:
403:
404: Vector segments = new Vector();
405:
406:
407:
408:
409:
410:
411: for (int i = 0; i < pathA.size(); i++)
412: {
413: Segment v = (Segment) pathA.elementAt(i);
414: Segment path = v;
415: if (! v.isSegmentOutside(area) && v.node == null)
416: segments.add(v);
417: boolean node = false;
418: do
419: {
420: if ((v.node != null || node))
421: {
422: node = (v.node != null);
423: if (! v.isSegmentOutside(area))
424: segments.add(v);
425: }
426: v = v.next;
427: }
428: while (v != path);
429: }
430:
431: for (int i = 0; i < pathB.size(); i++)
432: {
433: Segment v = (Segment) pathB.elementAt(i);
434: Segment path = v;
435: if (! v.isSegmentOutside(this) && v.node == null)
436: segments.add(v);
437: v = v.next;
438: boolean node = false;
439: do
440: {
441: if ((v.node != null || node))
442: {
443: node = (v.node != null);
444: if (! v.isSegmentOutside(this))
445: segments.add(v);
446: }
447: v = v.next;
448: }
449: while (v != path);
450: }
451:
452: paths = weilerAtherton(segments);
453: deleteRedundantPaths(paths);
454: }
455:
456:
461: public void exclusiveOr(Area area)
462: {
463: if (area.isEmpty())
464: return;
465:
466: if (isEmpty())
467: {
468: Area B = (Area) area.clone();
469: solids = B.solids;
470: holes = B.holes;
471: return;
472: }
473: if (equals(area))
474: {
475: reset();
476: return;
477: }
478:
479: Vector pathA = new Vector();
480:
481: Area B = (Area) area.clone();
482: Vector pathB = new Vector();
483: pathA.addAll(solids);
484: pathA.addAll(holes);
485:
486:
487: setDirection(B.holes, true);
488: setDirection(B.solids, false);
489: pathB.addAll(B.solids);
490: pathB.addAll(B.holes);
491:
492: int nNodes = 0;
493:
494: for (int i = 0; i < pathA.size(); i++)
495: {
496: Segment a = (Segment) pathA.elementAt(i);
497: for (int j = 0; j < pathB.size(); j++)
498: {
499: Segment b = (Segment) pathB.elementAt(j);
500: nNodes += createNodes(a, b);
501: }
502: }
503:
504: Vector paths = new Vector();
505: Segment v;
506:
507:
508: Vector segments = new Vector();
509:
510:
511: for (int i = 0; i < pathA.size(); i++)
512: {
513: v = (Segment) pathA.elementAt(i);
514: Segment path = v;
515: do
516: {
517: segments.add(v);
518: v = v.next;
519: }
520: while (v != path);
521: }
522:
523: for (int i = 0; i < pathB.size(); i++)
524: {
525: v = (Segment) pathB.elementAt(i);
526: Segment path = v;
527: do
528: {
529: segments.add(v);
530: v = v.next;
531: }
532: while (v != path);
533: }
534:
535: paths = weilerAtherton(segments);
536: deleteRedundantPaths(paths);
537: }
538:
539:
542: public void reset()
543: {
544: solids = new Vector();
545: holes = new Vector();
546: }
547:
548:
552: public boolean isEmpty()
553: {
554: if (solids.size() == 0)
555: return true;
556:
557: double totalArea = 0;
558: for (int i = 0; i < solids.size(); i++)
559: totalArea += Math.abs(((Segment) solids.elementAt(i)).getSignedArea());
560: for (int i = 0; i < holes.size(); i++)
561: totalArea -= Math.abs(((Segment) holes.elementAt(i)).getSignedArea());
562: if (totalArea <= EPSILON)
563: return true;
564:
565: return false;
566: }
567:
568:
572: public boolean isPolygonal()
573: {
574: for (int i = 0; i < holes.size(); i++)
575: if (! ((Segment) holes.elementAt(i)).isPolygonal())
576: return false;
577: for (int i = 0; i < solids.size(); i++)
578: if (! ((Segment) solids.elementAt(i)).isPolygonal())
579: return false;
580: return true;
581: }
582:
583:
594: public boolean isRectangular()
595: {
596: if (isEmpty())
597: return true;
598:
599: if (holes.size() != 0 || solids.size() != 1)
600: return false;
601:
602: Segment path = (Segment) solids.elementAt(0);
603: if (! path.isPolygonal())
604: return false;
605:
606: int nCorners = 0;
607: Segment s = path;
608: do
609: {
610: Segment s2 = s.next;
611: double d1 = (s.P2.getX() - s.P1.getX())*(s2.P2.getX() - s2.P1.getX())/
612: ((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2)));
613: double d2 = (s.P2.getY() - s.P1.getY())*(s2.P2.getY() - s2.P1.getY())/
614: ((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2)));
615: double dotproduct = d1 + d2;
616:
617:
618: if (d1 != 0 && d2 != 0)
619: return false;
620:
621: if (Math.abs(dotproduct) == 0)
622: nCorners++;
623: else if ((Math.abs(1.0 - dotproduct) > 0))
624: return false;
625:
626: s = s.next;
627: }
628: while (s != path);
629:
630: return nCorners == 4;
631: }
632:
633:
640: public boolean isSingular()
641: {
642: return (holes.size() == 0 && solids.size() <= 1);
643: }
644:
645:
651: public Rectangle2D getBounds2D()
652: {
653: if (solids.size() == 0)
654: return new Rectangle2D.Double(0.0, 0.0, 0.0, 0.0);
655:
656: double xmin;
657: double xmax;
658: double ymin;
659: double ymax;
660: xmin = xmax = ((Segment) solids.elementAt(0)).P1.getX();
661: ymin = ymax = ((Segment) solids.elementAt(0)).P1.getY();
662:
663: for (int path = 0; path < solids.size(); path++)
664: {
665: Rectangle2D r = ((Segment) solids.elementAt(path)).getPathBounds();
666: xmin = Math.min(r.getMinX(), xmin);
667: ymin = Math.min(r.getMinY(), ymin);
668: xmax = Math.max(r.getMaxX(), xmax);
669: ymax = Math.max(r.getMaxY(), ymax);
670: }
671:
672: return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin)));
673: }
674:
675:
682: public Rectangle getBounds()
683: {
684: return getBounds2D().getBounds();
685: }
686:
687:
693: public Object clone()
694: {
695: try
696: {
697: Area clone = new Area();
698: for (int i = 0; i < solids.size(); i++)
699: clone.solids.add(((Segment) solids.elementAt(i)).cloneSegmentList());
700: for (int i = 0; i < holes.size(); i++)
701: clone.holes.add(((Segment) holes.elementAt(i)).cloneSegmentList());
702: return clone;
703: }
704: catch (CloneNotSupportedException e)
705: {
706: throw (Error) new InternalError().initCause(e);
707: }
708: }
709:
710:
718: public boolean equals(Area area)
719: {
720: if (area == null)
721: return false;
722:
723: if (! getBounds2D().equals(area.getBounds2D()))
724: return false;
725:
726: if (solids.size() != area.solids.size()
727: || holes.size() != area.holes.size())
728: return false;
729:
730: Vector pathA = new Vector();
731: pathA.addAll(solids);
732: pathA.addAll(holes);
733: Vector pathB = new Vector();
734: pathB.addAll(area.solids);
735: pathB.addAll(area.holes);
736:
737: int nPaths = pathA.size();
738: boolean[][] match = new boolean[2][nPaths];
739:
740: for (int i = 0; i < nPaths; i++)
741: {
742: for (int j = 0; j < nPaths; j++)
743: {
744: Segment p1 = (Segment) pathA.elementAt(i);
745: Segment p2 = (Segment) pathB.elementAt(j);
746: if (! match[0][i] && ! match[1][j])
747: if (p1.pathEquals(p2))
748: match[0][i] = match[1][j] = true;
749: }
750: }
751:
752: boolean result = true;
753: for (int i = 0; i < nPaths; i++)
754: result = result && match[0][i] && match[1][i];
755: return result;
756: }
757:
758:
763: public void transform(AffineTransform at)
764: {
765: for (int i = 0; i < solids.size(); i++)
766: ((Segment) solids.elementAt(i)).transformSegmentList(at);
767: for (int i = 0; i < holes.size(); i++)
768: ((Segment) holes.elementAt(i)).transformSegmentList(at);
769:
770:
771: if ((at.getType() & AffineTransform.TYPE_FLIP) != 0)
772: {
773: setDirection(holes, false);
774: setDirection(solids, true);
775: }
776: }
777:
778:
785: public Area createTransformedArea(AffineTransform at)
786: {
787: Area a = (Area) clone();
788: a.transform(at);
789: return a;
790: }
791:
792:
799: public boolean contains(double x, double y)
800: {
801: int n = 0;
802: for (int i = 0; i < solids.size(); i++)
803: if (((Segment) solids.elementAt(i)).contains(x, y))
804: n++;
805:
806: for (int i = 0; i < holes.size(); i++)
807: if (((Segment) holes.elementAt(i)).contains(x, y))
808: n--;
809:
810: return (n != 0);
811: }
812:
813:
821: public boolean contains(Point2D p)
822: {
823: return contains(p.getX(), p.getY());
824: }
825:
826:
840: public boolean contains(double x, double y, double w, double h)
841: {
842: LineSegment[] l = new LineSegment[4];
843: l[0] = new LineSegment(x, y, x + w, y);
844: l[1] = new LineSegment(x, y + h, x + w, y + h);
845: l[2] = new LineSegment(x, y, x, y + h);
846: l[3] = new LineSegment(x + w, y, x + w, y + h);
847:
848:
849:
850:
851: for (int i = 0; i < 4; i++)
852: {
853: for (int path = 0; path < solids.size(); path++)
854: {
855: Segment v;
856: Segment start;
857: start = v = (Segment) solids.elementAt(path);
858: do
859: {
860: if (l[i].hasIntersections(v))
861: return false;
862: v = v.next;
863: }
864: while (v != start);
865: }
866: for (int path = 0; path < holes.size(); path++)
867: {
868: Segment v;
869: Segment start;
870: start = v = (Segment) holes.elementAt(path);
871: do
872: {
873: if (l[i].hasIntersections(v))
874: return false;
875: v = v.next;
876: }
877: while (v != start);
878: }
879: }
880:
881:
882: if (! contains(x, y))
883: return false;
884:
885:
886:
887: Rectangle2D r = new Rectangle2D.Double(x, y, w, h);
888: for (int path = 0; path < holes.size(); path++)
889: if (! ((Segment) holes.elementAt(path)).isSegmentOutside(r))
890: return false;
891:
892: return true;
893: }
894:
895:
907: public boolean contains(Rectangle2D r)
908: {
909: return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
910: }
911:
912:
923: public boolean intersects(double x, double y, double w, double h)
924: {
925: if (solids.size() == 0)
926: return false;
927:
928: LineSegment[] l = new LineSegment[4];
929: l[0] = new LineSegment(x, y, x + w, y);
930: l[1] = new LineSegment(x, y + h, x + w, y + h);
931: l[2] = new LineSegment(x, y, x, y + h);
932: l[3] = new LineSegment(x + w, y, x + w, y + h);
933:
934:
935: for (int i = 0; i < 4; i++)
936: {
937: for (int path = 0; path < solids.size(); path++)
938: {
939: Segment v;
940: Segment start;
941: start = v = (Segment) solids.elementAt(path);
942: do
943: {
944: if (l[i].hasIntersections(v))
945: return true;
946: v = v.next;
947: }
948: while (v != start);
949: }
950: for (int path = 0; path < holes.size(); path++)
951: {
952: Segment v;
953: Segment start;
954: start = v = (Segment) holes.elementAt(path);
955: do
956: {
957: if (l[i].hasIntersections(v))
958: return true;
959: v = v.next;
960: }
961: while (v != start);
962: }
963: }
964:
965:
966: if (contains(x + w * 0.5, y + h * 0.5))
967: return true;
968:
969:
970: Point2D p = ((Segment) solids.elementAt(0)).getMidPoint();
971: if ((new Rectangle2D.Double(x, y, w, h)).contains(p))
972: return true;
973: return false;
974: }
975:
976:
985: public boolean intersects(Rectangle2D r)
986: {
987: return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
988: }
989:
990:
997: public PathIterator getPathIterator(AffineTransform at)
998: {
999: return (new AreaIterator(at));
1000: }
1001:
1002:
1010: public PathIterator getPathIterator(AffineTransform at, double flatness)
1011: {
1012: return new FlatteningPathIterator(getPathIterator(at), flatness);
1013: }
1014:
1015:
1016:
1017:
1018:
1021: private class AreaIterator implements PathIterator
1022: {
1023: private Vector segments;
1024: private int index;
1025: private AffineTransform at;
1026:
1027:
1028: class IteratorSegment
1029: {
1030: int type;
1031: double[] coords;
1032:
1033: IteratorSegment()
1034: {
1035: coords = new double[6];
1036: }
1037: }
1038:
1039:
1044: public AreaIterator(AffineTransform at)
1045: {
1046: this.at = at;
1047: index = 0;
1048: segments = new Vector();
1049: Vector allpaths = new Vector();
1050: allpaths.addAll(solids);
1051: allpaths.addAll(holes);
1052:
1053: for (int i = 0; i < allpaths.size(); i++)
1054: {
1055: Segment v = (Segment) allpaths.elementAt(i);
1056: Segment start = v;
1057:
1058: IteratorSegment is = new IteratorSegment();
1059: is.type = SEG_MOVETO;
1060: is.coords[0] = start.P1.getX();
1061: is.coords[1] = start.P1.getY();
1062: segments.add(is);
1063:
1064: do
1065: {
1066: is = new IteratorSegment();
1067: is.type = v.pathIteratorFormat(is.coords);
1068: segments.add(is);
1069: v = v.next;
1070: }
1071: while (v != start);
1072:
1073: is = new IteratorSegment();
1074: is.type = SEG_CLOSE;
1075: segments.add(is);
1076: }
1077: }
1078:
1079: public int currentSegment(double[] coords)
1080: {
1081: IteratorSegment s = (IteratorSegment) segments.elementAt(index);
1082: if (at != null)
1083: at.transform(s.coords, 0, coords, 0, 3);
1084: else
1085: for (int i = 0; i < 6; i++)
1086: coords[i] = s.coords[i];
1087: return (s.type);
1088: }
1089:
1090: public int currentSegment(float[] coords)
1091: {
1092: IteratorSegment s = (IteratorSegment) segments.elementAt(index);
1093: double[] d = new double[6];
1094: if (at != null)
1095: {
1096: at.transform(s.coords, 0, d, 0, 3);
1097: for (int i = 0; i < 6; i++)
1098: coords[i] = (float) d[i];
1099: }
1100: else
1101: for (int i = 0; i < 6; i++)
1102: coords[i] = (float) s.coords[i];
1103: return (s.type);
1104: }
1105:
1106:
1107:
1108: public int getWindingRule()
1109: {
1110: return (PathIterator.WIND_EVEN_ODD);
1111: }
1112:
1113: public boolean isDone()
1114: {
1115: return (index >= segments.size());
1116: }
1117:
1118: public void next()
1119: {
1120: index++;
1121: }
1122: }
1123:
1124:
1132: private Vector weilerAtherton(Vector segments)
1133: {
1134: Vector paths = new Vector();
1135: while (segments.size() > 0)
1136: {
1137:
1138: Segment start = (Segment) segments.elementAt(0);
1139: Segment s = start;
1140: do
1141: {
1142: segments.remove(s);
1143: if (s.node != null)
1144: {
1145: s.next = s.node;
1146: s.node = null;
1147: }
1148: s = s.next;
1149: }
1150: while (s != start);
1151:
1152: paths.add(start);
1153: }
1154: return paths;
1155: }
1156:
1157:
1160: private class Intersection
1161: {
1162: Point2D p;
1163: double ta;
1164: double tb;
1165: Segment seg;
1166:
1167: public Intersection(Point2D p, double ta, double tb)
1168: {
1169: this.p = p;
1170: this.ta = ta;
1171: this.tb = tb;
1172: }
1173: }
1174:
1175:
1184: private int getRecursionDepth(CubicSegment curve)
1185: {
1186: double x0 = curve.P1.getX();
1187: double y0 = curve.P1.getY();
1188:
1189: double x1 = curve.cp1.getX();
1190: double y1 = curve.cp1.getY();
1191:
1192: double x2 = curve.cp2.getX();
1193: double y2 = curve.cp2.getY();
1194:
1195: double x3 = curve.P2.getX();
1196: double y3 = curve.P2.getY();
1197:
1198: double L0 = Math.max(Math.max(Math.abs(x0 - 2 * x1 + x2),
1199: Math.abs(x1 - 2 * x2 + x3)),
1200: Math.max(Math.abs(y0 - 2 * y1 + y2),
1201: Math.abs(y1 - 2 * y2 + y3)));
1202:
1203: double f = Math.sqrt(2) * 6.0 * L0 / (8.0 * RS_EPSILON);
1204:
1205: int r0 = (int) Math.ceil(Math.log(f) / Math.log(4.0));
1206: return (r0);
1207: }
1208:
1209:
1224: private void recursiveSubdivide(CubicCurve2D c1, CubicCurve2D c2,
1225: int depth1, int depth2, double t1,
1226: double t2, double w1, double w2)
1227: {
1228: boolean flat1 = depth1 <= 0;
1229: boolean flat2 = depth2 <= 0;
1230:
1231: if (flat1 && flat2)
1232: {
1233: double xlk = c1.getP2().getX() - c1.getP1().getX();
1234: double ylk = c1.getP2().getY() - c1.getP1().getY();
1235:
1236: double xnm = c2.getP2().getX() - c2.getP1().getX();
1237: double ynm = c2.getP2().getY() - c2.getP1().getY();
1238:
1239: double xmk = c2.getP1().getX() - c1.getP1().getX();
1240: double ymk = c2.getP1().getY() - c1.getP1().getY();
1241: double det = xnm * ylk - ynm * xlk;
1242:
1243: if (det + 1.0 == 1.0)
1244: return;
1245:
1246: double detinv = 1.0 / det;
1247: double s = (xnm * ymk - ynm * xmk) * detinv;
1248: double t = (xlk * ymk - ylk * xmk) * detinv;
1249: if ((s < 0.0) || (s > 1.0) || (t < 0.0) || (t > 1.0))
1250: return;
1251:
1252: double[] temp = new double[2];
1253: temp[0] = t1 + s * w1;
1254: temp[1] = t2 + t * w1;
1255: cc_intersections.add(temp);
1256: return;
1257: }
1258:
1259: CubicCurve2D.Double c11 = new CubicCurve2D.Double();
1260: CubicCurve2D.Double c12 = new CubicCurve2D.Double();
1261: CubicCurve2D.Double c21 = new CubicCurve2D.Double();
1262: CubicCurve2D.Double c22 = new CubicCurve2D.Double();
1263:
1264: if (! flat1 && ! flat2)
1265: {
1266: depth1--;
1267: depth2--;
1268: w1 = w1 * 0.5;
1269: w2 = w2 * 0.5;
1270: c1.subdivide(c11, c12);
1271: c2.subdivide(c21, c22);
1272: if (c11.getBounds2D().intersects(c21.getBounds2D()))
1273: recursiveSubdivide(c11, c21, depth1, depth2, t1, t2, w1, w2);
1274: if (c11.getBounds2D().intersects(c22.getBounds2D()))
1275: recursiveSubdivide(c11, c22, depth1, depth2, t1, t2 + w2, w1, w2);
1276: if (c12.getBounds2D().intersects(c21.getBounds2D()))
1277: recursiveSubdivide(c12, c21, depth1, depth2, t1 + w1, t2, w1, w2);
1278: if (c12.getBounds2D().intersects(c22.getBounds2D()))
1279: recursiveSubdivide(c12, c22, depth1, depth2, t1 + w1, t2 + w2, w1, w2);
1280: return;
1281: }
1282:
1283: if (! flat1)
1284: {
1285: depth1--;
1286: c1.subdivide(c11, c12);
1287: w1 = w1 * 0.5;
1288: if (c11.getBounds2D().intersects(c2.getBounds2D()))
1289: recursiveSubdivide(c11, c2, depth1, depth2, t1, t2, w1, w2);
1290: if (c12.getBounds2D().intersects(c2.getBounds2D()))
1291: recursiveSubdivide(c12, c2, depth1, depth2, t1 + w1, t2, w1, w2);
1292: return;
1293: }
1294:
1295: depth2--;
1296: c2.subdivide(c21, c22);
1297: w2 = w2 * 0.5;
1298: if (c1.getBounds2D().intersects(c21.getBounds2D()))
1299: recursiveSubdivide(c1, c21, depth1, depth2, t1, t2, w1, w2);
1300: if (c1.getBounds2D().intersects(c22.getBounds2D()))
1301: recursiveSubdivide(c1, c22, depth1, depth2, t1, t2 + w2, w1, w2);
1302: }
1303:
1304:
1323: Intersection[] cubicCubicIntersect(CubicSegment curve1, CubicSegment curve2)
1324: {
1325: Rectangle2D r1 = curve1.getBounds();
1326: Rectangle2D r2 = curve2.getBounds();
1327:
1328: if (! r1.intersects(r2))
1329: return null;
1330:
1331: cc_intersections = new Vector();
1332: recursiveSubdivide(curve1.getCubicCurve2D(), curve2.getCubicCurve2D(),
1333: getRecursionDepth(curve1), getRecursionDepth(curve2),
1334: 0.0, 0.0, 1.0, 1.0);
1335:
1336: if (cc_intersections.size() == 0)
1337: return null;
1338:
1339: Intersection[] results = new Intersection[cc_intersections.size()];
1340: for (int i = 0; i < cc_intersections.size(); i++)
1341: {
1342: double[] temp = (double[]) cc_intersections.elementAt(i);
1343: results[i] = new Intersection(curve1.evaluatePoint(temp[0]), temp[0],
1344: temp[1]);
1345: }
1346: cc_intersections = null;
1347: return (results);
1348: }
1349:
1350:
1357: Intersection[] lineQuadIntersect(LineSegment l, QuadSegment c)
1358: {
1359: double[] y = new double[3];
1360: double[] x = new double[3];
1361: double[] r = new double[3];
1362: int nRoots;
1363: double x0 = c.P1.getX();
1364: double y0 = c.P1.getY();
1365: double x1 = c.cp.getX();
1366: double y1 = c.cp.getY();
1367: double x2 = c.P2.getX();
1368: double y2 = c.P2.getY();
1369:
1370: double lx0 = l.P1.getX();
1371: double ly0 = l.P1.getY();
1372: double lx1 = l.P2.getX();
1373: double ly1 = l.P2.getY();
1374: double dx = lx1 - lx0;
1375: double dy = ly1 - ly0;
1376:
1377:
1378: y[0] = y0;
1379: y[1] = 2 * (y1 - y0);
1380: y[2] = (y2 - 2 * y1 + y0);
1381:
1382: x[0] = x0;
1383: x[1] = 2 * (x1 - x0);
1384: x[2] = (x2 - 2 * x1 + x0);
1385:
1386:
1387: if (dy == 0 && dx == 0)
1388: return null;
1389:
1390:
1391: if (dx == 0 || (dy / dx) > 1.0)
1392: {
1393: double k = dx / dy;
1394: x[0] -= lx0;
1395: y[0] -= ly0;
1396: y[0] *= k;
1397: y[1] *= k;
1398: y[2] *= k;
1399: }
1400: else
1401: {
1402: double k = dy / dx;
1403: x[0] -= lx0;
1404: y[0] -= ly0;
1405: x[0] *= k;
1406: x[1] *= k;
1407: x[2] *= k;
1408: }
1409:
1410: for (int i = 0; i < 3; i++)
1411: r[i] = y[i] - x[i];
1412:
1413: if ((nRoots = QuadCurve2D.solveQuadratic(r)) > 0)
1414: {
1415: Intersection[] temp = new Intersection[nRoots];
1416: int intersections = 0;
1417: for (int i = 0; i < nRoots; i++)
1418: {
1419: double t = r[i];
1420: if (t >= 0.0 && t <= 1.0)
1421: {
1422: Point2D p = c.evaluatePoint(t);
1423:
1424:
1425: if (dx == 0)
1426: p.setLocation(lx0, p.getY());
1427: if (dy == 0)
1428: p.setLocation(p.getX(), ly0);
1429:
1430: if (p.getX() <= Math.max(lx0, lx1)
1431: && p.getX() >= Math.min(lx0, lx1)
1432: && p.getY() <= Math.max(ly0, ly1)
1433: && p.getY() >= Math.min(ly0, ly1))
1434: {
1435: double lineparameter = p.distance(l.P1) / l.P2.distance(l.P1);
1436: temp[i] = new Intersection(p, lineparameter, t);
1437: intersections++;
1438: }
1439: }
1440: else
1441: temp[i] = null;
1442: }
1443: if (intersections == 0)
1444: return null;
1445:
1446: Intersection[] rValues = new Intersection[intersections];
1447:
1448: for (int i = 0; i < nRoots; i++)
1449: if (temp[i] != null)
1450: rValues[--intersections] = temp[i];
1451: return (rValues);
1452: }
1453: return null;
1454: }
1455:
1456:
1462: Intersection[] lineCubicIntersect(LineSegment l, CubicSegment c)
1463: {
1464: double[] y = new double[4];
1465: double[] x = new double[4];
1466: double[] r = new double[4];
1467: int nRoots;
1468: double x0 = c.P1.getX();
1469: double y0 = c.P1.getY();
1470: double x1 = c.cp1.getX();
1471: double y1 = c.cp1.getY();
1472: double x2 = c.cp2.getX();
1473: double y2 = c.cp2.getY();
1474: double x3 = c.P2.getX();
1475: double y3 = c.P2.getY();
1476:
1477: double lx0 = l.P1.getX();
1478: double ly0 = l.P1.getY();
1479: double lx1 = l.P2.getX();
1480: double ly1 = l.P2.getY();
1481: double dx = lx1 - lx0;
1482: double dy = ly1 - ly0;
1483:
1484:
1485: y[0] = y0;
1486: y[1] = 3 * (y1 - y0);
1487: y[2] = 3 * (y2 + y0 - 2 * y1);
1488: y[3] = y3 - 3 * y2 + 3 * y1 - y0;
1489:
1490: x[0] = x0;
1491: x[1] = 3 * (x1 - x0);
1492: x[2] = 3 * (x2 + x0 - 2 * x1);
1493: x[3] = x3 - 3 * x2 + 3 * x1 - x0;
1494:
1495:
1496: if (dy == 0 && dx == 0)
1497: return null;
1498:
1499:
1500: if (dx == 0 || (dy / dx) > 1.0)
1501: {
1502: double k = dx / dy;
1503: x[0] -= lx0;
1504: y[0] -= ly0;
1505: y[0] *= k;
1506: y[1] *= k;
1507: y[2] *= k;
1508: y[3] *= k;
1509: }
1510: else
1511: {
1512: double k = dy / dx;
1513: x[0] -= lx0;
1514: y[0] -= ly0;
1515: x[0] *= k;
1516: x[1] *= k;
1517: x[2] *= k;
1518: x[3] *= k;
1519: }
1520: for (int i = 0; i < 4; i++)
1521: r[i] = y[i] - x[i];
1522:
1523: if ((nRoots = CubicCurve2D.solveCubic(r)) > 0)
1524: {
1525: Intersection[] temp = new Intersection[nRoots];
1526: int intersections = 0;
1527: for (int i = 0; i < nRoots; i++)
1528: {
1529: double t = r[i];
1530: if (t >= 0.0 && t <= 1.0)
1531: {
1532:
1533: Point2D p = c.evaluatePoint(t);
1534: if (dx == 0)
1535: p.setLocation(lx0, p.getY());
1536: if (dy == 0)
1537: p.setLocation(p.getX(), ly0);
1538:
1539: if (p.getX() <= Math.max(lx0, lx1)
1540: && p.getX() >= Math.min(lx0, lx1)
1541: && p.getY() <= Math.max(ly0, ly1)
1542: && p.getY() >= Math.min(ly0, ly1))
1543: {
1544: double lineparameter = p.distance(l.P1) / l.P2.distance(l.P1);
1545: temp[i] = new Intersection(p, lineparameter, t);
1546: intersections++;
1547: }
1548: }
1549: else
1550: temp[i] = null;
1551: }
1552:
1553: if (intersections == 0)
1554: return null;
1555:
1556: Intersection[] rValues = new Intersection[intersections];
1557: for (int i = 0; i < nRoots; i++)
1558: if (temp[i] != null)
1559: rValues[--intersections] = temp[i];
1560: return (rValues);
1561: }
1562: return null;
1563: }
1564:
1565:
1570: Intersection linesIntersect(LineSegment a, LineSegment b)
1571: {
1572: Point2D P1 = a.P1;
1573: Point2D P2 = a.P2;
1574: Point2D P3 = b.P1;
1575: Point2D P4 = b.P2;
1576:
1577: if (! Line2D.linesIntersect(P1.getX(), P1.getY(), P2.getX(), P2.getY(),
1578: P3.getX(), P3.getY(), P4.getX(), P4.getY()))
1579: return null;
1580:
1581: double x1 = P1.getX();
1582: double y1 = P1.getY();
1583: double rx = P2.getX() - x1;
1584: double ry = P2.getY() - y1;
1585:
1586: double x2 = P3.getX();
1587: double y2 = P3.getY();
1588: double sx = P4.getX() - x2;
1589: double sy = P4.getY() - y2;
1590:
1591: double determinant = sx * ry - sy * rx;
1592: double nom = (sx * (y2 - y1) + sy * (x1 - x2));
1593:
1594:
1595: if (Math.abs(determinant) < EPSILON)
1596: return null;
1597:
1598: nom = nom / determinant;
1599:
1600: if (nom == 0.0)
1601: return null;
1602: if (nom == 1.0)
1603: return null;
1604:
1605: Point2D p = new Point2D.Double(x1 + nom * rx, y1 + nom * ry);
1606:
1607: return new Intersection(p, p.distance(P1) / P1.distance(P2),
1608: p.distance(P3) / P3.distance(P4));
1609: }
1610:
1611:
1616: boolean pointEquals(Point2D a, Point2D b)
1617: {
1618: return (a.equals(b) || a.distance(b) < PE_EPSILON);
1619: }
1620:
1621:
1625: private Vector makeSegment(Shape s)
1626: {
1627: Vector paths = new Vector();
1628: PathIterator pi = s.getPathIterator(null);
1629: double[] coords = new double[6];
1630: Segment subpath = null;
1631: Segment current = null;
1632: double cx;
1633: double cy;
1634: double subpathx;
1635: double subpathy;
1636: cx = cy = subpathx = subpathy = 0.0;
1637:
1638: this.windingRule = pi.getWindingRule();
1639:
1640: while (! pi.isDone())
1641: {
1642: Segment v;
1643: switch (pi.currentSegment(coords))
1644: {
1645: case PathIterator.SEG_MOVETO:
1646: if (subpath != null)
1647: {
1648: current.next = new LineSegment(cx, cy, subpathx, subpathy);
1649: current = current.next;
1650: current.next = subpath;
1651: }
1652: subpath = null;
1653: subpathx = cx = coords[0];
1654: subpathy = cy = coords[1];
1655: break;
1656:
1657:
1658: case PathIterator.SEG_CLOSE:
1659: if (subpath != null && (subpathx != cx || subpathy != cy))
1660: {
1661: current.next = new LineSegment(cx, cy, subpathx, subpathy);
1662: current = current.next;
1663: current.next = subpath;
1664: cx = subpathx;
1665: cy = subpathy;
1666: subpath = null;
1667: }
1668: else if (subpath != null)
1669: {
1670: current.next = subpath;
1671: subpath = null;
1672: }
1673: break;
1674: case PathIterator.SEG_LINETO:
1675: if (cx != coords[0] || cy != coords[1])
1676: {
1677: v = new LineSegment(cx, cy, coords[0], coords[1]);
1678: if (subpath == null)
1679: {
1680: subpath = current = v;
1681: paths.add(subpath);
1682: }
1683: else
1684: {
1685: current.next = v;
1686: current = current.next;
1687: }
1688: cx = coords[0];
1689: cy = coords[1];
1690: }
1691: break;
1692: case PathIterator.SEG_QUADTO:
1693: v = new QuadSegment(cx, cy, coords[0], coords[1], coords[2],
1694: coords[3]);
1695: if (subpath == null)
1696: {
1697: subpath = current = v;
1698: paths.add(subpath);
1699: }
1700: else
1701: {
1702: current.next = v;
1703: current = current.next;
1704: }
1705: cx = coords[2];
1706: cy = coords[3];
1707: break;
1708: case PathIterator.SEG_CUBICTO:
1709: v = new CubicSegment(cx, cy, coords[0], coords[1], coords[2],
1710: coords[3], coords[4], coords[5]);
1711: if (subpath == null)
1712: {
1713: subpath = current = v;
1714: paths.add(subpath);
1715: }
1716: else
1717: {
1718: current.next = v;
1719: current = current.next;
1720: }
1721:
1722:
1723: double[] lpts = ((CubicSegment) v).getLoop();
1724: if (lpts != null)
1725: {
1726:
1727: v.subdivideInsert(lpts[0]);
1728: v.next.subdivideInsert((lpts[1] - lpts[0]) / (1.0 - lpts[0]));
1729:
1730: CubicSegment loop = (CubicSegment) v.next;
1731: v.next = loop.next;
1732: loop.next = loop;
1733:
1734: v.P2 = v.next.P1 = loop.P2 = loop.P1;
1735: paths.add(loop);
1736: current = v.next;
1737: }
1738:
1739: cx = coords[4];
1740: cy = coords[5];
1741: break;
1742: }
1743: pi.next();
1744: }
1745:
1746: if (subpath != null)
1747: {
1748: if (subpathx != cx || subpathy != cy)
1749: {
1750: current.next = new LineSegment(cx, cy, subpathx, subpathy);
1751: current = current.next;
1752: current.next = subpath;
1753: }
1754: else
1755: current.next = subpath;
1756: }
1757:
1758: if (paths.size() == 0)
1759: return (null);
1760:
1761: return (paths);
1762: }
1763:
1764:
1769: private int createNodes(Segment A, Segment B)
1770: {
1771: int nNodes = 0;
1772:
1773: Segment a = A;
1774: Segment b = B;
1775:
1776: do
1777: {
1778: do
1779: {
1780: nNodes += a.splitIntersections(b);
1781: b = b.next;
1782: }
1783: while (b != B);
1784:
1785: a = a.next;
1786: }
1787: while (a != A);
1788:
1789: return (nNodes);
1790: }
1791:
1792:
1797: private int createNodesSelf(Segment A)
1798: {
1799: int nNodes = 0;
1800: Segment a = A;
1801:
1802: if (A.next == A)
1803: return 0;
1804:
1805: do
1806: {
1807: Segment b = a.next;
1808: do
1809: {
1810: if (b != a)
1811: nNodes += a.splitIntersections(b);
1812: b = b.next;
1813: }
1814: while (b != A);
1815: a = a.next;
1816: }
1817: while (a != A);
1818:
1819: return (nNodes);
1820: }
1821:
1822:
1827: private void deleteRedundantPaths(Vector paths)
1828: {
1829: int npaths = paths.size();
1830:
1831: int[][] contains = new int[npaths][npaths];
1832: int[][] windingNumbers = new int[npaths][2];
1833: int neg;
1834: Rectangle2D[] bb = new Rectangle2D[npaths];
1835:
1836: neg = ((windingRule == PathIterator.WIND_NON_ZERO) ? -1 : 1);
1837:
1838: for (int i = 0; i < npaths; i++)
1839: bb[i] = ((Segment) paths.elementAt(i)).getPathBounds();
1840:
1841:
1842: for (int i = 0; i < npaths; i++)
1843: {
1844: Segment pathA = (Segment) paths.elementAt(i);
1845: pathA.nullNodes();
1846: int windingA = pathA.hasClockwiseOrientation() ? 1 : neg;
1847:
1848: for (int j = 0; j < npaths; j++)
1849: if (i != j)
1850: {
1851: Segment pathB = (Segment) paths.elementAt(j);
1852:
1853:
1854: if (bb[i].intersects(bb[j]))
1855: {
1856: Segment s = pathB.next;
1857: while (s.P1.getY() == s.P2.getY() && s != pathB)
1858: s = s.next;
1859: Point2D p = s.getMidPoint();
1860: if (pathA.contains(p.getX(), p.getY()))
1861: contains[i][j] = windingA;
1862: }
1863: else
1864:
1865: contains[i][j] = 0;
1866: }
1867: else
1868: contains[i][j] = windingA;
1869: }
1870:
1871: for (int i = 0; i < npaths; i++)
1872: {
1873: windingNumbers[i][0] = 0;
1874: for (int j = 0; j < npaths; j++)
1875: windingNumbers[i][0] += contains[j][i];
1876: windingNumbers[i][1] = contains[i][i];
1877: }
1878:
1879: Vector solids = new Vector();
1880: Vector holes = new Vector();
1881:
1882: if (windingRule == PathIterator.WIND_NON_ZERO)
1883: {
1884: for (int i = 0; i < npaths; i++)
1885: {
1886: if (windingNumbers[i][0] == 0)
1887: holes.add(paths.elementAt(i));
1888: else if (windingNumbers[i][0] - windingNumbers[i][1] == 0
1889: && Math.abs(windingNumbers[i][0]) == 1)
1890: solids.add(paths.elementAt(i));
1891: }
1892: }
1893: else
1894: {
1895: windingRule = PathIterator.WIND_NON_ZERO;
1896: for (int i = 0; i < npaths; i++)
1897: {
1898: if ((windingNumbers[i][0] & 1) == 0)
1899: holes.add(paths.elementAt(i));
1900: else if ((windingNumbers[i][0] & 1) == 1)
1901: solids.add(paths.elementAt(i));
1902: }
1903: }
1904:
1905: setDirection(holes, false);
1906: setDirection(solids, true);
1907: this.holes = holes;
1908: this.solids = solids;
1909: }
1910:
1911:
1916: private void setDirection(Vector paths, boolean clockwise)
1917: {
1918: Segment v;
1919: for (int i = 0; i < paths.size(); i++)
1920: {
1921: v = (Segment) paths.elementAt(i);
1922: if (clockwise != v.hasClockwiseOrientation())
1923: v.reverseAll();
1924: }
1925: }
1926:
1927:
1931: private abstract class Segment implements Cloneable
1932: {
1933:
1934: Point2D P1;
1935: Point2D P2;
1936: Segment next;
1937: Segment node;
1938:
1939: Segment()
1940: {
1941: P1 = P2 = null;
1942: node = next = null;
1943: }
1944:
1945:
1948: abstract void reverseCoords();
1949:
1950:
1953: abstract Point2D getMidPoint();
1954:
1955:
1958: abstract Rectangle2D getBounds();
1959:
1960:
1963: abstract void transform(AffineTransform at);
1964:
1965:
1968: abstract int getType();
1969:
1970:
1972: abstract int splitIntersections(Segment b);
1973:
1974:
1977: abstract int pathIteratorFormat(double[] coords);
1978:
1979:
1987: abstract int rayCrossing(double x, double y);
1988:
1989:
1994: abstract void subdivideInsert(double t);
1995:
1996:
2000: abstract double curveArea();
2001:
2002:
2005: abstract boolean equals(Segment b);
2006:
2007:
2010: boolean contains(double x, double y)
2011: {
2012: Segment v = this;
2013: int crossings = 0;
2014: do
2015: {
2016: int n = v.rayCrossing(x, y);
2017: crossings += n;
2018: v = v.next;
2019: }
2020: while (v != this);
2021: return ((crossings & 1) == 1);
2022: }
2023:
2024:
2027: void nullNodes()
2028: {
2029: Segment v = this;
2030: do
2031: {
2032: v.node = null;
2033: v = v.next;
2034: }
2035: while (v != this);
2036: }
2037:
2038:
2041: void transformSegmentList(AffineTransform at)
2042: {
2043: Segment v = this;
2044: do
2045: {
2046: v.transform(at);
2047: v = v.next;
2048: }
2049: while (v != this);
2050: }
2051:
2052:
2056: boolean hasClockwiseOrientation()
2057: {
2058: return (getSignedArea() > 0.0);
2059: }
2060:
2061:
2064: public Rectangle2D getPathBounds()
2065: {
2066: double xmin;
2067: double xmax;
2068: double ymin;
2069: double ymax;
2070: xmin = xmax = P1.getX();
2071: ymin = ymax = P1.getY();
2072:
2073: Segment v = this;
2074: do
2075: {
2076: Rectangle2D r = v.getBounds();
2077: xmin = Math.min(r.getMinX(), xmin);
2078: ymin = Math.min(r.getMinY(), ymin);
2079: xmax = Math.max(r.getMaxX(), xmax);
2080: ymax = Math.max(r.getMaxY(), ymax);
2081: v = v.next;
2082: }
2083: while (v != this);
2084:
2085: return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin)));
2086: }
2087:
2088:
2091: double getSignedArea()
2092: {
2093: Segment s;
2094: double area = 0.0;
2095:
2096: s = this;
2097: do
2098: {
2099: area += s.curveArea();
2100:
2101: area += s.P1.getX() * s.next.P1.getY()
2102: - s.P1.getY() * s.next.P1.getX();
2103: s = s.next;
2104: }
2105: while (s != this);
2106:
2107: return area;
2108: }
2109:
2110:
2113: void reverseAll()
2114: {
2115: reverseCoords();
2116: Segment v = next;
2117: Segment former = this;
2118: while (v != this)
2119: {
2120: v.reverseCoords();
2121: Segment vnext = v.next;
2122: v.next = former;
2123: former = v;
2124: v = vnext;
2125: }
2126: next = former;
2127: }
2128:
2129:
2132: void insert(Segment v)
2133: {
2134: Segment n = next;
2135: next = v;
2136: v.next = n;
2137: }
2138:
2139:
2142: boolean isPolygonal()
2143: {
2144: Segment v = this;
2145: do
2146: {
2147: if (! (v instanceof LineSegment))
2148: return false;
2149: v = v.next;
2150: }
2151: while (v != this);
2152: return true;
2153: }
2154:
2155:
2158: Segment cloneSegmentList() throws CloneNotSupportedException
2159: {
2160: Vector list = new Vector();
2161: Segment v = next;
2162:
2163: while (v != this)
2164: {
2165: list.add(v);
2166: v = v.next;
2167: }
2168:
2169: Segment clone = (Segment) this.clone();
2170: v = clone;
2171: for (int i = 0; i < list.size(); i++)
2172: {
2173: clone.next = (Segment) ((Segment) list.elementAt(i)).clone();
2174: clone = clone.next;
2175: }
2176: clone.next = v;
2177: return v;
2178: }
2179:
2180:
2185: int createNode(Segment b, Intersection i)
2186: {
2187: Point2D p = i.p;
2188: if ((pointEquals(P1, p) || pointEquals(P2, p))
2189: && (pointEquals(b.P1, p) || pointEquals(b.P2, p)))
2190: return 0;
2191:
2192: subdivideInsert(i.ta);
2193: b.subdivideInsert(i.tb);
2194:
2195:
2196: b.P2 = b.next.P1 = P2 = next.P1 = i.p;
2197:
2198: node = b.next;
2199: b.node = next;
2200: return 1;
2201: }
2202:
2203:
2210: protected int createNodes(Segment b, Intersection[] x)
2211: {
2212: Vector v = new Vector();
2213: for (int i = 0; i < x.length; i++)
2214: {
2215: Point2D p = x[i].p;
2216: if (! ((pointEquals(P1, p) || pointEquals(P2, p))
2217: && (pointEquals(b.P1, p) || pointEquals(b.P2, p))))
2218: v.add(x[i]);
2219: }
2220:
2221: int nNodes = v.size();
2222: Intersection[] A = new Intersection[nNodes];
2223: Intersection[] B = new Intersection[nNodes];
2224: for (int i = 0; i < nNodes; i++)
2225: A[i] = B[i] = (Intersection) v.elementAt(i);
2226:
2227:
2228:
2229:
2230: for (int i = 0; i < nNodes - 1; i++)
2231: {
2232: for (int j = i + 1; j < nNodes; j++)
2233: {
2234: if (A[i].ta > A[j].ta)
2235: {
2236: Intersection swap = A[i];
2237: A[i] = A[j];
2238: A[j] = swap;
2239: }
2240: if (B[i].tb > B[j].tb)
2241: {
2242: Intersection swap = B[i];
2243: B[i] = B[j];
2244: B[j] = swap;
2245: }
2246: }
2247: }
2248:
2249: Segment s = this;
2250: for (int i = 0; i < nNodes; i++)
2251: {
2252: s.subdivideInsert(A[i].ta);
2253:
2254:
2255: for (int j = i + 1; j < nNodes; j++)
2256: A[j].ta = (A[j].ta - A[i].ta) / (1.0 - A[i].ta);
2257:
2258: A[i].seg = s;
2259: s = s.next;
2260: }
2261:
2262:
2263: s = b;
2264: for (int i = 0; i < nNodes; i++)
2265: {
2266: s.subdivideInsert(B[i].tb);
2267:
2268: for (int j = i + 1; j < nNodes; j++)
2269: B[j].tb = (B[j].tb - B[i].tb) / (1.0 - B[i].tb);
2270:
2271:
2272: B[i].seg.node = s.next;
2273: s.node = B[i].seg.next;
2274:
2275:
2276: B[i].seg.P2 = B[i].seg.next.P1 = s.P2 = s.next.P1 = B[i].p;
2277: s = s.next;
2278: }
2279: return nNodes;
2280: }
2281:
2282:
2286: boolean pathEquals(Segment B)
2287: {
2288: if (! getPathBounds().equals(B.getPathBounds()))
2289: return false;
2290:
2291: Segment startA = getTopLeft();
2292: Segment startB = B.getTopLeft();
2293: Segment a = startA;
2294: Segment b = startB;
2295: do
2296: {
2297: if (! a.equals(b))
2298: return false;
2299:
2300: if (a instanceof LineSegment)
2301: a = ((LineSegment) a).lastCoLinear();
2302: if (b instanceof LineSegment)
2303: b = ((LineSegment) b).lastCoLinear();
2304:
2305: a = a.next;
2306: b = b.next;
2307: }
2308: while (a != startA && b != startB);
2309: return true;
2310: }
2311:
2312:
2315: Segment getTopLeft()
2316: {
2317: Segment v = this;
2318: Segment tl = this;
2319: do
2320: {
2321: if (v.P1.getY() < tl.P1.getY())
2322: tl = v;
2323: else if (v.P1.getY() == tl.P1.getY())
2324: {
2325: if (v.P1.getX() < tl.P1.getX())
2326: tl = v;
2327: }
2328: v = v.next;
2329: }
2330: while (v != this);
2331: return tl;
2332: }
2333:
2334:
2337: boolean isSegmentOutside(Shape shape)
2338: {
2339: return ! shape.contains(getMidPoint());
2340: }
2341: }
2342:
2343: private class LineSegment extends Segment
2344: {
2345: public LineSegment(double x1, double y1, double x2, double y2)
2346: {
2347: super();
2348: P1 = new Point2D.Double(x1, y1);
2349: P2 = new Point2D.Double(x2, y2);
2350: }
2351:
2352: public LineSegment(Point2D p1, Point2D p2)
2353: {
2354: super();
2355: P1 = (Point2D) p1.clone();
2356: P2 = (Point2D) p2.clone();
2357: }
2358:
2359:
2362: public Object clone()
2363: {
2364: return new LineSegment(P1, P2);
2365: }
2366:
2367:
2370: void transform(AffineTransform at)
2371: {
2372: P1 = at.transform(P1, null);
2373: P2 = at.transform(P2, null);
2374: }
2375:
2376:
2379: void reverseCoords()
2380: {
2381: Point2D p = P1;
2382: P1 = P2;
2383: P2 = p;
2384: }
2385:
2386:
2389: Point2D getMidPoint()
2390: {
2391: return (new Point2D.Double(0.5 * (P1.getX() + P2.getX()),
2392: 0.5 * (P1.getY() + P2.getY())));
2393: }
2394:
2395:
2399: double curveArea()
2400: {
2401: return 0;
2402: }
2403:
2404:
2407: int getType()
2408: {
2409: return (PathIterator.SEG_LINETO);
2410: }
2411:
2412:
2417: void subdivideInsert(double t)
2418: {
2419: Point2D p = new Point2D.Double((P2.getX() - P1.getX()) * t + P1.getX(),
2420: (P2.getY() - P1.getY()) * t + P1.getY());
2421: insert(new LineSegment(p, P2));
2422: P2 = p;
2423: next.node = node;
2424: node = null;
2425: }
2426:
2427:
2430: boolean isCoLinear(LineSegment b)
2431: {
2432: double x1 = P1.getX();
2433: double y1 = P1.getY();
2434: double x2 = P2.getX();
2435: double y2 = P2.getY();
2436: double x3 = b.P1.getX();
2437: double y3 = b.P1.getY();
2438: double x4 = b.P2.getX();
2439: double y4 = b.P2.getY();
2440:
2441: if ((y1 - y3) * (x4 - x3) - (x1 - x3) * (y4 - y3) != 0.0)
2442: return false;
2443:
2444: return ((x2 - x1) * (y4 - y3) - (y2 - y1) * (x4 - x3) == 0.0);
2445: }
2446:
2447:
2451: Segment lastCoLinear()
2452: {
2453: Segment prev = this;
2454: Segment v = next;
2455:
2456: while (v instanceof LineSegment)
2457: {
2458: if (isCoLinear((LineSegment) v))
2459: {
2460: prev = v;
2461: v = v.next;
2462: }
2463: else
2464: return prev;
2465: }
2466: return prev;
2467: }
2468:
2469:
2474: boolean equals(Segment b)
2475: {
2476: if (! (b instanceof LineSegment))
2477: return false;
2478: Point2D p1 = P1;
2479: Point2D p3 = b.P1;
2480:
2481: if (! p1.equals(p3))
2482: return false;
2483:
2484: Point2D p2 = lastCoLinear().P2;
2485: Point2D p4 = ((LineSegment) b).lastCoLinear().P2;
2486: return (p2.equals(p4));
2487: }
2488:
2489:
2492: int pathIteratorFormat(double[] coords)
2493: {
2494: coords[0] = P2.getX();
2495: coords[1] = P2.getY();
2496: return (PathIterator.SEG_LINETO);
2497: }
2498:
2499:
2502: boolean hasIntersections(Segment b)
2503: {
2504: if (b instanceof LineSegment)
2505: return (linesIntersect(this, (LineSegment) b) != null);
2506:
2507: if (b instanceof QuadSegment)
2508: return (lineQuadIntersect(this, (QuadSegment) b) != null);
2509:
2510: if (b instanceof CubicSegment)
2511: return (lineCubicIntersect(this, (CubicSegment) b) != null);
2512:
2513: return false;
2514: }
2515:
2516:
2520: int splitIntersections(Segment b)
2521: {
2522: if (b instanceof LineSegment)
2523: {
2524: Intersection i = linesIntersect(this, (LineSegment) b);
2525:
2526: if (i == null)
2527: return 0;
2528:
2529: return createNode(b, i);
2530: }
2531:
2532: Intersection[] x = null;
2533:
2534: if (b instanceof QuadSegment)
2535: x = lineQuadIntersect(this, (QuadSegment) b);
2536:
2537: if (b instanceof CubicSegment)
2538: x = lineCubicIntersect(this, (CubicSegment) b);
2539:
2540: if (x == null)
2541: return 0;
2542:
2543: if (x.length == 1)
2544: return createNode(b, (Intersection) x[0]);
2545:
2546: return createNodes(b, x);
2547: }
2548:
2549:
2552: Rectangle2D getBounds()
2553: {
2554: return (new Rectangle2D.Double(Math.min(P1.getX(), P2.getX()),
2555: Math.min(P1.getY(), P2.getY()),
2556: Math.abs(P1.getX() - P2.getX()),
2557: Math.abs(P1.getY() - P2.getY())));
2558: }
2559:
2560:
2564: int rayCrossing(double x, double y)
2565: {
2566: double x0 = P1.getX() - x;
2567: double y0 = P1.getY() - y;
2568: double x1 = P2.getX() - x;
2569: double y1 = P2.getY() - y;
2570:
2571: if (y0 * y1 > 0)
2572: return 0;
2573:
2574: if (x0 < 0 && x1 < 0)
2575: return 0;
2576:
2577: if (y0 == 0.0)
2578: y0 -= EPSILON;
2579:
2580: if (y1 == 0.0)
2581: y1 -= EPSILON;
2582:
2583: if (Line2D.linesIntersect(x0, y0, x1, y1,
2584: EPSILON, 0.0, Double.MAX_VALUE, 0.0))
2585: return 1;
2586: return 0;
2587: }
2588: }
2589:
2590:
2598: private class QuadSegment extends Segment
2599: {
2600: Point2D cp;
2601:
2602:
2606: QuadSegment(double x1, double y1, double cx, double cy, double x2,
2607: double y2)
2608: {
2609: super();
2610: P1 = new Point2D.Double(x1, y1);
2611: P2 = new Point2D.Double(x2, y2);
2612: cp = new Point2D.Double(cx, cy);
2613: }
2614:
2615:
2618: public Object clone()
2619: {
2620: return new QuadSegment(P1.getX(), P1.getY(), cp.getX(), cp.getY(),
2621: P2.getX(), P2.getY());
2622: }
2623:
2624:
2630: double curveArea()
2631: {
2632: double x0 = P1.getX();
2633: double y0 = P1.getY();
2634: double x1 = cp.getX();
2635: double y1 = cp.getY();
2636: double x2 = P2.getX();
2637: double y2 = P2.getY();
2638:
2639: double P = (y2 - 2 * y1 + y0);
2640: double Q = 2 * (y1 - y0);
2641:
2642: double A = (x2 - 2 * x1 + x0);
2643: double B = 2 * (x1 - x0);
2644:
2645: double area = (B * P - A * Q) / 3.0;
2646: return (area);
2647: }
2648:
2649:
2652: boolean equals(Segment b)
2653: {
2654: if (! (b instanceof QuadSegment))
2655: return false;
2656:
2657: return (P1.equals(b.P1) && cp.equals(((QuadSegment) b).cp)
2658: && P2.equals(b.P2));
2659: }
2660:
2661:
2665: Point2D evaluatePoint(double t)
2666: {
2667: double x0 = P1.getX();
2668: double y0 = P1.getY();
2669: double x1 = cp.getX();
2670: double y1 = cp.getY();
2671: double x2 = P2.getX();
2672: double y2 = P2.getY();
2673:
2674: return new Point2D.Double(t * t * (x2 - 2 * x1 + x0) + 2 * t * (x1 - x0)
2675: + x0,
2676: t * t * (y2 - 2 * y1 + y0) + 2 * t * (y1 - y0)
2677: + y0);
2678: }
2679:
2680:
2683: Rectangle2D getBounds()
2684: {
2685: double x0 = P1.getX();
2686: double y0 = P1.getY();
2687: double x1 = cp.getX();
2688: double y1 = cp.getY();
2689: double x2 = P2.getX();
2690: double y2 = P2.getY();
2691: double r0;
2692: double r1;
2693:
2694: double xmax = Math.max(x0, x2);
2695: double ymax = Math.max(y0, y2);
2696: double xmin = Math.min(x0, x2);
2697: double ymin = Math.min(y0, y2);
2698:
2699: r0 = 2 * (y1 - y0);
2700: r1 = 2 * (y2 - 2 * y1 + y0);
2701: if (r1 != 0.0)
2702: {
2703: double t = -r0 / r1;
2704: if (t > 0.0 && t < 1.0)
2705: {
2706: double y = evaluatePoint(t).getY();
2707: ymax = Math.max(y, ymax);
2708: ymin = Math.min(y, ymin);
2709: }
2710: }
2711: r0 = 2 * (x1 - x0);
2712: r1 = 2 * (x2 - 2 * x1 + x0);
2713: if (r1 != 0.0)
2714: {
2715: double t = -r0 / r1;
2716: if (t > 0.0 && t < 1.0)
2717: {
2718: double x = evaluatePoint(t).getY();
2719: xmax = Math.max(x, xmax);
2720: xmin = Math.min(x, xmin);
2721: }
2722: }
2723:
2724: return (new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin));
2725: }
2726:
2727:
2730: CubicSegment getCubicSegment()
2731: {
2732: double x1 = P1.getX() + 2.0 * (cp.getX() - P1.getX()) / 3.0;
2733: double y1 = P1.getY() + 2.0 * (cp.getY() - P1.getY()) / 3.0;
2734: double x2 = cp.getX() + (P2.getX() - cp.getX()) / 3.0;
2735: double y2 = cp.getY() + (P2.getY() - cp.getY()) / 3.0;
2736:
2737: return new CubicSegment(P1.getX(), P1.getY(), x1, y1, x2, y2, P2.getX(),
2738: P2.getY());
2739: }
2740:
2741:
2744: Point2D getMidPoint()
2745: {
2746: return evaluatePoint(0.5);
2747: }
2748:
2749:
2752: int getType()
2753: {
2754: return (PathIterator.SEG_QUADTO);
2755: }
2756:
2757:
2760: int pathIteratorFormat(double[] coords)
2761: {
2762: coords[0] = cp.getX();
2763: coords[1] = cp.getY();
2764: coords[2] = P2.getX();
2765: coords[3] = P2.getY();
2766: return (PathIterator.SEG_QUADTO);
2767: }
2768:
2769:
2773: int rayCrossing(double x, double y)
2774: {
2775: double x0 = P1.getX() - x;
2776: double y0 = P1.getY() - y;
2777: double x1 = cp.getX() - x;
2778: double y1 = cp.getY() - y;
2779: double x2 = P2.getX() - x;
2780: double y2 = P2.getY() - y;
2781: double[] r = new double[3];
2782: int nRoots;
2783: int nCrossings = 0;
2784:
2785:
2786: if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0) && (y0 * y1 <= 0 || y1 * y2 <= 0))
2787: {
2788: if (y0 == 0.0)
2789: y0 -= EPSILON;
2790: if (y2 == 0.0)
2791: y2 -= EPSILON;
2792:
2793: r[0] = y0;
2794: r[1] = 2 * (y1 - y0);
2795: r[2] = (y2 - 2 * y1 + y0);
2796:
2797: nRoots = QuadCurve2D.solveQuadratic(r);
2798: for (int i = 0; i < nRoots; i++)
2799: if (r[i] > 0.0f && r[i] < 1.0f)
2800: {
2801: double t = r[i];
2802: if (t * t * (x2 - 2 * x1 + x0) + 2 * t * (x1 - x0) + x0 > 0.0)
2803: nCrossings++;
2804: }
2805: }
2806: return nCrossings;
2807: }
2808:
2809:
2812: void reverseCoords()
2813: {
2814: Point2D temp = P1;
2815: P1 = P2;
2816: P2 = temp;
2817: }
2818:
2819:
2825: int splitIntersections(Segment b)
2826: {
2827: if (b instanceof LineSegment)
2828: return (b.splitIntersections(this));
2829:
2830: if (b instanceof CubicSegment)
2831: return (b.splitIntersections(this));
2832:
2833: if (b instanceof QuadSegment)
2834: {
2835:
2836:
2837:
2838:
2839: Intersection[] x = cubicCubicIntersect(getCubicSegment(),
2840: ((QuadSegment) b)
2841: .getCubicSegment());
2842: if (x == null)
2843: return 0;
2844:
2845: if (x.length == 1)
2846: return createNode(b, (Intersection) x[0]);
2847:
2848: return createNodes(b, x);
2849: }
2850: return 0;
2851: }
2852:
2853:
2858: void subdivideInsert(double t)
2859: {
2860: double x0 = P1.getX();
2861: double y0 = P1.getY();
2862: double x1 = cp.getX();
2863: double y1 = cp.getY();
2864: double x2 = P2.getX();
2865: double y2 = P2.getY();
2866:
2867: double p10x = x0 + t * (x1 - x0);
2868: double p10y = y0 + t * (y1 - y0);
2869: double p11x = x1 + t * (x2 - x1);
2870: double p11y = y1 + t * (y2 - y1);
2871: double p20x = p10x + t * (p11x - p10x);
2872: double p20y = p10y + t * (p11y - p10y);
2873:
2874: insert(new QuadSegment(p20x, p20y, p11x, p11y, x2, y2));
2875: P2 = next.P1;
2876: cp.setLocation(p10x, p10y);
2877:
2878: next.node = node;
2879: node = null;
2880: }
2881:
2882:
2885: void transform(AffineTransform at)
2886: {
2887: P1 = at.transform(P1, null);
2888: P2 = at.transform(P2, null);
2889: cp = at.transform(cp, null);
2890: }
2891: }
2892:
2893:
2896: private class CubicSegment extends Segment
2897: {
2898: Point2D cp1;
2899: Point2D cp2;
2900:
2901:
2906: public CubicSegment(double x1, double y1, double c1x, double c1y,
2907: double c2x, double c2y, double x2, double y2)
2908: {
2909: super();
2910: P1 = new Point2D.Double(x1, y1);
2911: P2 = new Point2D.Double(x2, y2);
2912: cp1 = new Point2D.Double(c1x, c1y);
2913: cp2 = new Point2D.Double(c2x, c2y);
2914: }
2915:
2916:
2919: public Object clone()
2920: {
2921: return new CubicSegment(P1.getX(), P1.getY(), cp1.getX(), cp1.getY(),
2922: cp2.getX(), cp2.getY(), P2.getX(), P2.getY());
2923: }
2924:
2925:
2931: double curveArea()
2932: {
2933: double x0 = P1.getX();
2934: double y0 = P1.getY();
2935: double x1 = cp1.getX();
2936: double y1 = cp1.getY();
2937: double x2 = cp2.getX();
2938: double y2 = cp2.getY();
2939: double x3 = P2.getX();
2940: double y3 = P2.getY();
2941:
2942: double P = y3 - 3 * y2 + 3 * y1 - y0;
2943: double Q = 3 * (y2 + y0 - 2 * y1);
2944: double R = 3 * (y1 - y0);
2945:
2946: double A = x3 - 3 * x2 + 3 * x1 - x0;
2947: double B = 3 * (x2 + x0 - 2 * x1);
2948: double C = 3 * (x1 - x0);
2949:
2950: double area = (B * P - A * Q) / 5.0 + (C * P - A * R) / 2.0
2951: + (C * Q - B * R) / 3.0;
2952:
2953: return (area);
2954: }
2955:
2956:
2959: boolean equals(Segment b)
2960: {
2961: if (! (b instanceof CubicSegment))
2962: return false;
2963:
2964: return (P1.equals(b.P1) && cp1.equals(((CubicSegment) b).cp1)
2965: && cp2.equals(((CubicSegment) b).cp2) && P2.equals(b.P2));
2966: }
2967:
2968:
2972: Point2D evaluatePoint(double t)
2973: {
2974: double x0 = P1.getX();
2975: double y0 = P1.getY();
2976: double x1 = cp1.getX();
2977: double y1 = cp1.getY();
2978: double x2 = cp2.getX();
2979: double y2 = cp2.getY();
2980: double x3 = P2.getX();
2981: double y3 = P2.getY();
2982:
2983: return new Point2D.Double(-(t * t * t) * (x0 - 3 * x1 + 3 * x2 - x3)
2984: + 3 * t * t * (x0 - 2 * x1 + x2)
2985: + 3 * t * (x1 - x0) + x0,
2986: -(t * t * t) * (y0 - 3 * y1 + 3 * y2 - y3)
2987: + 3 * t * t * (y0 - 2 * y1 + y2)
2988: + 3 * t * (y1 - y0) + y0);
2989: }
2990:
2991:
2994: Rectangle2D getBounds()
2995: {
2996: double x0 = P1.getX();
2997: double y0 = P1.getY();
2998: double x1 = cp1.getX();
2999: double y1 = cp1.getY();
3000: double x2 = cp2.getX();
3001: double y2 = cp2.getY();
3002: double x3 = P2.getX();
3003: double y3 = P2.getY();
3004: double[] r = new double[3];
3005:
3006: double xmax = Math.max(x0, x3);
3007: double ymax = Math.max(y0, y3);
3008: double xmin = Math.min(x0, x3);
3009: double ymin = Math.min(y0, y3);
3010:
3011: r[0] = 3 * (y1 - y0);
3012: r[1] = 6.0 * (y2 + y0 - 2 * y1);
3013: r[2] = 3.0 * (y3 - 3 * y2 + 3 * y1 - y0);
3014:
3015: int n = QuadCurve2D.solveQuadratic(r);
3016: for (int i = 0; i < n; i++)
3017: {
3018: double t = r[i];
3019: if (t > 0 && t < 1.0)
3020: {
3021: double y = evaluatePoint(t).getY();
3022: ymax = Math.max(y, ymax);
3023: ymin = Math.min(y, ymin);
3024: }
3025: }
3026:
3027: r[0] = 3 * (x1 - x0);
3028: r[1] = 6.0 * (x2 + x0 - 2 * x1);
3029: r[2] = 3.0 * (x3 - 3 * x2 + 3 * x1 - x0);
3030: n = QuadCurve2D.solveQuadratic(r);
3031: for (int i = 0; i < n; i++)
3032: {
3033: double t = r[i];
3034: if (t > 0 && t < 1.0)
3035: {
3036: double x = evaluatePoint(t).getX();
3037: xmax = Math.max(x, xmax);
3038: xmin = Math.min(x, xmin);
3039: }
3040: }
3041: return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin)));
3042: }
3043:
3044:
3047: CubicCurve2D getCubicCurve2D()
3048: {
3049: return new CubicCurve2D.Double(P1.getX(), P1.getY(), cp1.getX(),
3050: cp1.getY(), cp2.getX(), cp2.getY(),
3051: P2.getX(), P2.getY());
3052: }
3053:
3054:
3058: double[] getLoop()
3059: {
3060: double x0 = P1.getX();
3061: double y0 = P1.getY();
3062: double x1 = cp1.getX();
3063: double y1 = cp1.getY();
3064: double x2 = cp2.getX();
3065: double y2 = cp2.getY();
3066: double x3 = P2.getX();
3067: double y3 = P2.getY();
3068: double[] r = new double[4];
3069: double k;
3070: double R;
3071: double T;
3072: double A;
3073: double B;
3074: double[] results = new double[2];
3075:
3076: R = x3 - 3 * x2 + 3 * x1 - x0;
3077: T = y3 - 3 * y2 + 3 * y1 - y0;
3078:
3079:
3080: if (R == 0.0 && T == 0.0)
3081: return null;
3082:
3083:
3084: if (R != 0.0 && T != 0.0)
3085: {
3086: A = 3 * (x2 + x0 - 2 * x1) / R;
3087: B = 3 * (x1 - x0) / R;
3088:
3089: double P = 3 * (y2 + y0 - 2 * y1) / T;
3090: double Q = 3 * (y1 - y0) / T;
3091:
3092: if (A == P || Q == B)
3093: return null;
3094:
3095: k = (Q - B) / (A - P);
3096: }
3097: else
3098: {
3099: if (R == 0.0)
3100: {
3101:
3102: k = -(3 * (x1 - x0)) / (3 * (x2 + x0 - 2 * x1));
3103: A = 3 * (y2 + y0 - 2 * y1) / T;
3104: B = 3 * (y1 - y0) / T;
3105: }
3106: else
3107: {
3108:
3109: k = -(3 * (y1 - y0)) / (3 * (y2 + y0 - 2 * y1));
3110: A = 3 * (x2 + x0 - 2 * x1) / R;
3111: B = 3 * (x1 - x0) / R;
3112: }
3113: }
3114:
3115: r[0] = -k * k * k - A * k * k - B * k;
3116: r[1] = 3 * k * k + 2 * k * A + 2 * B;
3117: r[2] = -3 * k;
3118: r[3] = 2;
3119:
3120: int n = CubicCurve2D.solveCubic(r);
3121: if (n != 3)
3122: return null;
3123:
3124:
3125: double t;
3126: for (int i = 0; i < 2; i++)
3127: for (int j = i + 1; j < 3; j++)
3128: if (r[j] < r[i])
3129: {
3130: t = r[i];
3131: r[i] = r[j];
3132: r[j] = t;
3133: }
3134:
3135: if (Math.abs(r[0] + r[2] - k) < 1E-13)
3136: if (r[0] >= 0.0 && r[0] <= 1.0 && r[2] >= 0.0 && r[2] <= 1.0)
3137: if (evaluatePoint(r[0]).distance(evaluatePoint(r[2])) < PE_EPSILON * 10)
3138: {
3139: results[0] = r[0];
3140: results[1] = r[2];
3141: return (results);
3142: }
3143: return null;
3144: }
3145:
3146:
3149: Point2D getMidPoint()
3150: {
3151: return evaluatePoint(0.5);
3152: }
3153:
3154:
3157: int getType()
3158: {
3159: return (PathIterator.SEG_CUBICTO);
3160: }
3161:
3162:
3165: int pathIteratorFormat(double[] coords)
3166: {
3167: coords[0] = cp1.getX();
3168: coords[1] = cp1.getY();
3169: coords[2] = cp2.getX();
3170: coords[3] = cp2.getY();
3171: coords[4] = P2.getX();
3172: coords[5] = P2.getY();
3173: return (PathIterator.SEG_CUBICTO);
3174: }
3175:
3176:
3180: int rayCrossing(double x, double y)
3181: {
3182: double x0 = P1.getX() - x;
3183: double y0 = P1.getY() - y;
3184: double x1 = cp1.getX() - x;
3185: double y1 = cp1.getY() - y;
3186: double x2 = cp2.getX() - x;
3187: double y2 = cp2.getY() - y;
3188: double x3 = P2.getX() - x;
3189: double y3 = P2.getY() - y;
3190: double[] r = new double[4];
3191: int nRoots;
3192: int nCrossings = 0;
3193:
3194:
3195: if ((x0 > 0.0 || x1 > 0.0 || x2 > 0.0 || x3 > 0.0)
3196: && (y0 * y1 <= 0 || y1 * y2 <= 0 || y2 * y3 <= 0))
3197: {
3198: if (y0 == 0.0)
3199: y0 -= EPSILON;
3200: if (y3 == 0.0)
3201: y3 -= EPSILON;
3202:
3203: r[0] = y0;
3204: r[1] = 3 * (y1 - y0);
3205: r[2] = 3 * (y2 + y0 - 2 * y1);
3206: r[3] = y3 - 3 * y2 + 3 * y1 - y0;
3207:
3208: if ((nRoots = CubicCurve2D.solveCubic(r)) > 0)
3209: for (int i = 0; i < nRoots; i++)
3210: {
3211: if (r[i] > 0.0 && r[i] < 1.0)
3212: {
3213: double t = r[i];
3214: if (-(t * t * t) * (x0 - 3 * x1 + 3 * x2 - x3)
3215: + 3 * t * t * (x0 - 2 * x1 + x2) + 3 * t * (x1 - x0)
3216: + x0 > 0.0)
3217: nCrossings++;
3218: }
3219: }
3220: }
3221: return nCrossings;
3222: }
3223:
3224:
3227: void reverseCoords()
3228: {
3229: Point2D p = P1;
3230: P1 = P2;
3231: P2 = p;
3232: p = cp1;
3233: cp1 = cp2;
3234: cp2 = p;
3235: }
3236:
3237:
3241: int splitIntersections(Segment b)
3242: {
3243: if (b instanceof LineSegment)
3244: return (b.splitIntersections(this));
3245:
3246: Intersection[] x = null;
3247:
3248: if (b instanceof QuadSegment)
3249: x = cubicCubicIntersect(this, ((QuadSegment) b).getCubicSegment());
3250:
3251: if (b instanceof CubicSegment)
3252: x = cubicCubicIntersect(this, (CubicSegment) b);
3253:
3254: if (x == null)
3255: return 0;
3256:
3257: if (x.length == 1)
3258: return createNode(b, x[0]);
3259:
3260: return createNodes(b, x);
3261: }
3262:
3263:
3268: void subdivideInsert(double t)
3269: {
3270: CubicSegment s = (CubicSegment) clone();
3271: double p1x = (s.cp1.getX() - s.P1.getX()) * t + s.P1.getX();
3272: double p1y = (s.cp1.getY() - s.P1.getY()) * t + s.P1.getY();
3273:
3274: double px = (s.cp2.getX() - s.cp1.getX()) * t + s.cp1.getX();
3275: double py = (s.cp2.getY() - s.cp1.getY()) * t + s.cp1.getY();
3276:
3277: s.cp2.setLocation((s.P2.getX() - s.cp2.getX()) * t + s.cp2.getX(),
3278: (s.P2.getY() - s.cp2.getY()) * t + s.cp2.getY());
3279:
3280: s.cp1.setLocation((s.cp2.getX() - px) * t + px,
3281: (s.cp2.getY() - py) * t + py);
3282:
3283: double p2x = (px - p1x) * t + p1x;
3284: double p2y = (py - p1y) * t + p1y;
3285:
3286: double p3x = (s.cp1.getX() - p2x) * t + p2x;
3287: double p3y = (s.cp1.getY() - p2y) * t + p2y;
3288: s.P1.setLocation(p3x, p3y);
3289:
3290:
3291: insert(s);
3292:
3293:
3294: cp1.setLocation(p1x, p1y);
3295: cp2.setLocation(p2x, p2y);
3296: P2 = s.P1;
3297: next.node = node;
3298: node = null;
3299: }
3300:
3301:
3304: void transform(AffineTransform at)
3305: {
3306: P1 = at.transform(P1, null);
3307: P2 = at.transform(P2, null);
3308: cp1 = at.transform(cp1, null);
3309: cp2 = at.transform(cp2, null);
3310: }
3311: }
3312: }