| /* Copyright (C) 2001 Free Software Foundation | |
| This file is part of libjava. | |
| This software is copyrighted work licensed under the terms of the | |
| Libjava License. Please consult the file "LIBJAVA_LICENSE" for | |
| details. */ | |
| package java.awt; | |
| import java.awt.geom.*; | |
| import java.io.Serializable; | |
| import java.util.Arrays; | |
| /** | |
| * @author Tom Tromey <tromey@redhat.com> | |
| * @date May 10, 2001 | |
| */ | |
| /** The Polygon class represents a closed region whose boundary is | |
| made of line segments. The Polygon is defined by its vertices. */ | |
| public class Polygon implements Shape, Serializable | |
| { | |
| /** The bounds of the polygon. This is null until the bounds have | |
| * been computed for the first time; then it is correctly | |
| * maintained whenever it is modified. */ | |
| protected Rectangle bounds; | |
| /** The number of points in the polygon. */ | |
| public int npoints; | |
| /** The x coordinates of the points. */ | |
| public int[] xpoints; | |
| /** The y coordinates of the points. */ | |
| public int[] ypoints; | |
| /** Create a new, empty Polygon. */ | |
| public Polygon () | |
| { | |
| this.xpoints = new int[0]; | |
| this.ypoints = new int[0]; | |
| this.npoints = 0; | |
| } | |
| /** Create a new Polygon from the given vertices. | |
| * @param xpoints The x coordinates | |
| * @param ypoints The y coordinates | |
| * @param npoints The number of points | |
| */ | |
| public Polygon (int[] xpoints, int[] ypoints, int npoints) | |
| { | |
| // We make explicit copies instead of relying on clone so that we | |
| // ensure the new arrays are the same size. | |
| this.xpoints = new int[npoints]; | |
| this.ypoints = new int[npoints]; | |
| System.arraycopy (xpoints, 0, this.xpoints, 0, npoints); | |
| System.arraycopy (ypoints, 0, this.ypoints, 0, npoints); | |
| } | |
| /** Append the specified point to this Polygon. | |
| * @param x The x coordinate | |
| * @param y The y coordinate | |
| */ | |
| public void addPoint (int x, int y) | |
| { | |
| int[] newx = new int[npoints + 1]; | |
| System.arraycopy (xpoints, 0, newx, 0, npoints); | |
| int[] newy = new int[npoints + 1]; | |
| System.arraycopy (ypoints, 0, newy, 0, npoints); | |
| newx[npoints] = x; | |
| newy[npoints] = y; | |
| ++npoints; | |
| xpoints = newx; | |
| ypoints = newy; | |
| // It is simpler to just recompute. | |
| if (bounds != null) | |
| computeBoundingBox (); | |
| } | |
| /** Return true if the indicated point is inside this Polygon. | |
| * This uses an even-odd rule to determine insideness. | |
| * @param x The x coordinate | |
| * @param y The y coordinate | |
| * @returns true if the point is contained by this Polygon. | |
| */ | |
| public boolean contains (double x, double y) | |
| { | |
| // What we do is look at each line segment. If the line segment | |
| // crosses the "scan line" at y at a point x' < x, then we | |
| // increment our counter. At the end, an even number means the | |
| // point is outside the polygon. Instead of a number, though, we | |
| // use a boolean. | |
| boolean inside = false; | |
| for (int i = 0; i < npoints; ++i) | |
| { | |
| // Handle the wrap case. | |
| int x2 = (i == npoints) ? xpoints[0] : xpoints[i + 1]; | |
| int y2 = (i == npoints) ? ypoints[0] : ypoints[i + 1]; | |
| if (ypoints[i] == y2) | |
| { | |
| // We ignore horizontal lines. This might give weird | |
| // results in some situations -- ? | |
| continue; | |
| } | |
| double t = (y - ypoints[i]) / (double) (y2 - ypoints[i]); | |
| double x3 = xpoints[i] + t * (x2 - xpoints[i]); | |
| if (x3 < x) | |
| inside = ! inside; | |
| } | |
| return inside; | |
| } | |
| /** Return true if the indicated rectangle is entirely inside this | |
| * Polygon. | |
| * This uses an even-odd rule to determine insideness. | |
| * @param x The x coordinate | |
| * @param y The y coordinate | |
| * @param w The width | |
| * @param h The height | |
| * @returns true if the rectangle is contained by this Polygon. | |
| */ | |
| public boolean contains (double x, double y, double w, double h) | |
| { | |
| return intersectOrContains (x, y, w, h, false); | |
| } | |
| /** Return true if the indicated point is inside this Polygon. | |
| * This uses an even-odd rule to determine insideness. | |
| * @param x The x coordinate | |
| * @param y The y coordinate | |
| * @returns true if the point is contained by this Polygon. | |
| */ | |
| public boolean contains (int x, int y) | |
| { | |
| return contains ((double) x, (double) y); | |
| } | |
| /** Return true if the indicated point is inside this Polygon. | |
| * This uses an even-odd rule to determine insideness. | |
| * @param p The point | |
| * @returns true if the point is contained by this Polygon. | |
| */ | |
| public boolean contains (Point p) | |
| { | |
| return contains (p.x, p.y); | |
| } | |
| /** Return true if the indicated point is inside this Polygon. | |
| * This uses an even-odd rule to determine insideness. | |
| * @param p The point | |
| * @returns true if the point is contained by this Polygon. | |
| */ | |
| public boolean contains (Point2D p) | |
| { | |
| return contains (p.getX (), p.getY ()); | |
| } | |
| /** Return true if the indicated rectangle is entirely inside this | |
| * Polygon. This uses an even-odd rule to determine insideness. | |
| * @param r The rectangle | |
| * @returns true if the rectangle is contained by this Polygon. | |
| */ | |
| public boolean contains (Rectangle2D r) | |
| { | |
| return contains (r.getX (), r.getY (), r.getWidth (), r.getHeight ()); | |
| } | |
| /** Returns the bounds of this Polygon. | |
| * @deprecated Use getBounds() instead. | |
| */ | |
| public Rectangle getBoundingBox () | |
| { | |
| if (bounds == null) | |
| computeBoundingBox (); | |
| return bounds; | |
| } | |
| /** Returns the bounds of this Polygon. */ | |
| public Rectangle getBounds () | |
| { | |
| if (bounds == null) | |
| computeBoundingBox (); | |
| return bounds; | |
| } | |
| /** Returns the bounds of this Polygon. */ | |
| public Rectangle2D getBounds2D () | |
| { | |
| if (bounds == null) | |
| computeBoundingBox (); | |
| return bounds; // Why not? | |
| } | |
| /** Return an iterator for the boundary of this Polygon. | |
| * @param at A transform to apply to the coordinates. | |
| * @returns A path iterator for the Polygon's boundary. | |
| */ | |
| public PathIterator getPathIterator (AffineTransform at) | |
| { | |
| return new Iterator (at); | |
| } | |
| /** Return an iterator for the boundary of this Polygon. | |
| * @param at A transform to apply to the coordinates. | |
| * @param flatness The flatness of the result; it is ignored by | |
| * this class. | |
| * @returns A path iterator for the Polygon's boundary. | |
| */ | |
| public PathIterator getPathIterator (AffineTransform at, double flatness) | |
| { | |
| // We ignore the flatness. | |
| return new Iterator (at); | |
| } | |
| /** @deprecated use contains(int,int). */ | |
| public boolean inside (int x, int y) | |
| { | |
| return contains (x, y); | |
| } | |
| /** Return true if this Polygon's interior intersects the given | |
| * rectangle's interior. | |
| * @param x The x coordinate | |
| * @param y The y coordinate | |
| * @param w The width | |
| * @param h The height | |
| */ | |
| public boolean intersects (double x, double y, double w, double h) | |
| { | |
| return intersectOrContains (x, y, w, h, true); | |
| } | |
| /** Return true if this Polygon's interior intersects the given | |
| * rectangle's interior. | |
| * @param r The rectangle | |
| */ | |
| public boolean intersects (Rectangle2D r) | |
| { | |
| return intersects (r.getX (), r.getY (), r.getWidth (), r.getHeight ()); | |
| } | |
| // This tests for intersection with or containment of a rectangle, | |
| // depending on the INTERSECT argument. | |
| private boolean intersectOrContains (double x, double y, double w, double h, | |
| boolean intersect) | |
| { | |
| // First compute the rectangle of possible intersection points. | |
| Rectangle r = getBounds (); | |
| int minx = Math.max (r.x, (int) x); | |
| int maxx = Math.min (r.x + r.width, (int) (x + w)); | |
| int miny = Math.max (r.y, (int) y); | |
| int maxy = Math.min (r.y + r.height, (int) (y + h)); | |
| if (miny > maxy) | |
| return false; | |
| double[] crosses = new double[npoints + 1]; | |
| for (; miny < maxy; ++miny) | |
| { | |
| // First compute every place where the polygon might intersect | |
| // the scan line at Y. | |
| int ins = 0; | |
| for (int i = 0; i < npoints; ++i) | |
| { | |
| // Handle the wrap case. | |
| int x2 = (i == npoints) ? xpoints[0] : xpoints[i + 1]; | |
| int y2 = (i == npoints) ? ypoints[0] : ypoints[i + 1]; | |
| if (ypoints[i] == y2) | |
| { | |
| // We ignore horizontal lines. This might give weird | |
| // results in some situations -- ? | |
| continue; | |
| } | |
| double t = (((double) miny - ypoints[i]) | |
| / (double) (y2 - ypoints[i])); | |
| double x3 = xpoints[i] + t * (x2 - xpoints[i]); | |
| crosses[ins++] = x3; | |
| } | |
| // Now we can sort into increasing order and look to see if | |
| // any point in the rectangle is in the polygon. We examine | |
| // every other pair due to our even-odd rule. | |
| Arrays.sort (crosses, 0, ins); | |
| int i = intersect ? 0 : 1; | |
| for (; i < ins - 1; i += 2) | |
| { | |
| // Pathological case. | |
| if (crosses[i] == crosses[i + 1]) | |
| continue; | |
| // Found a point on the inside. | |
| if ((crosses[i] >= x && crosses[i] < x + w) | |
| || (crosses[i + 1] >= x && crosses[i + 1] < x + w)) | |
| { | |
| // If we're checking containment then we just lost. | |
| // But if we're checking intersection then we just | |
| // won. | |
| return intersect; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| /** Translates all the vertices of the polygon via a given vector. | |
| * @param deltaX The X offset | |
| * @param deltaY The Y offset | |
| */ | |
| public void translate (int deltaX, int deltaY) | |
| { | |
| for (int i = 0; i < npoints; ++i) | |
| { | |
| xpoints[i] += deltaX; | |
| ypoints[i] += deltaY; | |
| } | |
| if (bounds != null) | |
| { | |
| bounds.x += deltaX; | |
| bounds.y += deltaY; | |
| } | |
| } | |
| // This computes the bounding box if required. | |
| private void computeBoundingBox () | |
| { | |
| if (npoints == 0) | |
| { | |
| // This is wrong if the user adds a new point, but we | |
| // account for that in addPoint(). | |
| bounds = new Rectangle (0, 0, 0, 0); | |
| } | |
| else | |
| { | |
| int maxx = xpoints[0]; | |
| int minx = xpoints[0]; | |
| int maxy = ypoints[0]; | |
| int miny = ypoints[0]; | |
| for (int i = 1; i < npoints; ++i) | |
| { | |
| maxx = Math.max (maxx, xpoints[i]); | |
| minx = Math.min (minx, xpoints[i]); | |
| maxy = Math.max (maxy, ypoints[i]); | |
| miny = Math.min (miny, ypoints[i]); | |
| } | |
| bounds = new Rectangle (minx, miny, maxx - minx, maxy - miny); | |
| } | |
| } | |
| private class Iterator implements PathIterator | |
| { | |
| public AffineTransform xform; | |
| public int where; | |
| public Iterator (AffineTransform xform) | |
| { | |
| this.xform = xform; | |
| where = 0; | |
| } | |
| public int currentSegment (double[] coords) | |
| { | |
| int r; | |
| if (where < npoints) | |
| { | |
| coords[0] = xpoints[where]; | |
| coords[1] = ypoints[where]; | |
| r = (where == 0) ? SEG_MOVETO : SEG_LINETO; | |
| xform.transform (coords, 0, coords, 0, 1); | |
| ++where; | |
| } | |
| else | |
| r = SEG_CLOSE; | |
| return r; | |
| } | |
| public int currentSegment (float[] coords) | |
| { | |
| int r; | |
| if (where < npoints) | |
| { | |
| coords[0] = xpoints[where]; | |
| coords[1] = ypoints[where]; | |
| r = (where == 0) ? SEG_MOVETO : SEG_LINETO; | |
| xform.transform (coords, 0, coords, 0, 1); | |
| } | |
| else | |
| r = SEG_CLOSE; | |
| return r; | |
| } | |
| public int getWindingRule () | |
| { | |
| return WIND_EVEN_ODD; | |
| } | |
| public boolean isDone () | |
| { | |
| return where == npoints + 1; | |
| } | |
| public void next () | |
| { | |
| ++where; | |
| } | |
| } | |
| } |