Changeset 1391 for branches/GNU/src/gcc/libjava/java/awt/Polygon.java
- Timestamp:
- Apr 27, 2004, 8:39:34 PM (22 years ago)
- Location:
- branches/GNU/src/gcc
- Files:
-
- 2 edited
-
. (modified) (1 prop)
-
libjava/java/awt/Polygon.java (modified) (1 diff, 1 prop)
Legend:
- Unmodified
- Added
- Removed
-
branches/GNU/src/gcc
- Property svn:ignore
-
old new 26 26 configure.vr 27 27 configure.vrs 28 28 29 Makefile 29 dir.info30 30 lost+found 31 31 update.out
-
- Property svn:ignore
-
branches/GNU/src/gcc/libjava/java/awt/Polygon.java
-
Property cvs2svn:cvs-rev
changed from
1.1to1.1.1.2
r1390 r1391 1 /* Copyright (C) 2001 Free Software Foundation 2 3 This file is part of libjava. 4 5 This software is copyrighted work licensed under the terms of the 6 Libjava License. Please consult the file "LIBJAVA_LICENSE" for 7 details. */ 1 /* Polygon.java -- class representing a polygon 2 Copyright (C) 1999, 2002 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 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. 10 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. 15 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. 20 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. 25 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. */ 37 8 38 9 39 package java.awt; 10 40 11 import java.awt.geom.*; 41 import java.awt.geom.AffineTransform; 42 import java.awt.geom.PathIterator; 43 import java.awt.geom.Point2D; 44 import java.awt.geom.Rectangle2D; 12 45 import java.io.Serializable; 13 import java.util.Arrays;14 46 15 47 /** 16 * @author Tom Tromey <[email protected]> 17 * @date May 10, 2001 48 * This class represents a polygon, a closed, two-dimensional region in a 49 * coordinate space. The region is bounded by an arbitrary number of line 50 * segments, between (x,y) coordinate vertices. The polygon has even-odd 51 * winding, meaning that a point is inside the shape if it crosses the 52 * boundary an odd number of times on the way to infinity. 53 * 54 * <p>There are some public fields; if you mess with them in an inconsistent 55 * manner, it is your own fault when you get NullPointerException, 56 * ArrayIndexOutOfBoundsException, or invalid results. Also, this class is 57 * not threadsafe. 58 * 59 * @author Aaron M. Renn <[email protected]> 60 * @author Eric Blake <[email protected]> 61 * @since 1.0 62 * @status updated to 1.4 18 63 */ 19 20 /** The Polygon class represents a closed region whose boundary is21 made of line segments. The Polygon is defined by its vertices. */22 64 public class Polygon implements Shape, Serializable 23 65 { 24 /** The bounds of the polygon. This is null until the bounds have 25 * been computed for the first time; then it is correctly 26 * maintained whenever it is modified. */ 66 /** 67 * Compatible with JDK 1.0+. 68 */ 69 private static final long serialVersionUID = -6460061437900069969L; 70 71 /** 72 * This total number of endpoints. 73 * 74 * @serial the number of endpoints, possibly less than the array sizes 75 */ 76 public int npoints; 77 78 /** 79 * The array of X coordinates of endpoints. This should not be null. 80 * 81 * @see #addPoint(int, int) 82 * @serial the x coordinates 83 */ 84 public int[] xpoints; 85 86 /** 87 * The array of Y coordinates of endpoints. This should not be null. 88 * 89 * @see #addPoint(int, int) 90 * @serial the y coordinates 91 */ 92 public int[] ypoints; 93 94 /** 95 * The bounding box of this polygon. This is lazily created and cached, so 96 * it must be invalidated after changing points. 97 * 98 * @see #getBounds() 99 * @serial the bounding box, or null 100 */ 27 101 protected Rectangle bounds; 28 102 29 /** The number of points in the polygon. */ 30 public int npoints; 31 32 /** The x coordinates of the points. */ 33 public int[] xpoints; 34 35 /** The y coordinates of the points. */ 36 public int[] ypoints; 37 38 /** Create a new, empty Polygon. */ 39 public Polygon () 40 { 41 this.xpoints = new int[0]; 42 this.ypoints = new int[0]; 43 this.npoints = 0; 44 } 45 46 /** Create a new Polygon from the given vertices. 47 * @param xpoints The x coordinates 48 * @param ypoints The y coordinates 49 * @param npoints The number of points 50 */ 51 public Polygon (int[] xpoints, int[] ypoints, int npoints) 52 { 53 // We make explicit copies instead of relying on clone so that we 54 // ensure the new arrays are the same size. 103 /** 104 * Cached flattened version - condense points and parallel lines, so the 105 * result has area if there are >= 3 condensed vertices. flat[0] is the 106 * number of condensed points, and (flat[odd], flat[odd+1]) form the 107 * condensed points. 108 * 109 * @see #condense() 110 * @see #contains(double, double) 111 * @see #contains(double, double, double, double) 112 */ 113 private transient int[] condensed; 114 115 /** 116 * Initializes an empty polygon. 117 */ 118 public Polygon() 119 { 120 // Leave room for growth. 121 xpoints = new int[4]; 122 ypoints = new int[4]; 123 } 124 125 /** 126 * Create a new polygon with the specified endpoints. The arrays are copied, 127 * so that future modifications to the parameters do not affect the polygon. 128 * 129 * @param xpoints the array of X coordinates for this polygon 130 * @param ypoints the array of Y coordinates for this polygon 131 * @param npoints the total number of endpoints in this polygon 132 * @throws NegativeArraySizeException if npoints is negative 133 * @throws IndexOutOfBoundsException if npoints exceeds either array 134 * @throws NullPointerException if xpoints or ypoints is null 135 */ 136 public Polygon(int[] xpoints, int[] ypoints, int npoints) 137 { 55 138 this.xpoints = new int[npoints]; 56 139 this.ypoints = new int[npoints]; 57 System.arraycopy (xpoints, 0, this.xpoints, 0, npoints); 58 System.arraycopy (ypoints, 0, this.ypoints, 0, npoints); 59 } 60 61 /** Append the specified point to this Polygon. 62 * @param x The x coordinate 63 * @param y The y coordinate 64 */ 65 public void addPoint (int x, int y) 66 { 67 int[] newx = new int[npoints + 1]; 68 System.arraycopy (xpoints, 0, newx, 0, npoints); 69 int[] newy = new int[npoints + 1]; 70 System.arraycopy (ypoints, 0, newy, 0, npoints); 71 newx[npoints] = x; 72 newy[npoints] = y; 73 ++npoints; 74 xpoints = newx; 75 ypoints = newy; 76 77 // It is simpler to just recompute. 140 System.arraycopy(xpoints, 0, this.xpoints, 0, npoints); 141 System.arraycopy(ypoints, 0, this.ypoints, 0, npoints); 142 this.npoints = npoints; 143 } 144 145 /** 146 * Reset the polygon to be empty. The arrays are left alone, to avoid object 147 * allocation, but the number of points is set to 0, and all cached data 148 * is discarded. If you are discarding a huge number of points, it may be 149 * more efficient to just create a new Polygon. 150 * 151 * @see #invalidate() 152 * @since 1.4 153 */ 154 public void reset() 155 { 156 npoints = 0; 157 invalidate(); 158 } 159 160 /** 161 * Invalidate or flush all cached data. After direct manipulation of the 162 * public member fields, this is necessary to avoid inconsistent results 163 * in methods like <code>contains</code>. 164 * 165 * @see #getBounds() 166 * @since 1.4 167 */ 168 public void invalidate() 169 { 170 bounds = null; 171 condensed = null; 172 } 173 174 /** 175 * Translates the polygon by adding the specified values to all X and Y 176 * coordinates. This updates the bounding box, if it has been calculated. 177 * 178 * @param dx the amount to add to all X coordinates 179 * @param dy the amount to add to all Y coordinates 180 * @since 1.1 181 */ 182 public void translate(int dx, int dy) 183 { 184 int i = npoints; 185 while (--i >= 0) 186 { 187 xpoints[i] += dx; 188 xpoints[i] += dy; 189 } 78 190 if (bounds != null) 79 computeBoundingBox (); 80 } 81 82 /** Return true if the indicated point is inside this Polygon. 83 * This uses an even-odd rule to determine insideness. 84 * @param x The x coordinate 85 * @param y The y coordinate 86 * @returns true if the point is contained by this Polygon. 87 */ 88 public boolean contains (double x, double y) 89 { 90 // What we do is look at each line segment. If the line segment 91 // crosses the "scan line" at y at a point x' < x, then we 92 // increment our counter. At the end, an even number means the 93 // point is outside the polygon. Instead of a number, though, we 94 // use a boolean. 191 { 192 bounds.x += dx; 193 bounds.y += dy; 194 } 195 condensed = null; 196 } 197 198 /** 199 * Adds the specified endpoint to the polygon. This updates the bounding 200 * box, if it has been created. 201 * 202 * @param x the X coordinate of the point to add 203 * @param y the Y coordiante of the point to add 204 */ 205 public void addPoint(int x, int y) 206 { 207 if (npoints + 1 > xpoints.length) 208 { 209 int[] newx = new int[npoints + 1]; 210 System.arraycopy(xpoints, 0, newx, 0, npoints); 211 xpoints = newx; 212 } 213 if (npoints + 1 > ypoints.length) 214 { 215 int[] newy = new int[npoints + 1]; 216 System.arraycopy(ypoints, 0, newy, 0, npoints); 217 ypoints = newy; 218 } 219 xpoints[npoints] = x; 220 ypoints[npoints] = y; 221 npoints++; 222 if (bounds != null) 223 { 224 if (npoints == 1) 225 { 226 bounds.x = x; 227 bounds.y = y; 228 } 229 else 230 { 231 if (x < bounds.x) 232 { 233 bounds.width += bounds.x - x; 234 bounds.x = x; 235 } 236 else if (x > bounds.x + bounds.width) 237 bounds.width = x - bounds.x; 238 if (y < bounds.y) 239 { 240 bounds.height += bounds.y - y; 241 bounds.y = y; 242 } 243 else if (y > bounds.y + bounds.height) 244 bounds.height = y - bounds.y; 245 } 246 } 247 condensed = null; 248 } 249 250 /** 251 * Returns the bounding box of this polygon. This is the smallest 252 * rectangle with sides parallel to the X axis that will contain this 253 * polygon. 254 * 255 * @return the bounding box for this polygon 256 * @see #getBounds2D() 257 * @since 1.1 258 */ 259 public Rectangle getBounds() 260 { 261 if (bounds == null) 262 { 263 if (npoints == 0) 264 return bounds = new Rectangle(); 265 int i = npoints - 1; 266 int minx = xpoints[i]; 267 int maxx = minx; 268 int miny = ypoints[i]; 269 int maxy = miny; 270 while (--i >= 0) 271 { 272 int x = xpoints[i]; 273 int y = ypoints[i]; 274 if (x < minx) 275 minx = x; 276 else if (x > maxx) 277 maxx = x; 278 if (y < miny) 279 miny = y; 280 else if (y > maxy) 281 maxy = y; 282 } 283 bounds = new Rectangle(minx, maxy, maxx - minx, maxy - miny); 284 } 285 return bounds; 286 } 287 288 /** 289 * Returns the bounding box of this polygon. This is the smallest 290 * rectangle with sides parallel to the X axis that will contain this 291 * polygon. 292 * 293 * @return the bounding box for this polygon 294 * @see #getBounds2D() 295 * @deprecated use {@link #getBounds()} instead 296 */ 297 public Rectangle getBoundingBox() 298 { 299 return getBounds(); 300 } 301 302 /** 303 * Tests whether or not the specified point is inside this polygon. 304 * 305 * @param p the point to test 306 * @return true if the point is inside this polygon 307 * @throws NullPointerException if p is null 308 * @see #contains(double, double) 309 */ 310 public boolean contains(Point p) 311 { 312 return contains(p.getX(), p.getY()); 313 } 314 315 /** 316 * Tests whether or not the specified point is inside this polygon. 317 * 318 * @param x the X coordinate of the point to test 319 * @param y the Y coordinate of the point to test 320 * @return true if the point is inside this polygon 321 * @see #contains(double, double) 322 * @since 1.1 323 */ 324 public boolean contains(int x, int y) 325 { 326 return contains((double) x, (double) y); 327 } 328 329 /** 330 * Tests whether or not the specified point is inside this polygon. 331 * 332 * @param x the X coordinate of the point to test 333 * @param y the Y coordinate of the point to test 334 * @return true if the point is inside this polygon 335 * @see #contains(double, double) 336 * @deprecated use {@link #contains(int, int)} instead 337 */ 338 public boolean inside(int x, int y) 339 { 340 return contains((double) x, (double) y); 341 } 342 343 /** 344 * Returns a high-precision bounding box of this polygon. This is the 345 * smallest rectangle with sides parallel to the X axis that will contain 346 * this polygon. 347 * 348 * @return the bounding box for this polygon 349 * @see #getBounds() 350 * @since 1.2 351 */ 352 public Rectangle2D getBounds2D() 353 { 354 // For polygons, the integer version is exact! 355 return getBounds(); 356 } 357 358 /** 359 * Tests whether or not the specified point is inside this polygon. 360 * 361 * @param x the X coordinate of the point to test 362 * @param y the Y coordinate of the point to test 363 * @return true if the point is inside this polygon 364 * @since 1.2 365 */ 366 public boolean contains(double x, double y) 367 { 368 // First, the obvious bounds checks. 369 if (! condense() || ! getBounds().contains(x, y)) 370 return false; 371 // A point is contained if a ray to (-inf, y) crosses an odd number 372 // of segments. This must obey the semantics of Shape when the point is 373 // exactly on a segment or vertex: a point is inside only if the adjacent 374 // point in the increasing x or y direction is also inside. Note that we 375 // are guaranteed that the condensed polygon has area, and no consecutive 376 // segments with identical slope. 95 377 boolean inside = false; 96 for (int i = 0; i < npoints; ++i) 97 { 98 // Handle the wrap case. 99 int x2 = (i == npoints) ? xpoints[0] : xpoints[i + 1]; 100 int y2 = (i == npoints) ? ypoints[0] : ypoints[i + 1]; 101 102 if (ypoints[i] == y2) 103 { 104 // We ignore horizontal lines. This might give weird 105 // results in some situations -- ? 106 continue; 107 } 108 109 double t = (y - ypoints[i]) / (double) (y2 - ypoints[i]); 110 double x3 = xpoints[i] + t * (x2 - xpoints[i]); 111 if (x3 < x) 112 inside = ! inside; 113 } 114 378 int limit = condensed[0]; 379 int curx = condensed[(limit << 1) - 1]; 380 int cury = condensed[limit << 1]; 381 for (int i = 1; i <= limit; i++) 382 { 383 int priorx = curx; 384 int priory = cury; 385 curx = condensed[(i << 1) - 1]; 386 cury = condensed[i << 1]; 387 if ((priorx > x && curx > x) // Left of segment, or NaN. 388 || (priory > y && cury > y) // Below segment, or NaN. 389 || (priory < y && cury < y)) // Above segment. 390 continue; 391 if (priory == cury) // Horizontal segment, y == cury == priory 392 { 393 if (priorx < x && curx < x) // Right of segment. 394 { 395 inside = ! inside; 396 continue; 397 } 398 // Did we approach this segment from above or below? 399 // This mess is necessary to obey rules of Shape. 400 priory = condensed[((limit + i - 2) % limit) << 1]; 401 boolean above = priory > cury; 402 if ((curx == x && (curx > priorx || above)) 403 || (priorx == x && (curx < priorx || ! above)) 404 || (curx > priorx && ! above) || above) 405 inside = ! inside; 406 continue; 407 } 408 if (priorx == x && priory == y) // On prior vertex. 409 continue; 410 if (priorx == curx // Vertical segment. 411 || (priorx < x && curx < x)) // Right of segment. 412 { 413 inside = ! inside; 414 continue; 415 } 416 // The point is inside the segment's bounding box, compare slopes. 417 double leftx = curx > priorx ? priorx : curx; 418 double lefty = curx > priorx ? priory : cury; 419 double slopeseg = (double) (cury - priory) / (curx - priorx); 420 double slopepoint = (double) (y - lefty) / (x - leftx); 421 if ((slopeseg > 0 && slopeseg > slopepoint) 422 || slopeseg < slopepoint) 423 inside = ! inside; 424 } 115 425 return inside; 116 426 } 117 427 118 /** Return true if the indicated rectangle is entirely inside this 119 * Polygon. 120 * This uses an even-odd rule to determine insideness. 121 * @param x The x coordinate 122 * @param y The y coordinate 123 * @param w The width 124 * @param h The height 125 * @returns true if the rectangle is contained by this Polygon. 126 */ 127 public boolean contains (double x, double y, double w, double h) 128 { 129 return intersectOrContains (x, y, w, h, false); 130 } 131 132 /** Return true if the indicated point is inside this Polygon. 133 * This uses an even-odd rule to determine insideness. 134 * @param x The x coordinate 135 * @param y The y coordinate 136 * @returns true if the point is contained by this Polygon. 137 */ 138 public boolean contains (int x, int y) 139 { 140 return contains ((double) x, (double) y); 141 } 142 143 /** Return true if the indicated point is inside this Polygon. 144 * This uses an even-odd rule to determine insideness. 145 * @param p The point 146 * @returns true if the point is contained by this Polygon. 147 */ 148 public boolean contains (Point p) 149 { 150 return contains (p.x, p.y); 151 } 152 153 /** Return true if the indicated point is inside this Polygon. 154 * This uses an even-odd rule to determine insideness. 155 * @param p The point 156 * @returns true if the point is contained by this Polygon. 157 */ 158 public boolean contains (Point2D p) 159 { 160 return contains (p.getX (), p.getY ()); 161 } 162 163 /** Return true if the indicated rectangle is entirely inside this 164 * Polygon. This uses an even-odd rule to determine insideness. 165 * @param r The rectangle 166 * @returns true if the rectangle is contained by this Polygon. 167 */ 168 public boolean contains (Rectangle2D r) 169 { 170 return contains (r.getX (), r.getY (), r.getWidth (), r.getHeight ()); 171 } 172 173 /** Returns the bounds of this Polygon. 174 * @deprecated Use getBounds() instead. 175 */ 176 public Rectangle getBoundingBox () 177 { 178 if (bounds == null) 179 computeBoundingBox (); 180 return bounds; 181 } 182 183 /** Returns the bounds of this Polygon. */ 184 public Rectangle getBounds () 185 { 186 if (bounds == null) 187 computeBoundingBox (); 188 return bounds; 189 } 190 191 /** Returns the bounds of this Polygon. */ 192 public Rectangle2D getBounds2D () 193 { 194 if (bounds == null) 195 computeBoundingBox (); 196 return bounds; // Why not? 197 } 198 199 /** Return an iterator for the boundary of this Polygon. 200 * @param at A transform to apply to the coordinates. 201 * @returns A path iterator for the Polygon's boundary. 202 */ 203 public PathIterator getPathIterator (AffineTransform at) 204 { 205 return new Iterator (at); 206 } 207 208 /** Return an iterator for the boundary of this Polygon. 209 * @param at A transform to apply to the coordinates. 210 * @param flatness The flatness of the result; it is ignored by 211 * this class. 212 * @returns A path iterator for the Polygon's boundary. 213 */ 214 public PathIterator getPathIterator (AffineTransform at, double flatness) 215 { 216 // We ignore the flatness. 217 return new Iterator (at); 218 } 219 220 /** @deprecated use contains(int,int). */ 221 public boolean inside (int x, int y) 222 { 223 return contains (x, y); 224 } 225 226 /** Return true if this Polygon's interior intersects the given 227 * rectangle's interior. 228 * @param x The x coordinate 229 * @param y The y coordinate 230 * @param w The width 231 * @param h The height 232 */ 233 public boolean intersects (double x, double y, double w, double h) 234 { 235 return intersectOrContains (x, y, w, h, true); 236 } 237 238 /** Return true if this Polygon's interior intersects the given 239 * rectangle's interior. 240 * @param r The rectangle 241 */ 242 public boolean intersects (Rectangle2D r) 243 { 244 return intersects (r.getX (), r.getY (), r.getWidth (), r.getHeight ()); 245 } 246 247 // This tests for intersection with or containment of a rectangle, 248 // depending on the INTERSECT argument. 249 private boolean intersectOrContains (double x, double y, double w, double h, 250 boolean intersect) 251 { 252 // First compute the rectangle of possible intersection points. 253 Rectangle r = getBounds (); 254 int minx = Math.max (r.x, (int) x); 255 int maxx = Math.min (r.x + r.width, (int) (x + w)); 256 int miny = Math.max (r.y, (int) y); 257 int maxy = Math.min (r.y + r.height, (int) (y + h)); 258 259 if (miny > maxy) 428 /** 429 * Tests whether or not the specified point is inside this polygon. 430 * 431 * @param p the point to test 432 * @return true if the point is inside this polygon 433 * @throws NullPointerException if p is null 434 * @see #contains(double, double) 435 * @since 1.2 436 */ 437 public boolean contains(Point2D p) 438 { 439 return contains(p.getX(), p.getY()); 440 } 441 442 /** 443 * Test if a high-precision rectangle intersects the shape. This is true 444 * if any point in the rectangle is in the shape. This implementation is 445 * precise. 446 * 447 * @param x the x coordinate of the rectangle 448 * @param y the y coordinate of the rectangle 449 * @param w the width of the rectangle, treated as point if negative 450 * @param h the height of the rectangle, treated as point if negative 451 * @return true if the rectangle intersects this shape 452 * @since 1.2 453 */ 454 public boolean intersects(double x, double y, double w, double h) 455 { 456 // First, the obvious bounds checks. 457 if (w <= 0 || h <= 0 || npoints == 0 || 458 ! getBounds().intersects(x, y, w, h)) 459 return false; // Disjoint bounds. 460 if ((x <= bounds.x && x + w >= bounds.x + bounds.width 461 && y <= bounds.y && y + h >= bounds.y + bounds.height) 462 || contains(x, y)) 463 return true; // Rectangle contains the polygon, or one point matches. 464 // If any vertex is in the rectangle, the two might intersect. 465 int curx = 0; 466 int cury = 0; 467 for (int i = 0; i < npoints; i++) 468 { 469 curx = xpoints[i]; 470 cury = ypoints[i]; 471 if (curx >= x && curx < x + w && cury >= y && cury < y + h 472 && contains(curx, cury)) // Boundary check necessary. 473 return true; 474 } 475 // Finally, if at least one of the four bounding lines intersect any 476 // segment of the polygon, return true. Be careful of the semantics of 477 // Shape; coinciding lines do not necessarily return true. 478 for (int i = 0; i < npoints; i++) 479 { 480 int priorx = curx; 481 int priory = cury; 482 curx = xpoints[i]; 483 cury = ypoints[i]; 484 if (priorx == curx) // Vertical segment. 485 { 486 if (curx < x || curx >= x + w) // Outside rectangle. 487 continue; 488 if ((cury >= y + h && priory <= y) 489 || (cury <= y && priory >= y + h)) 490 return true; // Bisects rectangle. 491 continue; 492 } 493 if (priory == cury) // Horizontal segment. 494 { 495 if (cury < y || cury >= y + h) // Outside rectangle. 496 continue; 497 if ((curx >= x + w && priorx <= x) 498 || (curx <= x && priorx >= x + w)) 499 return true; // Bisects rectangle. 500 continue; 501 } 502 // Slanted segment. 503 double slope = (double) (cury - priory) / (curx - priorx); 504 double intersect = slope * (x - curx) + cury; 505 if (intersect > y && intersect < y + h) // Intersects left edge. 506 return true; 507 intersect = slope * (x + w - curx) + cury; 508 if (intersect > y && intersect < y + h) // Intersects right edge. 509 return true; 510 intersect = (y - cury) / slope + curx; 511 if (intersect > x && intersect < x + w) // Intersects bottom edge. 512 return true; 513 intersect = (y + h - cury) / slope + cury; 514 if (intersect > x && intersect < x + w) // Intersects top edge. 515 return true; 516 } 517 return false; 518 } 519 520 /** 521 * Test if a high-precision rectangle intersects the shape. This is true 522 * if any point in the rectangle is in the shape. This implementation is 523 * precise. 524 * 525 * @param r the rectangle 526 * @return true if the rectangle intersects this shape 527 * @throws NullPointerException if r is null 528 * @see #intersects(double, double, double, double) 529 * @since 1.2 530 */ 531 public boolean intersects(Rectangle2D r) 532 { 533 return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 534 } 535 536 /** 537 * Test if a high-precision rectangle lies completely in the shape. This is 538 * true if all points in the rectangle are in the shape. This implementation 539 * is precise. 540 * 541 * @param x the x coordinate of the rectangle 542 * @param y the y coordinate of the rectangle 543 * @param w the width of the rectangle, treated as point if negative 544 * @param h the height of the rectangle, treated as point if negative 545 * @return true if the rectangle is contained in this shape 546 * @since 1.2 547 */ 548 public boolean contains(double x, double y, double w, double h) 549 { 550 // First, the obvious bounds checks. 551 if (w <= 0 || h <= 0 || ! contains(x, y) 552 || ! bounds.contains(x, y, w, h)) 260 553 return false; 261 262 double[] crosses = new double[npoints + 1]; 263 264 for (; miny < maxy; ++miny) 265 { 266 // First compute every place where the polygon might intersect 267 // the scan line at Y. 268 int ins = 0; 269 for (int i = 0; i < npoints; ++i) 270 { 271 // Handle the wrap case. 272 int x2 = (i == npoints) ? xpoints[0] : xpoints[i + 1]; 273 int y2 = (i == npoints) ? ypoints[0] : ypoints[i + 1]; 274 275 if (ypoints[i] == y2) 276 { 277 // We ignore horizontal lines. This might give weird 278 // results in some situations -- ? 279 continue; 280 } 281 282 double t = (((double) miny - ypoints[i]) 283 / (double) (y2 - ypoints[i])); 284 double x3 = xpoints[i] + t * (x2 - xpoints[i]); 285 crosses[ins++] = x3; 286 } 287 288 // Now we can sort into increasing order and look to see if 289 // any point in the rectangle is in the polygon. We examine 290 // every other pair due to our even-odd rule. 291 Arrays.sort (crosses, 0, ins); 292 int i = intersect ? 0 : 1; 293 for (; i < ins - 1; i += 2) 294 { 295 // Pathological case. 296 if (crosses[i] == crosses[i + 1]) 297 continue; 298 299 // Found a point on the inside. 300 if ((crosses[i] >= x && crosses[i] < x + w) 301 || (crosses[i + 1] >= x && crosses[i + 1] < x + w)) 302 { 303 // If we're checking containment then we just lost. 304 // But if we're checking intersection then we just 305 // won. 306 return intersect; 307 } 308 } 309 } 310 311 return false; 312 } 313 314 /** Translates all the vertices of the polygon via a given vector. 315 * @param deltaX The X offset 316 * @param deltaY The Y offset 317 */ 318 public void translate (int deltaX, int deltaY) 319 { 320 for (int i = 0; i < npoints; ++i) 321 { 322 xpoints[i] += deltaX; 323 ypoints[i] += deltaY; 324 } 325 326 if (bounds != null) 327 { 328 bounds.x += deltaX; 329 bounds.y += deltaY; 330 } 331 } 332 333 // This computes the bounding box if required. 334 private void computeBoundingBox () 335 { 336 if (npoints == 0) 337 { 338 // This is wrong if the user adds a new point, but we 339 // account for that in addPoint(). 340 bounds = new Rectangle (0, 0, 0, 0); 341 } 342 else 343 { 344 int maxx = xpoints[0]; 345 int minx = xpoints[0]; 346 int maxy = ypoints[0]; 347 int miny = ypoints[0]; 348 349 for (int i = 1; i < npoints; ++i) 350 { 351 maxx = Math.max (maxx, xpoints[i]); 352 minx = Math.min (minx, xpoints[i]); 353 maxy = Math.max (maxy, ypoints[i]); 354 miny = Math.min (miny, ypoints[i]); 355 } 356 357 bounds = new Rectangle (minx, miny, maxx - minx, maxy - miny); 358 } 359 } 360 361 private class Iterator implements PathIterator 362 { 363 public AffineTransform xform; 364 public int where; 365 366 public Iterator (AffineTransform xform) 554 // Now, if any of the four bounding lines intersects a polygon segment, 555 // return false. The previous check had the side effect of setting 556 // the condensed array, which we use. Be careful of the semantics of 557 // Shape; coinciding lines do not necessarily return false. 558 int limit = condensed[0]; 559 int curx = condensed[(limit << 1) - 1]; 560 int cury = condensed[limit << 1]; 561 for (int i = 1; i <= limit; i++) 562 { 563 int priorx = curx; 564 int priory = cury; 565 curx = condensed[(i << 1) - 1]; 566 cury = condensed[i << 1]; 567 if (curx > x && curx < x + w && cury > y && cury < y + h) 568 return false; // Vertex is in rectangle. 569 if (priorx == curx) // Vertical segment. 570 { 571 if (curx < x || curx > x + w) // Outside rectangle. 572 continue; 573 if ((cury >= y + h && priory <= y) 574 || (cury <= y && priory >= y + h)) 575 return false; // Bisects rectangle. 576 continue; 577 } 578 if (priory == cury) // Horizontal segment. 579 { 580 if (cury < y || cury > y + h) // Outside rectangle. 581 continue; 582 if ((curx >= x + w && priorx <= x) 583 || (curx <= x && priorx >= x + w)) 584 return false; // Bisects rectangle. 585 continue; 586 } 587 // Slanted segment. 588 double slope = (double) (cury - priory) / (curx - priorx); 589 double intersect = slope * (x - curx) + cury; 590 if (intersect > y && intersect < y + h) // Intersects left edge. 591 return false; 592 intersect = slope * (x + w - curx) + cury; 593 if (intersect > y && intersect < y + h) // Intersects right edge. 594 return false; 595 intersect = (y - cury) / slope + curx; 596 if (intersect > x && intersect < x + w) // Intersects bottom edge. 597 return false; 598 intersect = (y + h - cury) / slope + cury; 599 if (intersect > x && intersect < x + w) // Intersects top edge. 600 return false; 601 } 602 return true; 603 } 604 605 /** 606 * Test if a high-precision rectangle lies completely in the shape. This is 607 * true if all points in the rectangle are in the shape. This implementation 608 * is precise. 609 * 610 * @param r the rectangle 611 * @return true if the rectangle is contained in this shape 612 * @throws NullPointerException if r is null 613 * @see #contains(double, double, double, double) 614 * @since 1.2 615 */ 616 public boolean contains(Rectangle2D r) 617 { 618 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 619 } 620 621 /** 622 * Return an iterator along the shape boundary. If the optional transform 623 * is provided, the iterator is transformed accordingly. Each call returns 624 * a new object, independent from others in use. This class is not 625 * threadsafe to begin with, so the path iterator is not either. 626 * 627 * @param transform an optional transform to apply to the iterator 628 * @return a new iterator over the boundary 629 * @since 1.2 630 */ 631 public PathIterator getPathIterator(final AffineTransform transform) 632 { 633 return new PathIterator() 367 634 { 368 this.xform = xform; 369 where = 0; 370 } 371 372 public int currentSegment (double[] coords) 373 { 374 int r; 375 376 if (where < npoints) 377 { 378 coords[0] = xpoints[where]; 379 coords[1] = ypoints[where]; 380 r = (where == 0) ? SEG_MOVETO : SEG_LINETO; 381 xform.transform (coords, 0, coords, 0, 1); 382 ++where; 383 } 384 else 385 r = SEG_CLOSE; 386 387 return r; 388 } 389 390 public int currentSegment (float[] coords) 391 { 392 int r; 393 394 if (where < npoints) 395 { 396 coords[0] = xpoints[where]; 397 coords[1] = ypoints[where]; 398 r = (where == 0) ? SEG_MOVETO : SEG_LINETO; 399 xform.transform (coords, 0, coords, 0, 1); 400 } 401 else 402 r = SEG_CLOSE; 403 404 return r; 405 } 406 407 public int getWindingRule () 408 { 409 return WIND_EVEN_ODD; 410 } 411 412 public boolean isDone () 413 { 414 return where == npoints + 1; 415 } 416 417 public void next () 418 { 419 ++where; 420 } 421 } 422 } 635 /** The current vertex of iteration. */ 636 private int vertex; 637 638 public int getWindingRule() 639 { 640 return WIND_EVEN_ODD; 641 } 642 643 public boolean isDone() 644 { 645 return vertex > npoints; 646 } 647 648 public void next() 649 { 650 vertex++; 651 } 652 653 public int currentSegment(float[] coords) 654 { 655 if (vertex >= npoints) 656 return SEG_CLOSE; 657 coords[0] = xpoints[vertex]; 658 coords[1] = ypoints[vertex]; 659 if (transform != null) 660 transform.transform(coords, 0, coords, 0, 1); 661 return vertex == 0 ? SEG_MOVETO : SEG_LINETO; 662 } 663 664 public int currentSegment(double[] coords) 665 { 666 if (vertex >= npoints) 667 return SEG_CLOSE; 668 coords[0] = xpoints[vertex]; 669 coords[1] = ypoints[vertex]; 670 if (transform != null) 671 transform.transform(coords, 0, coords, 0, 1); 672 return vertex == 0 ? SEG_MOVETO : SEG_LINETO; 673 } 674 }; 675 } 676 677 /** 678 * Return an iterator along the flattened version of the shape boundary. 679 * Since polygons are already flat, the flatness parameter is ignored, and 680 * the resulting iterator only has SEG_MOVETO, SEG_LINETO and SEG_CLOSE 681 * points. If the optional transform is provided, the iterator is 682 * transformed accordingly. Each call returns a new object, independent 683 * from others in use. This class is not threadsafe to begin with, so the 684 * path iterator is not either. 685 * 686 * @param transform an optional transform to apply to the iterator 687 * @param double the maximum distance for deviation from the real boundary 688 * @return a new iterator over the boundary 689 * @since 1.2 690 */ 691 public PathIterator getPathIterator(AffineTransform transform, 692 double flatness) 693 { 694 return getPathIterator(transform); 695 } 696 697 /** 698 * Helper for contains, which caches a condensed version of the polygon. 699 * This condenses all colinear points, so that consecutive segments in 700 * the condensed version always have different slope. 701 * 702 * @return true if the condensed polygon has area 703 * @see #condensed 704 * @see #contains(double, double) 705 */ 706 private boolean condense() 707 { 708 if (npoints <= 2) 709 return false; 710 if (condensed != null) 711 return condensed[0] > 2; 712 condensed = new int[npoints * 2 + 1]; 713 int curx = xpoints[npoints - 1]; 714 int cury = ypoints[npoints - 1]; 715 double curslope = Double.NaN; 716 int count = 0; 717 outer: 718 for (int i = 0; i < npoints; i++) 719 { 720 int priorx = curx; 721 int priory = cury; 722 double priorslope = curslope; 723 curx = xpoints[i]; 724 cury = ypoints[i]; 725 while (curx == priorx && cury == priory) 726 { 727 if (++i == npoints) 728 break outer; 729 curx = xpoints[i]; 730 cury = ypoints[i]; 731 } 732 curslope = (curx == priorx ? Double.POSITIVE_INFINITY 733 : (double) (cury - priory) / (curx - priorx)); 734 if (priorslope == curslope) 735 { 736 if (count > 1 && condensed[(count << 1) - 3] == curx 737 && condensed[(count << 1) - 2] == cury) 738 { 739 count--; 740 continue; 741 } 742 } 743 else 744 count++; 745 condensed[(count << 1) - 1] = curx; 746 condensed[count << 1] = cury; 747 } 748 condensed[0] = count; 749 return count > 2; 750 } 751 } // class Polygon -
Property cvs2svn:cvs-rev
changed from
Note:
See TracChangeset
for help on using the changeset viewer.
