Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / java / awt / geom / Arc2D.java
blob5ce3b08e82f4b73c30f17a9ceeecb948f2c1180b
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)
9 any later version.
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
19 02111-1307 USA.
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
24 combination.
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;
43 /**
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)
55 * @since 1.2
57 public abstract class Arc2D extends RectangularShape
59 /**
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;
65 /**
66 * A closed arc with a single segment connecting the endpoints (a chord).
68 public static final int CHORD = 1;
70 /**
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. */
77 private int type;
79 /**
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();
89 this.type = type;
92 /**
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
112 * @see #OPEN
113 * @see #CHORD
114 * @see #PIE
115 * @see #setArcType(int)
117 public int getArcType()
119 return type;
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
155 * y-axis).
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
172 * y-axis).
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,
183 int type)
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
191 * y-axis).
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
221 * circular.
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)
256 Point2D p = p3;
257 p3 = p1;
258 p1 = p;
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);
272 if (theta1 < 0)
273 theta1 += 2 * Math.PI;
274 if (theta2 < 0)
275 theta2 += 2 * Math.PI;
276 if (theta2 < theta1)
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)
327 // Normalize.
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.
347 double mx = getX();
348 double my = getY();
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;
357 if (extent < 0)
358 extent += 360;
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
383 * @see #getArcType()
385 public void setArcType(int type)
387 if (type < OPEN || type > PIE)
388 throw new IllegalArgumentException();
389 this.type = type;
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
399 * @see #getFrame()
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
410 * overall ellipse.
412 * @return the bounds of the arc
413 * @see #getBounds()
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());
433 if (type == PIE)
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);
444 return result;
448 * Construct a bounding box in a precision appropriate for the subclass.
450 * @param x the x coordinate
451 * @param y the y coordinate
452 * @param w the width
453 * @param h the height
454 * @return the rectangle for use in getBounds2D
456 protected abstract Rectangle2D makeBounds(double x, double y, double w,
457 double h);
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;
472 if (extent == 0)
473 return false;
475 if (extent >= 360 || extent <= -360)
476 return true;
478 if (extent < 0)
480 end = start;
481 start += extent;
484 start %= 360;
485 while (start < 0)
486 start += 360;
488 end %= 360;
489 while (end < start)
490 end += 360;
492 a %= 360;
493 while (a < start)
494 a += 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)
517 return false;
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)
524 return false;
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))
542 return true;
543 return sgn > 0;
545 else
547 if (! containsAngle(angle))
548 return false;
549 return sgn < 0;
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();
568 if (extent == 0)
569 return false;
571 if (contains(x, y) || contains(x, y + h) || contains(x + w, y)
572 || contains(x + w, y + h))
573 return true;
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))
591 return true;
592 if (rect.intersectsLine(mx, my, x2, y2))
593 return true;
595 else// check the chord
596 if (rect.intersectsLine(x1, y1, x2, y2))
597 return true;
599 // Check the Arc segment against the four edges
600 double dx;
602 // Check the Arc segment against the four edges
603 double dy;
604 dy = y - my;
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))))
610 return true;
611 if (mx - dx >= x && mx - dx <= x + w
612 && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx))))
613 return true;
615 dy = (y + h) - my;
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))))
621 return true;
622 if (mx - dx >= x && mx - dx <= x + w
623 && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx))))
624 return true;
626 dx = x - mx;
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))))
632 return true;
633 if (my - dy >= y && my - dy <= y + h
634 && containsAngle(Math.toDegrees(Math.atan2(dy, dx))))
635 return true;
638 dx = (x + w) - mx;
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))))
644 return true;
645 if (my - dy >= y && my - dy <= y + h
646 && containsAngle(Math.toDegrees(Math.atan2(dy, dx))))
647 return true;
650 // Check whether the arc is contained within the box
651 if (rect.contains(mx, my))
652 return true;
654 return false;
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();
669 if (extent == 0)
670 return false;
672 if (! (contains(x, y) && contains(x, y + h) && contains(x + w, y)
673 && contains(x + w, y + h)))
674 return false;
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))
691 return false;
693 if (rect.intersectsLine(mx, my, x2, y2))
694 return false;
696 else if (rect.intersectsLine(x1, y1, x2, y2))
697 return false;
698 return true;
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. */
734 private int current;
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.
766 * @param a the arc
767 * @param xform the transform
769 public ArcIterator(Arc2D a, AffineTransform xform)
771 this.xform = xform;
772 x = a.getX();
773 y = a.getY();
774 w = a.getWidth();
775 h = a.getHeight();
776 double start = a.getAngleStart() * (Math.PI / 180);
777 double extent = a.getAngleExtent() * (Math.PI / 180);
779 if (extent < 0)
781 extent = -extent;
782 start = 2 * Math.PI - extent + start;
784 this.start = start;
785 this.extent = extent;
787 type = a.type;
788 if (w < 0 || h < 0)
789 limit = -1;
790 else if (extent == 0)
791 limit = type;
792 else if (extent <= Math.PI / 2.0)
793 limit = type + 1;
794 else if (extent <= Math.PI)
795 limit = type + 2;
796 else if (extent <= 3.0 * (Math.PI / 2.0))
797 limit = type + 3;
798 else
799 limit = type + 4;
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)
810 this.xform = xform;
811 x = e.getX();
812 y = e.getY();
813 w = e.getWidth();
814 h = e.getHeight();
815 start = 0;
816 extent = 2 * Math.PI;
817 type = CHORD;
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.
844 public void next()
846 current++;
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];
863 return code;
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)
876 double rx = w / 2;
877 double ry = h / 2;
878 double xmid = x + rx;
879 double ymid = y + ry;
881 if (current > limit)
882 throw new NoSuchElementException("arc iterator out of bounds");
884 if (current == 0)
886 coords[0] = xmid + rx * Math.cos(start);
887 coords[1] = ymid - ry * Math.sin(start);
888 if (xform != null)
889 xform.transform(coords, 0, coords, 0, 1);
890 return SEG_MOVETO;
893 if (type != OPEN && current == limit)
894 return SEG_CLOSE;
896 if ((current == limit - 1) && (type == PIE))
898 coords[0] = xmid;
899 coords[1] = ymid;
900 if (xform != null)
901 xform.transform(coords, 0, coords, 0, 1);
902 return SEG_LINETO;
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
932 // point.
933 cvec[0] = 0;
934 cvec[1] = len;
935 trans.scale(rx, ry);
936 trans.rotate(angle);
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.
943 cvec[0] = 0;
944 cvec[1] = -len;
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];
950 // end point
951 coords[4] = x1;
952 coords[5] = y1;
954 if (xform != null)
955 xform.transform(coords, 0, coords, 0, 3);
957 return SEG_CUBICTO;
959 } // class ArcIterator
962 * This class implements an arc in double precision.
964 * @author Eric Blake (ebb9@email.byu.edu)
965 * @since 1.2
967 public static class Double extends Arc2D
969 /** The x coordinate of the box bounding the ellipse of this arc. */
970 public double x;
972 /** The y coordinate of the box bounding the ellipse of this arc. */
973 public double y;
975 /** The width of the box bounding the ellipse of this arc. */
976 public double width;
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. */
982 public double start;
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.
990 public Double()
992 super(OPEN);
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)
1003 super(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)
1021 super(type);
1022 this.x = x;
1023 this.y = y;
1024 width = w;
1025 height = h;
1026 this.start = start;
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)
1042 super(type);
1043 x = r.getX();
1044 y = r.getY();
1045 width = r.getWidth();
1046 height = r.getHeight();
1047 this.start = start;
1048 this.extent = extent;
1052 * Return the x coordinate of the bounding box.
1054 * @return the value of x
1056 public double getX()
1058 return x;
1062 * Return the y coordinate of the bounding box.
1064 * @return the value of y
1066 public double getY()
1068 return y;
1072 * Return the width of the bounding box.
1074 * @return the value of width
1076 public double getWidth()
1078 return width;
1082 * Return the height of the bounding box.
1084 * @return the value of height
1086 public double getHeight()
1088 return height;
1092 * Return the start angle of the arc, in degrees.
1094 * @return the value of start
1096 public double getAngleStart()
1098 return start;
1102 * Return the extent of the arc, in degrees.
1104 * @return the value of extent
1106 public double getAngleExtent()
1108 return extent;
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)
1136 this.x = x;
1137 this.y = y;
1138 width = w;
1139 height = h;
1140 this.start = start;
1141 this.extent = extent;
1142 setArcType(type);
1146 * Sets the start angle of the arc.
1148 * @param start the new start angle
1150 public void setAngleStart(double start)
1152 this.start = 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);
1178 } // class Double
1181 * This class implements an arc in float precision.
1183 * @author Eric Blake (ebb9@email.byu.edu)
1184 * @since 1.2
1186 public static class Float extends Arc2D
1188 /** The x coordinate of the box bounding the ellipse of this arc. */
1189 public float x;
1191 /** The y coordinate of the box bounding the ellipse of this arc. */
1192 public float y;
1194 /** The width of the box bounding the ellipse of this arc. */
1195 public float width;
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. */
1201 public float start;
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.
1209 public Float()
1211 super(OPEN);
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)
1222 super(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)
1240 super(type);
1241 this.x = x;
1242 this.y = y;
1243 width = w;
1244 height = h;
1245 this.start = start;
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)
1261 super(type);
1262 x = (float) r.getX();
1263 y = (float) r.getY();
1264 width = (float) r.getWidth();
1265 height = (float) r.getHeight();
1266 this.start = start;
1267 this.extent = (float) extent;
1271 * Return the x coordinate of the bounding box.
1273 * @return the value of x
1275 public double getX()
1277 return x;
1281 * Return the y coordinate of the bounding box.
1283 * @return the value of y
1285 public double getY()
1287 return y;
1291 * Return the width of the bounding box.
1293 * @return the value of width
1295 public double getWidth()
1297 return width;
1301 * Return the height of the bounding box.
1303 * @return the value of height
1305 public double getHeight()
1307 return height;
1311 * Return the start angle of the arc, in degrees.
1313 * @return the value of start
1315 public double getAngleStart()
1317 return start;
1321 * Return the extent of the arc, in degrees.
1323 * @return the value of extent
1325 public double getAngleExtent()
1327 return extent;
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)
1355 this.x = (float) x;
1356 this.y = (float) y;
1357 width = (float) w;
1358 height = (float) h;
1359 this.start = (float) start;
1360 this.extent = (float) extent;
1361 setArcType(type);
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);
1397 } // class Float
1398 } // class Arc2D