1 /* Arc2D.java -- represents an arc in 2-D space
2 Copyright (C) 2002, 2003, 2004 Free Software Foundation
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package java
.awt
.geom
;
40 import java
.util
.NoSuchElementException
;
44 * This class represents all arcs (segments of an ellipse in 2-D space). The
45 * arcs are defined by starting angle and extent (arc length) in degrees, as
46 * opposed to radians (like the rest of Java), and can be open, chorded, or
47 * wedge shaped. The angles are skewed according to the ellipse, so that 45
48 * degrees always points to the upper right corner (positive x, negative y)
49 * of the bounding rectangle. A positive extent draws a counterclockwise arc,
50 * and while the angle can be any value, the path iterator only traverses the
51 * first 360 degrees. Storage is up to the subclasses.
53 * @author Eric Blake (ebb9@email.byu.edu)
54 * @author Sven de Marothy (sven@physto.se)
57 public abstract class Arc2D
extends RectangularShape
60 * An open arc, with no segment connecting the endpoints. This type of
61 * arc still contains the same points as a chorded version.
63 public static final int OPEN
= 0;
66 * A closed arc with a single segment connecting the endpoints (a chord).
68 public static final int CHORD
= 1;
71 * A closed arc with two segments, one from each endpoint, meeting at the
72 * center of the ellipse.
74 public static final int PIE
= 2;
76 /** The closure type of this arc. */
80 * Create a new arc, with the specified closure type.
82 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}.
83 * @throws IllegalArgumentException if type is invalid
85 protected Arc2D(int type
)
87 if (type
< OPEN
|| type
> PIE
)
88 throw new IllegalArgumentException();
93 * Get the starting angle of the arc in degrees.
95 * @return the starting angle
96 * @see #setAngleStart(double)
98 public abstract double getAngleStart();
101 * Get the extent angle of the arc in degrees.
103 * @return the extent angle
104 * @see #setAngleExtent(double)
106 public abstract double getAngleExtent();
109 * Return the closure type of the arc.
111 * @return the closure type
115 * @see #setArcType(int)
117 public int getArcType()
123 * Returns the starting point of the arc.
125 * @return the start point
127 public Point2D
getStartPoint()
129 double angle
= Math
.toRadians(getAngleStart());
130 double rx
= getWidth() / 2;
131 double ry
= getHeight() / 2;
132 double x
= getX() + rx
+ rx
* Math
.cos(angle
);
133 double y
= getY() + ry
- ry
* Math
.sin(angle
);
134 return new Point2D
.Double(x
, y
);
138 * Returns the ending point of the arc.
140 * @return the end point
142 public Point2D
getEndPoint()
144 double angle
= Math
.toRadians(getAngleStart() + getAngleExtent());
145 double rx
= getWidth() / 2;
146 double ry
= getHeight() / 2;
147 double x
= getX() + rx
+ rx
* Math
.cos(angle
);
148 double y
= getY() + ry
- ry
* Math
.sin(angle
);
149 return new Point2D
.Double(x
, y
);
153 * Set the parameters of the arc. The angles are in degrees, and a positive
154 * extent sweeps counterclockwise (from the positive x-axis to the negative
157 * @param x the new x coordinate of the upper left of the bounding box
158 * @param y the new y coordinate of the upper left of the bounding box
159 * @param w the new width of the bounding box
160 * @param h the new height of the bounding box
161 * @param start the start angle, in degrees
162 * @param extent the arc extent, in degrees
163 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
164 * @throws IllegalArgumentException if type is invalid
166 public abstract void setArc(double x
, double y
, double w
, double h
,
167 double start
, double extent
, int type
);
170 * Set the parameters of the arc. The angles are in degrees, and a positive
171 * extent sweeps counterclockwise (from the positive x-axis to the negative
174 * @param p the upper left point of the bounding box
175 * @param d the dimensions of the bounding box
176 * @param start the start angle, in degrees
177 * @param extent the arc extent, in degrees
178 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
179 * @throws IllegalArgumentException if type is invalid
180 * @throws NullPointerException if p or d is null
182 public void setArc(Point2D p
, Dimension2D d
, double start
, double extent
,
185 setArc(p
.getX(), p
.getY(), d
.getWidth(), d
.getHeight(), start
, extent
, type
);
189 * Set the parameters of the arc. The angles are in degrees, and a positive
190 * extent sweeps counterclockwise (from the positive x-axis to the negative
193 * @param r the new bounding box
194 * @param start the start angle, in degrees
195 * @param extent the arc extent, in degrees
196 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
197 * @throws IllegalArgumentException if type is invalid
198 * @throws NullPointerException if r is null
200 public void setArc(Rectangle2D r
, double start
, double extent
, int type
)
202 setArc(r
.getX(), r
.getY(), r
.getWidth(), r
.getHeight(), start
, extent
, type
);
206 * Set the parameters of the arc from the given one.
208 * @param a the arc to copy
209 * @throws NullPointerException if a is null
211 public void setArc(Arc2D a
)
213 setArc(a
.getX(), a
.getY(), a
.getWidth(), a
.getHeight(), a
.getAngleStart(),
214 a
.getAngleExtent(), a
.getArcType());
218 * Set the parameters of the arc. The angles are in degrees, and a positive
219 * extent sweeps counterclockwise (from the positive x-axis to the negative
220 * y-axis). This controls the center point and radius, so the arc will be
223 * @param x the x coordinate of the center of the circle
224 * @param y the y coordinate of the center of the circle
225 * @param r the radius of the circle
226 * @param start the start angle, in degrees
227 * @param extent the arc extent, in degrees
228 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
229 * @throws IllegalArgumentException if type is invalid
231 public void setArcByCenter(double x
, double y
, double r
, double start
,
232 double extent
, int type
)
234 setArc(x
- r
, y
- r
, r
+ r
, r
+ r
, start
, extent
, type
);
238 * Sets the parameters of the arc by finding the tangents of two lines, and
239 * using the specified radius. The arc will be circular, will begin on the
240 * tangent point of the line extending from p1 to p2, and will end on the
241 * tangent point of the line extending from p2 to p3.
243 * XXX What happens if the points are colinear, or the radius negative?
245 * @param p1 the first point
246 * @param p2 the tangent line intersection point
247 * @param p3 the third point
248 * @param r the radius of the arc
249 * @throws NullPointerException if any point is null
251 public void setArcByTangent(Point2D p1
, Point2D p2
, Point2D p3
, double r
)
253 if ((p2
.getX() - p1
.getX()) * (p3
.getY() - p1
.getY())
254 - (p3
.getX() - p1
.getX()) * (p2
.getY() - p1
.getY()) > 0)
261 // normalized tangent vectors
262 double dx1
= (p1
.getX() - p2
.getX()) / p1
.distance(p2
);
263 double dy1
= (p1
.getY() - p2
.getY()) / p1
.distance(p2
);
264 double dx2
= (p2
.getX() - p3
.getX()) / p3
.distance(p2
);
265 double dy2
= (p2
.getY() - p3
.getY()) / p3
.distance(p2
);
266 double theta1
= Math
.atan2(dx1
, dy1
);
267 double theta2
= Math
.atan2(dx2
, dy2
);
269 double dx
= r
* Math
.cos(theta2
) - r
* Math
.cos(theta1
);
270 double dy
= -r
* Math
.sin(theta2
) + r
* Math
.sin(theta1
);
273 theta1
+= 2 * Math
.PI
;
275 theta2
+= 2 * Math
.PI
;
277 theta2
+= 2 * Math
.PI
;
279 // Vectors of the lines, not normalized, note we change
280 // the direction of line 2.
281 dx1
= p1
.getX() - p2
.getX();
282 dy1
= p1
.getY() - p2
.getY();
283 dx2
= p3
.getX() - p2
.getX();
284 dy2
= p3
.getY() - p2
.getY();
286 // Calculate the tangent point to the second line
287 double t2
= -(dx1
* dy
- dy1
* dx
) / (dx2
* dy1
- dx1
* dy2
);
288 double x2
= t2
* (p3
.getX() - p2
.getX()) + p2
.getX();
289 double y2
= t2
* (p3
.getY() - p2
.getY()) + p2
.getY();
291 // calculate the center point
292 double x
= x2
- r
* Math
.cos(theta2
);
293 double y
= y2
+ r
* Math
.sin(theta2
);
295 setArc(x
- r
, y
- r
, 2 * r
, 2 * r
, Math
.toDegrees(theta1
),
296 Math
.toDegrees(theta2
- theta1
), getArcType());
300 * Set the start, in degrees.
302 * @param start the new start angle
303 * @see #getAngleStart()
305 public abstract void setAngleStart(double start
);
308 * Set the extent, in degrees.
310 * @param extent the new extent angle
311 * @see #getAngleExtent()
313 public abstract void setAngleExtent(double extent
);
316 * Sets the starting angle to the angle of the given point relative to
317 * the center of the arc. The extent remains constant; in other words,
318 * this rotates the arc.
320 * @param p the new start point
321 * @throws NullPointerException if p is null
322 * @see #getStartPoint()
323 * @see #getAngleStart()
325 public void setAngleStart(Point2D p
)
328 double x
= p
.getX() - (getX() + getWidth() / 2);
329 double y
= p
.getY() - (getY() + getHeight() / 2);
330 setAngleStart(Math
.toDegrees(Math
.atan2(-y
, x
)));
334 * Sets the starting and extent angles to those of the given points
335 * relative to the center of the arc. The arc will be non-empty, and will
336 * extend counterclockwise.
338 * @param x1 the first x coordinate
339 * @param y1 the first y coordinate
340 * @param x2 the second x coordinate
341 * @param y2 the second y coordinate
342 * @see #setAngleStart(Point2D)
344 public void setAngles(double x1
, double y1
, double x2
, double y2
)
346 // Normalize the points.
349 double mw
= getWidth();
350 double mh
= getHeight();
351 x1
= x1
- (mx
+ mw
/ 2);
352 y1
= y1
- (my
+ mh
/ 2);
353 x2
= x2
- (mx
+ mw
/ 2);
354 y2
= y2
- (my
+ mh
/ 2);
355 double start
= Math
.toDegrees(Math
.atan2(-y1
, x1
));
356 double extent
= Math
.toDegrees(Math
.atan2(-y2
, x2
)) - start
;
359 setAngleStart(start
);
360 setAngleExtent(extent
);
364 * Sets the starting and extent angles to those of the given points
365 * relative to the center of the arc. The arc will be non-empty, and will
366 * extend counterclockwise.
368 * @param p1 the first point
369 * @param p2 the second point
370 * @throws NullPointerException if either point is null
371 * @see #setAngleStart(Point2D)
373 public void setAngles(Point2D p1
, Point2D p2
)
375 setAngles(p1
.getX(), p1
.getY(), p2
.getX(), p2
.getY());
379 * Set the closure type of this arc.
381 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
382 * @throws IllegalArgumentException if type is invalid
385 public void setArcType(int type
)
387 if (type
< OPEN
|| type
> PIE
)
388 throw new IllegalArgumentException();
393 * Sets the location and bounds of the ellipse of which this arc is a part.
395 * @param x the new x coordinate
396 * @param y the new y coordinate
397 * @param w the new width
398 * @param h the new height
401 public void setFrame(double x
, double y
, double w
, double h
)
403 setArc(x
, y
, w
, h
, getAngleStart(), getAngleExtent(), type
);
407 * Gets the bounds of the arc. This is much tighter than
408 * <code>getBounds</code>, as it takes into consideration the start and
409 * end angles, and the center point of a pie wedge, rather than just the
412 * @return the bounds of the arc
415 public Rectangle2D
getBounds2D()
417 double extent
= getAngleExtent();
418 if (Math
.abs(extent
) >= 360)
419 return makeBounds(getX(), getY(), getWidth(), getHeight());
421 // Find the minimal bounding box. This determined by its extrema,
422 // which are the center, the endpoints of the arc, and any local
423 // maximum contained by the arc.
424 double rX
= getWidth() / 2;
425 double rY
= getHeight() / 2;
426 double centerX
= getX() + rX
;
427 double centerY
= getY() + rY
;
429 Point2D p1
= getStartPoint();
430 Rectangle2D result
= makeBounds(p1
.getX(), p1
.getY(), 0, 0);
431 result
.add(getEndPoint());
434 result
.add(centerX
, centerY
);
435 if (containsAngle(0))
436 result
.add(centerX
+ rX
, centerY
);
437 if (containsAngle(90))
438 result
.add(centerX
, centerY
- rY
);
439 if (containsAngle(180))
440 result
.add(centerX
- rX
, centerY
);
441 if (containsAngle(270))
442 result
.add(centerX
, centerY
+ rY
);
448 * Construct a bounding box in a precision appropriate for the subclass.
450 * @param x the x coordinate
451 * @param y the y coordinate
453 * @param h the height
454 * @return the rectangle for use in getBounds2D
456 protected abstract Rectangle2D
makeBounds(double x
, double y
, double w
,
460 * Tests if the given angle, in degrees, is included in the arc.
461 * All angles are normalized to be between 0 and 360 degrees.
463 * @param a the angle to test
464 * @return true if it is contained
466 public boolean containsAngle(double a
)
468 double start
= getAngleStart();
469 double extent
= getAngleExtent();
470 double end
= start
+ extent
;
475 if (extent
>= 360 || extent
<= -360)
496 return a
>= start
&& a
< end
; // starting angle included, ending angle not
500 * Determines if the arc contains the given point. If the bounding box
501 * is empty, then this will return false.
503 * The area considered 'inside' an arc of type OPEN is the same as the
504 * area inside an equivalent filled CHORD-type arc. The area considered
505 * 'inside' a CHORD-type arc is the same as the filled area.
507 * @param x the x coordinate to test
508 * @param y the y coordinate to test
509 * @return true if the point is inside the arc
511 public boolean contains(double x
, double y
)
513 double w
= getWidth();
514 double h
= getHeight();
515 double extent
= getAngleExtent();
516 if (w
<= 0 || h
<= 0 || extent
== 0)
519 double mx
= getX() + w
/ 2;
520 double my
= getY() + h
/ 2;
521 double dx
= (x
- mx
) * 2 / w
;
522 double dy
= (y
- my
) * 2 / h
;
523 if ((dx
* dx
+ dy
* dy
) >= 1.0)
526 double angle
= Math
.toDegrees(Math
.atan2(-dy
, dx
));
527 if (getArcType() == PIE
)
528 return containsAngle(angle
);
530 double a1
= Math
.toRadians(getAngleStart());
531 double a2
= Math
.toRadians(getAngleStart() + extent
);
532 double x1
= mx
+ getWidth() * Math
.cos(a1
) / 2;
533 double y1
= my
- getHeight() * Math
.sin(a1
) / 2;
534 double x2
= mx
+ getWidth() * Math
.cos(a2
) / 2;
535 double y2
= my
- getHeight() * Math
.sin(a2
) / 2;
536 double sgn
= ((x2
- x1
) * (my
- y1
) - (mx
- x1
) * (y2
- y1
)) * ((x2
- x1
) * (y
537 - y1
) - (x
- x1
) * (y2
- y1
));
539 if (Math
.abs(extent
) > 180)
541 if (containsAngle(angle
))
547 if (! containsAngle(angle
))
554 * Tests if a given rectangle intersects the area of the arc.
556 * For a definition of the 'inside' area, see the contains() method.
557 * @see #contains(double, double)
559 * @param x the x coordinate of the rectangle
560 * @param y the y coordinate of the rectangle
561 * @param w the width of the rectangle
562 * @param h the height of the rectangle
563 * @return true if the two shapes share common points
565 public boolean intersects(double x
, double y
, double w
, double h
)
567 double extent
= getAngleExtent();
571 if (contains(x
, y
) || contains(x
, y
+ h
) || contains(x
+ w
, y
)
572 || contains(x
+ w
, y
+ h
))
575 Rectangle2D rect
= new Rectangle2D
.Double(x
, y
, w
, h
);
577 double a
= getWidth() / 2.0;
578 double b
= getHeight() / 2.0;
580 double mx
= getX() + a
;
581 double my
= getY() + b
;
582 double x1
= mx
+ a
* Math
.cos(Math
.toRadians(getAngleStart()));
583 double y1
= my
- b
* Math
.sin(Math
.toRadians(getAngleStart()));
584 double x2
= mx
+ a
* Math
.cos(Math
.toRadians(getAngleStart() + extent
));
585 double y2
= my
- b
* Math
.sin(Math
.toRadians(getAngleStart() + extent
));
587 if (getArcType() != CHORD
)
589 // check intersections against the pie radii
590 if (rect
.intersectsLine(mx
, my
, x1
, y1
))
592 if (rect
.intersectsLine(mx
, my
, x2
, y2
))
595 else// check the chord
596 if (rect
.intersectsLine(x1
, y1
, x2
, y2
))
599 // Check the Arc segment against the four edges
602 // Check the Arc segment against the four edges
605 dx
= a
* Math
.sqrt(1 - ((dy
* dy
) / (b
* b
)));
606 if (! java
.lang
.Double
.isNaN(dx
))
608 if (mx
+ dx
>= x
&& mx
+ dx
<= x
+ w
609 && containsAngle(Math
.toDegrees(Math
.atan2(-dy
, dx
))))
611 if (mx
- dx
>= x
&& mx
- dx
<= x
+ w
612 && containsAngle(Math
.toDegrees(Math
.atan2(-dy
, -dx
))))
616 dx
= a
* Math
.sqrt(1 - ((dy
* dy
) / (b
* b
)));
617 if (! java
.lang
.Double
.isNaN(dx
))
619 if (mx
+ dx
>= x
&& mx
+ dx
<= x
+ w
620 && containsAngle(Math
.toDegrees(Math
.atan2(-dy
, dx
))))
622 if (mx
- dx
>= x
&& mx
- dx
<= x
+ w
623 && containsAngle(Math
.toDegrees(Math
.atan2(-dy
, -dx
))))
627 dy
= b
* Math
.sqrt(1 - ((dx
* dx
) / (a
* a
)));
628 if (! java
.lang
.Double
.isNaN(dy
))
630 if (my
+ dy
>= y
&& my
+ dy
<= y
+ h
631 && containsAngle(Math
.toDegrees(Math
.atan2(-dy
, dx
))))
633 if (my
- dy
>= y
&& my
- dy
<= y
+ h
634 && containsAngle(Math
.toDegrees(Math
.atan2(dy
, dx
))))
639 dy
= b
* Math
.sqrt(1 - ((dx
* dx
) / (a
* a
)));
640 if (! java
.lang
.Double
.isNaN(dy
))
642 if (my
+ dy
>= y
&& my
+ dy
<= y
+ h
643 && containsAngle(Math
.toDegrees(Math
.atan2(-dy
, dx
))))
645 if (my
- dy
>= y
&& my
- dy
<= y
+ h
646 && containsAngle(Math
.toDegrees(Math
.atan2(dy
, dx
))))
650 // Check whether the arc is contained within the box
651 if (rect
.contains(mx
, my
))
658 * Tests if a given rectangle is contained in the area of the arc.
660 * @param x the x coordinate of the rectangle
661 * @param y the y coordinate of the rectangle
662 * @param w the width of the rectangle
663 * @param h the height of the rectangle
664 * @return true if the arc contains the rectangle
666 public boolean contains(double x
, double y
, double w
, double h
)
668 double extent
= getAngleExtent();
672 if (! (contains(x
, y
) && contains(x
, y
+ h
) && contains(x
+ w
, y
)
673 && contains(x
+ w
, y
+ h
)))
676 Rectangle2D rect
= new Rectangle2D
.Double(x
, y
, w
, h
);
678 double a
= getWidth() / 2.0;
679 double b
= getHeight() / 2.0;
681 double mx
= getX() + a
;
682 double my
= getY() + b
;
683 double x1
= mx
+ a
* Math
.cos(Math
.toRadians(getAngleStart()));
684 double y1
= my
- b
* Math
.sin(Math
.toRadians(getAngleStart()));
685 double x2
= mx
+ a
* Math
.cos(Math
.toRadians(getAngleStart() + extent
));
686 double y2
= my
- b
* Math
.sin(Math
.toRadians(getAngleStart() + extent
));
687 if (getArcType() != CHORD
)
689 // check intersections against the pie radii
690 if (rect
.intersectsLine(mx
, my
, x1
, y1
))
693 if (rect
.intersectsLine(mx
, my
, x2
, y2
))
696 else if (rect
.intersectsLine(x1
, y1
, x2
, y2
))
702 * Tests if a given rectangle is contained in the area of the arc.
704 * @param r the rectangle
705 * @return true if the arc contains the rectangle
707 public boolean contains(Rectangle2D r
)
709 return contains(r
.getX(), r
.getY(), r
.getWidth(), r
.getHeight());
713 * Returns an iterator over this arc, with an optional transformation.
714 * This iterator is threadsafe, so future modifications to the arc do not
715 * affect the iteration.
717 * @param at the transformation, or null
718 * @return a path iterator
720 public PathIterator
getPathIterator(AffineTransform at
)
722 return new ArcIterator(this, at
);
726 * This class is used to iterate over an arc. Since ellipses are a subclass
727 * of arcs, this is used by Ellipse2D as well.
729 * @author Eric Blake (ebb9@email.byu.edu)
731 static final class ArcIterator
implements PathIterator
733 /** The current iteration. */
736 /** The last iteration. */
737 private final int limit
;
739 /** The optional transformation. */
740 private final AffineTransform xform
;
742 /** The x coordinate of the bounding box. */
743 private final double x
;
745 /** The y coordinate of the bounding box. */
746 private final double y
;
748 /** The width of the bounding box. */
749 private final double w
;
751 /** The height of the bounding box. */
752 private final double h
;
754 /** The start angle, in radians (not degrees). */
755 private final double start
;
757 /** The extent angle, in radians (not degrees). */
758 private final double extent
;
760 /** The arc closure type. */
761 private final int type
;
764 * Construct a new iterator over an arc.
767 * @param xform the transform
769 public ArcIterator(Arc2D a
, AffineTransform xform
)
776 double start
= a
.getAngleStart() * (Math
.PI
/ 180);
777 double extent
= a
.getAngleExtent() * (Math
.PI
/ 180);
782 start
= 2 * Math
.PI
- extent
+ start
;
785 this.extent
= extent
;
790 else if (extent
== 0)
792 else if (extent
<= Math
.PI
/ 2.0)
794 else if (extent
<= Math
.PI
)
796 else if (extent
<= 3.0 * (Math
.PI
/ 2.0))
803 * Construct a new iterator over an ellipse.
805 * @param e the ellipse
806 * @param xform the transform
808 public ArcIterator(Ellipse2D e
, AffineTransform xform
)
816 extent
= 2 * Math
.PI
;
818 limit
= (w
< 0 || h
< 0) ?
-1 : 5;
822 * Return the winding rule.
824 * @return {@link PathIterator#WIND_NON_ZERO}
826 public int getWindingRule()
828 return WIND_NON_ZERO
;
832 * Test if the iteration is complete.
834 * @return true if more segments exist
836 public boolean isDone()
838 return current
> limit
;
842 * Advance the iterator.
850 * Put the current segment into the array, and return the segment type.
852 * @param coords an array of 6 elements
853 * @return the segment type
854 * @throws NullPointerException if coords is null
855 * @throws ArrayIndexOutOfBoundsException if coords is too small
857 public int currentSegment(float[] coords
)
859 double[] double_coords
= new double[6];
860 int code
= currentSegment(double_coords
);
861 for (int i
= 0; i
< 6; ++i
)
862 coords
[i
] = (float) double_coords
[i
];
867 * Put the current segment into the array, and return the segment type.
869 * @param coords an array of 6 elements
870 * @return the segment type
871 * @throws NullPointerException if coords is null
872 * @throws ArrayIndexOutOfBoundsException if coords is too small
874 public int currentSegment(double[] coords
)
878 double xmid
= x
+ rx
;
879 double ymid
= y
+ ry
;
882 throw new NoSuchElementException("arc iterator out of bounds");
886 coords
[0] = xmid
+ rx
* Math
.cos(start
);
887 coords
[1] = ymid
- ry
* Math
.sin(start
);
889 xform
.transform(coords
, 0, coords
, 0, 1);
893 if (type
!= OPEN
&& current
== limit
)
896 if ((current
== limit
- 1) && (type
== PIE
))
901 xform
.transform(coords
, 0, coords
, 0, 1);
905 // note that this produces a cubic approximation of the arc segment,
906 // not a true ellipsoid. there's no ellipsoid path segment code,
907 // unfortunately. the cubic approximation looks about right, though.
908 double kappa
= (Math
.sqrt(2.0) - 1.0) * (4.0 / 3.0);
909 double quad
= (Math
.PI
/ 2.0);
911 double curr_begin
= start
+ (current
- 1) * quad
;
912 double curr_extent
= Math
.min((start
+ extent
) - curr_begin
, quad
);
913 double portion_of_a_quadrant
= curr_extent
/ quad
;
915 double x0
= xmid
+ rx
* Math
.cos(curr_begin
);
916 double y0
= ymid
- ry
* Math
.sin(curr_begin
);
918 double x1
= xmid
+ rx
* Math
.cos(curr_begin
+ curr_extent
);
919 double y1
= ymid
- ry
* Math
.sin(curr_begin
+ curr_extent
);
921 AffineTransform trans
= new AffineTransform();
922 double[] cvec
= new double[2];
923 double len
= kappa
* portion_of_a_quadrant
;
924 double angle
= curr_begin
;
926 // in a hypothetical "first quadrant" setting, our first control
927 // vector would be sticking up, from [1,0] to [1,kappa].
929 // let us recall however that in java2d, y coords are upside down
930 // from what one would consider "normal" first quadrant rules, so we
931 // will *subtract* the y value of this control vector from our first
937 trans
.transform(cvec
, 0, cvec
, 0, 1);
938 coords
[0] = x0
+ cvec
[0];
939 coords
[1] = y0
- cvec
[1];
941 // control vector #2 would, ideally, be sticking out and to the
942 // right, in a first quadrant arc segment. again, subtraction of y.
945 trans
.rotate(curr_extent
);
946 trans
.transform(cvec
, 0, cvec
, 0, 1);
947 coords
[2] = x1
+ cvec
[0];
948 coords
[3] = y1
- cvec
[1];
955 xform
.transform(coords
, 0, coords
, 0, 3);
959 } // class ArcIterator
962 * This class implements an arc in double precision.
964 * @author Eric Blake (ebb9@email.byu.edu)
967 public static class Double
extends Arc2D
969 /** The x coordinate of the box bounding the ellipse of this arc. */
972 /** The y coordinate of the box bounding the ellipse of this arc. */
975 /** The width of the box bounding the ellipse of this arc. */
978 /** The height of the box bounding the ellipse of this arc. */
979 public double height
;
981 /** The start angle of this arc, in degrees. */
984 /** The extent angle of this arc, in degrees. */
985 public double extent
;
988 * Create a new, open arc at (0,0) with 0 extent.
996 * Create a new arc of the given type at (0,0) with 0 extent.
998 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
999 * @throws IllegalArgumentException if type is invalid
1001 public Double(int type
)
1007 * Create a new arc with the given dimensions.
1009 * @param x the x coordinate
1010 * @param y the y coordinate
1011 * @param w the width
1012 * @param h the height
1013 * @param start the start angle, in degrees
1014 * @param extent the extent, in degrees
1015 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1016 * @throws IllegalArgumentException if type is invalid
1018 public Double(double x
, double y
, double w
, double h
, double start
,
1019 double extent
, int type
)
1027 this.extent
= extent
;
1031 * Create a new arc with the given dimensions.
1033 * @param r the bounding box
1034 * @param start the start angle, in degrees
1035 * @param extent the extent, in degrees
1036 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1037 * @throws IllegalArgumentException if type is invalid
1038 * @throws NullPointerException if r is null
1040 public Double(Rectangle2D r
, double start
, double extent
, int type
)
1045 width
= r
.getWidth();
1046 height
= r
.getHeight();
1048 this.extent
= extent
;
1052 * Return the x coordinate of the bounding box.
1054 * @return the value of x
1056 public double getX()
1062 * Return the y coordinate of the bounding box.
1064 * @return the value of y
1066 public double getY()
1072 * Return the width of the bounding box.
1074 * @return the value of width
1076 public double getWidth()
1082 * Return the height of the bounding box.
1084 * @return the value of height
1086 public double getHeight()
1092 * Return the start angle of the arc, in degrees.
1094 * @return the value of start
1096 public double getAngleStart()
1102 * Return the extent of the arc, in degrees.
1104 * @return the value of extent
1106 public double getAngleExtent()
1112 * Tests if the arc contains points.
1114 * @return true if the arc has no interior
1116 public boolean isEmpty()
1118 return width
<= 0 || height
<= 0;
1122 * Sets the arc to the given dimensions.
1124 * @param x the x coordinate
1125 * @param y the y coordinate
1126 * @param w the width
1127 * @param h the height
1128 * @param start the start angle, in degrees
1129 * @param extent the extent, in degrees
1130 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1131 * @throws IllegalArgumentException if type is invalid
1133 public void setArc(double x
, double y
, double w
, double h
, double start
,
1134 double extent
, int type
)
1141 this.extent
= extent
;
1146 * Sets the start angle of the arc.
1148 * @param start the new start angle
1150 public void setAngleStart(double start
)
1156 * Sets the extent angle of the arc.
1158 * @param extent the new extent angle
1160 public void setAngleExtent(double extent
)
1162 this.extent
= extent
;
1166 * Creates a tight bounding box given dimensions that more precise than
1167 * the bounding box of the ellipse.
1169 * @param x the x coordinate
1170 * @param y the y coordinate
1171 * @param w the width
1172 * @param h the height
1174 protected Rectangle2D
makeBounds(double x
, double y
, double w
, double h
)
1176 return new Rectangle2D
.Double(x
, y
, w
, h
);
1181 * This class implements an arc in float precision.
1183 * @author Eric Blake (ebb9@email.byu.edu)
1186 public static class Float
extends Arc2D
1188 /** The x coordinate of the box bounding the ellipse of this arc. */
1191 /** The y coordinate of the box bounding the ellipse of this arc. */
1194 /** The width of the box bounding the ellipse of this arc. */
1197 /** The height of the box bounding the ellipse of this arc. */
1198 public float height
;
1200 /** The start angle of this arc, in degrees. */
1203 /** The extent angle of this arc, in degrees. */
1204 public float extent
;
1207 * Create a new, open arc at (0,0) with 0 extent.
1215 * Create a new arc of the given type at (0,0) with 0 extent.
1217 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1218 * @throws IllegalArgumentException if type is invalid
1220 public Float(int type
)
1226 * Create a new arc with the given dimensions.
1228 * @param x the x coordinate
1229 * @param y the y coordinate
1230 * @param w the width
1231 * @param h the height
1232 * @param start the start angle, in degrees
1233 * @param extent the extent, in degrees
1234 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1235 * @throws IllegalArgumentException if type is invalid
1237 public Float(float x
, float y
, float w
, float h
, float start
,
1238 float extent
, int type
)
1246 this.extent
= extent
;
1250 * Create a new arc with the given dimensions.
1252 * @param r the bounding box
1253 * @param start the start angle, in degrees
1254 * @param extent the extent, in degrees
1255 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1256 * @throws IllegalArgumentException if type is invalid
1257 * @throws NullPointerException if r is null
1259 public Float(Rectangle2D r
, float start
, float extent
, int type
)
1262 x
= (float) r
.getX();
1263 y
= (float) r
.getY();
1264 width
= (float) r
.getWidth();
1265 height
= (float) r
.getHeight();
1267 this.extent
= (float) extent
;
1271 * Return the x coordinate of the bounding box.
1273 * @return the value of x
1275 public double getX()
1281 * Return the y coordinate of the bounding box.
1283 * @return the value of y
1285 public double getY()
1291 * Return the width of the bounding box.
1293 * @return the value of width
1295 public double getWidth()
1301 * Return the height of the bounding box.
1303 * @return the value of height
1305 public double getHeight()
1311 * Return the start angle of the arc, in degrees.
1313 * @return the value of start
1315 public double getAngleStart()
1321 * Return the extent of the arc, in degrees.
1323 * @return the value of extent
1325 public double getAngleExtent()
1331 * Tests if the arc contains points.
1333 * @return true if the arc has no interior
1335 public boolean isEmpty()
1337 return width
<= 0 || height
<= 0;
1341 * Sets the arc to the given dimensions.
1343 * @param x the x coordinate
1344 * @param y the y coordinate
1345 * @param w the width
1346 * @param h the height
1347 * @param start the start angle, in degrees
1348 * @param extent the extent, in degrees
1349 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1350 * @throws IllegalArgumentException if type is invalid
1352 public void setArc(double x
, double y
, double w
, double h
, double start
,
1353 double extent
, int type
)
1359 this.start
= (float) start
;
1360 this.extent
= (float) extent
;
1365 * Sets the start angle of the arc.
1367 * @param start the new start angle
1369 public void setAngleStart(double start
)
1371 this.start
= (float) start
;
1375 * Sets the extent angle of the arc.
1377 * @param extent the new extent angle
1379 public void setAngleExtent(double extent
)
1381 this.extent
= (float) extent
;
1385 * Creates a tight bounding box given dimensions that more precise than
1386 * the bounding box of the ellipse.
1388 * @param x the x coordinate
1389 * @param y the y coordinate
1390 * @param w the width
1391 * @param h the height
1393 protected Rectangle2D
makeBounds(double x
, double y
, double w
, double h
)
1395 return new Rectangle2D
.Float((float) x
, (float) y
, (float) w
, (float) h
);