diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/java/awt/geom/Arc2D.java | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/java/awt/geom/Arc2D.java')
-rw-r--r-- | libjava/classpath/java/awt/geom/Arc2D.java | 1413 |
1 files changed, 1413 insertions, 0 deletions
diff --git a/libjava/classpath/java/awt/geom/Arc2D.java b/libjava/classpath/java/awt/geom/Arc2D.java new file mode 100644 index 000000000..928c5cfc8 --- /dev/null +++ b/libjava/classpath/java/awt/geom/Arc2D.java @@ -0,0 +1,1413 @@ +/* Arc2D.java -- represents an arc in 2-D space + Copyright (C) 2002, 2003, 2004 Free Software Foundation + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package java.awt.geom; + +import java.util.NoSuchElementException; + + +/** + * This class represents all arcs (segments of an ellipse in 2-D space). The + * arcs are defined by starting angle and extent (arc length) in degrees, as + * opposed to radians (like the rest of Java), and can be open, chorded, or + * wedge shaped. The angles are skewed according to the ellipse, so that 45 + * degrees always points to the upper right corner (positive x, negative y) + * of the bounding rectangle. A positive extent draws a counterclockwise arc, + * and while the angle can be any value, the path iterator only traverses the + * first 360 degrees. Storage is up to the subclasses. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @author Sven de Marothy (sven@physto.se) + * @since 1.2 + */ +public abstract class Arc2D extends RectangularShape +{ + /** + * An open arc, with no segment connecting the endpoints. This type of + * arc still contains the same points as a chorded version. + */ + public static final int OPEN = 0; + + /** + * A closed arc with a single segment connecting the endpoints (a chord). + */ + public static final int CHORD = 1; + + /** + * A closed arc with two segments, one from each endpoint, meeting at the + * center of the ellipse. + */ + public static final int PIE = 2; + + /** The closure type of this arc. This is package-private to avoid an + * accessor method. */ + int type; + + /** + * Create a new arc, with the specified closure type. + * + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}. + * @throws IllegalArgumentException if type is invalid + */ + protected Arc2D(int type) + { + if (type < OPEN || type > PIE) + throw new IllegalArgumentException(); + this.type = type; + } + + /** + * Get the starting angle of the arc in degrees. + * + * @return the starting angle + * @see #setAngleStart(double) + */ + public abstract double getAngleStart(); + + /** + * Get the extent angle of the arc in degrees. + * + * @return the extent angle + * @see #setAngleExtent(double) + */ + public abstract double getAngleExtent(); + + /** + * Return the closure type of the arc. + * + * @return the closure type + * @see #OPEN + * @see #CHORD + * @see #PIE + * @see #setArcType(int) + */ + public int getArcType() + { + return type; + } + + /** + * Returns the starting point of the arc. + * + * @return the start point + */ + public Point2D getStartPoint() + { + double angle = Math.toRadians(getAngleStart()); + double rx = getWidth() / 2; + double ry = getHeight() / 2; + double x = getX() + rx + rx * Math.cos(angle); + double y = getY() + ry - ry * Math.sin(angle); + return new Point2D.Double(x, y); + } + + /** + * Returns the ending point of the arc. + * + * @return the end point + */ + public Point2D getEndPoint() + { + double angle = Math.toRadians(getAngleStart() + getAngleExtent()); + double rx = getWidth() / 2; + double ry = getHeight() / 2; + double x = getX() + rx + rx * Math.cos(angle); + double y = getY() + ry - ry * Math.sin(angle); + return new Point2D.Double(x, y); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param x the new x coordinate of the upper left of the bounding box + * @param y the new y coordinate of the upper left of the bounding box + * @param w the new width of the bounding box + * @param h the new height of the bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public abstract void setArc(double x, double y, double w, double h, + double start, double extent, int type); + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param p the upper left point of the bounding box + * @param d the dimensions of the bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if p or d is null + */ + public void setArc(Point2D p, Dimension2D d, double start, double extent, + int type) + { + setArc(p.getX(), p.getY(), d.getWidth(), d.getHeight(), start, extent, type); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). + * + * @param r the new bounding box + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public void setArc(Rectangle2D r, double start, double extent, int type) + { + setArc(r.getX(), r.getY(), r.getWidth(), r.getHeight(), start, extent, type); + } + + /** + * Set the parameters of the arc from the given one. + * + * @param a the arc to copy + * @throws NullPointerException if a is null + */ + public void setArc(Arc2D a) + { + setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(), a.getAngleStart(), + a.getAngleExtent(), a.getArcType()); + } + + /** + * Set the parameters of the arc. The angles are in degrees, and a positive + * extent sweeps counterclockwise (from the positive x-axis to the negative + * y-axis). This controls the center point and radius, so the arc will be + * circular. + * + * @param x the x coordinate of the center of the circle + * @param y the y coordinate of the center of the circle + * @param r the radius of the circle + * @param start the start angle, in degrees + * @param extent the arc extent, in degrees + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArcByCenter(double x, double y, double r, double start, + double extent, int type) + { + setArc(x - r, y - r, r + r, r + r, start, extent, type); + } + + /** + * Sets the parameters of the arc by finding the tangents of two lines, and + * using the specified radius. The arc will be circular, will begin on the + * tangent point of the line extending from p1 to p2, and will end on the + * tangent point of the line extending from p2 to p3. + * + * XXX What happens if the points are colinear, or the radius negative? + * + * @param p1 the first point + * @param p2 the tangent line intersection point + * @param p3 the third point + * @param r the radius of the arc + * @throws NullPointerException if any point is null + */ + public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, double r) + { + if ((p2.getX() - p1.getX()) * (p3.getY() - p1.getY()) + - (p3.getX() - p1.getX()) * (p2.getY() - p1.getY()) > 0) + { + Point2D p = p3; + p3 = p1; + p1 = p; + } + + // normalized tangent vectors + double dx1 = (p1.getX() - p2.getX()) / p1.distance(p2); + double dy1 = (p1.getY() - p2.getY()) / p1.distance(p2); + double dx2 = (p2.getX() - p3.getX()) / p3.distance(p2); + double dy2 = (p2.getY() - p3.getY()) / p3.distance(p2); + double theta1 = Math.atan2(dx1, dy1); + double theta2 = Math.atan2(dx2, dy2); + + double dx = r * Math.cos(theta2) - r * Math.cos(theta1); + double dy = -r * Math.sin(theta2) + r * Math.sin(theta1); + + if (theta1 < 0) + theta1 += 2 * Math.PI; + if (theta2 < 0) + theta2 += 2 * Math.PI; + if (theta2 < theta1) + theta2 += 2 * Math.PI; + + // Vectors of the lines, not normalized, note we change + // the direction of line 2. + dx1 = p1.getX() - p2.getX(); + dy1 = p1.getY() - p2.getY(); + dx2 = p3.getX() - p2.getX(); + dy2 = p3.getY() - p2.getY(); + + // Calculate the tangent point to the second line + double t2 = -(dx1 * dy - dy1 * dx) / (dx2 * dy1 - dx1 * dy2); + double x2 = t2 * (p3.getX() - p2.getX()) + p2.getX(); + double y2 = t2 * (p3.getY() - p2.getY()) + p2.getY(); + + // calculate the center point + double x = x2 - r * Math.cos(theta2); + double y = y2 + r * Math.sin(theta2); + + setArc(x - r, y - r, 2 * r, 2 * r, Math.toDegrees(theta1), + Math.toDegrees(theta2 - theta1), getArcType()); + } + + /** + * Set the start, in degrees. + * + * @param start the new start angle + * @see #getAngleStart() + */ + public abstract void setAngleStart(double start); + + /** + * Set the extent, in degrees. + * + * @param extent the new extent angle + * @see #getAngleExtent() + */ + public abstract void setAngleExtent(double extent); + + /** + * Sets the starting angle to the angle of the given point relative to + * the center of the arc. The extent remains constant; in other words, + * this rotates the arc. + * + * @param p the new start point + * @throws NullPointerException if p is null + * @see #getStartPoint() + * @see #getAngleStart() + */ + public void setAngleStart(Point2D p) + { + // Normalize. + double x = p.getX() - (getX() + getWidth() / 2); + double y = p.getY() - (getY() + getHeight() / 2); + setAngleStart(Math.toDegrees(Math.atan2(-y, x))); + } + + /** + * Sets the starting and extent angles to those of the given points + * relative to the center of the arc. The arc will be non-empty, and will + * extend counterclockwise. + * + * @param x1 the first x coordinate + * @param y1 the first y coordinate + * @param x2 the second x coordinate + * @param y2 the second y coordinate + * @see #setAngleStart(Point2D) + */ + public void setAngles(double x1, double y1, double x2, double y2) + { + // Normalize the points. + double mx = getX(); + double my = getY(); + double mw = getWidth(); + double mh = getHeight(); + x1 = x1 - (mx + mw / 2); + y1 = y1 - (my + mh / 2); + x2 = x2 - (mx + mw / 2); + y2 = y2 - (my + mh / 2); + double start = Math.toDegrees(Math.atan2(-y1, x1)); + double extent = Math.toDegrees(Math.atan2(-y2, x2)) - start; + if (extent < 0) + extent += 360; + setAngleStart(start); + setAngleExtent(extent); + } + + /** + * Sets the starting and extent angles to those of the given points + * relative to the center of the arc. The arc will be non-empty, and will + * extend counterclockwise. + * + * @param p1 the first point + * @param p2 the second point + * @throws NullPointerException if either point is null + * @see #setAngleStart(Point2D) + */ + public void setAngles(Point2D p1, Point2D p2) + { + setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY()); + } + + /** + * Set the closure type of this arc. + * + * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @see #getArcType() + */ + public void setArcType(int type) + { + if (type < OPEN || type > PIE) + throw new IllegalArgumentException(); + this.type = type; + } + + /** + * Sets the location and bounds of the ellipse of which this arc is a part. + * + * @param x the new x coordinate + * @param y the new y coordinate + * @param w the new width + * @param h the new height + * @see #getFrame() + */ + public void setFrame(double x, double y, double w, double h) + { + setArc(x, y, w, h, getAngleStart(), getAngleExtent(), type); + } + + /** + * Gets the bounds of the arc. This is much tighter than + * <code>getBounds</code>, as it takes into consideration the start and + * end angles, and the center point of a pie wedge, rather than just the + * overall ellipse. + * + * @return the bounds of the arc + * @see #getBounds() + */ + public Rectangle2D getBounds2D() + { + double extent = getAngleExtent(); + if (Math.abs(extent) >= 360) + return makeBounds(getX(), getY(), getWidth(), getHeight()); + + // Find the minimal bounding box. This determined by its extrema, + // which are the center, the endpoints of the arc, and any local + // maximum contained by the arc. + double rX = getWidth() / 2; + double rY = getHeight() / 2; + double centerX = getX() + rX; + double centerY = getY() + rY; + + Point2D p1 = getStartPoint(); + Rectangle2D result = makeBounds(p1.getX(), p1.getY(), 0, 0); + result.add(getEndPoint()); + + if (type == PIE) + result.add(centerX, centerY); + if (containsAngle(0)) + result.add(centerX + rX, centerY); + if (containsAngle(90)) + result.add(centerX, centerY - rY); + if (containsAngle(180)) + result.add(centerX - rX, centerY); + if (containsAngle(270)) + result.add(centerX, centerY + rY); + + return result; + } + + /** + * Construct a bounding box in a precision appropriate for the subclass. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @return the rectangle for use in getBounds2D + */ + protected abstract Rectangle2D makeBounds(double x, double y, double w, + double h); + + /** + * Tests if the given angle, in degrees, is included in the arc. + * All angles are normalized to be between 0 and 360 degrees. + * + * @param a the angle to test + * @return true if it is contained + */ + public boolean containsAngle(double a) + { + double start = getAngleStart(); + double extent = getAngleExtent(); + double end = start + extent; + + if (extent == 0) + return false; + + if (extent >= 360 || extent <= -360) + return true; + + if (extent < 0) + { + end = start; + start += extent; + } + + start %= 360; + while (start < 0) + start += 360; + + end %= 360; + while (end < start) + end += 360; + + a %= 360; + while (a < start) + a += 360; + + return a >= start && a < end; // starting angle included, ending angle not + } + + /** + * Determines if the arc contains the given point. If the bounding box + * is empty, then this will return false. + * + * The area considered 'inside' an arc of type OPEN is the same as the + * area inside an equivalent filled CHORD-type arc. The area considered + * 'inside' a CHORD-type arc is the same as the filled area. + * + * @param x the x coordinate to test + * @param y the y coordinate to test + * @return true if the point is inside the arc + */ + public boolean contains(double x, double y) + { + double w = getWidth(); + double h = getHeight(); + double extent = getAngleExtent(); + if (w <= 0 || h <= 0 || extent == 0) + return false; + + double mx = getX() + w / 2; + double my = getY() + h / 2; + double dx = (x - mx) * 2 / w; + double dy = (y - my) * 2 / h; + if ((dx * dx + dy * dy) >= 1.0) + return false; + + double angle = Math.toDegrees(Math.atan2(-dy, dx)); + if (getArcType() == PIE) + return containsAngle(angle); + + double a1 = Math.toRadians(getAngleStart()); + double a2 = Math.toRadians(getAngleStart() + extent); + double x1 = mx + getWidth() * Math.cos(a1) / 2; + double y1 = my - getHeight() * Math.sin(a1) / 2; + double x2 = mx + getWidth() * Math.cos(a2) / 2; + double y2 = my - getHeight() * Math.sin(a2) / 2; + double sgn = ((x2 - x1) * (my - y1) - (mx - x1) * (y2 - y1)) * ((x2 - x1) * (y + - y1) - (x - x1) * (y2 - y1)); + + if (Math.abs(extent) > 180) + { + if (containsAngle(angle)) + return true; + return sgn > 0; + } + else + { + if (! containsAngle(angle)) + return false; + return sgn < 0; + } + } + + /** + * Tests if a given rectangle intersects the area of the arc. + * + * For a definition of the 'inside' area, see the contains() method. + * @see #contains(double, double) + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return true if the two shapes share common points + */ + public boolean intersects(double x, double y, double w, double h) + { + double extent = getAngleExtent(); + if (extent == 0) + return false; + + if (contains(x, y) || contains(x, y + h) || contains(x + w, y) + || contains(x + w, y + h)) + return true; + + Rectangle2D rect = new Rectangle2D.Double(x, y, w, h); + + double a = getWidth() / 2.0; + double b = getHeight() / 2.0; + + double mx = getX() + a; + double my = getY() + b; + double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart())); + double y1 = my - b * Math.sin(Math.toRadians(getAngleStart())); + double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent)); + double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent)); + + if (getArcType() != CHORD) + { + // check intersections against the pie radii + if (rect.intersectsLine(mx, my, x1, y1)) + return true; + if (rect.intersectsLine(mx, my, x2, y2)) + return true; + } + else// check the chord + if (rect.intersectsLine(x1, y1, x2, y2)) + return true; + + // Check the Arc segment against the four edges + double dx; + + // Check the Arc segment against the four edges + double dy; + dy = y - my; + dx = a * Math.sqrt(1 - ((dy * dy) / (b * b))); + if (! java.lang.Double.isNaN(dx)) + { + if (mx + dx >= x && mx + dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (mx - dx >= x && mx - dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx)))) + return true; + } + dy = (y + h) - my; + dx = a * Math.sqrt(1 - ((dy * dy) / (b * b))); + if (! java.lang.Double.isNaN(dx)) + { + if (mx + dx >= x && mx + dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (mx - dx >= x && mx - dx <= x + w + && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx)))) + return true; + } + dx = x - mx; + dy = b * Math.sqrt(1 - ((dx * dx) / (a * a))); + if (! java.lang.Double.isNaN(dy)) + { + if (my + dy >= y && my + dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (my - dy >= y && my - dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(dy, dx)))) + return true; + } + + dx = (x + w) - mx; + dy = b * Math.sqrt(1 - ((dx * dx) / (a * a))); + if (! java.lang.Double.isNaN(dy)) + { + if (my + dy >= y && my + dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(-dy, dx)))) + return true; + if (my - dy >= y && my - dy <= y + h + && containsAngle(Math.toDegrees(Math.atan2(dy, dx)))) + return true; + } + + // Check whether the arc is contained within the box + if (rect.contains(mx, my)) + return true; + + return false; + } + + /** + * Tests if a given rectangle is contained in the area of the arc. + * + * @param x the x coordinate of the rectangle + * @param y the y coordinate of the rectangle + * @param w the width of the rectangle + * @param h the height of the rectangle + * @return true if the arc contains the rectangle + */ + public boolean contains(double x, double y, double w, double h) + { + double extent = getAngleExtent(); + if (extent == 0) + return false; + + if (! (contains(x, y) && contains(x, y + h) && contains(x + w, y) + && contains(x + w, y + h))) + return false; + + Rectangle2D rect = new Rectangle2D.Double(x, y, w, h); + + double a = getWidth() / 2.0; + double b = getHeight() / 2.0; + + double mx = getX() + a; + double my = getY() + b; + double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart())); + double y1 = my - b * Math.sin(Math.toRadians(getAngleStart())); + double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent)); + double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent)); + if (getArcType() != CHORD) + { + // check intersections against the pie radii + if (rect.intersectsLine(mx, my, x1, y1)) + return false; + + if (rect.intersectsLine(mx, my, x2, y2)) + return false; + } + else if (rect.intersectsLine(x1, y1, x2, y2)) + return false; + return true; + } + + /** + * Tests if a given rectangle is contained in the area of the arc. + * + * @param r the rectangle + * @return true if the arc contains the rectangle + */ + public boolean contains(Rectangle2D r) + { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Returns an iterator over this arc, with an optional transformation. + * This iterator is threadsafe, so future modifications to the arc do not + * affect the iteration. + * + * @param at the transformation, or null + * @return a path iterator + */ + public PathIterator getPathIterator(AffineTransform at) + { + return new ArcIterator(this, at); + } + + /** + * This class is used to iterate over an arc. Since ellipses are a subclass + * of arcs, this is used by Ellipse2D as well. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static final class ArcIterator implements PathIterator + { + /** The current iteration. */ + private int current; + + /** The last iteration. */ + private final int limit; + + /** The optional transformation. */ + private final AffineTransform xform; + + /** The x coordinate of the bounding box. */ + private final double x; + + /** The y coordinate of the bounding box. */ + private final double y; + + /** The width of the bounding box. */ + private final double w; + + /** The height of the bounding box. */ + private final double h; + + /** The start angle, in radians (not degrees). */ + private final double start; + + /** The extent angle, in radians (not degrees). */ + private final double extent; + + /** The arc closure type. */ + private final int type; + + /** + * Construct a new iterator over an arc. + * + * @param a the arc + * @param xform the transform + */ + public ArcIterator(Arc2D a, AffineTransform xform) + { + this.xform = xform; + x = a.getX(); + y = a.getY(); + w = a.getWidth(); + h = a.getHeight(); + double start = Math.toRadians(a.getAngleStart()); + double extent = Math.toRadians(a.getAngleExtent()); + + this.start = start; + this.extent = extent; + + type = a.type; + if (w < 0 || h < 0) + limit = -1; + else if (extent == 0) + limit = type; + else if (Math.abs(extent) <= Math.PI / 2.0) + limit = type + 1; + else if (Math.abs(extent) <= Math.PI) + limit = type + 2; + else if (Math.abs(extent) <= 3.0 * (Math.PI / 2.0)) + limit = type + 3; + else + limit = type + 4; + } + + /** + * Construct a new iterator over an ellipse. + * + * @param e the ellipse + * @param xform the transform + */ + public ArcIterator(Ellipse2D e, AffineTransform xform) + { + this.xform = xform; + x = e.getX(); + y = e.getY(); + w = e.getWidth(); + h = e.getHeight(); + start = 0; + extent = 2 * Math.PI; + type = CHORD; + limit = (w < 0 || h < 0) ? -1 : 5; + } + + /** + * Return the winding rule. + * + * @return {@link PathIterator#WIND_NON_ZERO} + */ + public int getWindingRule() + { + return WIND_NON_ZERO; + } + + /** + * Test if the iteration is complete. + * + * @return true if more segments exist + */ + public boolean isDone() + { + return current > limit; + } + + /** + * Advance the iterator. + */ + public void next() + { + current++; + } + + /** + * Put the current segment into the array, and return the segment type. + * + * @param coords an array of 6 elements + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + */ + public int currentSegment(float[] coords) + { + double[] double_coords = new double[6]; + int code = currentSegment(double_coords); + for (int i = 0; i < 6; ++i) + coords[i] = (float) double_coords[i]; + return code; + } + + /** + * Put the current segment into the array, and return the segment type. + * + * @param coords an array of 6 elements + * @return the segment type + * @throws NullPointerException if coords is null + * @throws ArrayIndexOutOfBoundsException if coords is too small + */ + public int currentSegment(double[] coords) + { + double rx = w / 2; + double ry = h / 2; + double xmid = x + rx; + double ymid = y + ry; + + if (current > limit) + throw new NoSuchElementException("arc iterator out of bounds"); + + if (current == 0) + { + coords[0] = xmid + rx * Math.cos(start); + coords[1] = ymid - ry * Math.sin(start); + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_MOVETO; + } + + if (type != OPEN && current == limit) + return SEG_CLOSE; + + if ((current == limit - 1) && (type == PIE)) + { + coords[0] = xmid; + coords[1] = ymid; + if (xform != null) + xform.transform(coords, 0, coords, 0, 1); + return SEG_LINETO; + } + + // note that this produces a cubic approximation of the arc segment, + // not a true ellipsoid. there's no ellipsoid path segment code, + // unfortunately. the cubic approximation looks about right, though. + double kappa = (Math.sqrt(2.0) - 1.0) * (4.0 / 3.0); + double quad = (Math.PI / 2.0); + + double curr_begin; + double curr_extent; + if (extent > 0) + { + curr_begin = start + (current - 1) * quad; + curr_extent = Math.min((start + extent) - curr_begin, quad); + } + else + { + curr_begin = start - (current - 1) * quad; + curr_extent = Math.max((start + extent) - curr_begin, -quad); + } + + double portion_of_a_quadrant = Math.abs(curr_extent / quad); + + double x0 = xmid + rx * Math.cos(curr_begin); + double y0 = ymid - ry * Math.sin(curr_begin); + + double x1 = xmid + rx * Math.cos(curr_begin + curr_extent); + double y1 = ymid - ry * Math.sin(curr_begin + curr_extent); + + AffineTransform trans = new AffineTransform(); + double[] cvec = new double[2]; + double len = kappa * portion_of_a_quadrant; + double angle = curr_begin; + + // in a hypothetical "first quadrant" setting, our first control + // vector would be sticking up, from [1,0] to [1,kappa]. + // + // let us recall however that in java2d, y coords are upside down + // from what one would consider "normal" first quadrant rules, so we + // will *subtract* the y value of this control vector from our first + // point. + cvec[0] = 0; + if (extent > 0) + cvec[1] = len; + else + cvec[1] = -len; + + trans.scale(rx, ry); + trans.rotate(angle); + trans.transform(cvec, 0, cvec, 0, 1); + coords[0] = x0 + cvec[0]; + coords[1] = y0 - cvec[1]; + + // control vector #2 would, ideally, be sticking out and to the + // right, in a first quadrant arc segment. again, subtraction of y. + cvec[0] = 0; + if (extent > 0) + cvec[1] = -len; + else + cvec[1] = len; + + trans.rotate(curr_extent); + trans.transform(cvec, 0, cvec, 0, 1); + coords[2] = x1 + cvec[0]; + coords[3] = y1 - cvec[1]; + + // end point + coords[4] = x1; + coords[5] = y1; + + if (xform != null) + xform.transform(coords, 0, coords, 0, 3); + + return SEG_CUBICTO; + } + } // class ArcIterator + + /** + * This class implements an arc in double precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + */ + public static class Double extends Arc2D + { + /** The x coordinate of the box bounding the ellipse of this arc. */ + public double x; + + /** The y coordinate of the box bounding the ellipse of this arc. */ + public double y; + + /** The width of the box bounding the ellipse of this arc. */ + public double width; + + /** The height of the box bounding the ellipse of this arc. */ + public double height; + + /** The start angle of this arc, in degrees. */ + public double start; + + /** The extent angle of this arc, in degrees. */ + public double extent; + + /** + * Create a new, open arc at (0,0) with 0 extent. + */ + public Double() + { + super(OPEN); + } + + /** + * Create a new arc of the given type at (0,0) with 0 extent. + * + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Double(int type) + { + super(type); + } + + /** + * Create a new arc with the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Double(double x, double y, double w, double h, double start, + double extent, int type) + { + super(type); + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + } + + /** + * Create a new arc with the given dimensions. + * + * @param r the bounding box + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public Double(Rectangle2D r, double start, double extent, int type) + { + super(type); + x = r.getX(); + y = r.getY(); + width = r.getWidth(); + height = r.getHeight(); + this.start = start; + this.extent = extent; + } + + /** + * Return the x coordinate of the bounding box. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate of the bounding box. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width of the bounding box. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height of the bounding box. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Return the start angle of the arc, in degrees. + * + * @return the value of start + */ + public double getAngleStart() + { + return start; + } + + /** + * Return the extent of the arc, in degrees. + * + * @return the value of extent + */ + public double getAngleExtent() + { + return extent; + } + + /** + * Tests if the arc contains points. + * + * @return true if the arc has no interior + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Sets the arc to the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArc(double x, double y, double w, double h, double start, + double extent, int type) + { + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + setArcType(type); + } + + /** + * Sets the start angle of the arc. + * + * @param start the new start angle + */ + public void setAngleStart(double start) + { + this.start = start; + } + + /** + * Sets the extent angle of the arc. + * + * @param extent the new extent angle + */ + public void setAngleExtent(double extent) + { + this.extent = extent; + } + + /** + * Creates a tight bounding box given dimensions that more precise than + * the bounding box of the ellipse. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + protected Rectangle2D makeBounds(double x, double y, double w, double h) + { + return new Rectangle2D.Double(x, y, w, h); + } + } // class Double + + /** + * This class implements an arc in float precision. + * + * @author Eric Blake (ebb9@email.byu.edu) + * @since 1.2 + */ + public static class Float extends Arc2D + { + /** The x coordinate of the box bounding the ellipse of this arc. */ + public float x; + + /** The y coordinate of the box bounding the ellipse of this arc. */ + public float y; + + /** The width of the box bounding the ellipse of this arc. */ + public float width; + + /** The height of the box bounding the ellipse of this arc. */ + public float height; + + /** The start angle of this arc, in degrees. */ + public float start; + + /** The extent angle of this arc, in degrees. */ + public float extent; + + /** + * Create a new, open arc at (0,0) with 0 extent. + */ + public Float() + { + super(OPEN); + } + + /** + * Create a new arc of the given type at (0,0) with 0 extent. + * + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Float(int type) + { + super(type); + } + + /** + * Create a new arc with the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public Float(float x, float y, float w, float h, float start, + float extent, int type) + { + super(type); + this.x = x; + this.y = y; + width = w; + height = h; + this.start = start; + this.extent = extent; + } + + /** + * Create a new arc with the given dimensions. + * + * @param r the bounding box + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + * @throws NullPointerException if r is null + */ + public Float(Rectangle2D r, float start, float extent, int type) + { + super(type); + x = (float) r.getX(); + y = (float) r.getY(); + width = (float) r.getWidth(); + height = (float) r.getHeight(); + this.start = start; + this.extent = extent; + } + + /** + * Return the x coordinate of the bounding box. + * + * @return the value of x + */ + public double getX() + { + return x; + } + + /** + * Return the y coordinate of the bounding box. + * + * @return the value of y + */ + public double getY() + { + return y; + } + + /** + * Return the width of the bounding box. + * + * @return the value of width + */ + public double getWidth() + { + return width; + } + + /** + * Return the height of the bounding box. + * + * @return the value of height + */ + public double getHeight() + { + return height; + } + + /** + * Return the start angle of the arc, in degrees. + * + * @return the value of start + */ + public double getAngleStart() + { + return start; + } + + /** + * Return the extent of the arc, in degrees. + * + * @return the value of extent + */ + public double getAngleExtent() + { + return extent; + } + + /** + * Tests if the arc contains points. + * + * @return true if the arc has no interior + */ + public boolean isEmpty() + { + return width <= 0 || height <= 0; + } + + /** + * Sets the arc to the given dimensions. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + * @param start the start angle, in degrees + * @param extent the extent, in degrees + * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE} + * @throws IllegalArgumentException if type is invalid + */ + public void setArc(double x, double y, double w, double h, double start, + double extent, int type) + { + this.x = (float) x; + this.y = (float) y; + width = (float) w; + height = (float) h; + this.start = (float) start; + this.extent = (float) extent; + setArcType(type); + } + + /** + * Sets the start angle of the arc. + * + * @param start the new start angle + */ + public void setAngleStart(double start) + { + this.start = (float) start; + } + + /** + * Sets the extent angle of the arc. + * + * @param extent the new extent angle + */ + public void setAngleExtent(double extent) + { + this.extent = (float) extent; + } + + /** + * Creates a tight bounding box given dimensions that more precise than + * the bounding box of the ellipse. + * + * @param x the x coordinate + * @param y the y coordinate + * @param w the width + * @param h the height + */ + protected Rectangle2D makeBounds(double x, double y, double w, double h) + { + return new Rectangle2D.Float((float) x, (float) y, (float) w, (float) h); + } + } // class Float +} // class Arc2D |