1
2
3
4
5
6
7
8
9 package math.geom2d.circulinear;
10
11 import static java.lang.Math.PI;
12
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Iterator;
16 import java.util.TreeMap;
17 import java.util.TreeSet;
18
19 import math.geom2d.Angle2D;
20 import math.geom2d.Point2D;
21 import math.geom2d.Shape2D;
22 import math.geom2d.Vector2D;
23 import math.geom2d.conic.Circle2D;
24 import math.geom2d.conic.CircleArc2D;
25 import math.geom2d.conic.CircularShape2D;
26 import math.geom2d.curve.ContinuousCurve2D;
27 import math.geom2d.curve.Curve2D;
28 import math.geom2d.curve.Curve2DUtils;
29 import math.geom2d.curve.CurveSet2D;
30 import math.geom2d.curve.SmoothCurve2D;
31 import math.geom2d.line.LinearShape2D;
32 import math.geom2d.point.PointSet2D;
33
34
35
36
37
38
39
40 public class CirculinearCurve2DUtils {
41
42
43
44
45
46
47
48 public static CirculinearCurve2D convert(Curve2D curve) {
49
50 if (curve instanceof CirculinearCurve2D)
51 return (CirculinearCurve2D) curve;
52
53
54 if (curve instanceof ContinuousCurve2D) {
55
56 ContinuousCurve2D continuous = (ContinuousCurve2D) curve;
57 Collection<? extends SmoothCurve2D> smoothPieces =
58 continuous.getSmoothPieces();
59
60
61 ArrayList<CirculinearElement2D> elements =
62 new ArrayList<CirculinearElement2D>(smoothPieces.size());
63
64
65 for (SmoothCurve2D smooth : smoothPieces) {
66 if (smooth instanceof CirculinearElement2D)
67 elements.add((CirculinearElement2D) smooth);
68 else
69 throw new NonCirculinearClassException(smooth);
70 }
71
72
73 return new PolyCirculinearCurve2D<CirculinearElement2D>(elements);
74 }
75
76
77 if (curve instanceof CurveSet2D) {
78
79 CurveSet2D<?> set = (CurveSet2D<?>) curve;
80 Collection<? extends ContinuousCurve2D> continuousCurves =
81 set.getContinuousCurves();
82
83
84 ArrayList<CirculinearContinuousCurve2D> curves =
85 new ArrayList<CirculinearContinuousCurve2D>(
86 continuousCurves.size());
87
88
89 for (ContinuousCurve2D continuous : continuousCurves) {
90 if (continuous instanceof CirculinearContinuousCurve2D)
91 curves.add((CirculinearContinuousCurve2D) continuous);
92 else
93 curves.add((CirculinearContinuousCurve2D) convert(continuous));
94 }
95
96
97 return new CirculinearCurveSet2D<CirculinearContinuousCurve2D>(curves);
98 }
99
100 return null;
101 }
102
103
104
105
106 public static double getLength(
107 CurveSet2D<? extends CirculinearCurve2D> curve,
108 double pos) {
109
110 double length = 0;
111
112
113 int index = curve.getCurveIndex(pos);
114 for(int i=0; i<index; i++)
115 length += curve.getCurve(i).getLength();
116
117
118 if(index<curve.getCurveNumber()) {
119 double pos2 = curve.getLocalPosition(pos-2*index);
120 length += curve.getCurve(index).getLength(pos2);
121 }
122
123
124 return length;
125 }
126
127
128
129
130 public static double getPosition(
131 CurveSet2D<? extends CirculinearCurve2D> curveSet,
132 double length) {
133
134
135 double pos = 0;
136
137
138 int index = 0;
139
140
141 double cumLength = getLength(curveSet, curveSet.getT0());
142
143
144 for(CirculinearCurve2D curve : curveSet.getCurves()) {
145
146 double curveLength = curve.getLength();
147
148
149 if(cumLength+curveLength<length) {
150 cumLength += curveLength;
151 index ++;
152 } else {
153
154 double pos2 = curve.getPosition(length-cumLength);
155 pos = curveSet.getGlobalPosition(index, pos2);
156 break;
157 }
158 }
159
160
161 return pos;
162 }
163
164 public static CirculinearCurve2D createParallel(
165 CirculinearCurve2D curve, double dist) {
166
167
168 if (curve instanceof CirculinearContinuousCurve2D) {
169 return createContinuousParallel(
170 (CirculinearContinuousCurve2D)curve, dist);
171 }
172
173
174 CirculinearCurveSet2D<CirculinearContinuousCurve2D> parallel =
175 new CirculinearCurveSet2D<CirculinearContinuousCurve2D>();
176
177
178 for(CirculinearContinuousCurve2D continuous :
179 curve.getContinuousCurves()){
180 CirculinearContinuousCurve2D contParallel =
181 createContinuousParallel(continuous, dist);
182 if(contParallel!=null)
183 parallel.addCurve(contParallel);
184 }
185
186
187 return parallel;
188 }
189
190
191 public static CirculinearContinuousCurve2D createContinuousParallel(
192 CirculinearContinuousCurve2D curve, double dist) {
193
194
195 if (curve instanceof CirculinearElement2D) {
196 return (CirculinearElement2D)((CirculinearElement2D)curve).getParallel(dist);
197 }
198
199
200 Collection<? extends CirculinearElement2D> elements =
201 curve.getSmoothPieces();
202 Iterator<? extends CirculinearElement2D> iterator =
203 elements.iterator();
204
205
206 CirculinearElement2D previous = null;
207 CirculinearElement2D current = null;
208
209
210 PolyCirculinearCurve2D<CirculinearContinuousCurve2D> parallel =
211 new PolyCirculinearCurve2D<CirculinearContinuousCurve2D> ();
212
213
214 if(!iterator.hasNext())
215 return parallel;
216
217
218 previous = iterator.next();
219 CirculinearElement2D elementParallel = (CirculinearElement2D)previous.getParallel(dist);
220 parallel.addCurve(elementParallel);
221
222
223 while(iterator.hasNext()){
224
225 current = iterator.next();
226
227
228 addCircularJunction(parallel, previous, current, dist);
229
230
231 parallel.addCurve((CirculinearElement2D)current.getParallel(dist));
232
233
234 previous = current;
235 }
236
237
238 if(curve.isClosed()) {
239 current = elements.iterator().next();
240 addCircularJunction(parallel, previous, current, dist);
241 parallel.setClosed(true);
242 }
243
244 return parallel;
245 }
246
247 private static void addCircularJunction(
248 PolyCirculinearCurve2D<CirculinearContinuousCurve2D> parallel,
249 CirculinearElement2D previous,
250 CirculinearElement2D current, double dist) {
251
252 Point2D center = current.getFirstPoint();
253
254
255 Vector2D vp = previous.getTangent(previous.getT1());
256 Vector2D vc = current.getTangent(current.getT0());
257
258
259 double startAngle, endAngle;
260 if(dist>0) {
261 startAngle = vp.getAngle() - PI/2;
262 endAngle = vc.getAngle() - PI/2;
263 } else {
264 startAngle = vp.getAngle() + PI/2;
265 endAngle = vc.getAngle() + PI/2;
266 }
267
268
269 startAngle = Angle2D.formatAngle(startAngle);
270 endAngle = Angle2D.formatAngle(endAngle);
271
272
273 double diffAngle = endAngle-startAngle;
274 diffAngle = Math.min(diffAngle, 2*PI-diffAngle);
275
276
277
278 if(Math.abs(diffAngle)<1e-10)
279 return;
280
281
282 parallel.addCurve(new CircleArc2D(
283 center, Math.abs(dist), startAngle, endAngle, dist>0));
284 }
285
286
287
288
289
290
291 public static Collection<Point2D> findSelfIntersections(
292 CirculinearCurve2D curve) {
293
294
295 ArrayList<CirculinearElement2D> elements =
296 new ArrayList<CirculinearElement2D>();
297
298
299 for(CirculinearContinuousCurve2D cont : curve.getContinuousCurves())
300 elements.addAll(cont.getSmoothPieces());
301
302
303 ArrayList<Point2D> result = new ArrayList<Point2D>(0);
304
305
306 int n = elements.size();
307 for(int i=0; i<n-1; i++) {
308 CirculinearElement2D elem1 = elements.get(i);
309 for(int j=i; j<n; j++) {
310 CirculinearElement2D elem2 = elements.get(j);
311
312 for(Point2D inter : findIntersections(elem1, elem2)) {
313
314 if(inter.equals(elem1.getLastPoint()) &&
315 inter.equals(elem2.getFirstPoint())) continue;
316 if(inter.equals(elem1.getFirstPoint()) &&
317 inter.equals(elem2.getLastPoint())) continue;
318
319 result.add(inter);
320 }
321 }
322 }
323
324
325 return result;
326 }
327
328 public static double [][] locateSelfIntersections(
329 CurveSet2D<? extends CirculinearElement2D> curve) {
330
331
332 ArrayList<Double> list1 = new ArrayList<Double>(0);
333 ArrayList<Double> list2 = new ArrayList<Double>(0);
334
335
336 int n = curve.getCurveNumber();
337 for(int i=0; i<n-1; i++) {
338 CirculinearElement2D elem1 = curve.getCurve(i);
339 for(int j=i+1; j<n; j++) {
340 CirculinearElement2D elem2 = curve.getCurve(j);
341
342 for(Point2D inter : findIntersections(elem1, elem2)) {
343
344 if(!Double.isInfinite(elem1.getT1())&&!Double.isInfinite(elem2.getT0()))
345 if(inter.equals(elem1.getLastPoint()) &&
346 inter.equals(elem2.getFirstPoint())) continue;
347 if(!Double.isInfinite(elem1.getT0())&&!Double.isInfinite(elem2.getT1()))
348 if(inter.equals(elem1.getFirstPoint()) &&
349 inter.equals(elem2.getLastPoint())) continue;
350
351
352 list1.add(2*i + Curve2DUtils.toUnitSegment(
353 elem1.getPosition(inter),
354 elem1.getT0(), elem1.getT1()));
355 list2.add(2*j + Curve2DUtils.toUnitSegment(
356 elem2.getPosition(inter),
357 elem2.getT0(), elem2.getT1()));
358 }
359 }
360 }
361
362
363 int np = list1.size();
364 double[][] result = new double[np][2];
365 for(int i=0; i<np; i++){
366 result[i][0] = list1.get(i);
367 result[i][1] = list2.get(i);
368 }
369
370
371 return result;
372 }
373
374 public static Collection<Point2D> findIntersections(
375 CirculinearCurve2D curve1, CirculinearCurve2D curve2) {
376
377
378 ArrayList<CirculinearElement2D> elements1 =
379 new ArrayList<CirculinearElement2D>();
380 ArrayList<CirculinearElement2D> elements2 =
381 new ArrayList<CirculinearElement2D>();
382
383
384 for(CirculinearContinuousCurve2D cont : curve1.getContinuousCurves())
385 elements1.addAll(cont.getSmoothPieces());
386 for(CirculinearContinuousCurve2D cont : curve2.getContinuousCurves())
387 elements2.addAll(cont.getSmoothPieces());
388
389
390 ArrayList<Point2D> result = new ArrayList<Point2D>(0);
391
392
393 int n1 = elements1.size();
394 int n2 = elements2.size();
395 for(int i=0; i<n1; i++) {
396 CirculinearElement2D elem1 = elements1.get(i);
397 for(int j=0; j<n2; j++) {
398 CirculinearElement2D elem2 = elements2.get(j);
399
400 for(Point2D inter : findIntersections(elem1, elem2)) {
401
402 result.add(inter);
403 }
404 }
405 }
406
407
408 return result;
409 }
410
411
412
413
414
415
416
417 public static double[][] locateIntersections(
418 CirculinearCurve2D curve1, CirculinearCurve2D curve2) {
419
420
421 ArrayList<Double> list1 = new ArrayList<Double>(0);
422 ArrayList<Double> list2 = new ArrayList<Double>(0);
423
424
425 ArrayList<CirculinearElement2D> elements1 =
426 new ArrayList<CirculinearElement2D>();
427 ArrayList<CirculinearElement2D> elements2 =
428 new ArrayList<CirculinearElement2D>();
429
430
431 for(CirculinearContinuousCurve2D cont : curve1.getContinuousCurves())
432 elements1.addAll(cont.getSmoothPieces());
433 for(CirculinearContinuousCurve2D cont : curve2.getContinuousCurves())
434 elements2.addAll(cont.getSmoothPieces());
435
436
437 int n1 = elements1.size();
438 int n2 = elements2.size();
439 for(int i=0; i<n1; i++) {
440 CirculinearElement2D elem1 = elements1.get(i);
441 for(int j=0; j<n2; j++) {
442 CirculinearElement2D elem2 = elements2.get(j);
443
444 for(Point2D inter : findIntersections(elem1, elem2)) {
445
446 list1.add(curve1.getPosition(inter));
447 list2.add(curve2.getPosition(inter));
448 }
449 }
450 }
451
452
453 int np = list1.size();
454 double[][] result = new double[np][2];
455 for(int i=0; i<np; i++){
456 result[i][0] = list1.get(i);
457 result[i][1] = list2.get(i);
458 }
459
460
461 return result;
462 }
463
464
465
466
467 public static Collection<Point2D> findIntersections(
468 CirculinearElement2D elem1,
469 CirculinearElement2D elem2) {
470
471
472 if(elem1 instanceof LinearShape2D) {
473 return elem2.getIntersections((LinearShape2D) elem1);
474 }
475 if(elem2 instanceof LinearShape2D) {
476 return elem1.getIntersections((LinearShape2D) elem2);
477 }
478
479
480
481 Circle2D circ1 = ((CircularShape2D) elem1).getSupportingCircle();
482 Circle2D circ2 = ((CircularShape2D) elem2).getSupportingCircle();
483
484
485 ArrayList<Point2D> pts = new ArrayList<Point2D>(2);
486
487
488
489 for(Point2D inter : Circle2D.getIntersections(circ1, circ2)) {
490 if(elem1.contains(inter) && elem2.contains(inter))
491 pts.add(inter);
492 }
493
494
495 return pts;
496 }
497
498
499
500
501
502
503
504 public static Collection<CirculinearContinuousCurve2D>
505 splitContinuousCurve(CirculinearContinuousCurve2D curve) {
506
507 double pos0, pos1, pos2;
508
509
510 ArrayList<CirculinearContinuousCurve2D> result =
511 new ArrayList<CirculinearContinuousCurve2D>();
512
513
514 if (curve instanceof CirculinearElement2D){
515 result.add(curve);
516 return result;
517 }
518
519
520
521 PolyCirculinearCurve2D<CirculinearElement2D> polyCurve =
522 createPolyCurve(curve.getSmoothPieces(), curve.isClosed());
523
524
525 double[][] couples = locateSelfIntersections(polyCurve);
526
527
528 if(couples.length==0){
529
530 result.add(createPolyCurve(polyCurve.getSmoothPieces(),
531 curve.isClosed()));
532 return result;
533 }
534
535
536 TreeMap<Double, Double> twins = new TreeMap<Double, Double>();
537 for (int i=0; i<couples.length; i++) {
538 pos1 = couples[i][0];
539 pos2 = couples[i][1];
540 twins.put(pos1, pos2);
541 twins.put(pos2, pos1);
542 }
543
544
545 ArrayList<CirculinearElement2D> elements;
546
547
548
549
550
551 elements = new ArrayList<CirculinearElement2D>();
552
553
554 pos1 = polyCurve.getT0();
555 pos2 = twins.firstKey();
556 pos0 = pos2;
557
558
559
560 addElements(elements, polyCurve.getSubCurve(pos1, pos2));
561 do {
562
563 pos1 = twins.remove(pos2);
564
565
566 if(twins.higherKey(pos1)==null)
567 break;
568
569
570 pos2 = twins.higherKey(pos1);
571
572
573 addElements(elements, polyCurve.getSubCurve(pos1, pos2));
574 } while(true);
575
576
577 pos2 = polyCurve.getT1();
578 addElements(elements, polyCurve.getSubCurve(pos1, pos2));
579
580
581 result.add(createPolyCurve(elements, curve.isClosed()));
582
583
584 while(!twins.isEmpty()) {
585
586 elements = new ArrayList<CirculinearElement2D>();
587
588
589 pos0 = twins.firstKey();
590 pos1 = twins.get(pos0);
591 pos2 = twins.higherKey(pos1);
592
593
594 addElements(elements, polyCurve.getSubCurve(pos1, pos2));
595
596 while(pos2!=pos0) {
597
598 pos1 = twins.remove(pos2);
599
600
601 if(twins.higherKey(pos1)==null)
602 break;
603
604
605 pos2 = twins.higherKey(pos1);
606
607
608 addElements(elements, polyCurve.getSubCurve(pos1, pos2));
609 }
610
611 pos1 = twins.remove(pos2);
612
613
614
615 result.add(createPolyCurve(elements, true));
616 }
617
618 return result;
619 }
620
621
622
623
624
625 private static PolyCirculinearCurve2D<CirculinearElement2D>
626 createPolyCurve(Collection<? extends CirculinearElement2D> elements,
627 boolean closed) {
628 return new PolyCirculinearCurve2D<CirculinearElement2D>(
629 elements, closed);
630 }
631
632 public static Collection<CirculinearContour2D>
633 splitIntersectingContours(
634 CirculinearContour2D curve1,
635 CirculinearContour2D curve2) {
636
637 double pos0, pos1, pos2;
638
639
640
641
642
643 ArrayList<CirculinearContour2D> contours =
644 new ArrayList<CirculinearContour2D>();
645
646
647 double[][] couples = locateIntersections(curve1, curve2);
648
649
650 if(couples.length==0){
651
652 contours.add(curve1);
653 contours.add(curve2);
654 return contours;
655 }
656
657
658 TreeMap<Double, Double> twins1 = new TreeMap<Double, Double>();
659 TreeMap<Double, Double> twins2 = new TreeMap<Double, Double>();
660
661
662 TreeSet<Double> positions1 = new TreeSet<Double>();
663 TreeSet<Double> positions2 = new TreeSet<Double>();
664
665
666 for (int i=0; i<couples.length; i++) {
667 pos1 = couples[i][0];
668 pos2 = couples[i][1];
669 twins1.put(pos1, pos2);
670 twins2.put(pos2, pos1);
671 positions1.add(pos1);
672 positions2.add(pos2);
673 }
674
675
676 ArrayList<CirculinearElement2D> elements;
677
678
679 while(!twins1.isEmpty()) {
680
681 elements = new ArrayList<CirculinearElement2D>();
682
683
684 pos0 = twins2.firstEntry().getValue();
685 pos1 = pos0;
686
687 do {
688 pos2 = nextValue(positions1, pos1);
689
690
691 addElements(elements, (CirculinearContinuousCurve2D)curve1.getSubCurve(pos1, pos2));
692
693
694 pos1 = twins1.remove(pos2);
695
696
697 pos2 = nextValue(positions2, pos1);
698
699
700 addElements(elements, (CirculinearContinuousCurve2D)curve2.getSubCurve(pos1, pos2));
701
702
703 pos1 = twins2.remove(pos2);
704
705 } while (pos1!=pos0);
706
707
708
709 contours.add(
710 new BoundaryPolyCirculinearCurve2D<CirculinearElement2D>(
711 elements, true));
712 }
713
714 return contours;
715 }
716
717
718
719
720
721
722 public static Collection<CirculinearContour2D>
723 splitIntersectingContours(Collection<? extends CirculinearContour2D> curves) {
724
725 double pos0=0, pos1, pos2;
726
727
728
729
730
731 CirculinearContour2D[] curveArray =
732 curves.toArray(new CirculinearContour2D[0]);
733
734
735
736
737 int nCurves = curves.size();
738 ArrayList<TreeMap<Double, Integer>> twinIndices =
739 new ArrayList<TreeMap<Double, Integer>>(nCurves);
740 ArrayList<TreeMap<Double, Double>> twinPositions =
741 new ArrayList<TreeMap<Double, Double>>(nCurves);
742
743
744 for(int i=0; i<nCurves; i++){
745 twinIndices.add(i, new TreeMap<Double, Integer>());
746 twinPositions.add(i, new TreeMap<Double, Double>());
747 }
748
749
750
751 ArrayList<TreeSet<Double>> positions =
752 new ArrayList<TreeSet<Double>>(nCurves);
753
754
755 for(int i=0; i<nCurves; i++){
756 positions.add(i, new TreeSet<Double>());
757 }
758
759
760 for(int i=0; i<nCurves-1; i++) {
761 CirculinearContour2D curve1 = curveArray[i];
762
763 for(int j=i+1; j<nCurves; j++) {
764 CirculinearContour2D curve2 = curveArray[j];
765 double[][] couples = locateIntersections(curve1, curve2);
766
767
768 for (int k=0; k<couples.length; k++) {
769
770 pos1 = couples[k][0];
771 pos2 = couples[k][1];
772
773
774 positions.get(i).add(pos1);
775 positions.get(j).add(pos2);
776
777
778 twinIndices.get(i).put(pos1, j);
779 twinIndices.get(j).put(pos2, i);
780
781
782
783 twinPositions.get(i).put(pos1, pos2);
784 twinPositions.get(j).put(pos2, pos1);
785 }
786 }
787 }
788
789
790 ArrayList<CirculinearContour2D> contours =
791 new ArrayList<CirculinearContour2D>();
792
793
794 for(int i=0; i<nCurves; i++) {
795
796 if (twinPositions.get(i).isEmpty()) {
797 contours.add(curveArray[i]);
798 }
799 }
800
801
802 for(int i=0; i<nCurves; i++) {
803
804 if (curveArray[i].isBounded())
805 continue;
806
807
808 if (twinPositions.get(i).isEmpty()) {
809 continue;
810 }
811
812
813 pos0 = twinPositions.get(i).firstEntry().getKey();
814 int ind0 = twinIndices.get(i).firstEntry().getValue();
815
816
817 ArrayList<CirculinearElement2D> elements =
818 new ArrayList<CirculinearElement2D>();
819
820
821 CirculinearContour2D curve0 = curveArray[i];
822 addElements(elements, (CirculinearContinuousCurve2D)curve0.getSubCurve(curve0.getT0(), pos0));
823
824
825 pos1 = twinPositions.get(i).firstEntry().getValue();
826 int ind = ind0;
827
828 do {
829
830 CirculinearContour2D curve = curveArray[ind];
831
832
833 pos2 = nextValue(positions.get(ind), pos1);
834
835 if((pos2<pos1) && !curve.isBounded()) {
836
837
838
839 addElements(elements, (CirculinearContinuousCurve2D)curve.getSubCurve(pos1, curve.getT1()));
840 } else {
841
842
843 addElements(elements, (CirculinearContinuousCurve2D)curve.getSubCurve(pos1, pos2));
844
845
846 pos1 = twinPositions.get(ind).remove(pos2);
847 ind = twinIndices.get(ind).remove(pos2);
848 }
849 } while (ind!=ind0);
850
851 twinPositions.get(i).remove(pos0);
852 twinIndices.get(i).remove(pos0);
853
854
855
856 contours.add(
857 BoundaryPolyCirculinearCurve2D.create(elements, true));
858 }
859
860
861
862 while(!isAllEmpty(twinPositions)) {
863
864 ArrayList<CirculinearElement2D> elements =
865 new ArrayList<CirculinearElement2D>();
866
867
868 int ind0=0, ind;
869
870
871 for(int i=0;i<nCurves;i++) {
872
873 if(twinPositions.get(i).isEmpty())
874 continue;
875
876
877 pos0 = twinPositions.get(i).firstEntry().getValue();
878 ind0 = twinIndices.get(i).firstEntry().getValue();
879 break;
880 }
881
882 if (ind0==0) {
883 System.out.println("No more intersections, but was not detected");
884 }
885
886 pos1 = pos0;
887 ind = ind0;
888
889 do {
890 pos2 = nextValue(positions.get(ind), pos1);
891
892
893 addElements(elements, (CirculinearContinuousCurve2D)curveArray[ind].getSubCurve(pos1, pos2));
894
895
896 pos1 = twinPositions.get(ind).remove(pos2);
897 ind = twinIndices.get(ind).remove(pos2);
898 } while (pos1!=pos0 || ind!=ind0);
899
900
901
902 contours.add(
903 BoundaryPolyCirculinearCurve2D.create(elements, true));
904 }
905
906 return contours;
907 }
908
909
910
911
912
913 private static void addElements(
914 Collection<CirculinearElement2D> elements,
915 CirculinearContinuousCurve2D curve) {
916 elements.addAll(curve.getSmoothPieces());
917 }
918
919 private static boolean isAllEmpty(Collection<TreeMap<Double, Double>> coll) {
920 for(TreeMap<?,?> map : coll) {
921 if (!map.isEmpty())
922 return false;
923 }
924 return true;
925 }
926
927
928
929
930
931 private static double nextValue(TreeSet<Double> tree, double value) {
932 if(tree.higher(value)==null)
933 return tree.first();
934 else
935 return tree.higher(value);
936 }
937
938
939
940
941
942
943
944
945
946
947
948
949
950 public static CirculinearDomain2D computeBuffer(
951 CirculinearCurve2D curve, double dist) {
952
953 ArrayList<CirculinearContour2D> contours =
954 new ArrayList<CirculinearContour2D>();
955
956
957 for(CirculinearContinuousCurve2D cont : curve.getContinuousCurves()) {
958
959 for(CirculinearContinuousCurve2D splitted :
960 splitContinuousCurve(cont)) {
961
962 contours.addAll(computeBufferSimpleContour(splitted, dist));
963 }
964 }
965
966
967 contours = new ArrayList<CirculinearContour2D>(
968 splitIntersectingContours(contours));
969
970
971 ArrayList<CirculinearContour2D> contours2 =
972 new ArrayList<CirculinearContour2D>(contours.size());
973 for(CirculinearContour2D contour : contours) {
974
975
976 if(findIntersections(curve, contour).size()>0)
977 continue;
978
979
980
981 double distCurves =
982 getDistanceCurveSingularPoints(curve, contour);
983 if(distCurves<dist-Shape2D.ACCURACY)
984 continue;
985
986
987 contours2.add(contour);
988 }
989
990
991
992 return new GenericCirculinearDomain2D(
993 new CirculinearBoundarySet2D<CirculinearContour2D>(
994 contours2));
995 }
996
997
998
999
1000 public static CirculinearDomain2D computeBuffer(PointSet2D set,
1001 double dist) {
1002
1003 Collection<CirculinearContour2D> contours =
1004 new ArrayList<CirculinearContour2D>(set.getPointNumber());
1005
1006
1007 for(Point2D point : set) {
1008 contours.add(new Circle2D(point, Math.abs(dist), dist>0));
1009 }
1010
1011
1012 contours = splitIntersectingContours(contours);
1013
1014
1015 ArrayList<CirculinearContour2D> contours2 =
1016 new ArrayList<CirculinearContour2D>(contours.size());
1017 for(CirculinearContour2D ring : contours) {
1018
1019
1020
1021 double minDist = getDistanceCurvePoints(ring, set.getPoints());
1022 if(minDist<dist-Shape2D.ACCURACY)
1023 continue;
1024
1025
1026 contours2.add(ring);
1027 }
1028
1029 return new GenericCirculinearDomain2D(new CirculinearBoundarySet2D(contours2));
1030 }
1031
1032
1033
1034
1035
1036 public static Collection<? extends CirculinearContour2D>
1037 computeBufferSimpleContour(CirculinearContinuousCurve2D curve, double d) {
1038
1039 Collection<CirculinearContinuousCurve2D> parallels =
1040 createFreeParallels(curve, d);
1041
1042 Collection<CirculinearContour2D> contours =
1043 createContoursFromParallels(curve, parallels);
1044
1045
1046 Collection<CirculinearContour2D> contours2 =
1047 removeIntersectingContours(contours, curve, d);
1048
1049
1050 return contours2;
1051 }
1052
1053
1054
1055
1056
1057
1058 private static Collection<CirculinearContinuousCurve2D> createFreeParallels(
1059 CirculinearContinuousCurve2D curve, double d) {
1060
1061
1062 CirculinearContinuousCurve2D parallel1, parallel2;
1063 parallel1 = (CirculinearContinuousCurve2D)curve.getParallel(d);
1064 parallel2 = (CirculinearContinuousCurve2D)curve.getParallel(-d).getReverseCurve();
1065
1066
1067 ArrayList<CirculinearContinuousCurve2D> curves =
1068 new ArrayList<CirculinearContinuousCurve2D>();
1069
1070
1071 for(CirculinearContinuousCurve2D split : splitContinuousCurve(parallel1)) {
1072 if(findIntersections(curve, split).size()==0)
1073 curves.add(split);
1074 }
1075 for(CirculinearContinuousCurve2D split : splitContinuousCurve(parallel2)) {
1076 if(findIntersections(curve, split).size()==0)
1077 curves.add(split);
1078 }
1079
1080 return curves;
1081 }
1082
1083
1084
1085
1086
1087
1088 private static Collection<CirculinearContour2D> createContoursFromParallels(
1089 CirculinearContinuousCurve2D curve,
1090 Collection<CirculinearContinuousCurve2D> parallels) {
1091
1092 ArrayList<CirculinearContour2D> contours =
1093 new ArrayList<CirculinearContour2D>();
1094
1095
1096
1097 if(curve.isClosed()){
1098 for(CirculinearContinuousCurve2D continuous : parallels) {
1099 contours.add(convertCurveToBoundary(continuous));
1100 }
1101 return contours;
1102 }
1103
1104 return createContoursFromParallels(parallels);
1105 }
1106
1107
1108
1109
1110
1111 private static Collection<CirculinearContour2D>
1112 createContoursFromParallels(
1113 Collection<CirculinearContinuousCurve2D> parallels) {
1114
1115
1116 ArrayList<CirculinearContour2D> contours =
1117 new ArrayList<CirculinearContour2D>();
1118
1119
1120
1121 CirculinearContinuousCurve2D curve1=null;
1122 CirculinearContinuousCurve2D curve2=null;
1123
1124 for(CirculinearContinuousCurve2D continuous : parallels) {
1125 if(continuous.isClosed()){
1126
1127 contours.add(convertCurveToBoundary(continuous));
1128 } else {
1129 if(curve1==null){
1130 curve1 = continuous;
1131 } else if (curve2==null) {
1132 curve2 = continuous;
1133 } else {
1134
1135 System.err.println("more than 2 free curves....");
1136 return contours;
1137 }
1138 }
1139 }
1140
1141 if(curve1!=null && curve2!=null) {
1142 contours.addAll(createSingleContourFromTwoParallels(curve1, curve2));
1143 }
1144
1145 return contours;
1146 }
1147
1148
1149
1150
1151
1152 private static Collection<CirculinearContour2D>
1153 createSingleContourFromTwoParallels(
1154 CirculinearContinuousCurve2D curve1,
1155 CirculinearContinuousCurve2D curve2) {
1156
1157
1158 ArrayList<CirculinearContour2D> contours =
1159 new ArrayList<CirculinearContour2D>();
1160
1161
1162 if(curve1!=null && curve2!=null){
1163
1164 ArrayList<CirculinearElement2D> elements =
1165 new ArrayList<CirculinearElement2D>();
1166
1167
1168 boolean b0 = Curve2DUtils.isLeftInfinite(curve1);
1169 boolean b1 = Curve2DUtils.isRightInfinite(curve1);
1170
1171 if (b0 && b1) {
1172
1173
1174
1175 contours.add(convertCurveToBoundary(curve1));
1176 contours.add(convertCurveToBoundary(curve2));
1177 } else if (b0 && !b1) {
1178
1179
1180
1181
1182 Point2D p11 = curve1.getFirstPoint();
1183 Point2D p22 = curve2.getLastPoint();
1184
1185
1186 elements.addAll(curve2.getSmoothPieces());
1187 elements.add(createCircularCap(p22, p11));
1188 elements.addAll(curve1.getSmoothPieces());
1189
1190
1191 contours.add(new GenericCirculinearRing2D(elements));
1192 } else if (b1 && !b0) {
1193
1194
1195
1196
1197 Point2D p12 = curve1.getLastPoint();
1198 Point2D p21 = curve2.getFirstPoint();
1199
1200
1201 elements.addAll(curve1.getSmoothPieces());
1202 elements.add(createCircularCap(p12, p21));
1203 elements.addAll(curve2.getSmoothPieces());
1204
1205
1206 contours.add(new GenericCirculinearRing2D(elements));
1207
1208 } else {
1209
1210
1211
1212 Point2D p11 = curve1.getFirstPoint();
1213 Point2D p12 = curve1.getLastPoint();
1214 Point2D p21 = curve2.getFirstPoint();
1215 Point2D p22 = curve2.getLastPoint();
1216
1217
1218
1219 elements.addAll(curve1.getSmoothPieces());
1220 elements.add(createCircularCap(p12, p21));
1221 elements.addAll(curve2.getSmoothPieces());
1222 elements.add(createCircularCap(p22, p11));
1223
1224
1225 contours.add(new GenericCirculinearRing2D(elements));
1226 }
1227 }
1228
1229 return contours;
1230 }
1231
1232 private static Collection<CirculinearContour2D> removeIntersectingContours (
1233 Collection<CirculinearContour2D> contours,
1234 CirculinearCurve2D curve, double d) {
1235
1236 ArrayList<CirculinearContour2D> contours2 =
1237 new ArrayList<CirculinearContour2D>();
1238
1239
1240 for(CirculinearContour2D contour : contours)
1241
1242 for(CirculinearContinuousCurve2D splitted :
1243 splitContinuousCurve(contour)) {
1244
1245
1246
1247
1248 double dist = getDistanceCurvePoints(curve,
1249 splitted.getSingularPoints());
1250
1251
1252 if(dist-d<-Shape2D.ACCURACY)
1253 continue;
1254
1255
1256 contours2.add(convertCurveToBoundary(splitted));
1257 }
1258
1259
1260 return contours2;
1261 }
1262
1263
1264
1265
1266
1267
1268
1269 private static CirculinearContour2D convertCurveToBoundary (
1270 CirculinearContinuousCurve2D curve) {
1271
1272 if (curve instanceof CirculinearContour2D)
1273 return (CirculinearContour2D) curve;
1274
1275
1276 if (curve.isClosed())
1277 return GenericCirculinearRing2D.create(curve.getSmoothPieces());
1278
1279 return BoundaryPolyCirculinearCurve2D.create(curve.getSmoothPieces());
1280 }
1281
1282 private static CircleArc2D createCircularCap(Point2D p1, Point2D p2){
1283 Point2D center = Point2D.midPoint(p1, p2);
1284 double radius = p1.getDistance(p2)/2;
1285 double angle1 = Angle2D.getHorizontalAngle(center, p1);
1286 double angle2 = Angle2D.getHorizontalAngle(center, p2);
1287 return CircleArc2D.create(center, radius, angle1, angle2, true);
1288 }
1289
1290 public static double getDistanceCurvePoints(
1291 CirculinearCurve2D curve, Collection<? extends Point2D> points){
1292 double minDist = Double.MAX_VALUE;
1293 for(Point2D point : points){
1294 minDist = Math.min(minDist, curve.getDistance(point));
1295 }
1296 return minDist;
1297 }
1298
1299 private static double getDistanceCurveSingularPoints(
1300 CirculinearCurve2D ref, CirculinearCurve2D curve){
1301
1302 Collection<Point2D> points = curve.getSingularPoints();
1303
1304
1305 if(points.isEmpty()) {
1306 points = new ArrayList<Point2D>();
1307 double t = Curve2DUtils.choosePosition(curve.getT0(), curve.getT1());
1308 points.add(curve.getPoint(t));
1309 }
1310
1311
1312 double minDist = Double.MAX_VALUE;
1313 for(Point2D point : points){
1314 minDist = Math.min(minDist, ref.getDistance(point));
1315 }
1316 return minDist;
1317 }
1318 }