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.conic;
28
29 import java.awt.Graphics2D;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Iterator;
33 import java.util.Locale;
34
35 import math.geom2d.*;
36 import math.geom2d.circulinear.CircleLine2D;
37 import math.geom2d.circulinear.CirculinearBoundary2D;
38 import math.geom2d.circulinear.CirculinearCurve2DUtils;
39 import math.geom2d.circulinear.CirculinearDomain2D;
40 import math.geom2d.circulinear.CirculinearElement2D;
41 import math.geom2d.curve.Curve2D;
42 import math.geom2d.curve.Curve2DUtils;
43 import math.geom2d.curve.CurveArray2D;
44 import math.geom2d.curve.CurveSet2D;
45 import math.geom2d.curve.SmoothCurve2D;
46 import math.geom2d.line.AbstractLine2D;
47 import math.geom2d.line.LinearShape2D;
48 import math.geom2d.line.StraightLine2D;
49 import math.geom2d.transform.CircleInversion2D;
50
51
52
53
54
55
56
57
58 public class Circle2D extends Ellipse2D
59 implements Cloneable, CirculinearElement2D, CirculinearBoundary2D,
60 CircularShape2D, CircleLine2D {
61
62
63 protected double r = 0;
64
65
66
67
68
69
70 public Circle2D() {
71 this(0, 0, 0, true);
72 }
73
74
75 public Circle2D(Point2D center, double radius) {
76 this(center.getX(), center.getY(), radius, true);
77 }
78
79
80 public Circle2D(Point2D center, double radius, boolean direct) {
81 this(center.getX(), center.getY(), radius, direct);
82 }
83
84
85 public Circle2D(double xcenter, double ycenter, double radius) {
86 this(xcenter, ycenter, radius, true);
87 }
88
89
90 public Circle2D(double xcenter, double ycenter, double radius,
91 boolean direct) {
92 super(xcenter, ycenter, radius, radius, 0, direct);
93 this.r = radius;
94 }
95
96
97
98
99
100
101
102
103 public static Circle2D create(Point2D center, double radius) {
104 return new Circle2D(center, radius);
105 }
106
107
108
109
110 public static Circle2D create(Point2D p1, Point2D p2, Point2D p3) {
111 if(Point2D.isColinear(p1, p2, p3))
112 throw new ColinearPointsException(p1, p2, p3);
113
114
115 StraightLine2D line12 = StraightLine2D.createMedian(p1, p2);
116 StraightLine2D line23 = StraightLine2D.createMedian(p2, p3);
117
118
119 assert !AbstractLine2D.isParallel(line12, line23) :
120 "If points are not colinear, medians should not be parallel";
121
122
123 Point2D center = AbstractLine2D.getIntersection(line12, line23);
124 double radius = Point2D.getDistance(center, p2);
125
126
127 return new Circle2D(center, radius);
128 }
129
130 public static Collection<Point2D> getIntersections(Circle2D circle1,
131 Circle2D circle2) {
132 ArrayList<Point2D> intersections = new ArrayList<Point2D>(2);
133
134
135 Point2D center1 = circle1.getCenter();
136 Point2D center2 = circle2.getCenter();
137 double r1 = circle1.getRadius();
138 double r2 = circle2.getRadius();
139
140 double d = Point2D.getDistance(center1, center2);
141
142
143 if (d<Math.abs(r1-r2)|d>(r1+r2))
144 return intersections;
145
146
147 double angle = Angle2D.getHorizontalAngle(center1, center2);
148
149
150 double d1 = d/2+(r1*r1-r2*r2)/(2*d);
151 Point2D tmp = Point2D.createPolar(center1, d1, angle);
152
153
154 double h = Math.sqrt(r1*r1-d1*d1);
155 intersections.add(Point2D.createPolar(tmp, h, angle+Math.PI/2));
156 intersections.add(Point2D.createPolar(tmp, h, angle-Math.PI/2));
157
158 return intersections;
159 }
160
161
162
163
164
165
166
167 public static Collection<Point2D> getIntersections(
168 CircularShape2D circle,
169 LinearShape2D line) {
170
171 ArrayList<Point2D> intersections = new ArrayList<Point2D>(2);
172
173
174 Circle2D parent = circle.getSupportingCircle();
175 Point2D center = parent.getCenter();
176 double radius = parent.getRadius();
177
178
179
180 StraightLine2D perp = StraightLine2D.createPerpendicular(line, center);
181
182
183 Point2D inter = perp.getIntersection(new StraightLine2D(line));
184 assert(inter!=null);
185 double dist = inter.getDistance(center);
186
187
188
189 if (Math.abs(dist-radius)<Shape2D.ACCURACY) {
190 if (line.contains(inter) && circle.contains(inter))
191 intersections.add(inter);
192 return intersections;
193 }
194
195
196
197 double angle = line.getHorizontalAngle();
198 double d2 = Math.sqrt(radius*radius-dist*dist);
199
200
201 Point2D p1 = Point2D.createPolar(inter, d2, angle+Math.PI);
202 Point2D p2 = Point2D.createPolar(inter, d2, angle);
203
204
205 if (line.contains(p1) && circle.contains(p1))
206 intersections.add(p1);
207 if (line.contains(p2) && circle.contains(p2))
208 intersections.add(p2);
209
210
211 return intersections;
212 }
213
214
215
216
217
218 public double getRadius() {
219 return r;
220 }
221
222
223
224
225 @Deprecated
226 public void setRadius(double radius) {
227 this.r = radius;
228 this.r1 = this.r2 = radius;
229 }
230
231
232
233
234 @Deprecated
235 public void setCircle(double xc, double yc, double r) {
236 this.xc = xc;
237 this.yc = yc;
238 this.r = r;
239 this.r1 = r;
240 this.r2 = r;
241 }
242
243
244
245
246 @Deprecated
247 public void setCircle(Point2D center, double r) {
248 this.xc = center.getX();
249 this.yc = center.getY();
250 this.r = r;
251 this.r1 = r;
252 this.r2 = r;
253 }
254
255
256
257
258
259
260
261 public Circle2D getSupportingCircle() {
262 return this;
263 }
264
265
266
267
268
269 @Override
270 public Type getConicType() {
271 return Conic2D.Type.CIRCLE;
272 }
273
274 @Override
275 public boolean isCircle() {
276 return true;
277 }
278
279
280
281
282
283
284
285
286 @Override
287 public double[] getConicCoefficients() {
288 return new double[] { 1, 0, 1, -2*xc, -2*yc, xc*xc+yc*yc-r*r };
289 }
290
291
292
293
294 @Override
295 public double getEccentricity() {
296 return 0;
297 }
298
299
300
301
302
303 @Override
304 public Point2D getFocus1() {
305 return new Point2D(xc, yc);
306 }
307
308
309
310
311
312 @Override
313 public Point2D getFocus2() {
314 return new Point2D(xc, yc);
315 }
316
317
318
319
320
321
322
323 public CirculinearDomain2D getBuffer(double dist) {
324 return CirculinearCurve2DUtils.computeBuffer(this, dist);
325 }
326
327
328
329
330
331
332 @Override
333 public Circle2D getParallel(double d) {
334 double rp = Math.max(direct ? r+d : r-d, 0);
335 return new Circle2D(xc, yc, rp, direct);
336 }
337
338
339 public double getLength() {
340 return r*Math.PI*2;
341 }
342
343
344
345
346 public double getLength(double pos) {
347 return pos*this.r;
348 }
349
350
351
352
353 public double getPosition(double length) {
354 return length/this.r;
355 }
356
357
358
359
360 public CircleLine2D transform(CircleInversion2D inv) {
361
362 Point2D center = inv.getCenter();
363 Point2D c1 = this.getCenter();
364
365
366 if(center.getDistance(c1)<Shape2D.ACCURACY) {
367 double r0 = inv.getRadius();
368 double r2 = r0*r0 / this.r;
369 return new Circle2D(center, r2, this.direct);
370 }
371
372
373 StraightLine2D line = new StraightLine2D(center, c1);
374
375
376 Collection<Point2D> points = this.getIntersections(line);
377 Iterator<Point2D> iter = points.iterator();
378 Point2D p1 = iter.next().transform(inv);
379 Point2D p2 = iter.next().transform(inv);
380
381
382
383 if(this.getDistance(center)<Shape2D.ACCURACY) {
384 Point2D p0 = center.getDistance(p1)<Shape2D.ACCURACY ? p2 : p1;
385 p0 = p0.transform(inv);
386 return StraightLine2D.createPerpendicular(line, p0);
387 }
388
389
390
391
392 double d = p1.getDistance(p2);
393 c1 = Point2D.midPoint(p1, p2);
394
395
396 return new Circle2D(c1, d/2, !this.isDirect());
397 }
398
399
400
401
402
403 @Override
404 public Vector2D getTangent(double t) {
405 if (!direct)
406 t = -t;
407 double cot = Math.cos(theta);
408 double sit = Math.sin(theta);
409 double cost = Math.cos(t);
410 double sint = Math.sin(t);
411
412 if (direct)
413 return new Vector2D(
414 -r*sint*cot-r*cost*sit,
415 -r*sint*sit+r*cost*cot);
416 else
417 return new Vector2D(
418 r*sint*cot+r*cost*sit,
419 r*sint*sit-r*cost*cot);
420 }
421
422
423
424
425
426
427
428 @Override
429 public Collection<? extends Circle2D> getSmoothPieces() {
430 ArrayList<Circle2D> list = new ArrayList<Circle2D>(1);
431 list.add(this);
432 return list;
433 }
434
435
436
437
438
439
440
441
442
443 @Override
444 public boolean isInside(java.awt.geom.Point2D point) {
445 double xp = (point.getX()-this.xc)/this.r;
446 double yp = (point.getY()-this.yc)/this.r;
447 return (xp*xp+yp*yp<1)^!direct;
448 }
449
450 @Override
451 public double getSignedDistance(java.awt.geom.Point2D point) {
452 return getSignedDistance(point.getX(), point.getY());
453 }
454
455 @Override
456 public double getSignedDistance(double x, double y) {
457 if (direct)
458 return Point2D.getDistance(xc, yc, x, y)-r;
459 else
460 return r-Point2D.getDistance(xc, yc, x, y);
461 }
462
463
464
465
466
467
468
469
470
471 @Override
472 public Point2D getPoint(double t) {
473 double angle = theta+t;
474 if (!direct)
475 angle = theta-t;
476 return new Point2D(xc+r*Math.cos(angle), yc+r*Math.sin(angle));
477 }
478
479
480
481
482
483
484 @Override
485 public Point2D getFirstPoint() {
486 return new Point2D(xc+r*Math.cos(theta), yc+r*Math.sin(theta));
487 }
488
489
490
491
492
493
494 @Override
495 public Point2D getLastPoint() {
496 return new Point2D(xc+r*Math.cos(theta), yc+r*Math.sin(theta));
497 }
498
499 @Override
500 public double getPosition(java.awt.geom.Point2D point) {
501 double angle =
502 Angle2D.getHorizontalAngle(xc, yc, point.getX(), point.getY());
503 if (direct)
504 return Angle2D.formatAngle(angle-theta);
505 else
506 return Angle2D.formatAngle(theta-angle);
507 }
508
509
510
511
512
513 @Override
514 public Circle2D getReverseCurve() {
515 return new Circle2D(this.getCenter().getX(), this.getCenter().getY(),
516 this.getRadius(), !this.direct);
517 }
518
519
520
521
522 @Override
523 public CircleArc2D getSubCurve(double t0, double t1) {
524 double startAngle, extent;
525 if (this.direct) {
526 startAngle = t0;
527 extent = Angle2D.formatAngle(t1-t0);
528 } else {
529 extent = -Angle2D.formatAngle(t1-t0);
530 startAngle = Angle2D.formatAngle(-t0);
531 }
532 return new CircleArc2D(this, startAngle, extent);
533 }
534
535 @Override
536 public Collection<? extends Circle2D> getContinuousCurves() {
537 return wrapCurve(this);
538 }
539
540
541
542
543 @Override
544 public double getDistance(java.awt.geom.Point2D point) {
545 return Math.abs(Point2D.getDistance(xc, yc, point.getX(),
546 point.getY())
547 -r);
548 }
549
550 @Override
551 public double getDistance(double x, double y) {
552 return Math.abs(Point2D.getDistance(xc, yc, x, y)-r);
553 }
554
555
556
557
558
559
560
561 @Override
562 public Collection<Point2D> getIntersections(LinearShape2D line) {
563 return Circle2D.getIntersections(this, line);
564 }
565
566
567
568
569
570
571
572 @Override
573 public CurveSet2D<? extends CircularShape2D> clip(Box2D box) {
574
575 CurveSet2D<SmoothCurve2D> set =
576 Curve2DUtils.clipSmoothCurve(this, box);
577
578
579 CurveArray2D<CircularShape2D> result =
580 new CurveArray2D<CircularShape2D>(set.getCurveNumber());
581
582
583 for (Curve2D curve : set.getCurves()) {
584 if (curve instanceof CircleArc2D)
585 result.addCurve((CircleArc2D) curve);
586 if (curve instanceof Circle2D)
587 result.addCurve((Circle2D) curve);
588 }
589 return result;
590 }
591
592
593
594
595
596
597
598 @Override
599 public boolean contains(double x, double y) {
600 return Math.abs(getDistance(x, y))<=Shape2D.ACCURACY;
601 }
602
603 @Override
604 public java.awt.geom.GeneralPath appendPath(java.awt.geom.GeneralPath path) {
605 double cot = Math.cos(theta);
606 double sit = Math.sin(theta);
607 double cost, sint;
608
609 if (direct)
610 for (double t = .1; t<Math.PI*2; t += .1) {
611 cost = Math.cos(t);
612 sint = Math.sin(t);
613 path.lineTo(
614 (float) (xc+r*cost*cot-r*sint*sit),
615 (float) (yc+r*cost*sit+r*sint*cot));
616 }
617 else
618 for (double t = .1; t<Math.PI*2; t += .1) {
619 cost = Math.cos(t);
620 sint = Math.sin(t);
621 path.lineTo(
622 (float) (xc+r*cost*cot+r*sint*sit),
623 (float) (yc+r*cost*sit-r*sint*cot));
624 }
625
626
627 path.lineTo((float) (xc+r*cot), (float) (yc+r*sit));
628
629 return path;
630 }
631
632 @Override
633 public void draw(Graphics2D g2) {
634 java.awt.geom.Ellipse2D.Double ellipse =
635 new java.awt.geom.Ellipse2D.Double(xc-r, yc-r, 2*r, 2*r);
636 g2.draw(ellipse);
637 }
638
639
640
641
642 @Override
643 public String toString() {
644 return String.format(Locale.US,
645 "Circle2D(%7.2f,%7.2f,%7.2f,%s)",
646 xc, yc, r, direct?"true":"false");
647 }
648
649 @Override
650 public boolean equals(Object obj) {
651 if (!(obj instanceof Ellipse2D))
652 return false;
653
654 if (obj instanceof Circle2D) {
655 Circle2D circle = (Circle2D) obj;
656
657 if (Math.abs(circle.xc-xc)>Shape2D.ACCURACY)
658 return false;
659 if (Math.abs(circle.yc-yc)>Shape2D.ACCURACY)
660 return false;
661 if (Math.abs(circle.r-r)>Shape2D.ACCURACY)
662 return false;
663 if (circle.direct!=direct)
664 return false;
665 return true;
666 }
667 return super.equals(obj);
668 }
669
670 @Override
671 public Circle2D clone() {
672 return new Circle2D(xc, yc, r, direct);
673 }
674
675 }