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., 51 Franklin Street, Fifth Floor, 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. This is package-private to avoid an
81 * Create a new arc, with the specified closure type.
83 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}.
84 * @throws IllegalArgumentException if type is invalid
86 protected Arc2D(int type)
88 if (type < OPEN || type > PIE)
89 throw new IllegalArgumentException();
94 * Get the starting angle of the arc in degrees.
96 * @return the starting angle
97 * @see #setAngleStart(double)
99 public abstract double getAngleStart();
102 * Get the extent angle of the arc in degrees.
104 * @return the extent angle
105 * @see #setAngleExtent(double)
107 public abstract double getAngleExtent();
110 * Return the closure type of the arc.
112 * @return the closure type
116 * @see #setArcType(int)
118 public int getArcType()
124 * Returns the starting point of the arc.
126 * @return the start point
128 public Point2D getStartPoint()
130 double angle = Math.toRadians(getAngleStart());
131 double rx = getWidth() / 2;
132 double ry = getHeight() / 2;
133 double x = getX() + rx + rx * Math.cos(angle);
134 double y = getY() + ry - ry * Math.sin(angle);
135 return new Point2D.Double(x, y);
139 * Returns the ending point of the arc.
141 * @return the end point
143 public Point2D getEndPoint()
145 double angle = Math.toRadians(getAngleStart() + getAngleExtent());
146 double rx = getWidth() / 2;
147 double ry = getHeight() / 2;
148 double x = getX() + rx + rx * Math.cos(angle);
149 double y = getY() + ry - ry * Math.sin(angle);
150 return new Point2D.Double(x, y);
154 * Set the parameters of the arc. The angles are in degrees, and a positive
155 * extent sweeps counterclockwise (from the positive x-axis to the negative
158 * @param x the new x coordinate of the upper left of the bounding box
159 * @param y the new y coordinate of the upper left of the bounding box
160 * @param w the new width of the bounding box
161 * @param h the new height of the bounding box
162 * @param start the start angle, in degrees
163 * @param extent the arc extent, in degrees
164 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
165 * @throws IllegalArgumentException if type is invalid
167 public abstract void setArc(double x, double y, double w, double h,
168 double start, double extent, int type);
171 * Set the parameters of the arc. The angles are in degrees, and a positive
172 * extent sweeps counterclockwise (from the positive x-axis to the negative
175 * @param p the upper left point of the bounding box
176 * @param d the dimensions of the bounding box
177 * @param start the start angle, in degrees
178 * @param extent the arc extent, in degrees
179 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
180 * @throws IllegalArgumentException if type is invalid
181 * @throws NullPointerException if p or d is null
183 public void setArc(Point2D p, Dimension2D d, double start, double extent,
186 setArc(p.getX(), p.getY(), d.getWidth(), d.getHeight(), start, extent, type);
190 * Set the parameters of the arc. The angles are in degrees, and a positive
191 * extent sweeps counterclockwise (from the positive x-axis to the negative
194 * @param r the new bounding box
195 * @param start the start angle, in degrees
196 * @param extent the arc extent, in degrees
197 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
198 * @throws IllegalArgumentException if type is invalid
199 * @throws NullPointerException if r is null
201 public void setArc(Rectangle2D r, double start, double extent, int type)
203 setArc(r.getX(), r.getY(), r.getWidth(), r.getHeight(), start, extent, type);
207 * Set the parameters of the arc from the given one.
209 * @param a the arc to copy
210 * @throws NullPointerException if a is null
212 public void setArc(Arc2D a)
214 setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(), a.getAngleStart(),
215 a.getAngleExtent(), a.getArcType());
219 * Set the parameters of the arc. The angles are in degrees, and a positive
220 * extent sweeps counterclockwise (from the positive x-axis to the negative
221 * y-axis). This controls the center point and radius, so the arc will be
224 * @param x the x coordinate of the center of the circle
225 * @param y the y coordinate of the center of the circle
226 * @param r the radius of the circle
227 * @param start the start angle, in degrees
228 * @param extent the arc extent, in degrees
229 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
230 * @throws IllegalArgumentException if type is invalid
232 public void setArcByCenter(double x, double y, double r, double start,
233 double extent, int type)
235 setArc(x - r, y - r, r + r, r + r, start, extent, type);
239 * Sets the parameters of the arc by finding the tangents of two lines, and
240 * using the specified radius. The arc will be circular, will begin on the
241 * tangent point of the line extending from p1 to p2, and will end on the
242 * tangent point of the line extending from p2 to p3.
244 * XXX What happens if the points are colinear, or the radius negative?
246 * @param p1 the first point
247 * @param p2 the tangent line intersection point
248 * @param p3 the third point
249 * @param r the radius of the arc
250 * @throws NullPointerException if any point is null
252 public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, double r)
254 if ((p2.getX() - p1.getX()) * (p3.getY() - p1.getY())
255 - (p3.getX() - p1.getX()) * (p2.getY() - p1.getY()) > 0)
262 // normalized tangent vectors
263 double dx1 = (p1.getX() - p2.getX()) / p1.distance(p2);
264 double dy1 = (p1.getY() - p2.getY()) / p1.distance(p2);
265 double dx2 = (p2.getX() - p3.getX()) / p3.distance(p2);
266 double dy2 = (p2.getY() - p3.getY()) / p3.distance(p2);
267 double theta1 = Math.atan2(dx1, dy1);
268 double theta2 = Math.atan2(dx2, dy2);
270 double dx = r * Math.cos(theta2) - r * Math.cos(theta1);
271 double dy = -r * Math.sin(theta2) + r * Math.sin(theta1);
274 theta1 += 2 * Math.PI;
276 theta2 += 2 * Math.PI;
278 theta2 += 2 * Math.PI;
280 // Vectors of the lines, not normalized, note we change
281 // the direction of line 2.
282 dx1 = p1.getX() - p2.getX();
283 dy1 = p1.getY() - p2.getY();
284 dx2 = p3.getX() - p2.getX();
285 dy2 = p3.getY() - p2.getY();
287 // Calculate the tangent point to the second line
288 double t2 = -(dx1 * dy - dy1 * dx) / (dx2 * dy1 - dx1 * dy2);
289 double x2 = t2 * (p3.getX() - p2.getX()) + p2.getX();
290 double y2 = t2 * (p3.getY() - p2.getY()) + p2.getY();
292 // calculate the center point
293 double x = x2 - r * Math.cos(theta2);
294 double y = y2 + r * Math.sin(theta2);
296 setArc(x - r, y - r, 2 * r, 2 * r, Math.toDegrees(theta1),
297 Math.toDegrees(theta2 - theta1), getArcType());
301 * Set the start, in degrees.
303 * @param start the new start angle
304 * @see #getAngleStart()
306 public abstract void setAngleStart(double start);
309 * Set the extent, in degrees.
311 * @param extent the new extent angle
312 * @see #getAngleExtent()
314 public abstract void setAngleExtent(double extent);
317 * Sets the starting angle to the angle of the given point relative to
318 * the center of the arc. The extent remains constant; in other words,
319 * this rotates the arc.
321 * @param p the new start point
322 * @throws NullPointerException if p is null
323 * @see #getStartPoint()
324 * @see #getAngleStart()
326 public void setAngleStart(Point2D p)
329 double x = p.getX() - (getX() + getWidth() / 2);
330 double y = p.getY() - (getY() + getHeight() / 2);
331 setAngleStart(Math.toDegrees(Math.atan2(-y, x)));
335 * Sets the starting and extent angles to those of the given points
336 * relative to the center of the arc. The arc will be non-empty, and will
337 * extend counterclockwise.
339 * @param x1 the first x coordinate
340 * @param y1 the first y coordinate
341 * @param x2 the second x coordinate
342 * @param y2 the second y coordinate
343 * @see #setAngleStart(Point2D)
345 public void setAngles(double x1, double y1, double x2, double y2)
347 // Normalize the points.
350 double mw = getWidth();
351 double mh = getHeight();
352 x1 = x1 - (mx + mw / 2);
353 y1 = y1 - (my + mh / 2);
354 x2 = x2 - (mx + mw / 2);
355 y2 = y2 - (my + mh / 2);
356 double start = Math.toDegrees(Math.atan2(-y1, x1));
357 double extent = Math.toDegrees(Math.atan2(-y2, x2)) - start;
360 setAngleStart(start);
361 setAngleExtent(extent);
365 * Sets the starting and extent angles to those of the given points
366 * relative to the center of the arc. The arc will be non-empty, and will
367 * extend counterclockwise.
369 * @param p1 the first point
370 * @param p2 the second point
371 * @throws NullPointerException if either point is null
372 * @see #setAngleStart(Point2D)
374 public void setAngles(Point2D p1, Point2D p2)
376 setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY());
380 * Set the closure type of this arc.
382 * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
383 * @throws IllegalArgumentException if type is invalid
386 public void setArcType(int type)
388 if (type < OPEN || type > PIE)
389 throw new IllegalArgumentException();
394 * Sets the location and bounds of the ellipse of which this arc is a part.
396 * @param x the new x coordinate
397 * @param y the new y coordinate
398 * @param w the new width
399 * @param h the new height
402 public void setFrame(double x, double y, double w, double h)
404 setArc(x, y, w, h, getAngleStart(), getAngleExtent(), type);
408 * Gets the bounds of the arc. This is much tighter than
409 * <code>getBounds</code>, as it takes into consideration the start and
410 * end angles, and the center point of a pie wedge, rather than just the
413 * @return the bounds of the arc
416 public Rectangle2D getBounds2D()
418 double extent = getAngleExtent();
419 if (Math.abs(extent) >= 360)
420 return makeBounds(getX(), getY(), getWidth(), getHeight());
422 // Find the minimal bounding box. This determined by its extrema,
423 // which are the center, the endpoints of the arc, and any local
424 // maximum contained by the arc.
425 double rX = getWidth() / 2;
426 double rY = getHeight() / 2;
427 double centerX = getX() + rX;
428 double centerY = getY() + rY;
430 Point2D p1 = getStartPoint();
431 Rectangle2D result = makeBounds(p1.getX(), p1.getY(), 0, 0);
432 result.add(getEndPoint());
435 result.add(centerX, centerY);
436 if (containsAngle(0))
437 result.add(centerX + rX, centerY);
438 if (containsAngle(90))
439 result.add(centerX, centerY - rY);
440 if (containsAngle(180))
441 result.add(centerX - rX, centerY);
442 if (containsAngle(270))
443 result.add(centerX, centerY + rY);
449 * Construct a bounding box in a precision appropriate for the subclass.
451 * @param x the x coordinate
452 * @param y the y coordinate
454 * @param h the height
455 * @return the rectangle for use in getBounds2D
457 protected abstract Rectangle2D makeBounds(double x, double y, double w,
461 * Tests if the given angle, in degrees, is included in the arc.
462 * All angles are normalized to be between 0 and 360 degrees.
464 * @param a the angle to test
465 * @return true if it is contained
467 public boolean containsAngle(double a)
469 double start = getAngleStart();
470 double extent = getAngleExtent();
471 double end = start + extent;
476 if (extent >= 360 || extent <= -360)
497 return a >= start && a < end; // starting angle included, ending angle not
501 * Determines if the arc contains the given point. If the bounding box
502 * is empty, then this will return false.
504 * The area considered 'inside' an arc of type OPEN is the same as the
505 * area inside an equivalent filled CHORD-type arc. The area considered
506 * 'inside' a CHORD-type arc is the same as the filled area.
508 * @param x the x coordinate to test
509 * @param y the y coordinate to test
510 * @return true if the point is inside the arc
512 public boolean contains(double x, double y)
514 double w = getWidth();
515 double h = getHeight();
516 double extent = getAngleExtent();
517 if (w <= 0 || h <= 0 || extent == 0)
520 double mx = getX() + w / 2;
521 double my = getY() + h / 2;
522 double dx = (x - mx) * 2 / w;
523 double dy = (y - my) * 2 / h;
524 if ((dx * dx + dy * dy) >= 1.0)
527 double angle = Math.toDegrees(Math.atan2(-dy, dx));
528 if (getArcType() == PIE)
529 return containsAngle(angle);
531 double a1 = Math.toRadians(getAngleStart());
532 double a2 = Math.toRadians(getAngleStart() + extent);
533 double x1 = mx + getWidth() * Math.cos(a1) / 2;
534 double y1 = my - getHeight() * Math.sin(a1) / 2;
535 double x2 = mx + getWidth() * Math.cos(a2) / 2;
536 double y2 = my - getHeight() * Math.sin(a2) / 2;
537 double sgn = ((x2 - x1) * (my - y1) - (mx - x1) * (y2 - y1)) * ((x2 - x1) * (y
538 - y1) - (x - x1) * (y2 - y1));
540 if (Math.abs(extent) > 180)
542 if (containsAngle(angle))
548 if (! containsAngle(angle))
555 * Tests if a given rectangle intersects the area of the arc.
557 * For a definition of the 'inside' area, see the contains() method.
558 * @see #contains(double, double)
560 * @param x the x coordinate of the rectangle
561 * @param y the y coordinate of the rectangle
562 * @param w the width of the rectangle
563 * @param h the height of the rectangle
564 * @return true if the two shapes share common points
566 public boolean intersects(double x, double y, double w, double h)
568 double extent = getAngleExtent();
572 if (contains(x, y) || contains(x, y + h) || contains(x + w, y)
573 || contains(x + w, y + h))
576 Rectangle2D rect = new Rectangle2D.Double(x, y, w, h);
578 double a = getWidth() / 2.0;
579 double b = getHeight() / 2.0;
581 double mx = getX() + a;
582 double my = getY() + b;
583 double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart()));
584 double y1 = my - b * Math.sin(Math.toRadians(getAngleStart()));
585 double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent));
586 double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent));
588 if (getArcType() != CHORD)
590 // check intersections against the pie radii
591 if (rect.intersectsLine(mx, my, x1, y1))
593 if (rect.intersectsLine(mx, my, x2, y2))
596 else// check the chord
597 if (rect.intersectsLine(x1, y1, x2, y2))
600 // Check the Arc segment against the four edges
603 // Check the Arc segment against the four edges
606 dx = a * Math.sqrt(1 - ((dy * dy) / (b * b)));
607 if (! java.lang.Double.isNaN(dx))
609 if (mx + dx >= x && mx + dx <= x + w
610 && containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
612 if (mx - dx >= x && mx - dx <= x + w
613 && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx))))
617 dx = a * Math.sqrt(1 - ((dy * dy) / (b * b)));
618 if (! java.lang.Double.isNaN(dx))
620 if (mx + dx >= x && mx + dx <= x + w
621 && containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
623 if (mx - dx >= x && mx - dx <= x + w
624 && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx))))
628 dy = b * Math.sqrt(1 - ((dx * dx) / (a * a)));
629 if (! java.lang.Double.isNaN(dy))
631 if (my + dy >= y && my + dy <= y + h
632 && containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
634 if (my - dy >= y && my - dy <= y + h
635 && containsAngle(Math.toDegrees(Math.atan2(dy, dx))))
640 dy = b * Math.sqrt(1 - ((dx * dx) / (a * a)));
641 if (! java.lang.Double.isNaN(dy))
643 if (my + dy >= y && my + dy <= y + h
644 && containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
646 if (my - dy >= y && my - dy <= y + h
647 && containsAngle(Math.toDegrees(Math.atan2(dy, dx))))
651 // Check whether the arc is contained within the box
652 if (rect.contains(mx, my))
659 * Tests if a given rectangle is contained in the area of the arc.
661 * @param x the x coordinate of the rectangle
662 * @param y the y coordinate of the rectangle
663 * @param w the width of the rectangle
664 * @param h the height of the rectangle
665 * @return true if the arc contains the rectangle
667 public boolean contains(double x, double y, double w, double h)
669 double extent = getAngleExtent();
673 if (! (contains(x, y) && contains(x, y + h) && contains(x + w, y)
674 && contains(x + w, y + h)))
677 Rectangle2D rect = new Rectangle2D.Double(x, y, w, h);
679 double a = getWidth() / 2.0;
680 double b = getHeight() / 2.0;
682 double mx = getX() + a;
683 double my = getY() + b;
684 double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart()));
685 double y1 = my - b * Math.sin(Math.toRadians(getAngleStart()));
686 double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent));
687 double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent));
688 if (getArcType() != CHORD)
690 // check intersections against the pie radii
691 if (rect.intersectsLine(mx, my, x1, y1))
694 if (rect.intersectsLine(mx, my, x2, y2))
697 else if (rect.intersectsLine(x1, y1, x2, y2))
703 * Tests if a given rectangle is contained in the area of the arc.
705 * @param r the rectangle
706 * @return true if the arc contains the rectangle
708 public boolean contains(Rectangle2D r)
710 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
714 * Returns an iterator over this arc, with an optional transformation.
715 * This iterator is threadsafe, so future modifications to the arc do not
716 * affect the iteration.
718 * @param at the transformation, or null
719 * @return a path iterator
721 public PathIterator getPathIterator(AffineTransform at)
723 return new ArcIterator(this, at);
727 * This class is used to iterate over an arc. Since ellipses are a subclass
728 * of arcs, this is used by Ellipse2D as well.
730 * @author Eric Blake (ebb9@email.byu.edu)
732 static final class ArcIterator implements PathIterator
734 /** The current iteration. */
737 /** The last iteration. */
738 private final int limit;
740 /** The optional transformation. */
741 private final AffineTransform xform;
743 /** The x coordinate of the bounding box. */
744 private final double x;
746 /** The y coordinate of the bounding box. */
747 private final double y;
749 /** The width of the bounding box. */
750 private final double w;
752 /** The height of the bounding box. */
753 private final double h;
755 /** The start angle, in radians (not degrees). */
756 private final double start;
758 /** The extent angle, in radians (not degrees). */
759 private final double extent;
761 /** The arc closure type. */
762 private final int type;
765 * Construct a new iterator over an arc.
768 * @param xform the transform
770 public ArcIterator(Arc2D a, AffineTransform xform)
777 double start = a.getAngleStart() * (Math.PI / 180);
778 double extent = a.getAngleExtent() * (Math.PI / 180);
783 start = 2 * Math.PI - extent + start;
786 this.extent = extent;
791 else if (extent == 0)
793 else if (extent <= Math.PI / 2.0)
795 else if (extent <= Math.PI)
797 else if (extent <= 3.0 * (Math.PI / 2.0))
804 * Construct a new iterator over an ellipse.
806 * @param e the ellipse
807 * @param xform the transform
809 public ArcIterator(Ellipse2D e, AffineTransform xform)
817 extent = 2 * Math.PI;
819 limit = (w < 0 || h < 0) ? -1 : 5;
823 * Return the winding rule.
825 * @return {@link PathIterator#WIND_NON_ZERO}
827 public int getWindingRule()
829 return WIND_NON_ZERO;
833 * Test if the iteration is complete.
835 * @return true if more segments exist
837 public boolean isDone()
839 return current > limit;
843 * Advance the iterator.
851 * Put the current segment into the array, and return the segment type.
853 * @param coords an array of 6 elements
854 * @return the segment type
855 * @throws NullPointerException if coords is null
856 * @throws ArrayIndexOutOfBoundsException if coords is too small
858 public int currentSegment(float[] coords)
860 double[] double_coords = new double[6];
861 int code = currentSegment(double_coords);
862 for (int i = 0; i < 6; ++i)
863 coords[i] = (float) double_coords[i];
868 * Put the current segment into the array, and return the segment type.
870 * @param coords an array of 6 elements
871 * @return the segment type
872 * @throws NullPointerException if coords is null
873 * @throws ArrayIndexOutOfBoundsException if coords is too small
875 public int currentSegment(double[] coords)
879 double xmid = x + rx;
880 double ymid = y + ry;
883 throw new NoSuchElementException("arc iterator out of bounds");
887 coords[0] = xmid + rx * Math.cos(start);
888 coords[1] = ymid - ry * Math.sin(start);
890 xform.transform(coords, 0, coords, 0, 1);
894 if (type != OPEN && current == limit)
897 if ((current == limit - 1) && (type == PIE))
902 xform.transform(coords, 0, coords, 0, 1);
906 // note that this produces a cubic approximation of the arc segment,
907 // not a true ellipsoid. there's no ellipsoid path segment code,
908 // unfortunately. the cubic approximation looks about right, though.
909 double kappa = (Math.sqrt(2.0) - 1.0) * (4.0 / 3.0);
910 double quad = (Math.PI / 2.0);
912 double curr_begin = start + (current - 1) * quad;
913 double curr_extent = Math.min((start + extent) - curr_begin, quad);
914 double portion_of_a_quadrant = curr_extent / quad;
916 double x0 = xmid + rx * Math.cos(curr_begin);
917 double y0 = ymid - ry * Math.sin(curr_begin);
919 double x1 = xmid + rx * Math.cos(curr_begin + curr_extent);
920 double y1 = ymid - ry * Math.sin(curr_begin + curr_extent);
922 AffineTransform trans = new AffineTransform();
923 double[] cvec = new double[2];
924 double len = kappa * portion_of_a_quadrant;
925 double angle = curr_begin;
927 // in a hypothetical "first quadrant" setting, our first control
928 // vector would be sticking up, from [1,0] to [1,kappa].
930 // let us recall however that in java2d, y coords are upside down
931 // from what one would consider "normal" first quadrant rules, so we
932 // will *subtract* the y value of this control vector from our first
938 trans.transform(cvec, 0, cvec, 0, 1);
939 coords[0] = x0 + cvec[0];
940 coords[1] = y0 - cvec[1];
942 // control vector #2 would, ideally, be sticking out and to the
943 // right, in a first quadrant arc segment. again, subtraction of y.
946 trans.rotate(curr_extent);
947 trans.transform(cvec, 0, cvec, 0, 1);
948 coords[2] = x1 + cvec[0];
949 coords[3] = y1 - cvec[1];
956 xform.transform(coords, 0, coords, 0, 3);
960 } // class ArcIterator
963 * This class implements an arc in double precision.
965 * @author Eric Blake (ebb9@email.byu.edu)
968 public static class Double extends Arc2D
970 /** The x coordinate of the box bounding the ellipse of this arc. */
973 /** The y coordinate of the box bounding the ellipse of this arc. */
976 /** The width of the box bounding the ellipse of this arc. */
979 /** The height of the box bounding the ellipse of this arc. */
980 public double height;
982 /** The start angle of this arc, in degrees. */
985 /** The extent angle of this arc, in degrees. */
986 public double extent;
989 * Create a new, open arc at (0,0) with 0 extent.
997 * Create a new arc of the given type at (0,0) with 0 extent.
999 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1000 * @throws IllegalArgumentException if type is invalid
1002 public Double(int type)
1008 * Create a new arc with the given dimensions.
1010 * @param x the x coordinate
1011 * @param y the y coordinate
1012 * @param w the width
1013 * @param h the height
1014 * @param start the start angle, in degrees
1015 * @param extent the extent, in degrees
1016 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1017 * @throws IllegalArgumentException if type is invalid
1019 public Double(double x, double y, double w, double h, double start,
1020 double extent, int type)
1028 this.extent = extent;
1032 * Create a new arc with the given dimensions.
1034 * @param r the bounding box
1035 * @param start the start angle, in degrees
1036 * @param extent the extent, in degrees
1037 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1038 * @throws IllegalArgumentException if type is invalid
1039 * @throws NullPointerException if r is null
1041 public Double(Rectangle2D r, double start, double extent, int type)
1046 width = r.getWidth();
1047 height = r.getHeight();
1049 this.extent = extent;
1053 * Return the x coordinate of the bounding box.
1055 * @return the value of x
1057 public double getX()
1063 * Return the y coordinate of the bounding box.
1065 * @return the value of y
1067 public double getY()
1073 * Return the width of the bounding box.
1075 * @return the value of width
1077 public double getWidth()
1083 * Return the height of the bounding box.
1085 * @return the value of height
1087 public double getHeight()
1093 * Return the start angle of the arc, in degrees.
1095 * @return the value of start
1097 public double getAngleStart()
1103 * Return the extent of the arc, in degrees.
1105 * @return the value of extent
1107 public double getAngleExtent()
1113 * Tests if the arc contains points.
1115 * @return true if the arc has no interior
1117 public boolean isEmpty()
1119 return width <= 0 || height <= 0;
1123 * Sets the arc to the given dimensions.
1125 * @param x the x coordinate
1126 * @param y the y coordinate
1127 * @param w the width
1128 * @param h the height
1129 * @param start the start angle, in degrees
1130 * @param extent the extent, in degrees
1131 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1132 * @throws IllegalArgumentException if type is invalid
1134 public void setArc(double x, double y, double w, double h, double start,
1135 double extent, int type)
1142 this.extent = extent;
1147 * Sets the start angle of the arc.
1149 * @param start the new start angle
1151 public void setAngleStart(double start)
1157 * Sets the extent angle of the arc.
1159 * @param extent the new extent angle
1161 public void setAngleExtent(double extent)
1163 this.extent = extent;
1167 * Creates a tight bounding box given dimensions that more precise than
1168 * the bounding box of the ellipse.
1170 * @param x the x coordinate
1171 * @param y the y coordinate
1172 * @param w the width
1173 * @param h the height
1175 protected Rectangle2D makeBounds(double x, double y, double w, double h)
1177 return new Rectangle2D.Double(x, y, w, h);
1182 * This class implements an arc in float precision.
1184 * @author Eric Blake (ebb9@email.byu.edu)
1187 public static class Float extends Arc2D
1189 /** The x coordinate of the box bounding the ellipse of this arc. */
1192 /** The y coordinate of the box bounding the ellipse of this arc. */
1195 /** The width of the box bounding the ellipse of this arc. */
1198 /** The height of the box bounding the ellipse of this arc. */
1199 public float height;
1201 /** The start angle of this arc, in degrees. */
1204 /** The extent angle of this arc, in degrees. */
1205 public float extent;
1208 * Create a new, open arc at (0,0) with 0 extent.
1216 * Create a new arc of the given type at (0,0) with 0 extent.
1218 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1219 * @throws IllegalArgumentException if type is invalid
1221 public Float(int type)
1227 * Create a new arc with the given dimensions.
1229 * @param x the x coordinate
1230 * @param y the y coordinate
1231 * @param w the width
1232 * @param h the height
1233 * @param start the start angle, in degrees
1234 * @param extent the extent, in degrees
1235 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1236 * @throws IllegalArgumentException if type is invalid
1238 public Float(float x, float y, float w, float h, float start,
1239 float extent, int type)
1247 this.extent = extent;
1251 * Create a new arc with the given dimensions.
1253 * @param r the bounding box
1254 * @param start the start angle, in degrees
1255 * @param extent the extent, in degrees
1256 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1257 * @throws IllegalArgumentException if type is invalid
1258 * @throws NullPointerException if r is null
1260 public Float(Rectangle2D r, float start, float extent, int type)
1263 x = (float) r.getX();
1264 y = (float) r.getY();
1265 width = (float) r.getWidth();
1266 height = (float) r.getHeight();
1268 this.extent = (float) extent;
1272 * Return the x coordinate of the bounding box.
1274 * @return the value of x
1276 public double getX()
1282 * Return the y coordinate of the bounding box.
1284 * @return the value of y
1286 public double getY()
1292 * Return the width of the bounding box.
1294 * @return the value of width
1296 public double getWidth()
1302 * Return the height of the bounding box.
1304 * @return the value of height
1306 public double getHeight()
1312 * Return the start angle of the arc, in degrees.
1314 * @return the value of start
1316 public double getAngleStart()
1322 * Return the extent of the arc, in degrees.
1324 * @return the value of extent
1326 public double getAngleExtent()
1332 * Tests if the arc contains points.
1334 * @return true if the arc has no interior
1336 public boolean isEmpty()
1338 return width <= 0 || height <= 0;
1342 * Sets the arc to the given dimensions.
1344 * @param x the x coordinate
1345 * @param y the y coordinate
1346 * @param w the width
1347 * @param h the height
1348 * @param start the start angle, in degrees
1349 * @param extent the extent, in degrees
1350 * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1351 * @throws IllegalArgumentException if type is invalid
1353 public void setArc(double x, double y, double w, double h, double start,
1354 double extent, int type)
1360 this.start = (float) start;
1361 this.extent = (float) extent;
1366 * Sets the start angle of the arc.
1368 * @param start the new start angle
1370 public void setAngleStart(double start)
1372 this.start = (float) start;
1376 * Sets the extent angle of the arc.
1378 * @param extent the new extent angle
1380 public void setAngleExtent(double extent)
1382 this.extent = (float) extent;
1386 * Creates a tight bounding box given dimensions that more precise than
1387 * the bounding box of the ellipse.
1389 * @param x the x coordinate
1390 * @param y the y coordinate
1391 * @param w the width
1392 * @param h the height
1394 protected Rectangle2D makeBounds(double x, double y, double w, double h)
1396 return new Rectangle2D.Float((float) x, (float) y, (float) w, (float) h);