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