View Javadoc

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 }