1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17/** 18 * @author Denis M. Kishenko 19 * @version $Revision$ 20 */ 21 22package java.awt; 23 24import java.awt.Point; 25import java.awt.Rectangle; 26import java.awt.Shape; 27import java.awt.geom.AffineTransform; 28import java.awt.geom.PathIterator; 29import java.awt.geom.Point2D; 30import java.awt.geom.Rectangle2D; 31import java.io.Serializable; 32import java.util.NoSuchElementException; 33 34import org.apache.harmony.awt.gl.*; 35import org.apache.harmony.awt.internal.nls.Messages; 36 37/** 38 * The Polygon class defines an closed area specified by n vertices and n edges. 39 * The coordinates of the vertices are specified by x, y arrays. The edges are 40 * the line segments from the point (x[i], y[i]) to the point (x[i+1], y[i+1]), 41 * for -1 < i < (n-1) plus the line segment from the point (x[n-1], y[n-1]) to 42 * the point (x[0], y[0]) point. The Polygon is empty if the number of vertices 43 * is zero. 44 * 45 * @since Android 1.0 46 */ 47public class Polygon implements Shape, Serializable { 48 49 /** 50 * The Constant serialVersionUID. 51 */ 52 private static final long serialVersionUID = -6460061437900069969L; 53 54 /** 55 * The points buffer capacity. 56 */ 57 private static final int BUFFER_CAPACITY = 4; 58 59 /** 60 * The number of Polygon vertices. 61 */ 62 public int npoints; 63 64 /** 65 * The array of X coordinates of the vertices. 66 */ 67 public int[] xpoints; 68 69 /** 70 * The array of Y coordinates of the vertices. 71 */ 72 public int[] ypoints; 73 74 /** 75 * The smallest Rectangle that completely contains this Polygon. 76 */ 77 protected Rectangle bounds; 78 79 /* 80 * Polygon path iterator 81 */ 82 /** 83 * The internal Class Iterator. 84 */ 85 class Iterator implements PathIterator { 86 87 /** 88 * The source Polygon object. 89 */ 90 public Polygon p; 91 92 /** 93 * The path iterator transformation. 94 */ 95 public AffineTransform t; 96 97 /** 98 * The current segment index. 99 */ 100 public int index; 101 102 /** 103 * Constructs a new Polygon.Iterator for the given polygon and 104 * transformation 105 * 106 * @param at 107 * the AffineTransform object to apply rectangle path. 108 * @param p 109 * the p. 110 */ 111 public Iterator(AffineTransform at, Polygon p) { 112 this.p = p; 113 this.t = at; 114 if (p.npoints == 0) { 115 index = 1; 116 } 117 } 118 119 public int getWindingRule() { 120 return WIND_EVEN_ODD; 121 } 122 123 public boolean isDone() { 124 return index > p.npoints; 125 } 126 127 public void next() { 128 index++; 129 } 130 131 public int currentSegment(double[] coords) { 132 if (isDone()) { 133 // awt.110=Iterator out of bounds 134 throw new NoSuchElementException(Messages.getString("awt.110")); //$NON-NLS-1$ 135 } 136 if (index == p.npoints) { 137 return SEG_CLOSE; 138 } 139 coords[0] = p.xpoints[index]; 140 coords[1] = p.ypoints[index]; 141 if (t != null) { 142 t.transform(coords, 0, coords, 0, 1); 143 } 144 return index == 0 ? SEG_MOVETO : SEG_LINETO; 145 } 146 147 public int currentSegment(float[] coords) { 148 if (isDone()) { 149 // awt.110=Iterator out of bounds 150 throw new NoSuchElementException(Messages.getString("awt.110")); //$NON-NLS-1$ 151 } 152 if (index == p.npoints) { 153 return SEG_CLOSE; 154 } 155 coords[0] = p.xpoints[index]; 156 coords[1] = p.ypoints[index]; 157 if (t != null) { 158 t.transform(coords, 0, coords, 0, 1); 159 } 160 return index == 0 ? SEG_MOVETO : SEG_LINETO; 161 } 162 } 163 164 /** 165 * Instantiates a new empty polygon. 166 */ 167 public Polygon() { 168 xpoints = new int[BUFFER_CAPACITY]; 169 ypoints = new int[BUFFER_CAPACITY]; 170 } 171 172 /** 173 * Instantiates a new polygon with the specified number of vertices, and the 174 * given arrays of x, y vertex coordinates. The length of each coordinate 175 * array may not be less than the specified number of vertices but may be 176 * greater. Only the first n elements are used from each coordinate array. 177 * 178 * @param xpoints 179 * the array of X vertex coordinates. 180 * @param ypoints 181 * the array of Y vertex coordinates. 182 * @param npoints 183 * the number vertices of the polygon. 184 * @throws IndexOutOfBoundsException 185 * if the length of xpoints or ypoints is less than n. 186 * @throws NegativeArraySizeException 187 * if n is negative. 188 */ 189 public Polygon(int[] xpoints, int[] ypoints, int npoints) { 190 if (npoints > xpoints.length || npoints > ypoints.length) { 191 // awt.111=Parameter npoints is greater than array length 192 throw new IndexOutOfBoundsException(Messages.getString("awt.111")); //$NON-NLS-1$ 193 } 194 if (npoints < 0) { 195 // awt.112=Negative number of points 196 throw new NegativeArraySizeException(Messages.getString("awt.112")); //$NON-NLS-1$ 197 } 198 this.npoints = npoints; 199 this.xpoints = new int[npoints]; 200 this.ypoints = new int[npoints]; 201 System.arraycopy(xpoints, 0, this.xpoints, 0, npoints); 202 System.arraycopy(ypoints, 0, this.ypoints, 0, npoints); 203 } 204 205 /** 206 * Resets the current Polygon to an empty Polygon. More precisely, the 207 * number of Polygon vertices is set to zero, but x, y coordinates arrays 208 * are not affected. 209 */ 210 public void reset() { 211 npoints = 0; 212 bounds = null; 213 } 214 215 /** 216 * Invalidates the data that depends on the vertex coordinates. This method 217 * should be called after direct manipulations of the x, y vertex 218 * coordinates arrays to avoid unpredictable results of methods which rely 219 * on the bounding box. 220 */ 221 public void invalidate() { 222 bounds = null; 223 } 224 225 /** 226 * Adds the point to the Polygon and updates the bounding box accordingly. 227 * 228 * @param px 229 * the X coordinate of the added vertex. 230 * @param py 231 * the Y coordinate of the added vertex. 232 */ 233 public void addPoint(int px, int py) { 234 if (npoints == xpoints.length) { 235 int[] tmp; 236 237 tmp = new int[xpoints.length + BUFFER_CAPACITY]; 238 System.arraycopy(xpoints, 0, tmp, 0, xpoints.length); 239 xpoints = tmp; 240 241 tmp = new int[ypoints.length + BUFFER_CAPACITY]; 242 System.arraycopy(ypoints, 0, tmp, 0, ypoints.length); 243 ypoints = tmp; 244 } 245 246 xpoints[npoints] = px; 247 ypoints[npoints] = py; 248 npoints++; 249 250 if (bounds != null) { 251 bounds.setFrameFromDiagonal(Math.min(bounds.getMinX(), px), Math.min(bounds.getMinY(), 252 py), Math.max(bounds.getMaxX(), px), Math.max(bounds.getMaxY(), py)); 253 } 254 } 255 256 /** 257 * Gets the bounding rectangle of the Polygon. The bounding rectangle is the 258 * smallest rectangle which contains the Polygon. 259 * 260 * @return the bounding rectangle of the Polygon. 261 * @see java.awt.Shape#getBounds() 262 */ 263 public Rectangle getBounds() { 264 if (bounds != null) { 265 return bounds; 266 } 267 if (npoints == 0) { 268 return new Rectangle(); 269 } 270 271 int bx1 = xpoints[0]; 272 int by1 = ypoints[0]; 273 int bx2 = bx1; 274 int by2 = by1; 275 276 for (int i = 1; i < npoints; i++) { 277 int x = xpoints[i]; 278 int y = ypoints[i]; 279 if (x < bx1) { 280 bx1 = x; 281 } else if (x > bx2) { 282 bx2 = x; 283 } 284 if (y < by1) { 285 by1 = y; 286 } else if (y > by2) { 287 by2 = y; 288 } 289 } 290 291 return bounds = new Rectangle(bx1, by1, bx2 - bx1, by2 - by1); 292 } 293 294 /** 295 * Gets the bounding rectangle of the Polygon. The bounding rectangle is the 296 * smallest rectangle which contains the Polygon. 297 * 298 * @return the bounding rectangle of the Polygon. 299 * @deprecated Use getBounds() method. 300 */ 301 @Deprecated 302 public Rectangle getBoundingBox() { 303 return getBounds(); 304 } 305 306 /** 307 * Gets the Rectangle2D which represents Polygon bounds. The bounding 308 * rectangle is the smallest rectangle which contains the Polygon. 309 * 310 * @return the bounding rectangle of the Polygon. 311 * @see java.awt.Shape#getBounds2D() 312 */ 313 public Rectangle2D getBounds2D() { 314 return getBounds().getBounds2D(); 315 } 316 317 /** 318 * Translates all vertices of Polygon the specified distances along X, Y 319 * axis. 320 * 321 * @param mx 322 * the distance to translate horizontally. 323 * @param my 324 * the distance to translate vertically. 325 */ 326 public void translate(int mx, int my) { 327 for (int i = 0; i < npoints; i++) { 328 xpoints[i] += mx; 329 ypoints[i] += my; 330 } 331 if (bounds != null) { 332 bounds.translate(mx, my); 333 } 334 } 335 336 /** 337 * Checks whether or not the point given by the coordinates x, y lies inside 338 * the Polygon. 339 * 340 * @param x 341 * the X coordinate of the point to check. 342 * @param y 343 * the Y coordinate of the point to check. 344 * @return true, if the specified point lies inside the Polygon, false 345 * otherwise. 346 * @deprecated Use contains(int, int) method. 347 */ 348 @Deprecated 349 public boolean inside(int x, int y) { 350 return contains((double)x, (double)y); 351 } 352 353 /** 354 * Checks whether or not the point given by the coordinates x, y lies inside 355 * the Polygon. 356 * 357 * @param x 358 * the X coordinate of the point to check. 359 * @param y 360 * the Y coordinate of the point to check. 361 * @return true, if the specified point lies inside the Polygon, false 362 * otherwise. 363 */ 364 public boolean contains(int x, int y) { 365 return contains((double)x, (double)y); 366 } 367 368 /** 369 * Checks whether or not the point with specified double coordinates lies 370 * inside the Polygon. 371 * 372 * @param x 373 * the X coordinate of the point to check. 374 * @param y 375 * the Y coordinate of the point to check. 376 * @return true, if the point given by the double coordinates lies inside 377 * the Polygon, false otherwise. 378 * @see java.awt.Shape#contains(double, double) 379 */ 380 public boolean contains(double x, double y) { 381 return Crossing.isInsideEvenOdd(Crossing.crossShape(this, x, y)); 382 } 383 384 /** 385 * Checks whether or not the rectangle determined by the parameters [x, y, 386 * width, height] lies inside the Polygon. 387 * 388 * @param x 389 * the X coordinate of the rectangles's left upper corner as a 390 * double. 391 * @param y 392 * the Y coordinate of the rectangles's left upper corner as a 393 * double. 394 * @param width 395 * the width of rectangle as a double. 396 * @param height 397 * the height of rectangle as a double. 398 * @return true, if the specified rectangle lies inside the Polygon, false 399 * otherwise. 400 * @see java.awt.Shape#contains(double, double, double, double) 401 */ 402 public boolean contains(double x, double y, double width, double height) { 403 int cross = Crossing.intersectShape(this, x, y, width, height); 404 return cross != Crossing.CROSSING && Crossing.isInsideEvenOdd(cross); 405 } 406 407 /** 408 * Checks whether or not the rectangle determined by the parameters [x, y, 409 * width, height] intersects the interior of the Polygon. 410 * 411 * @param x 412 * the X coordinate of the rectangles's left upper corner as a 413 * double. 414 * @param y 415 * the Y coordinate of the rectangles's left upper corner as a 416 * double. 417 * @param width 418 * the width of rectangle as a double. 419 * @param height 420 * the height of rectangle as a double. 421 * @return true, if the specified rectangle intersects the interior of the 422 * Polygon, false otherwise. 423 * @see java.awt.Shape#intersects(double, double, double, double) 424 */ 425 public boolean intersects(double x, double y, double width, double height) { 426 int cross = Crossing.intersectShape(this, x, y, width, height); 427 return cross == Crossing.CROSSING || Crossing.isInsideEvenOdd(cross); 428 } 429 430 /** 431 * Checks whether or not the specified rectangle lies inside the Polygon. 432 * 433 * @param rect 434 * the Rectangle2D object. 435 * @return true, if the specified rectangle lies inside the Polygon, false 436 * otherwise. 437 * @see java.awt.Shape#contains(java.awt.geom.Rectangle2D) 438 */ 439 public boolean contains(Rectangle2D rect) { 440 return contains(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); 441 } 442 443 /** 444 * Checks whether or not the specified Point lies inside the Polygon. 445 * 446 * @param point 447 * the Point object. 448 * @return true, if the specified Point lies inside the Polygon, false 449 * otherwise. 450 */ 451 public boolean contains(Point point) { 452 return contains(point.getX(), point.getY()); 453 } 454 455 /** 456 * Checks whether or not the specified Point2D lies inside the Polygon. 457 * 458 * @param point 459 * the Point2D object. 460 * @return true, if the specified Point2D lies inside the Polygon, false 461 * otherwise. 462 * @see java.awt.Shape#contains(java.awt.geom.Point2D) 463 */ 464 public boolean contains(Point2D point) { 465 return contains(point.getX(), point.getY()); 466 } 467 468 /** 469 * Checks whether or not the interior of rectangle specified by the 470 * Rectangle2D object intersects the interior of the Polygon. 471 * 472 * @param rect 473 * the Rectangle2D object. 474 * @return true, if the Rectangle2D intersects the interior of the Polygon, 475 * false otherwise. 476 * @see java.awt.Shape#intersects(java.awt.geom.Rectangle2D) 477 */ 478 public boolean intersects(Rectangle2D rect) { 479 return intersects(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); 480 } 481 482 /** 483 * Gets the PathIterator object which gives the coordinates of the polygon, 484 * transformed according to the specified AffineTransform. 485 * 486 * @param t 487 * the specified AffineTransform object or null. 488 * @return PathIterator object for the Polygon. 489 * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform) 490 */ 491 public PathIterator getPathIterator(AffineTransform t) { 492 return new Iterator(t, this); 493 } 494 495 /** 496 * Gets the PathIterator object which gives the coordinates of the polygon, 497 * transformed according to the specified AffineTransform. The flatness 498 * parameter is ignored. 499 * 500 * @param t 501 * the specified AffineTransform object or null. 502 * @param flatness 503 * the maximum number of the control points for a given curve 504 * which varies from colinear before a subdivided curve is 505 * replaced by a straight line connecting the endpoints. This 506 * parameter is ignored for the Polygon class. 507 * @return PathIterator object for the Polygon. 508 * @see java.awt.Shape#getPathIterator(java.awt.geom.AffineTransform, 509 * double) 510 */ 511 public PathIterator getPathIterator(AffineTransform t, double flatness) { 512 return new Iterator(t, this); 513 } 514 515} 516