1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package math.geom2d.curve;
25
26 import java.awt.Graphics2D;
27 import java.awt.Shape;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Iterator;
31
32 import math.geom2d.AffineTransform2D;
33 import math.geom2d.Box2D;
34 import math.geom2d.Point2D;
35 import math.geom2d.Shape2D;
36 import math.geom2d.line.LinearShape2D;
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public class CurveSet2D<T extends Curve2D> implements Curve2D, Iterable<T>, Cloneable {
51
52
53 protected ArrayList<T> curves;
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 @Deprecated
70 protected final static double toUnitSegment(double t, double t0, double t1) {
71 if (t<=t0)
72 return 0;
73 if (t>=t1)
74 return 1;
75
76 if (t0==Double.NEGATIVE_INFINITY&&t1==Double.POSITIVE_INFINITY)
77 return Math.atan(t)/Math.PI+.5;
78
79 if (t0==Double.NEGATIVE_INFINITY)
80 return Math.atan(t-t1)*2/Math.PI+1;
81
82 if (t1==Double.POSITIVE_INFINITY)
83 return Math.atan(t-t0)*2/Math.PI;
84
85
86 return (t-t0)/(t1-t0);
87 }
88
89
90
91
92
93
94
95
96
97
98
99 @Deprecated
100 protected final static double fromUnitSegment(double t, double t0, double t1) {
101 if (t<=0)
102 return t0;
103 if (t>=1)
104 return t1;
105
106 if (t0==Double.NEGATIVE_INFINITY&&t1==Double.POSITIVE_INFINITY)
107 return Math.tan((t-.5)*Math.PI);
108
109 if (t0==Double.NEGATIVE_INFINITY)
110 return Math.tan((t-1)*Math.PI/2)+t1;
111
112 if (t1==Double.POSITIVE_INFINITY)
113 return Math.tan(t*Math.PI/2)+t0;
114
115
116 return t*(t1-t0)+t0;
117 }
118
119
120
121
122
123
124
125
126 @Deprecated
127 public CurveSet2D() {
128 this.curves = new ArrayList<T>();
129 }
130
131
132
133
134
135
136 @Deprecated
137 public CurveSet2D(int n) {
138 this.curves = new ArrayList<T>(n);
139 }
140
141
142
143
144
145
146
147 @Deprecated
148 public CurveSet2D(T[] curves) {
149 this.curves = new ArrayList<T>(curves.length);
150 for (T element : curves)
151 this.addCurve(element);
152 }
153
154
155
156
157
158
159
160
161 @Deprecated
162 public CurveSet2D(Collection<? extends T> curves) {
163 this.curves = new ArrayList<T>(curves.size());
164 this.curves.addAll(curves);
165 }
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181 public double getLocalPosition(double t) {
182 int i = this.getCurveIndex(t);
183 T curve = curves.get(i);
184 double t0 = curve.getT0();
185 double t1 = curve.getT1();
186 return Curve2DUtils.fromUnitSegment(t-2*i, t0, t1);
187 }
188
189
190
191
192
193
194
195
196
197
198
199 public double getGlobalPosition(int i, double t) {
200 T curve = curves.get(i);
201 double t0 = curve.getT0();
202 double t1 = curve.getT1();
203 return Curve2DUtils.toUnitSegment(t, t0, t1)+i*2;
204 }
205
206
207
208
209
210
211
212
213 public int getCurveIndex(double t) {
214
215
216 if (curves.size()==0)
217 return 0;
218 if (t>curves.size()*2-1)
219 return curves.size()-1;
220
221
222 int nc = (int) Math.floor(t);
223
224
225 int indc = (int) Math.floor(nc/2);
226 if (indc*2==nc)
227 return indc;
228 else
229 return t-nc<.5 ? indc : indc+1;
230 }
231
232
233
234
235
236
237
238
239
240
241 public void addCurve(T curve) {
242 if (!curves.contains(curve))
243 curves.add(curve);
244 }
245
246
247
248
249
250
251 public void removeCurve(T curve) {
252 curves.remove(curve);
253 }
254
255
256
257
258 public boolean containsCurve(T curve) {
259 return curves.contains(curve);
260 }
261
262
263
264
265 public void clearCurves() {
266 curves.clear();
267 }
268
269
270
271
272
273
274 public Collection<T> getCurves() {
275 return curves;
276 }
277
278
279
280
281
282
283
284
285 public T getCurve(int index) {
286 return curves.get(index);
287 }
288
289
290
291
292
293
294
295
296
297 public T getChildCurve(double t) {
298 if (curves.size()==0)
299 return null;
300 return curves.get(getCurveIndex(t));
301 }
302
303
304
305
306
307
308 public T getFirstCurve() {
309 if (curves.size()==0)
310 return null;
311 return curves.get(0);
312 }
313
314
315
316
317
318
319 public T getLastCurve() {
320 if (curves.size()==0)
321 return null;
322 return curves.get(curves.size()-1);
323 }
324
325
326
327
328
329
330 public int getCurveNumber() {
331 return curves.size();
332 }
333
334
335
336
337 public boolean isEmpty() {
338 return curves.size()==0;
339 }
340
341
342
343
344 public Collection<Point2D> getIntersections(LinearShape2D line) {
345 ArrayList<Point2D> intersect = new ArrayList<Point2D>();
346
347
348 for (Curve2D curve : curves)
349 intersect.addAll(curve.getIntersections(line));
350
351 return intersect;
352 }
353
354 public double getT0() {
355 return 0;
356 }
357
358 public double getT1() {
359 return Math.max(curves.size()*2-1, 0);
360 }
361
362
363
364
365
366
367 public Point2D getPoint(double t) {
368 if (curves.size()==0)
369 return null;
370 if (t<getT0())
371 return this.getFirstCurve().getFirstPoint();
372 if (t>getT1())
373 return this.getLastCurve().getLastPoint();
374
375
376 int nc = (int) Math.floor(t);
377
378
379 int indc = (int) Math.floor(nc/2);
380 if (indc*2==nc) {
381 Curve2D curve = curves.get(indc);
382 double pos = Curve2DUtils.fromUnitSegment(t-nc,
383 curve.getT0(), curve.getT1());
384 return curve.getPoint(pos);
385 } else {
386
387
388 if (t-nc<.5)
389 return curves.get(indc).getLastPoint();
390 else
391 return curves.get(indc+1).getFirstPoint();
392 }
393 }
394
395
396
397
398
399
400 public Point2D getFirstPoint() {
401 if (curves.size()==0)
402 return null;
403 return getFirstCurve().getFirstPoint();
404 }
405
406
407
408
409
410
411 public Point2D getLastPoint() {
412 if (curves.size()==0)
413 return null;
414 return getLastCurve().getLastPoint();
415 }
416
417
418
419
420
421
422 public Collection<Point2D> getSingularPoints() {
423 ArrayList<Point2D> points = new ArrayList<Point2D>();
424 for (Curve2D curve : curves){
425 for (Point2D point : curve.getSingularPoints())
426 if (!points.contains(point))
427 points.add(point);
428 if(!Double.isInfinite(curve.getT0()))
429 points.add(curve.getFirstPoint());
430 if(!Double.isInfinite(curve.getT1()))
431 points.add(curve.getLastPoint());
432 }
433 return points;
434 }
435
436 public boolean isSingular(double pos) {
437 if (Math.abs(pos-Math.round(pos))<Shape2D.ACCURACY)
438 return true;
439
440 int nc = this.getCurveIndex(pos);
441
442 if (nc-Math.floor(pos/2.0)>0)
443 return true;
444
445
446 Curve2D curve = curves.get(nc);
447
448
449 return curve.isSingular(this.getLocalPosition(pos));
450 }
451
452 public double getPosition(java.awt.geom.Point2D point) {
453 double minDist = Double.MAX_VALUE, dist = minDist;
454 double x = point.getX(), y = point.getY();
455 double pos = 0, t0, t1;
456
457 int i = 0;
458 for (Curve2D curve : curves) {
459 dist = curve.getDistance(x, y);
460 if (dist<minDist) {
461 minDist = dist;
462 pos = curve.getPosition(point);
463
464 t0 = curve.getT0();
465 t1 = curve.getT1();
466 pos = Curve2DUtils.toUnitSegment(pos, t0, t1)+i*2;
467 }
468 i++;
469 }
470 return pos;
471 }
472
473 public double project(java.awt.geom.Point2D point) {
474 double minDist = Double.MAX_VALUE, dist = minDist;
475 double x = point.getX(), y = point.getY();
476 double pos = 0, t0, t1;
477
478 int i = 0;
479 for (Curve2D curve : curves) {
480 dist = curve.getDistance(x, y);
481 if (dist<minDist) {
482 minDist = dist;
483 pos = curve.project(point);
484
485 t0 = curve.getT0();
486 t1 = curve.getT1();
487 pos = Curve2DUtils.toUnitSegment(pos, t0, t1)+i*2;
488 }
489 i++;
490 }
491 return pos;
492 }
493
494 public Curve2D getReverseCurve() {
495 int n = curves.size();
496
497 Curve2D[] curves2 = new Curve2D[n];
498
499
500 for (int i = 0; i<n; i++)
501 curves2[i] = curves.get(n-1-i).getReverseCurve();
502
503
504 return new CurveArray2D<Curve2D>(curves2);
505 }
506
507
508
509
510 public CurveSet2D<? extends Curve2D> getSubCurve(double t0, double t1) {
511
512 int nc = curves.size();
513
514
515 CurveArray2D<Curve2D> res = new CurveArray2D<Curve2D>();
516 Curve2D curve;
517
518
519 t0 = Math.min(Math.max(t0, 0), nc*2-.6);
520 t1 = Math.min(Math.max(t1, 0), nc*2-.6);
521
522
523 double t0f = Math.floor(t0);
524 double t1f = Math.floor(t1);
525
526
527 int ind0 = (int) Math.floor(t0f/2);
528 int ind1 = (int) Math.floor(t1f/2);
529
530
531 if (t0-2*ind0>1.5)
532 ind0++;
533 if (t1-2*ind1>1.5)
534 ind1++;
535
536
537 t0f = 2*ind0;
538 t1f = 2*ind1;
539
540 double pos0, pos1;
541
542
543 if (ind0==ind1&&t0<t1) {
544 curve = curves.get(ind0);
545 pos0 = Curve2DUtils.fromUnitSegment(t0-t0f,
546 curve.getT0(), curve.getT1());
547 pos1 = Curve2DUtils.fromUnitSegment(t1-t1f,
548 curve.getT0(), curve.getT1());
549 res.addCurve(curve.getSubCurve(pos0, pos1));
550 return res;
551 }
552
553
554 curve = curves.get(ind0);
555 pos0 = Curve2DUtils.fromUnitSegment(t0-t0f,
556 curve.getT0(), curve.getT1());
557 res.addCurve(curve.getSubCurve(pos0, curve.getT1()));
558
559 if (ind1>ind0) {
560
561 for (int n = ind0+1; n<ind1; n++)
562 res.addCurve(curves.get(n));
563 } else {
564
565 for (int n = ind0+1; n<nc; n++)
566 res.addCurve(curves.get(n));
567
568
569 for (int n = 0; n<ind1; n++)
570 res.addCurve(curves.get(n));
571 }
572
573
574 curve = curves.get(ind1);
575 pos1 = Curve2DUtils.fromUnitSegment(t1-t1f,
576 curve.getT0(), curve.getT1());
577 res.addCurve(curve.getSubCurve(curve.getT0(), pos1));
578
579
580 return res;
581 }
582
583
584
585
586 public double getDistance(java.awt.geom.Point2D p) {
587 return getDistance(p.getX(), p.getY());
588 }
589
590 public double getDistance(double x, double y) {
591 double dist = Double.POSITIVE_INFINITY;
592 for (Curve2D curve : curves)
593 dist = Math.min(dist, curve.getDistance(x, y));
594 return dist;
595 }
596
597
598
599
600 public boolean isBounded() {
601 for (Curve2D curve : curves)
602 if (!curve.isBounded())
603 return false;
604 return true;
605 }
606
607
608
609
610
611
612
613 public CurveSet2D<? extends Curve2D> clip(Box2D box) {
614
615 return Curve2DUtils.clipCurveSet(this, box);
616 }
617
618
619
620
621 public Box2D getBoundingBox() {
622 double xmin = Double.MAX_VALUE;
623 double ymin = Double.MAX_VALUE;
624 double xmax = Double.MIN_VALUE;
625 double ymax = Double.MIN_VALUE;
626
627 Box2D box;
628 for (Curve2D curve : curves) {
629 box = curve.getBoundingBox();
630 xmin = Math.min(xmin, box.getMinX());
631 ymin = Math.min(ymin, box.getMinY());
632 xmax = Math.max(xmax, box.getMaxX());
633 ymax = Math.max(ymax, box.getMaxY());
634 }
635
636 return new Box2D(xmin, xmax, ymin, ymax);
637 }
638
639
640
641
642
643 public CurveSet2D<? extends Curve2D> transform(AffineTransform2D trans) {
644
645 CurveArray2D<Curve2D> result =
646 new CurveArray2D<Curve2D>(curves.size());
647
648
649 for (Curve2D curve : curves)
650 result.addCurve(curve.transform(trans));
651 return result;
652 }
653
654 public Collection<? extends ContinuousCurve2D> getContinuousCurves() {
655
656 ArrayList<ContinuousCurve2D> continuousCurves =
657 new ArrayList<ContinuousCurve2D>();
658
659
660
661 for (Curve2D curve : curves) {
662 if (curve instanceof ContinuousCurve2D) {
663 continuousCurves.add((ContinuousCurve2D) curve);
664 } else {
665 continuousCurves.addAll(curve.getContinuousCurves());
666 }
667 }
668
669 return continuousCurves;
670 }
671
672
673
674
675
676 public boolean contains(java.awt.geom.Point2D p) {
677 return contains(p.getX(), p.getY());
678 }
679
680
681 public boolean contains(double x, double y) {
682 for (Curve2D curve : curves) {
683 if (curve.contains(x, y))
684 return true;
685 }
686 return false;
687 }
688
689 public java.awt.geom.GeneralPath getGeneralPath() {
690
691 java.awt.geom.GeneralPath path = new java.awt.geom.GeneralPath();
692
693
694 if (curves.size()==0)
695 return path;
696
697
698 Point2D point;
699 for (ContinuousCurve2D curve : this.getContinuousCurves()) {
700 point = curve.getFirstPoint();
701 path.moveTo((float) point.getX(), (float) point.getY());
702 path = curve.appendPath(path);
703 }
704
705
706 return path;
707 }
708
709
710
711
712 public Shape getAsAWTShape() {
713 return this.getGeneralPath();
714 }
715
716 public void draw(Graphics2D g2) {
717 for(Curve2D curve : curves)
718 curve.draw(g2);
719 }
720
721
722
723
724
725
726
727
728 @Override
729 public boolean equals(Object obj) {
730
731 if (!(obj instanceof CurveSet2D))
732 return false;
733 CurveSet2D<?> curveSet = (CurveSet2D<?>) obj;
734
735
736 if (this.getCurveNumber()!=curveSet.getCurveNumber())
737 return false;
738
739
740 for(int i=0; i<curves.size(); i++)
741 if(!curves.get(i).equals(curveSet.curves.get(i)))
742 return false;
743
744
745 return true;
746 }
747
748 @Override
749 public CurveSet2D<? extends Curve2D> clone() {
750 ArrayList<Curve2D> array = new ArrayList<Curve2D>(curves.size());
751 for(T curve : curves)
752 array.add(curve);
753 return new CurveArray2D<Curve2D>(array);
754 }
755
756
757
758
759
760
761
762
763
764 public Iterator<T> iterator() {
765 return curves.iterator();
766 }
767 }