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: