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 package math.geom2d.line;
27
28
29 import java.util.ArrayList;
30 import java.util.Collection;
31
32 import math.geom2d.AffineTransform2D;
33 import math.geom2d.Angle2D;
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.CirculinearElement2D;
41 import math.geom2d.conic.Circle2D;
42 import math.geom2d.conic.CircleArc2D;
43 import math.geom2d.curve.AbstractSmoothCurve2D;
44
45 import math.geom2d.curve.ContinuousCurve2D;
46 import math.geom2d.curve.Curve2D;
47 import math.geom2d.curve.Curve2DUtils;
48 import math.geom2d.curve.CurveArray2D;
49 import math.geom2d.curve.CurveSet2D;
50 import math.geom2d.domain.SmoothOrientedCurve2D;
51 import math.geom2d.transform.CircleInversion2D;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 public abstract class AbstractLine2D extends AbstractSmoothCurve2D
73 implements SmoothOrientedCurve2D, LinearShape2D, CirculinearElement2D {
74
75
76
77
78
79
80
81
82
83
84 protected double x0, y0;
85
86
87
88
89 protected double dx, dy;
90
91
92
93
94
95
96
97
98 public final static Point2D getIntersection(AbstractLine2D l1,
99 AbstractLine2D l2) {
100 double t = ((l1.y0-l2.y0)*l2.dx-(l1.x0-l2.x0)*l2.dy)
101 /(l1.dx*l2.dy-l1.dy*l2.dx);
102 return new Point2D(l1.x0+t*l1.dx, l1.y0+t*l1.dy);
103 }
104
105
106
107
108 public final static boolean isColinear(AbstractLine2D line1,
109 AbstractLine2D line2) {
110
111 if (Math.abs(line1.dx*line2.dy-line1.dy*line2.dx)>ACCURACY)
112 return false;
113
114
115
116 return (Math.abs((line2.y0-line1.y0)*line2.dx-(line2.x0-line1.x0)
117 *line2.dy)
118 /Math.hypot(line2.dx, line2.dy)<Shape2D.ACCURACY);
119 }
120
121
122
123
124 public final static boolean isParallel(AbstractLine2D line1,
125 AbstractLine2D line2) {
126 return (Math.abs(line1.dx*line2.dy-line1.dy*line2.dx)<ACCURACY);
127 }
128
129
130
131
132 protected AbstractLine2D(double x0, double y0, double dx, double dy) {
133 this.x0 = x0;
134 this.y0 = y0;
135 this.dx = dx;
136 this.dy = dy;
137 }
138
139 protected AbstractLine2D(Point2D point, Vector2D vector) {
140 this.x0 = point.getX();
141 this.y0 = point.getY();
142 this.dx = vector.getX();
143 this.dy = vector.getY();
144 }
145
146 protected AbstractLine2D(LinearShape2D line) {
147 this(line.getOrigin(), line.getVector());
148 }
149
150
151
152
153 public boolean isColinear(LinearShape2D linear) {
154
155 if (!isParallel(linear))
156 return false;
157
158
159
160 StraightLine2D line = linear.getSupportingLine();
161 if (Math.abs(dx)>Math.abs(dy)) {
162 if (Math.abs((line.x0-x0)*dy/dx+y0-line.y0)>Shape2D.ACCURACY)
163 return false;
164 else
165 return true;
166 } else {
167 if (Math.abs((line.y0-y0)*dx/dy+x0-line.x0)>Shape2D.ACCURACY)
168 return false;
169 else
170 return true;
171 }
172 }
173
174
175
176
177 public boolean isParallel(LinearShape2D line) {
178 return Vector2D.isColinear(this.getVector(), line.getVector());
179 }
180
181
182
183
184
185 protected boolean supportContains(double x, double y) {
186 return (Math.abs((x-x0)*dy-(y-y0)*dx)/Math.hypot(dx, dy)<Shape2D.ACCURACY);
187 }
188
189
190
191
192
193
194
195
196
197
198 public double[][] getParametric() {
199 double tab[][] = new double[2][2];
200 tab[0][0] = x0;
201 tab[0][1] = dx;
202 tab[1][0] = y0;
203 tab[1][1] = dy;
204 return tab;
205 }
206
207
208
209
210
211
212
213 public double[] getCartesianEquation() {
214 double tab[] = new double[3];
215 tab[0] = dy;
216 tab[1] = -dx;
217 tab[2] = dx*y0-dy*x0;
218 return tab;
219 }
220
221
222
223
224
225
226
227
228 public double[] getPolarCoefficients() {
229 double tab[] = new double[2];
230 double d = getSignedDistance(0, 0);
231 tab[0] = Math.abs(d);
232 if (d>0)
233 tab[1] = (getHorizontalAngle()+Math.PI)%(2*Math.PI);
234 else
235 tab[1] = getHorizontalAngle();
236 return tab;
237 }
238
239
240
241
242
243
244
245
246
247 public double[] getSignedPolarCoefficients() {
248 double tab[] = new double[2];
249 tab[0] = getSignedDistance(0, 0);
250 tab[1] = getHorizontalAngle();
251 return tab;
252 }
253
254 public double getPositionOnLine(java.awt.geom.Point2D point) {
255 return getPositionOnLine(point.getX(), point.getY());
256 }
257
258
259
260
261
262
263
264
265 public double getPositionOnLine(double x, double y) {
266 return ((y-y0)*dy+(x-x0)*dx)/(dx*dx+dy*dy);
267 }
268
269
270
271
272
273
274
275
276
277 public Point2D getProjectedPoint(Point2D p) {
278 return getProjectedPoint(p.getX(), p.getY());
279 }
280
281
282
283
284
285
286
287
288
289 public Point2D getProjectedPoint(double x, double y) {
290 if (contains(x, y))
291 return new Point2D(x, y);
292
293
294 double t = getPositionOnLine(x, y);
295
296
297 return new Point2D(x0+t*dx, y0+t*dy);
298 }
299
300
301
302
303
304
305
306
307 public Point2D getSymmetric(Point2D p) {
308 return getSymmetric(p.getX(), p.getY());
309 }
310
311
312
313
314
315
316
317
318
319 public Point2D getSymmetric(double x, double y) {
320
321 double t = 2*getPositionOnLine(x, y);
322
323
324 return new Point2D(2*x0+t*dx-x, 2*y0+t*dy-y);
325 }
326
327
328
329
330
331
332
333
334 public StraightLine2D getParallel(Point2D point) {
335 return new StraightLine2D(point, this.dx, this.dy);
336 }
337
338
339
340
341
342
343
344
345 public StraightLine2D getPerpendicular(Point2D point) {
346 return new StraightLine2D(point, -this.dy, this.dx);
347 }
348
349
350
351
352
353 public Point2D getOrigin() {
354 return new Point2D(x0, y0);
355 }
356
357 public Vector2D getVector() {
358 return new Vector2D(dx, dy);
359 }
360
361
362
363
364
365 public double getHorizontalAngle() {
366 return (Math.atan2(dy, dx)+2*Math.PI)%(2*Math.PI);
367 }
368
369
370
371
372
373 public Point2D getIntersection(LinearShape2D line) {
374 Vector2D vect = line.getVector();
375 double dx2 = vect.getX();
376 double dy2 = vect.getY();
377
378
379 if (Math.abs(dx*dy2-dy*dx2)<Shape2D.ACCURACY)
380 return null;
381
382
383 Point2D origin = line.getOrigin();
384 double x2 = origin.getX();
385 double y2 = origin.getY();
386 double t = ((y0-y2)*dx2-(x0-x2)*dy2)/(dx*dy2-dy*dx2);
387
388
389 Point2D point = new Point2D(x0+t*dx, y0+t*dy);
390
391
392
393 if (contains(point)&&line.contains(point))
394 return point;
395 return null;
396 }
397
398 public StraightLine2D getSupportingLine() {
399 return new StraightLine2D(this);
400 }
401
402
403
404
405
406
407
408
409 public CirculinearDomain2D getBuffer(double dist) {
410 return CirculinearCurve2DUtils.computeBuffer(this, dist);
411 }
412
413
414
415
416 public double getLength() {
417 if(!this.isBounded()) return Double.POSITIVE_INFINITY;
418 return (getT1()-getT0())*Math.hypot(dx, dy);
419 }
420
421
422
423
424 public double getLength(double pos) {
425 return pos*Math.hypot(dx, dy);
426 }
427
428
429
430
431 public double getPosition(double length) {
432 return length/Math.hypot(dx, dy);
433 }
434
435
436
437
438 public CirculinearElement2D transform(CircleInversion2D inv) {
439
440 Point2D center = inv.getCenter();
441 double r = inv.getRadius();
442
443
444 Point2D po = this.getProjectedPoint(center);
445 double d = this.getDistance(po);
446
447
448 boolean inf0 = Double.isInfinite(this.getT0());
449 boolean inf1 = Double.isInfinite(this.getT1());
450
451
452
453 if (Math.abs(d)<Shape2D.ACCURACY){
454 if (inf0){
455 if (inf1){
456
457 return new StraightLine2D(this);
458 } else {
459
460
461 Point2D p2 = this.getLastPoint().transform(inv);
462 return new InvertedRay2D(p2, this.getVector());
463 }
464 } else {
465 Point2D p1 = this.getFirstPoint().transform(inv);
466 if (inf1){
467
468 return new Ray2D(p1, this.getVector());
469 } else {
470
471 Point2D p2 = this.getLastPoint().transform(inv);
472 return new LineSegment2D(p1, p2);
473 }
474 }
475 }
476
477
478 double angle = Angle2D.getHorizontalAngle(center, po);
479
480
481 double r2 = r*r/d/2;
482 Point2D c2 = Point2D.createPolar(center, r2, angle);
483
484
485 boolean direct = !this.isInside(center);
486
487
488 if (inf0 && inf1) {
489 return new Circle2D(c2, r2, direct);
490 }
491
492
493
494 Point2D p1 = inf0 ? center : this.getFirstPoint();
495 Point2D p2 = inf1 ? center : this.getLastPoint();
496
497
498 double theta1 = Angle2D.getHorizontalAngle(c2, p1);
499 double theta2 = Angle2D.getHorizontalAngle(c2, p2);
500
501
502 return new CircleArc2D(c2, r2, theta1, theta2, direct);
503 }
504
505
506
507
508
509 public double getWindingAngle(java.awt.geom.Point2D point) {
510
511 double t0 = this.getT0();
512 double t1 = this.getT1();
513
514 double angle1, angle2;
515 if (t0==Double.NEGATIVE_INFINITY)
516 angle1 = Angle2D.getHorizontalAngle(-dx, -dy);
517 else
518 angle1 = Angle2D.getHorizontalAngle(point.getX(), point.getY(), x0
519 +t0*dx, y0+t0*dy);
520
521 if (t1==Double.POSITIVE_INFINITY)
522 angle2 = Angle2D.getHorizontalAngle(dx, dy);
523 else
524 angle2 = Angle2D.getHorizontalAngle(point.getX(), point.getY(), x0
525 +t1*dx, y0+t1*dy);
526
527 if (this.isInside(point)) {
528 if (angle2>angle1)
529 return angle2-angle1;
530 else
531 return 2*Math.PI-angle1+angle2;
532 } else {
533 if (angle2>angle1)
534 return angle2-angle1-2*Math.PI;
535 else
536 return angle2-angle1;
537 }
538 }
539
540
541
542
543
544
545
546
547 public double getSignedDistance(java.awt.geom.Point2D p) {
548 return getSignedDistance(p.getX(), p.getY());
549 }
550
551
552
553
554
555
556
557
558 public double getSignedDistance(double x, double y) {
559 return ((x-x0)*dy-(y-y0)*dx)/Math.hypot(dx, dy);
560 }
561
562
563
564
565
566
567
568
569 public boolean isInside(java.awt.geom.Point2D p) {
570 return ((p.getX()-x0)*dy-(p.getY()-y0)*dx<0);
571 }
572
573
574
575
576 public Vector2D getTangent(double t) {
577 return new Vector2D(dx, dy);
578 }
579
580
581
582
583 public double getCurvature(double t) {
584 return 0.0;
585 }
586
587
588
589
590
591
592
593
594 public boolean isClosed() {
595 return false;
596 }
597
598
599
600
601
602
603
604
605 @Override
606 public Collection<? extends AbstractLine2D> getSmoothPieces() {
607 return wrapCurve(this);
608 }
609
610
611 public Collection<Point2D> getIntersections(LinearShape2D line) {
612 ArrayList<Point2D> points = new ArrayList<Point2D>();
613
614 Point2D point = getIntersection(line);
615 if (point==null)
616 return points;
617
618
619 points.add(point);
620 return points;
621 }
622
623
624
625
626
627
628
629
630
631 public double getPosition(java.awt.geom.Point2D point) {
632 double pos = this.getPositionOnLine(point);
633
634
635 if (pos<this.getT0())
636 return Double.NaN;
637 if (pos>this.getT1())
638 return Double.NaN;
639 return pos;
640 }
641
642
643
644
645
646
647
648
649
650
651 public double project(java.awt.geom.Point2D point) {
652 double pos = this.getPositionOnLine(point);
653
654
655 return Math.min(Math.max(pos, this.getT0()), this.getT1());
656 }
657
658
659
660
661
662
663 public AbstractLine2D getSubCurve(double t0, double t1) {
664 t0 = Math.max(t0, this.getT0());
665 t1 = Math.min(t1, this.getT1());
666 if (Double.isInfinite(t1)) {
667 if (Double.isInfinite(t0))
668 return new StraightLine2D(this);
669 else
670 return new Ray2D(this.getPoint(t0), this.getVector());
671 }
672
673 if (Double.isInfinite(t0))
674 return new InvertedRay2D(this.getPoint(t1), this.getVector());
675 else
676 return new LineSegment2D(this.getPoint(t0), this.getPoint(t1));
677
678 }
679
680 @Override
681 public Collection<? extends AbstractLine2D> getContinuousCurves() {
682 return wrapCurve(this);
683 }
684
685
686
687
688
689
690
691
692
693 public double getDistance(java.awt.geom.Point2D p) {
694 return getDistance(p.getX(), p.getY());
695 }
696
697
698
699
700
701
702
703
704
705
706 public double getDistance(double x, double y) {
707
708 Point2D proj = getProjectedPoint(x, y);
709
710
711 if (contains(proj))
712 return proj.distance(x, y);
713
714
715 double dist = Double.POSITIVE_INFINITY;
716 if(!Double.isInfinite(getT0()))
717 dist = getFirstPoint().getDistance(x, y);
718 if(!Double.isInfinite(getT1()))
719 dist = Math.min(dist, getLastPoint().getDistance(x, y));
720 return dist;
721 }
722
723 public boolean contains(java.awt.geom.Point2D p) {
724 return this.contains(p.getX(), p.getY());
725 }
726
727
728
729
730 public boolean isEmpty() {
731 return Math.hypot(dx, dy)<Shape2D.ACCURACY;
732 }
733
734 public abstract AbstractLine2D transform(AffineTransform2D transform);
735
736 public CurveSet2D<? extends AbstractLine2D> clip(Box2D box) {
737
738 CurveSet2D<ContinuousCurve2D> set = Curve2DUtils.clipContinuousCurve(
739 this, box);
740
741
742 CurveArray2D<AbstractLine2D> result =
743 new CurveArray2D<AbstractLine2D>(set.getCurveNumber());
744
745
746 for (Curve2D curve : set.getCurves()) {
747 if (curve instanceof AbstractLine2D)
748 result.addCurve((AbstractLine2D) curve);
749 }
750 return result;
751 }
752
753
754
755
756 @Override
757 public abstract AbstractLine2D clone();
758 }