/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Denis M. Kishenko * @version $Revision$ */ package java.awt; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.NoSuchElementException; import org.apache.harmony.awt.gl.*; import org.apache.harmony.awt.internal.nls.Messages; /** * The Polygon class defines an closed area specified by n vertices and n edges. * The coordinates of the vertices are specified by x, y arrays. The edges are * the line segments from the point (x[i], y[i]) to the point (x[i+1], y[i+1]), * for -1 < i < (n-1) plus the line segment from the point (x[n-1], y[n-1]) to * the point (x[0], y[0]) point. The Polygon is empty if the number of vertices * is zero. * * @since Android 1.0 */ public class Polygon implements Shape, Serializable { /** * The Constant serialVersionUID. */ private static final long serialVersionUID = -6460061437900069969L; /** * The points buffer capacity. */ private static final int BUFFER_CAPACITY = 4; /** * The number of Polygon vertices. */ public int npoints; /** * The array of X coordinates of the vertices. */ public int[] xpoints; /** * The array of Y coordinates of the vertices. */ public int[] ypoints; /** * The smallest Rectangle that completely contains this Polygon. */ protected Rectangle bounds; /* * Polygon path iterator */ /** * The internal Class Iterator. */ class Iterator implements PathIterator { /** * The source Polygon object. */ public Polygon p; /** * The path iterator transformation. */ public AffineTransform t; /** * The current segment index. */ public int index; /** * Constructs a new Polygon.Iterator for the given polygon and * transformation * * @param at * the AffineTransform object to apply rectangle path. * @param p * the p. */ public Iterator(AffineTransform at, Polygon p) { this.p = p; this.t = at; if (p.npoints == 0) { index = 1; } } public int getWindingRule() { return WIND_EVEN_ODD; } public boolean isDone() { return index > p.npoints; } public void next() { index++; } public int currentSegment(double[] coords) { if (isDone()) { // awt.110=Iterator out of bounds throw new NoSuchElementException(Messages.getString("awt.110")); //$NON-NLS-1$ } if (index == p.npoints) { return SEG_CLOSE; } coords[0] = p.xpoints[index]; coords[1] = p.ypoints[index]; if (t != null) { t.transform(coords, 0, coords, 0, 1); } return index == 0 ? SEG_MOVETO : SEG_LINETO; } public int currentSegment(float[] coords) { if (isDone()) { // awt.110=Iterator out of bounds throw new NoSuchElementException(Messages.getString("awt.110")); //$NON-NLS-1$ } if (index == p.npoints) { return SEG_CLOSE; } coords[0] = p.xpoints[index]; coords[1] = p.ypoints[index]; if (t != null) { t.transform(coords, 0, coords, 0, 1); } return index == 0 ? SEG_MOVETO : SEG_LINETO; } } /** * Instantiates a new empty polygon. */ public Polygon() { xpoints = new int[BUFFER_CAPACITY]; ypoints = new int[BUFFER_CAPACITY]; } /** * Instantiates a new polygon with the specified number of vertices, and the * given arrays of x, y vertex coordinates. The length of each coordinate * array may not be less than the specified number of vertices but may be * greater. Only the first n elements are used from each coordinate array. * * @param xpoints * the array of X vertex coordinates. * @param ypoints * the array of Y vertex coordinates. * @param npoints * the number vertices of the polygon. * @throws IndexOutOfBoundsException * if the length of xpoints or ypoints is less than n. * @throws NegativeArraySizeException * if n is negative. */ public Polygon(int[] xpoints, int[] ypoints, int npoints) { if (npoints > xpoints.length || npoints > ypoints.length) { // awt.111=Parameter npoints is greater than array length throw new IndexOutOfBoundsException(Messages.getString("awt.111")); //$NON-NLS-1$ } if (npoints < 0) { // awt.112=Negative number of points throw new NegativeArraySizeException(Messages.getString("awt.112")); //$NON-NLS-1$ } this.npoints = npoints; 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); } /** * Resets the current Polygon to an empty Polygon. More precisely, the * number of Polygon vertices is set to zero, but x, y coordinates arrays * are not affected. */ public void reset() { npoints = 0; bounds = null; } /** * Invalidates the data that depends on the vertex coordinates. This method * should be called after direct manipulations of the x, y vertex * coordinates arrays to avoid unpredictable results of methods which rely * on the bounding box. */ public void invalidate() { bounds = null; } /** * Adds the point to the Polygon and updates the bounding box accordingly. * * @param px * the X coordinate of the added vertex. * @param py * the Y coordinate of the added vertex. */ public void addPoint(int px, int py) { if (npoints == xpoints.length) { int[] tmp; tmp = new int[xpoints.length + BUFFER_CAPACITY]; System.arraycopy(xpoints, 0, tmp, 0, xpoints.length); xpoints = tmp; tmp = new int[ypoints.length + BUFFER_CAPACITY]; System.arraycopy(ypoints, 0, tmp, 0, ypoints.length); ypoints = tmp; } xpoints[npoints] = px; ypoints[npoints] = py; npoints++; if (bounds != null) { bounds.setFrameFromDiagonal(Math.min(bounds.getMinX(), px), Math.min(bounds.getMinY(), py), Math.max(bounds.getMaxX(), px), Math.max(bounds.getMaxY(), py)); } } /** * Gets the bounding rectangle of the Polygon. The bounding rectangle is the * smallest rectangle which contains the Polygon. * * @return the bounding rectangle of the Polygon. * @see java.awt.Shape#getBounds() */ public Rectangle getBounds() { if (bounds != null) { return bounds; } if (npoints == 0) { return new Rectangle(); } int bx1 = xpoints[0]; int by1 = ypoints[0]; int bx2 = bx1; int by2 = by1; for (int i = 1; i < npoints; i++) { int x = xpoints[i]; int y = ypoints[i]; if (x < bx1) { bx1 = x; } else if (x > bx2) { bx2 = x; } if (y < by1) { by1 = y; } else if (y > by2) { by2 = y; } } return bounds = new Rectangle(bx1, by1, bx2 - bx1, by2 - by1); } /** * Gets the bounding rectangle of the Polygon. The bounding rectangle is the * smallest rectangle which contains the Polygon. * * @return the bounding rectangle of the Polygon. * @deprecated Use getBounds() method. */ @Deprecated public Rectangle getBoundingBox() { return getBounds(); } /** * Gets the Rectangle2D which represents Polygon bounds. The bounding * rectangle is the smallest rectangle which contains the Polygon. * * @return the bounding rectangle of the Polygon. * @see java.awt.Shape#getBounds2D() */ public Rectangle2D getBounds2D() { return getBounds().getBounds2D(); } /** * Translates all vertices of Polygon the specified distances along X, Y * axis. * * @param mx * the distance to translate horizontally. * @param my * the distance to translate vertically. */ public void translate(int mx, int my) { for (int i = 0; i < npoints; i++) { xpoints[i] += mx; ypoints[i] += my; } if (bounds != null) { bounds.translate(mx, my); } } /** * Checks whether or not the point given by the coordinates x, y lies inside * the Polygon. * * @param x * the X coordinate of the point to check. * @param y * the Y coordinate of the point to check. * @return true, if the specified point lies inside the Polygon, false * otherwise. * @deprecated Use contains(int, int) method. */ @Deprecated public boolean inside(int x, int y) { return contains((double)x, (double)y); } /** * Checks whether or not the point given by the coordinates x, y lies inside * the Polygon. * * @param x * the X coordinate of the point to check. * @param y * the Y coordinate of the point to check. * @return true, if the specified point lies inside the Polygon, false * otherwise. */ public boolean contains(int x, int y) { return contains((double)x, (double)y); } /** * Checks whether or not the point with specified double coordinates lies * inside the Polygon. * * @param x * the X coordinate of the point to check. * @param y * the Y coordinate of the point to check. * @return true, if the point given by the double coordinates lies inside * the Polygon, false otherwise. * @see java.awt.Shape#contains(double, double) */ public boolean contains(double x, double y) { return Crossing.isInsideEvenOdd(Crossing.crossShape(this, x, y)); } /** * Checks whether or not the rectangle determined by the parameters [x, y, * width, height] lies inside the Polygon. * * @param x * the X coordinate of the rectangles's left upper corner as a * double. * @param y * the Y coordinate of the rectangles's left upper corner as a * double. * @param width * the width of rectangle as a double. * @param height * the height of rectangle as a double. * @return true, if the specified rectangle lies inside the Polygon, false * otherwise. * @see java.awt.Shape#contains(double, double, double, double) */ public boolean contains(double x, double y, double width, double height) { int cross = Crossing.intersectShape(this, x, y, width, height); return cross != Crossing.CROSSING && Crossing.isInsideEvenOdd(cross); } /** * Checks whether or not the rectangle determined by the parameters [x, y, * width, height] intersects the interior of the Polygon. * * @param x * the X coordinate of the rectangles's left upper corner as a * double. * @param y * the Y coordinate of the rectangles's left upper corner as a * double. * @param width * the width of rectangle as a double. * @param height * the height of rectangle as a double. * @return true, if the specified rectangle intersects the interior of the * Polygon, false otherwise. * @see java.awt.Shape#intersects(double, double, double, double) */ public boolean intersects(double x, double y, double width, double height) { int cross = Crossing.intersectShape(this, x, y, width, height); return cross == Crossing.CROSSING || Crossing.isInsideEvenOdd(cross); } /** * Checks whether or not the specified rectangle lies inside the Polygon. * * @param rect * the Rectangle2D object. * @return true, if the specified rectangle lies inside the Polygon, false * otherwise. * @see java.awt.Shape#contains(java.awt.geom.Rectangle2D) */ public boolean contains(Rectangle2D rect) { return contains(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); } /** * Checks whether or not the specified Point lies inside the Polygon. * * @param point * the Point object. * @return true, if the specified Point lies inside the Polygon, false * otherwise. */ public boolean contains(Point point) { return contains(point.getX(), point.getY()); } /** * Checks whether or not the specified Point2D lies inside the Polygon. * * @param point * the Point2D object. * @return true, if the specified Point2D lies inside the Polygon, false * otherwise. * @see java.awt.Shape#contains(java.awt.geom.Point2D) */ public boolean contains(Point2D point) { return contains(point.getX(), point.getY()); } /** * Checks whether or not the interior of rectangle specified by the * Rectangle2D object intersects the interior of the Polygon. * * @param rect * the Rectangle2D object. * @return true, if the Rectangle2D intersects the interior of the Polygon, * false otherwise. * @see java.awt.Shape#intersects(java.awt.geom.Rectangle2D) */ public boolean intersects(Rectangle2D rect) { return intersects(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); } /** * Gets the PathIterator object which gives the coordinates of the polygon, * transformed according to the specified AffineTransform. * * @param t * the specified AffineTransform object or null. * @return PathIterator object for the Polygon. * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform) */ public PathIterator getPathIterator(AffineTransform t) { return new Iterator(t, this); } /** * Gets the PathIterator object which gives the coordinates of the polygon, * transformed according to the specified AffineTransform. The flatness * parameter is ignored. * * @param t * the specified AffineTransform object or null. * @param flatness * the maximum number of the control points for a given curve * which varies from colinear before a subdivided curve is * replaced by a straight line connecting the endpoints. This * parameter is ignored for the Polygon class. * @return PathIterator object for the Polygon. * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform, * double) */ public PathIterator getPathIterator(AffineTransform t, double flatness) { return new Iterator(t, this); } }