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.conic;
27
28 import java.awt.Graphics2D;
29 import java.awt.Shape;
30 import java.util.ArrayList;
31 import java.util.Collection;
32
33 import math.geom2d.AffineTransform2D;
34 import math.geom2d.Angle2D;
35 import math.geom2d.Box2D;
36 import math.geom2d.Point2D;
37 import math.geom2d.Shape2D;
38 import math.geom2d.Vector2D;
39 import math.geom2d.curve.AbstractSmoothCurve2D;
40 import math.geom2d.curve.Curve2D;
41 import math.geom2d.curve.Curve2DUtils;
42 import math.geom2d.curve.CurveArray2D;
43 import math.geom2d.curve.CurveSet2D;
44 import math.geom2d.curve.SmoothCurve2D;
45 import math.geom2d.domain.Domain2D;
46 import math.geom2d.domain.GenericDomain2D;
47 import math.geom2d.domain.SmoothBoundary2D;
48 import math.geom2d.domain.SmoothOrientedCurve2D;
49 import math.geom2d.line.LinearShape2D;
50
51
52
53
54
55
56
57
58 public class Ellipse2D extends AbstractSmoothCurve2D
59 implements SmoothBoundary2D, Conic2D, Cloneable {
60
61
62
63
64
65
66
67
68
69 protected double xc;
70 protected double yc;
71
72
73 protected double r1;
74
75
76 protected double r2;
77
78
79 protected double theta = 0;
80
81
82 protected boolean direct = true;
83
84
85
86
87
88
89
90
91 public Ellipse2D() {
92 this(0, 0, 1, 1, 0, true);
93 }
94
95
96 public Ellipse2D(Point2D center, double l1, double l2) {
97 this(center.getX(), center.getY(), l1, l2, 0, true);
98 }
99
100
101 public Ellipse2D(double xc, double yc, double l1, double l2) {
102 this(xc, yc, l1, l2, 0, true);
103 }
104
105
106
107
108
109 public Ellipse2D(Point2D center, double l1, double l2, double theta) {
110 this(center.getX(), center.getY(), l1, l2, theta, true);
111 }
112
113
114
115
116
117 public Ellipse2D(double xc, double yc, double l1, double l2, double theta) {
118 this(xc, yc, l1, l2, theta, true);
119 }
120
121
122
123
124
125 public Ellipse2D(double xc, double yc, double l1, double l2, double theta,
126 boolean direct) {
127 this.xc = xc;
128 this.yc = yc;
129
130 r1 = l1;
131 r2 = l2;
132
133 this.theta = theta;
134 this.direct = direct;
135 }
136
137
138
139
140 public Ellipse2D(java.awt.geom.Ellipse2D ellipse) {
141 this(new Point2D(ellipse.getCenterX(), ellipse.getCenterY()),
142 ellipse.getWidth()/2, ellipse.getHeight()/2);
143 }
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 public static Ellipse2D create(Point2D focus1, Point2D focus2,
160 double chord) {
161 double x1 = focus1.getX();
162 double y1 = focus1.getY();
163 double x2 = focus2.getX();
164 double y2 = focus2.getY();
165
166 double xc = (x1+x2)/2;
167 double yc = (y1+y2)/2;
168 double theta = Angle2D.getHorizontalAngle(x1, y1, x2, y2);
169
170 double dist = focus1.getDistance(focus2);
171 if (dist<Shape2D.ACCURACY)
172 return new Circle2D(xc, yc, chord/2);
173
174 double r1 = chord/2;
175 double r2 = Math.sqrt(chord*chord-dist*dist)/2;
176
177 return new Ellipse2D(xc, yc, r1, r2, theta);
178 }
179
180
181 public static Ellipse2D create(Point2D center, double l1, double l2) {
182 return new Ellipse2D(center.getX(), center.getY(), l1, l2, 0, true);
183 }
184
185
186
187
188
189 public static Ellipse2D create(Point2D center, double l1, double l2,
190 double theta) {
191 return new Ellipse2D(center.getX(), center.getY(), l1, l2, theta, true);
192 }
193
194
195
196
197
198 public static Ellipse2D create(Point2D center, double l1, double l2,
199 double theta, boolean direct) {
200 return new Ellipse2D(center.getX(), center.getY(), l1, l2, theta, direct);
201 }
202
203
204
205
206 public static Ellipse2D create(java.awt.geom.Ellipse2D ellipse) {
207 return new Ellipse2D(
208 new Point2D(ellipse.getCenterX(), ellipse.getCenterY()),
209 ellipse.getWidth()/2, ellipse.getHeight()/2);
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223
224 public final static Ellipse2D reduceCentered(double[] coefs) {
225 double A = coefs[0];
226 double B = coefs[1];
227 double C = coefs[2];
228
229
230 double theta;
231 if (Math.abs(A-C)<Shape2D.ACCURACY) {
232 theta = Math.PI/4;
233 } else {
234 theta = Math.atan2(B, (A-C))/2.0;
235 if (B<0)
236 theta -= Math.PI;
237 theta = Angle2D.formatAngle(theta);
238 }
239
240
241 double[] coefs2 = Conic2DUtils.transformCentered(coefs,
242 AffineTransform2D.createRotation(-theta));
243
244
245 double f = 1;
246 if (coefs2.length>5)
247 f = Math.abs(coefs[5]);
248
249 assert Math.abs(coefs2[1]/f)<Shape2D.ACCURACY :
250 "Second conic coefficient should be zero";
251
252
253 double r1, r2;
254 if (coefs2[0]<coefs2[2]) {
255 r1 = Math.sqrt(f/coefs2[0]);
256 r2 = Math.sqrt(f/coefs2[2]);
257 } else {
258 r1 = Math.sqrt(f/coefs2[2]);
259 r2 = Math.sqrt(f/coefs2[0]);
260 theta = Angle2D.formatAngle(theta+Math.PI/2);
261 theta = Math.min(theta, Angle2D.formatAngle(theta+Math.PI));
262 }
263
264
265 if (Math.abs(r1-r2)<Shape2D.ACCURACY)
266 return new Circle2D(0, 0, r1);
267
268
269 return new Ellipse2D(0, 0, r1, r2, theta);
270 }
271
272
273
274
275
276
277
278
279
280 public final static Ellipse2D transformCentered(Ellipse2D ellipse,
281 AffineTransform2D trans) {
282
283 double r1 = ellipse.r1;
284 double r2 = ellipse.r2;
285 double theta = ellipse.theta;
286
287
288 double r1Sq = r1*r1;
289 double r2Sq = r2*r2;
290 double cot = Math.cos(theta);
291 double sit = Math.sin(theta);
292 double cotSq = cot*cot;
293 double sitSq = sit*sit;
294
295
296 double A = cotSq/r1Sq+sitSq/r2Sq;
297 double B = 2*cot*sit*(1/r1Sq-1/r2Sq);
298 double C = cotSq/r2Sq+sitSq/r1Sq;
299 double[] coefs = new double[] { A, B, C };
300
301
302 double[] coefs2 = Conic2DUtils.transformCentered(coefs, trans);
303
304
305 return Ellipse2D.reduceCentered(coefs2);
306 }
307
308
309
310
311
312
313
314
315 @Deprecated
316 public void setEllipse(double xc, double yc, double r1, double r2,
317 double theta) {
318 this.setEllipse(xc, yc, r1, r2, theta, true);
319 }
320
321
322
323
324 @Deprecated
325 public void setEllipse(double xc, double yc, double r1, double r2,
326 double theta, boolean direct) {
327 this.xc = xc;
328 this.yc = yc;
329 this.r1 = r1;
330 this.r2 = r2;
331 this.theta = theta;
332 this.direct = direct;
333 }
334
335
336
337
338 @Deprecated
339 public void setEllipse(Point2D center, double r1, double r2, double theta) {
340 this.setEllipse(center.getX(), center.getY(), r1, r2, theta, true);
341 }
342
343
344
345
346 @Deprecated
347 public void setEllipse(Point2D center, double r1, double r2, double theta,
348 boolean direct) {
349 this.setEllipse(center.getX(), center.getY(), r1, r2, theta, direct);
350 }
351
352
353
354
355 @Deprecated
356 public void setCenter(Point2D center) {
357 this.setCenter(center.getX(), center.getY());
358 }
359
360
361
362
363 @Deprecated
364 public void setCenter(double x, double y) {
365 this.xc = x;
366 this.yc = y;
367 }
368
369
370
371
372
373
374
375
376 public double getRho(double angle) {
377 double cot = Math.cos(angle-theta);
378 double sit = Math.cos(angle-theta);
379 return Math.sqrt(r1*r1*r2*r2/(r2*r2*cot*cot+r1*r1*sit*sit));
380 }
381
382 public Point2D getProjectedPoint(java.awt.geom.Point2D point) {
383 Vector2D polar = this.getProjectedVector(point, Shape2D.ACCURACY);
384 return new Point2D(point.getX()+polar.getX(), point.getY()+polar.getY());
385 }
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400 public Vector2D getProjectedVector(java.awt.geom.Point2D point, double eMax) {
401
402 double ot = 1.0/3.0;
403
404
405 double x = point.getX()-xc;
406 double y = point.getY()-yc;
407
408 double la, lb, theta;
409 if (r1>=r2) {
410 la = r1;
411 lb = r2;
412 theta = this.theta;
413 } else {
414 la = r2;
415 lb = r1;
416 theta = this.theta+Math.PI/2;
417 double tmp = x;
418 x = -y;
419 y = tmp;
420 }
421
422 double cot = Math.cos(theta);
423 double sit = Math.sin(theta);
424 double tmpx = x, tmpy = y;
425 x = tmpx*cot-tmpy*sit;
426 y = tmpx*sit+tmpy*cot;
427
428 double ae = la;
429 double f = 1-lb/la;
430 double e2 = f*(2.0-f);
431 double g = 1.0-f;
432 double g2 = g*g;
433
434 double ae2 = ae*ae;
435
436
437 double z = y;
438 double z2 = y*y;
439 double r = x;
440 double r2 = x*x;
441 double g2r2ma2 = g2*(r2-ae2);
442
443 double g2r2ma2pz2 = g2r2ma2+z2;
444 double dist = Math.sqrt(r2+z2);
445
446 boolean inside = (g2r2ma2pz2<=0);
447
448
449 if (dist<(1.0e-10*ae)) {
450 System.out.println("point at the center");
451 return Vector2D.createPolar(r, 0);
452 }
453
454 double cz = r/dist;
455 double sz = z/dist;
456 double t = z/(dist+r);
457
458
459
460
461 double a = 1.0-e2*cz*cz;
462 double b = g2*r*cz+z*sz;
463 double c = g2r2ma2pz2;
464 double b2 = b*b;
465 double ac = a*c;
466 double k = c/(b+Math.sqrt(b2-ac));
467
468 double phi = Math.atan2(z-k*sz, g2*(r-k*cz));
469
470
471 if (Math.abs(k)<(1.0e-10*dist)) {
472
473 return Vector2D.createPolar(k, phi);
474 }
475
476 for (int iterations = 0; iterations<100; ++iterations) {
477
478
479
480
481
482 a = g2r2ma2pz2+g2*(2.0*r+k)*k;
483 b = -4.0*k*z/a;
484 c = 2.0*(g2r2ma2pz2+(1.0+e2)*k*k)/a;
485 double d = b;
486
487
488
489
490 b += t;
491 c += t*b;
492 d += t*c;
493
494
495 b2 = b*b;
496 double Q = (3.0*c-b2)/9.0;
497 double R = (b*(9.0*c-2.0*b2)-27.0*d)/54.0;
498 double D = Q*Q*Q+R*R;
499 double tildeT, tildePhi;
500 if (D>=0) {
501 double rootD = Math.sqrt(D);
502 double rMr = R-rootD;
503 double rPr = R+rootD;
504 tildeT = ((rPr>0) ? Math.pow(rPr, ot) : -Math.pow(-rPr, ot))
505 +((rMr>0) ? Math.pow(rMr, ot) : -Math.pow(-rMr, ot))-b
506 *ot;
507 double tildeT2 = tildeT*tildeT;
508 double tildeT2P1 = 1.0+tildeT2;
509 tildePhi = Math.atan2(z*tildeT2P1-2*k*tildeT, g2
510 *(r*tildeT2P1-k*(1.0-tildeT2)));
511 } else {
512 Q = -Q;
513 double qRoot = Math.sqrt(Q);
514 double alpha = Math.acos(R/(Q*qRoot));
515 tildeT = 2*qRoot*Math.cos(alpha*ot)-b*ot;
516 double tildeT2 = tildeT*tildeT;
517 double tildeT2P1 = 1.0+tildeT2;
518 tildePhi = Math.atan2(z*tildeT2P1-2*k*tildeT, g2
519 *(r*tildeT2P1-k*(1.0-tildeT2)));
520 if ((tildePhi*phi)<0) {
521 tildeT = 2*qRoot*Math.cos((alpha+2*Math.PI)*ot)-b*ot;
522 tildeT2 = tildeT*tildeT;
523 tildeT2P1 = 1.0+tildeT2;
524 tildePhi = Math.atan2(z*tildeT2P1-2*k*tildeT, g2
525 *(r*tildeT2P1-k*(1.0-tildeT2)));
526 if (tildePhi*phi<0) {
527 tildeT = 2*qRoot*Math.cos((alpha+4*Math.PI)*ot)-b*ot;
528 tildeT2 = tildeT*tildeT;
529 tildeT2P1 = 1.0+tildeT2;
530 tildePhi = Math.atan2(z*tildeT2P1-2*k*tildeT, g2
531 *(r*tildeT2P1-k*(1.0-tildeT2)));
532 }
533 }
534 }
535
536
537 double dPhi = Math.abs(0.5*(tildePhi-phi));
538 phi = 0.5*(phi+tildePhi);
539 double cPhi = Math.cos(phi);
540 double sPhi = Math.sin(phi);
541 double coeff = Math.sqrt(1.0-e2*sPhi*sPhi);
542
543
544 if (false)
545 System.out.println(iterations+": phi = "+Math.toDegrees(phi)
546 +" +/- "+Math.toDegrees(dPhi)+", k = "+k);
547
548 b = ae/coeff;
549 double dR = r-cPhi*b;
550 double dZ = z-sPhi*b*g2;
551 k = Math.sqrt(dR*dR+dZ*dZ);
552 if (inside) {
553 k = -k;
554 }
555 t = dZ/(k+dR);
556
557 if (dPhi<1.0e-14) {
558 if (this.r1>=this.r2)
559 return Vector2D.createPolar(-k, phi+theta);
560
561 else
562 return Vector2D.createPolar(-k, phi+theta-Math.PI/2);
563
564
565 }
566 }
567
568 return null;
569 }
570
571
572
573
574
575
576 public Ellipse2D getParallel(double d) {
577 return new Ellipse2D(xc, yc, Math.abs(r1+d), Math.abs(r2+d), theta,
578 direct);
579 }
580
581
582
583
584 public boolean isDirect() {
585 return direct;
586 }
587
588 public boolean isCircle() {
589 return Math.abs(r1-r2)<Shape2D.ACCURACY;
590 }
591
592
593
594
595 public Conic2D.Type getConicType() {
596 if (Math.abs(r1-r2)<Shape2D.ACCURACY)
597 return Conic2D.Type.CIRCLE;
598 else
599 return Conic2D.Type.ELLIPSE;
600 }
601
602
603
604
605
606 public double[] getConicCoefficients() {
607
608
609 double r1Sq = this.r1*this.r1;
610 double r2Sq = this.r2*this.r2;
611
612
613 double sint = Math.sin(this.theta);
614 double cost = Math.cos(this.theta);
615 double sin2t = 2.0*sint*cost;
616 double sintSq = sint*sint;
617 double costSq = cost*cost;
618
619
620 double xcSq = xc*xc;
621 double ycSq = yc*yc;
622 double r1SqInv = 1.0/r1Sq;
623 double r2SqInv = 1.0/r2Sq;
624
625
626
627
628
629
630 double a = costSq/r1Sq+sintSq/r2Sq;
631 double b = (r2Sq-r1Sq)*sin2t/(r1Sq*r2Sq);
632 double c = costSq/r2Sq+sintSq/r1Sq;
633 double d = -yc*b-2*xc*a;
634 double e = -xc*b-2*yc*c;
635 double f = -1.0
636 +(xcSq+ycSq)*(r1SqInv+r2SqInv)/2.0
637 +(costSq-sintSq)*(xcSq-ycSq)*(r1SqInv-r2SqInv)/2.0
638 +xc*yc*(r1SqInv-r2SqInv)*sin2t;
639
640
641 return new double[] { a, b, c, d, e, f };
642 }
643
644
645
646
647 public double getSemiMajorAxisLength() {
648 return r1;
649 }
650
651
652
653
654 public double getSemiMinorAxisLength() {
655 return r2;
656 }
657
658
659
660
661
662
663 public double getEccentricity() {
664 double a = Math.max(r1, r2);
665 double b = Math.min(r1, r2);
666 double r = b/a;
667 return Math.sqrt(1-r*r);
668 }
669
670
671
672
673 public Point2D getCenter() {
674 return new Point2D(xc, yc);
675 }
676
677
678
679
680
681 public Point2D getFocus1() {
682 double a, b, theta;
683 if (r1>r2) {
684 a = r1;
685 b = r2;
686 theta = this.theta;
687 } else {
688 a = r2;
689 b = r1;
690 theta = this.theta+Math.PI/2;
691 }
692 return Point2D.createPolar(xc, yc, Math.sqrt(a*a-b*b), theta+Math.PI);
693 }
694
695
696
697
698
699 public Point2D getFocus2() {
700 double a, b, theta;
701 if (r1>r2) {
702 a = r1;
703 b = r2;
704 theta = this.theta;
705 } else {
706 a = r2;
707 b = r1;
708 theta = this.theta+Math.PI/2;
709 }
710 return Point2D.createPolar(xc, yc, Math.sqrt(a*a-b*b), theta);
711 }
712
713 public Vector2D getVector1() {
714 return new Vector2D(Math.cos(theta), Math.sin(theta));
715 }
716
717 public Vector2D getVector2() {
718 if (direct)
719 return new Vector2D(-Math.sin(theta), Math.cos(theta));
720 else
721 return new Vector2D(Math.sin(theta), -Math.cos(theta));
722 }
723
724
725
726
727 public double getAngle() {
728 return theta;
729 }
730
731
732
733
734 public Collection<? extends Ellipse2D> getBoundaryCurves() {
735 return wrapCurve(this);
736 }
737
738 public Domain2D getDomain() {
739 return new GenericDomain2D(this);
740 }
741
742 public void fill(Graphics2D g2) {
743
744 java.awt.geom.Ellipse2D.Double ellipse =
745 new java.awt.geom.Ellipse2D.Double(xc-r1, yc-r2, 2*r1, 2*r2);
746
747
748 java.awt.geom.AffineTransform trans = java.awt.geom.AffineTransform
749 .getRotateInstance(theta, xc, yc);
750 Shape shape = trans.createTransformedShape(ellipse);
751
752
753 g2.fill(shape);
754 }
755
756
757
758
759
760
761
762
763 public double getWindingAngle(java.awt.geom.Point2D point) {
764 if (this.getSignedDistance(point)>0)
765 return 0;
766 else
767 return direct ? Math.PI*2 : -Math.PI*2;
768 }
769
770
771
772
773
774
775 public boolean isInside(java.awt.geom.Point2D point) {
776 AffineTransform2D rot = AffineTransform2D.createRotation(this.xc,
777 this.yc, -this.theta);
778 Point2D pt = rot.transform(point);
779 double xp = (pt.getX()-this.xc)/this.r1;
780 double yp = (pt.getY()-this.yc)/this.r2;
781 return (xp*xp+yp*yp<1)^!direct;
782 }
783
784 public double getSignedDistance(java.awt.geom.Point2D point) {
785 Vector2D vector = this.getProjectedVector(point, 1e-10);
786 if (isInside(point))
787 return -vector.getNorm();
788 else
789 return vector.getNorm();
790 }
791
792 public double getSignedDistance(double x, double y) {
793 return getSignedDistance(new Point2D(x, y));
794 }
795
796
797
798
799 public Vector2D getTangent(double t) {
800 if (!direct)
801 t = -t;
802 double cot = Math.cos(theta);
803 double sit = Math.sin(theta);
804
805 if (direct)
806 return new Vector2D(
807 -r1*Math.sin(t)*cot-r2*Math.cos(t)*sit,
808 -r1*Math.sin(t)*sit+r2*Math.cos(t)*cot);
809 else
810 return new Vector2D(
811 r1*Math.sin(t)*cot+r2*Math.cos(t)*sit,
812 r1*Math.sin(t)*sit-r2*Math.cos(t)*cot);
813 }
814
815
816
817
818 public double getCurvature(double t) {
819 if (!direct)
820 t = -t;
821 double cot = Math.cos(t);
822 double sit = Math.sin(t);
823 double k = r1*r2/Math.pow(r2*r2*cot*cot+r1*r1*sit*sit, 1.5);
824 return direct ? k : -k;
825 }
826
827
828
829
830
831
832
833 public boolean isClosed() {
834 return true;
835 }
836
837
838
839
840
841
842
843 public double getT0() {
844 return 0;
845 }
846
847
848
849
850 public double getT1() {
851 return 2*Math.PI;
852 }
853
854
855
856
857
858
859 public Point2D getPoint(double t) {
860 if (!direct)
861 t = -t;
862 double cot = Math.cos(theta);
863 double sit = Math.sin(theta);
864 return new Point2D(xc+r1*Math.cos(t)*cot-r2*Math.sin(t)*sit, yc+r1
865 *Math.cos(t)*sit+r2*Math.sin(t)*cot);
866 }
867
868
869
870
871
872
873 @Override
874 public Point2D getFirstPoint() {
875 return new Point2D(xc+r1*Math.cos(theta), yc+r1*Math.sin(theta));
876 }
877
878
879
880
881
882
883 @Override
884 public Point2D getLastPoint() {
885 return new Point2D(xc+r1*Math.cos(theta), yc+r1*Math.sin(theta));
886 }
887
888 public double getPosition(java.awt.geom.Point2D point) {
889 double xp = point.getX();
890 double yp = point.getY();
891
892
893 xp = xp-this.xc;
894 yp = yp-this.yc;
895
896
897 double xp1 = xp*Math.cos(theta)+yp*Math.sin(theta);
898 double yp1 = -xp*Math.sin(theta)+yp*Math.cos(theta);
899 xp = xp1;
900 yp = yp1;
901
902
903 xp = xp/this.r1;
904 yp = yp/this.r2;
905
906 if (!direct)
907 yp = -yp;
908
909
910 double angle = Angle2D.getHorizontalAngle(xp, yp);
911
912 if (Math.abs(Math.hypot(xp, yp)-1)<Shape2D.ACCURACY)
913 return angle;
914 else
915 return Double.NaN;
916 }
917
918
919
920
921
922
923 public double project(java.awt.geom.Point2D point) {
924 double xp = point.getX();
925 double yp = point.getY();
926
927
928 xp = xp-this.xc;
929 yp = yp-this.yc;
930
931
932 double xp1 = xp*Math.cos(theta)+yp*Math.sin(theta);
933 double yp1 = -xp*Math.sin(theta)+yp*Math.cos(theta);
934 xp = xp1;
935 yp = yp1;
936
937
938 xp = xp/this.r1;
939 yp = yp/this.r2;
940
941
942 double angle = Angle2D.getHorizontalAngle(xp, yp);
943
944 return angle;
945 }
946
947
948
949
950
951 public Ellipse2D getReverseCurve() {
952 return new Ellipse2D(xc, yc, r1, r2, theta, !direct);
953 }
954
955 @Override
956 public Collection<? extends Ellipse2D> getContinuousCurves() {
957 return wrapCurve(this);
958 }
959
960
961
962
963 public EllipseArc2D getSubCurve(double t0, double t1) {
964 double startAngle, extent;
965 if (this.direct) {
966 startAngle = t0;
967 extent = Angle2D.formatAngle(t1-t0);
968 } else {
969 extent = -Angle2D.formatAngle(t1-t0);
970 startAngle = Angle2D.formatAngle(-t0);
971 }
972 return new EllipseArc2D(this, startAngle, extent);
973 }
974
975
976
977
978
979 public boolean isBounded() {
980 return true;
981 }
982
983 public boolean isEmpty() {
984 return false;
985 }
986
987 public double getDistance(java.awt.geom.Point2D point) {
988
989
990 return this.getAsPolyline(128).getDistance(point);
991 }
992
993 public double getDistance(double x, double y) {
994 return getDistance(new Point2D(x, y));
995 }
996
997
998
999
1000
1001
1002
1003 public CurveSet2D<? extends SmoothOrientedCurve2D> clip(Box2D box) {
1004
1005 CurveSet2D<SmoothCurve2D> set = Curve2DUtils.clipSmoothCurve(this, box);
1006
1007
1008 CurveArray2D<SmoothOrientedCurve2D> result =
1009 new CurveArray2D<SmoothOrientedCurve2D>(set.getCurveNumber());
1010
1011
1012 for (Curve2D curve : set.getCurves()) {
1013 if (curve instanceof EllipseArc2D)
1014 result.addCurve((EllipseArc2D) curve);
1015 if (curve instanceof Ellipse2D)
1016 result.addCurve((Ellipse2D) curve);
1017 }
1018 return result;
1019 }
1020
1021
1022
1023
1024 public Box2D getBoundingBox() {
1025
1026
1027
1028
1029
1030
1031
1032 double cot = Math.cos(theta);
1033 double sit = Math.sin(theta);
1034 double xm = Math.sqrt(r1*r1*cot*cot+r2*r2*sit*sit);
1035 double ym = Math.sqrt(r1*r1*sit*sit+r2*r2*cot*cot);
1036 return new Box2D(xc-xm, xc+xm, yc-ym, yc+ym);
1037 }
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047 public Collection<Point2D> getIntersections(LinearShape2D line) {
1048
1049 AffineTransform2D sca, rot, tra;
1050 sca = AffineTransform2D.createScaling(r1, r2);
1051 rot = AffineTransform2D.createRotation(theta);
1052 tra = AffineTransform2D.createTranslation(xc, yc);
1053 AffineTransform2D toUnit = sca.chain(rot).chain(tra).invert();
1054
1055
1056 LinearShape2D line2 = line.transform(toUnit);
1057
1058
1059 Collection<Point2D> points;
1060
1061
1062 Circle2D circle = new Circle2D(0, 0, 1);
1063 points = circle.getIntersections(line2);
1064 if (points.size()==0)
1065 return points;
1066
1067
1068 ArrayList<Point2D> res = new ArrayList<Point2D>(points.size());
1069 for (Point2D point : points)
1070 res.add(this.getPoint(circle.getPosition(point)));
1071
1072
1073 return res;
1074 }
1075
1076
1077
1078
1079
1080
1081
1082 public Ellipse2D transform(AffineTransform2D trans) {
1083 Ellipse2D result = Ellipse2D.transformCentered(this, trans);
1084 result.setCenter(this.getCenter().transform(trans));
1085 result.direct = !(this.direct^trans.isDirect());
1086 return result;
1087 }
1088
1089
1090
1091
1092
1093
1094
1095
1096 public boolean contains(java.awt.geom.Point2D p) {
1097 return contains(p.getX(), p.getY());
1098 }
1099
1100
1101
1102
1103
1104 public boolean contains(double x, double y) {
1105 return this.getDistance(x, y)<Shape2D.ACCURACY;
1106 }
1107
1108 public java.awt.geom.GeneralPath getGeneralPath() {
1109
1110 double cot = Math.cos(theta);
1111 double sit = Math.sin(theta);
1112
1113
1114 java.awt.geom.GeneralPath path = new java.awt.geom.GeneralPath();
1115
1116
1117 path.moveTo((float) (xc+r1*cot), (float) (yc+r1*sit));
1118
1119
1120 return this.appendPath(path);
1121 }
1122
1123
1124
1125
1126
1127
1128
1129 public java.awt.geom.GeneralPath appendPath(java.awt.geom.GeneralPath path) {
1130 double cot = Math.cos(theta);
1131 double sit = Math.sin(theta);
1132
1133
1134 if (direct)
1135 for (double t = .1; t<=2*Math.PI; t += .1)
1136 path.lineTo((float) (xc+r1*Math.cos(t)*cot-r2*Math.sin(t)*sit),
1137 (float) (yc+r2*Math.sin(t)*cot+r1*Math.cos(t)*sit));
1138 else
1139 for (double t = .1; t<=2*Math.PI; t += .1)
1140 path.lineTo((float) (xc+r1*Math.cos(t)*cot+r2*Math.sin(t)*sit),
1141 (float) (yc-r2*Math.sin(t)*cot+r1*Math.cos(t)*sit));
1142
1143
1144 path.lineTo((float) (xc+r1*cot), (float) (yc+r1*sit));
1145
1146 return path;
1147 }
1148
1149 @Override
1150 public void draw(Graphics2D g2) {
1151 java.awt.geom.Ellipse2D.Double ellipse =
1152 new java.awt.geom.Ellipse2D.Double(xc-r1, yc-r2, 2*r1, 2*r2);
1153 java.awt.geom.AffineTransform trans =
1154 java.awt.geom.AffineTransform.getRotateInstance(theta, xc, yc);
1155 g2.draw(trans.createTransformedShape(ellipse));
1156 }
1157
1158
1159
1160
1161 @Override
1162 public boolean equals(Object obj) {
1163 if (!(obj instanceof Ellipse2D))
1164 return false;
1165
1166 Ellipse2D ell = (Ellipse2D) obj;
1167
1168 if (!ell.getCenter().equals(this.getCenter()))
1169 return false;
1170 if (Math.abs(ell.r1-this.r1)>Shape2D.ACCURACY)
1171 return false;
1172 if (Math.abs(ell.r2-this.r2)>Shape2D.ACCURACY)
1173 return false;
1174 if (Math.abs(Angle2D.formatAngle(ell.getAngle()-this.getAngle()))>Shape2D.ACCURACY)
1175 return false;
1176 if (ell.isDirect()!=this.isDirect())
1177 return false;
1178 return true;
1179 }
1180
1181 @Override
1182 public Ellipse2D clone() {
1183 return new Ellipse2D(xc, yc, r1, r2, theta, direct);
1184 }
1185
1186 @Override
1187 public String toString() {
1188 return String.format("Ellipse2D(%f,%f,%f,%f,%f,%s)",
1189 xc, yc, r1, r2, theta, direct?"true":"false");
1190 }
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213 }