1 /* file : CircleArc2D.java
2 *
3 * Project : geometry
4 *
5 * ===========================================
6 *
7 * This library is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 2.1 of the License, or (at
10 * your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE.
15 *
16 * See the GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this library. if not, write to :
20 * The Free Software Foundation, Inc., 59 Temple Place, Suite 330,
21 * Boston, MA 02111-1307, USA.
22 *
23 * Created on 29 avr. 2006
24 *
25 */
26
27 package math.geom2d.conic;
28
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Locale;
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.circulinear.CirculinearCurve2DUtils;
40 import math.geom2d.circulinear.CirculinearDomain2D;
41 import math.geom2d.circulinear.CirculinearElement2D;
42 import math.geom2d.curve.Curve2D;
43 import math.geom2d.curve.Curve2DUtils;
44 import math.geom2d.curve.CurveArray2D;
45 import math.geom2d.curve.CurveSet2D;
46 import math.geom2d.curve.SmoothCurve2D;
47 import math.geom2d.line.LineSegment2D;
48 import math.geom2d.line.LinearShape2D;
49 import math.geom2d.line.Ray2D;
50 import math.geom2d.line.StraightLine2D;
51 import math.geom2d.transform.CircleInversion2D;
52
53 /**
54 * A circle arc, defined by the center and the radius of the containing circle,
55 * by a starting angle, and by a (signed) angle extent.
56 * <p>
57 * A circle arc is directed: if angle extent is positive, the arc is counter
58 * clockwise. Otherwise, it is clockwise.
59 * <p>
60 * A circle arc is parameterized using angle from center. The arc contains all
61 * points with a parametric equation of t, for each t between 0 and the angle
62 * extent.
63 *
64 * @author dlegland
65 */
66 public class CircleArc2D extends EllipseArc2D
67 implements Cloneable, CircularShape2D, CirculinearElement2D {
68
69 protected Circle2D circle;
70
71 // ====================================================================
72 // constructors
73
74 /**
75 * Create a circle arc whose support circle is centered on (0,0) and has a
76 * radius equal to 1. Start angle is 0, and angle extent is PI/2.
77 */
78 public CircleArc2D() {
79 this(0, 0, 1, 0, Math.PI/2);
80 }
81
82 // Constructors based on Circles
83
84 /**
85 * create a new circle arc based on an already existing circle.
86 */
87 public CircleArc2D(Circle2D circle, double startAngle, double angleExtent) {
88 this(circle.xc, circle.yc, circle.r, startAngle, angleExtent);
89 }
90
91 /**
92 * create a new circle arc based on an already existing circle, specifying
93 * if arc is direct or not.
94 */
95 public CircleArc2D(Circle2D circle, double startAngle, double endAngle,
96 boolean direct) {
97 this(circle.xc, circle.yc, circle.r, startAngle, endAngle, direct);
98 }
99
100 // Constructors based on points
101
102 /** Create a new circle arc with specified point center and radius */
103 public CircleArc2D(Point2D center, double radius, double startAngle,
104 double angleExtent) {
105 this(center.getX(), center.getY(), radius, startAngle, angleExtent);
106 }
107
108 /**
109 * Create a new circle arc with specified point center and radius, start and
110 * end angles, and by specifying whether arc is direct or not.
111 */
112 public CircleArc2D(Point2D center, double radius, double start, double end,
113 boolean direct) {
114 this(center.getX(), center.getY(), radius, start, end, direct);
115 }
116
117 // Constructors based on doubles
118
119 /**
120 * Base constructor, for constructiong arc from circle parameters, start and
121 * end angles, and by specifying whether arc is direct or not.
122 */
123 public CircleArc2D(double xc, double yc, double r, double start,
124 double end, boolean direct) {
125 super(xc, yc, r, r, 0, start, end, direct);
126 this.circle = new Circle2D(xc, yc, r);
127 this.ellipse = this.circle;
128 }
129
130 /** Base constructor with all parameters specified */
131 public CircleArc2D(double xc, double yc, double r, double start,
132 double extent) {
133 super(xc, yc, r, r, 0, start, extent);
134 this.circle = new Circle2D(xc, yc, r);
135 this.ellipse = this.circle;
136 }
137
138 // ====================================================================
139 // static factories
140
141 public static CircleArc2D create(Circle2D support, double startAngle,
142 double angleExtent) {
143 return new CircleArc2D(support, startAngle, angleExtent);
144 }
145
146 public static CircleArc2D create(Circle2D support, double startAngle,
147 double endAngle, boolean direct) {
148 return new CircleArc2D(support, startAngle, endAngle, direct);
149 }
150
151 public static CircleArc2D create(Point2D center, double radius,
152 double startAngle, double angleExtent) {
153 return new CircleArc2D(center, radius, startAngle, angleExtent);
154 }
155
156 public static CircleArc2D create(Point2D center, double radius,
157 double startAngle, double endAngle, boolean direct) {
158 return new CircleArc2D(center, radius, startAngle, endAngle, direct);
159 }
160
161 // ====================================================================
162 // methods specific to CircleArc2D
163
164 /**
165 * convert position on curve to angle with circle center.
166 */
167 private double positionToAngle(double t) {
168 if (t>Math.abs(angleExtent))
169 t = Math.abs(angleExtent);
170 if (t<0)
171 t = 0;
172 if (angleExtent<0)
173 t = -t;
174 t = t+startAngle;
175 return t;
176 }
177
178 /**
179 * Returns the circle which contains the circle arc.
180 * @deprecated use getSupportingCircle instead
181 */
182 @Deprecated
183 public Circle2D getSupportCircle() {
184 return circle;
185 }
186
187
188 /**
189 * Change the center of the support circle.
190 *
191 * @param point the new center of the arc.
192 * @deprecated conics will become imutable in a future release
193 */
194 @Deprecated
195 public void setCenter(Point2D point) {
196 circle.xc = point.getX();
197 circle.yc = point.getY();
198 }
199
200 /**
201 * Change the radius of the support circle
202 *
203 * @param r the new radius
204 * @deprecated conics will become imutable in a future release
205 */
206 @Deprecated
207 public void setRadius(double r) {
208 circle.r = r;
209 }
210
211 /**
212 * @deprecated conics will become imutable in a future release
213 */
214 @Deprecated
215 public void setArc(Point2D center, double radius, double start,
216 double extent) {
217 circle.xc = center.getX();
218 circle.yc = center.getY();
219 circle.r = radius;
220 startAngle = start;
221 angleExtent = extent;
222 }
223
224 // ===================================================================
225 // methods implementing CircularShape2D interface
226
227 /**
228 * Returns the circle which contains the circle arc.
229 */
230 public Circle2D getSupportingCircle() {
231 return circle;
232 }
233
234 // ===================================================================
235 // Methods implementing the CirculinearCurve2D interface
236
237 /* (non-Javadoc)
238 * @see math.geom2d.circulinear.CirculinearShape2D#getBuffer(double)
239 */
240 public CirculinearDomain2D getBuffer(double dist) {
241 return CirculinearCurve2DUtils.computeBuffer(this, dist);
242 }
243
244 public CircleArc2D getParallel (double dist) {
245 double r = circle.getRadius();
246 double r2 = Math.max(angleExtent>0 ? r+dist : r-dist, 0);
247 return new CircleArc2D(
248 circle.getCenter(), r2,
249 startAngle, angleExtent);
250 }
251
252 public double getLength() {
253 return circle.getRadius()*Math.abs(angleExtent);
254 }
255
256 /* (non-Javadoc)
257 * @see math.geom2d.circulinear.CirculinearCurve2D#getLength(double)
258 */
259 public double getLength(double pos) {
260 return pos*circle.getRadius();
261 }
262
263 /* (non-Javadoc)
264 * @see math.geom2d.circulinear.CirculinearCurve2D#getPosition(double)
265 */
266 public double getPosition(double length) {
267 return length/circle.getRadius();
268 }
269
270 /* (non-Javadoc)
271 * @see math.geom2d.circulinear.CirculinearCurve2D#transform(math.geom2d.transform.CircleInversion2D)
272 */
273 public CirculinearElement2D transform(CircleInversion2D inv) {
274 // Extract inversion parameters
275 Point2D center = inv.getCenter();
276
277 // transform the extremities
278 Point2D p1 = this.getFirstPoint().transform(inv);
279 Point2D p2 = this.getLastPoint().transform(inv);
280
281 CirculinearElement2D element = circle.transform(inv);
282
283 if(element instanceof Circle2D) {
284 return new CircleArc2D(
285 (Circle2D)element,
286 Angle2D.getHorizontalAngle(center, p1),
287 Angle2D.getHorizontalAngle(center, p2),
288 !this.isDirect());
289 } else if (element instanceof StraightLine2D) {
290 //TODO: add processing of special cases (arc contains transform center)
291 return new LineSegment2D(p1, p2);
292 }
293
294 System.err.println(
295 "CircleArc2D.transform(): error in transforming circle by inversion");
296 return null;
297
298 }
299
300 // ====================================================================
301 // methods from interface OrientedCurve2D
302
303 @Override
304 public double getWindingAngle(java.awt.geom.Point2D point) {
305 Point2D p1 = getFirstPoint();
306 Point2D p2 = getLastPoint();
307
308 // compute angle of point with extreme points
309 double angle1 = Angle2D.getHorizontalAngle(point, p1);
310 double angle2 = Angle2D.getHorizontalAngle(point, p2);
311
312 // boolean b0 = circle.isInside(point);
313 // boolean b1 = new StraightLine2D(p1,
314 // this.getTangent(0)).isInside(point);
315 // boolean b2 = new StraightLine2D(p2,
316 // this.getTangent(Math.abs(angleExtent))).isInside(point);
317 //
318 // // True if point is located in 'triangular' area formed by arc edge
319 // // and tangents at extremities
320 // boolean bl = new StraightLine2D(p2, p1).isInside(point) && b1 && b2;
321 //
322 // if(angleExtent>0){
323 // if(b0 || bl){
324 // if(angle2>angle1) return angle2 - angle1;
325 // else return 2*Math.PI - angle1 + angle2;
326 // }else{
327 // if(angle2>angle1) return angle2 - angle1 - 2*Math.PI;
328 // else return angle2 - angle1;
329 // }
330 // }else{
331 // if(b0 || bl){
332 // if(angle1>angle2) return angle1 - angle2;
333 // else return 2*Math.PI - angle2 + angle1;
334 // }else{
335 // if(angle1>angle2) return angle1 - angle2 - 2*Math.PI;
336 // else return angle1 - angle2;
337 // }
338 // }
339 // test on which 'side' of the arc the point lie
340 boolean b1 = (new StraightLine2D(p1, p2)).isInside(point);
341 boolean b2 = ellipse.isInside(point);
342
343 if (angleExtent>0) {
344 if (b1||b2) {
345 if (angle2>angle1)
346 return angle2-angle1;
347 else
348 return 2*Math.PI-angle1+angle2;
349 } else {
350 if (angle2>angle1)
351 return angle2-angle1-2*Math.PI;
352 else
353 return angle2-angle1;
354 }
355 } else {
356 if (!b1||b2) {
357 if (angle1>angle2)
358 return angle2-angle1;
359 else
360 return angle2-angle1-2*Math.PI;
361 } else {
362 if (angle1>angle2)
363 return angle2-angle1+2*Math.PI;
364 else
365 return angle2-angle1;
366 }
367 // }else{
368 // if(b1 || b2){
369 // if(angle1>angle2) return angle1 - angle2;
370 // else return 2*Math.PI - angle2 + angle1;
371 // }else{
372 // if(angle1>angle2) return angle1 - angle2 - 2*Math.PI;
373 // else return angle1 - angle2;
374 // }
375 }
376 }
377
378 @Override
379 public boolean isInside(java.awt.geom.Point2D point) {
380 return getSignedDistance(point.getX(), point.getY())<0;
381 }
382
383 @Override
384 public double getSignedDistance(java.awt.geom.Point2D p) {
385 return getSignedDistance(p.getX(), p.getY());
386 }
387
388 @Override
389 public double getSignedDistance(double x, double y) {
390 double dist = getDistance(x, y);
391 Point2D point = new Point2D(x, y);
392
393 boolean direct = angleExtent>0;
394 // boolean inCircle = Point2D.getDistance(x, y, xc, yc)<=r;
395 boolean inCircle = circle.isInside(point);
396 if (inCircle)
397 return angleExtent>0 ? -dist : dist;
398
399 Point2D p1 = circle.getPoint(startAngle);
400 Point2D p2 = circle.getPoint(startAngle+angleExtent);
401 boolean onLeft = (new StraightLine2D(p1, p2)).isInside(point);
402
403 if (direct&&!onLeft)
404 return dist;
405 if (!direct&&onLeft)
406 return -dist;
407
408 boolean left1 = (new Ray2D(p1, circle.getTangent(startAngle)))
409 .isInside(point);
410 if (direct&&!left1)
411 return dist;
412 if (!direct&&left1)
413 return -dist;
414
415 boolean left2 = (new Ray2D(p2, circle
416 .getTangent(startAngle+angleExtent))).isInside(point);
417 if (direct&&!left2)
418 return dist;
419 if (!direct&&left2)
420 return -dist;
421
422 if (direct)
423 return -dist;
424 else
425 return dist;
426 }
427
428 // ====================================================================
429 // methods from interface SmoothCurve2D
430
431 @Override
432 public Vector2D getTangent(double t) {
433 t = this.positionToAngle(t);
434
435 double r = circle.getRadius();
436 if (angleExtent>0)
437 return new Vector2D(-r*Math.sin(t), r*Math.cos(t));
438 else
439 return new Vector2D(r*Math.sin(t), -r*Math.cos(t));
440 }
441
442 // ===================================================================
443 // methods from interface ContinuousCurve2D
444
445 @Override
446 public Collection<? extends CircleArc2D> getSmoothPieces() {
447 ArrayList<CircleArc2D> list = new ArrayList<CircleArc2D>(1);
448 list.add(this);
449 return list;
450 }
451
452 /**
453 * a circle arc is never closed by definition.
454 */
455 @Override
456 public boolean isClosed() {
457 return false;
458 }
459
460 // ====================================================================
461 // methods from interface Curve2D
462
463 /** Always return 0 */
464 @Override
465 public double getT0() {
466 return 0;
467 }
468
469 /**
470 * return the last position of the circle are, which is given by the angle
471 * extent of the arc.
472 */
473 @Override
474 public double getT1() {
475 return Math.abs(this.angleExtent);
476 }
477
478 /**
479 * Returns the position of a point form the curvilinear position.
480 */
481 @Override
482 public Point2D getPoint(double t) {
483 t = this.positionToAngle(t);
484 return circle.getPoint(t);
485 }
486
487 /**
488 * return relative position between 0 and the angle extent.
489 */
490 @Override
491 public double getPosition(java.awt.geom.Point2D point) {
492 double angle = Angle2D.getHorizontalAngle(circle.getCenter(), point);
493 if (containsAngle(angle))
494 if (angleExtent>0)
495 return Angle2D.formatAngle(angle-startAngle);
496 else
497 return Angle2D.formatAngle(startAngle-angle);
498
499 // return either 0 or 1, depending on which extremity is closer.
500 return getFirstPoint().distance(point)<getLastPoint().distance(point) ? 0
501 : Math.abs(angleExtent);
502 }
503
504 /**
505 * Compute intersections of the circle arc with a line. Return an array of
506 * Point2D, of size 0, 1 or 2 depending on the distance between circle and
507 * line. If there are 2 intersections points, the first one in the array is
508 * the first one on the line.
509 */
510 @Override
511 public Collection<Point2D> getIntersections(LinearShape2D line) {
512 return Circle2D.getIntersections(this, line);
513 }
514
515 @Override
516 public double project(java.awt.geom.Point2D point) {
517 double angle = circle.project(point);
518
519 // Case of an angle contained in the circle arc
520 if (Angle2D.containsAngle(startAngle, startAngle+angleExtent, angle,
521 angleExtent>0)) {
522 if (angleExtent>0)
523 return Angle2D.formatAngle(angle-startAngle);
524 else
525 return Angle2D.formatAngle(startAngle-angle);
526 }
527
528 Point2D p1 = this.getFirstPoint();
529 Point2D p2 = this.getLastPoint();
530 if (p1.getDistance(point)<p2.getDistance(point))
531 return 0;
532 else
533 return Math.abs(angleExtent);
534
535 // // convert to arc parameterization
536 // if(angleExtent>0)
537 // angle = Angle2D.formatAngle(angle-startAngle);
538 // else
539 // angle = Angle2D.formatAngle(startAngle-angle);
540 //
541 // // ensure projection lies on the arc
542 // if(angle<0) return 0;
543 // if(angle>Math.abs(angleExtent)) return Math.abs(angleExtent);
544 //
545 // return angle;
546 }
547
548 // ====================================================================
549 // methods from interface Shape2D
550
551 @Override
552 public double getDistance(java.awt.geom.Point2D p) {
553 return getDistance(p.getX(), p.getY());
554 }
555
556 @Override
557 public double getDistance(double x, double y) {
558 double angle = Angle2D.getHorizontalAngle(circle.xc, circle.yc, x, y);
559
560 if (containsAngle(angle))
561 return Math.abs(Point2D.getDistance(circle.xc, circle.yc, x, y)
562 -circle.r);
563 else
564 return Math.min(getFirstPoint().getDistance(x, y), getLastPoint()
565 .getDistance(x, y));
566 }
567
568 /** Always return true */
569 @Override
570 public boolean isBounded() {
571 return true;
572 }
573
574 /**
575 * return a new CircleArc2D. Variables t0 and t1 must be comprised between 0
576 * and the angle extent of the arc.
577 */
578 @Override
579 public CircleArc2D getSubCurve(double t0, double t1) {
580 // convert position to angle
581 if (angleExtent>0) {
582 t0 = Angle2D.formatAngle(startAngle+t0);
583 t1 = Angle2D.formatAngle(startAngle+t1);
584 } else {
585 t0 = Angle2D.formatAngle(startAngle-t0);
586 t1 = Angle2D.formatAngle(startAngle-t1);
587 }
588
589 // check bounds of angles
590 if (!Angle2D.containsAngle(startAngle, startAngle+angleExtent, t0,
591 angleExtent>0))
592 t0 = startAngle;
593 if (!Angle2D.containsAngle(startAngle, startAngle+angleExtent, t1,
594 angleExtent>0))
595 t1 = Angle2D.formatAngle(startAngle+angleExtent);
596
597 // create new arc
598 return new CircleArc2D(circle, t0, t1, angleExtent>0);
599 }
600
601 /**
602 * Returns the circle arc which refers to the same parent circle, with same
603 * start angle, and with opposite angle extent.
604 */
605 @Override
606 public CircleArc2D getReverseCurve() {
607 return new CircleArc2D(this.circle, Angle2D.formatAngle(startAngle
608 +angleExtent), -angleExtent);
609 }
610
611 @Override
612 public Collection<? extends CircleArc2D> getContinuousCurves() {
613 return wrapCurve(this);
614 }
615
616 /**
617 * Clip the circle arc by a box. The result is a CurveSet2D, which contains
618 * only instances of CircleArc2D. If circle arc is not clipped, the result
619 * is an instance of CurveSet2D with zero curves.
620 */
621 @Override
622 public CurveSet2D<CircleArc2D> clip(Box2D box) {
623 // Clip he curve
624 CurveSet2D<SmoothCurve2D> set = Curve2DUtils.clipSmoothCurve(this, box);
625
626 // create a new structure for storing result
627 CurveArray2D<CircleArc2D> result =
628 new CurveArray2D<CircleArc2D>(set.getCurveNumber());
629
630 // convert result
631 for (Curve2D curve : set.getCurves()) {
632 if (curve instanceof CircleArc2D)
633 result.addCurve((CircleArc2D) curve);
634 }
635 return result;
636 }
637
638 /**
639 * Returns an instance of EllipseArc2D, or CircleArc2D if transform is a
640 * similarity.
641 */
642 @Override
643 public EllipseArc2D transform(AffineTransform2D trans) {
644 if (!AffineTransform2D.isSimilarity(trans))
645 return super.transform(trans);
646
647 // System.out.println("transform a circle");
648
649 // extract the control points
650 Point2D center = circle.getCenter();
651 Point2D point1 = this.getFirstPoint();
652 Point2D point2 = this.getLastPoint();
653
654 // transform each point
655 center = center.transform(trans);
656 point1 = point1.transform(trans);
657 point2 = point2.transform(trans);
658
659 // compute new angles
660 double angle1 = Angle2D.getHorizontalAngle(center, point1);
661 double angle2 = Angle2D.getHorizontalAngle(center, point2);
662
663 // compute factor of transform
664 double[] coefs = trans.getCoefficients();
665 double factor = Math.sqrt(coefs[0]*coefs[0]+coefs[3]*coefs[3]);
666
667 // compute parameters of new circle arc
668 double xc = center.getX(), yc = center.getY();
669 double r2 = circle.getRadius()*factor;
670 double startAngle = angle1;
671 double angleExtent = Angle2D.formatAngle(angle2-angle1);
672
673 boolean b1 = AffineTransform2D.isDirect(trans);
674 boolean b2 = this.isDirect();
675 if (b1&!b2|!b1&b2)
676 angleExtent = angleExtent-2*Math.PI;
677
678 // return new CircleArc
679 return new CircleArc2D(xc, yc, r2, startAngle, angleExtent);
680 }
681
682 // // following are inherited from EllipseArc2D
683 // public java.awt.Rectangle getBounds() {
684 // java.awt.geom.Rectangle2D bounds = this.getBounds2D();
685 // int xmin = (int) bounds.getMinX();
686 // int ymin = (int) bounds.getMinY();
687 // int xmax = (int) Math.ceil(bounds.getMaxX());
688 // int ymax = (int) Math.ceil(bounds.getMaxY());
689 // return new java.awt.Rectangle(xmin, ymin, xmax-xmin, ymax-ymin);
690 // }
691 //
692 // /**
693 // * Returns more precise bounds for the shape. Result is an instance of Box2D.
694 // */
695 // public java.awt.geom.Rectangle2D getBounds2D() {
696 // Point2D p; double x, y;
697 //
698 // double xc = circle.xc;
699 // double yc = circle.yc;
700 // double r = circle.r;
701 //
702 // p = getFirstPoint(); x = p.getX(); y = p.getY();
703 // double xmin = x; double ymin = y;
704 // double xmax = x; double ymax = y;
705 //
706 // p = getLastPoint(); x = p.getX(); y = p.getY();
707 // xmin = Math.min(xmin, x); ymin = Math.min(ymin, y);
708 // xmax = Math.max(xmax, x); ymax = Math.max(ymax, y);
709 //
710 // if(containsAngle(0)){
711 // x = xc+r; y = yc;
712 // xmin = Math.min(xmin, x); ymin = Math.min(ymin, y);
713 // xmax = Math.max(xmax, x); ymax = Math.max(ymax, y);
714 // }
715 //
716 // if(containsAngle(Math.PI)){
717 // x = xc-r; y = yc;
718 // xmin = Math.min(xmin, x); ymin = Math.min(ymin, y);
719 // xmax = Math.max(xmax, x); ymax = Math.max(ymax, y);
720 // }
721 //
722 // if(containsAngle(Math.PI/2)){
723 // x = xc; y = yc+r;
724 // xmin = Math.min(xmin, x); ymin = Math.min(ymin, y);
725 // xmax = Math.max(xmax, x); ymax = Math.max(ymax, y);
726 // }
727 //
728 // if(containsAngle(3*Math.PI/2)){
729 // x = xc; y = yc-r;
730 // xmin = Math.min(xmin, x); ymin = Math.min(ymin, y);
731 // xmax = Math.max(xmax, x); ymax = Math.max(ymax, y);
732 // }
733 //
734 // return new Box2D(xmin, ymin, (xmax-xmin), (ymax-ymin));
735 // }
736
737 @Override
738 public boolean contains(java.awt.geom.Point2D p) {
739 return contains(p.getX(), p.getY());
740 }
741
742 @Override
743 public boolean contains(double x, double y) {
744 // Check if radius is correct
745 double r = circle.getRadius();
746 if (Math.abs(Point2D.getDistance(circle.xc, circle.yc, x, y)-r)>Shape2D.ACCURACY)
747 return false;
748
749 // angle from circle center to point
750 double angle = Angle2D.getHorizontalAngle(circle.xc, circle.yc, x, y);
751
752 // check if angle is contained in interval [startAngle-angleExtent]
753 return this.containsAngle(angle);
754 }
755
756 // public java.awt.geom.GeneralPath appendPath(java.awt.geom.GeneralPath path){
757 // double cot = Math.cos(circle.theta);
758 // double sit = Math.sin(circle.theta);
759 // double xc = circle.xc;
760 // double yc = circle.yc;
761 // double r = circle.r;
762 // double endAngle = startAngle+angleExtent;
763 //
764 // if(angleExtent>0)
765 // for(double t=startAngle; t<endAngle; t+=angleExtent/100)
766 // path.lineTo((float)(xc+r*Math.cos(t)*cot-r*Math.sin(t)*sit),
767 // (float)(yc+r*Math.cos(t)*sit+r*Math.sin(t)*cot));
768 // else
769 // for(double t=startAngle; t>endAngle; t+=angleExtent/100)
770 // path.lineTo((float)(xc+r*Math.cos(t)*cot-r*Math.sin(t)*sit),
771 // (float)(yc+r*Math.cos(t)*sit+r*Math.sin(t)*cot));
772 //
773 // // position to the last point
774 // path.lineTo((float)(xc+r*Math.cos(endAngle)*cot-r*Math.sin(endAngle)*sit),
775 // (float)(yc+r*Math.cos(endAngle)*sit+r*Math.sin(endAngle)*cot));
776 //
777 // return path;
778 // }
779
780 // /* (non-Javadoc)
781 // * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform, double)
782 // */
783 // public java.awt.geom.GeneralPath getInnerPath() {
784 //
785 // // Creates the path
786 // java.awt.geom.GeneralPath path = new java.awt.geom.GeneralPath();
787 //
788 // double cot = Math.cos(circle.theta);
789 // double sit = Math.sin(circle.theta);
790 // double xc = circle.xc;
791 // double yc = circle.yc;
792 // double r = circle.r;
793 // double endAngle = startAngle+angleExtent;
794 //
795 // // position to the first point
796 // path.lineTo((float)(xc+r*Math.cos(startAngle)*cot-r*Math.sin(startAngle)*sit),
797 // (float)(yc+r*Math.cos(startAngle)*sit+r*Math.sin(startAngle)*cot));
798 //
799 // if(angleExtent>0)
800 // for(double t=startAngle; t<endAngle; t+=angleExtent/100)
801 // path.lineTo((float)(xc+r*Math.cos(t)*cot-r*Math.sin(t)*sit),
802 // (float)(yc+r*Math.cos(t)*sit+r*Math.sin(t)*cot));
803 // else
804 // for(double t=startAngle; t>endAngle; t+=angleExtent/100)
805 // path.lineTo((float)(xc+r*Math.cos(t)*cot-r*Math.sin(t)*sit),
806 // (float)(yc+r*Math.cos(t)*sit+r*Math.sin(t)*cot));
807 //
808 // return path;
809 // }
810
811 @Override
812 public String toString() {
813 Point2D center = circle.getCenter();
814 return String.format(Locale.US,
815 "CircleArc2D(%7.2f,%7.2f,%7.2f,%7.5f,%7.5f)",
816 center.getX(), center.getY(), circle.getRadius(),
817 getStartAngle(), getAngleExtent());
818 }
819
820 /**
821 * Two circle arc are equal if the have same center, same radius, same
822 * starting and ending angles, and same orientation.
823 */
824 @Override
825 public boolean equals(Object obj) {
826 if (!(obj instanceof EllipseArc2D))
827 return false;
828
829 if (!(obj instanceof CircleArc2D))
830 return super.equals(obj);
831
832 CircleArc2D arc = (CircleArc2D) obj;
833 // test whether supporting ellipses have same support
834 if (Math.abs(circle.xc-arc.circle.xc)>Shape2D.ACCURACY)
835 return false;
836 if (Math.abs(circle.yc-arc.circle.yc)>Shape2D.ACCURACY)
837 return false;
838 if (Math.abs(circle.r-arc.circle.r)>Shape2D.ACCURACY)
839 return false;
840 if (Math.abs(circle.r1-arc.circle.r1)>Shape2D.ACCURACY)
841 return false;
842 if (Math.abs(circle.r2-arc.circle.r2)>Shape2D.ACCURACY)
843 return false;
844 if (Math.abs(circle.theta-arc.circle.theta)>Shape2D.ACCURACY)
845 return false;
846
847 // test is angles are the same
848 if (Math.abs(Angle2D.formatAngle(startAngle)
849 -Angle2D.formatAngle(arc.startAngle))>Shape2D.ACCURACY)
850 return false;
851 if (Math.abs(Angle2D.formatAngle(angleExtent)
852 -Angle2D.formatAngle(arc.angleExtent))>Shape2D.ACCURACY)
853 return false;
854
855 // if no difference, this is the same
856 return true;
857 }
858
859 @Override
860 public CircleArc2D clone() {
861 return new CircleArc2D(circle.clone(), startAngle, angleExtent);
862 }
863 }