001/* 002 * To change this license header, choose License Headers in Project Properties. 003 * To change this template file, choose Tools | Templates 004 * and open the template in the editor. 005 */ 006 007package armyc2.c2sd.graphics2d; 008//package java.awt; 009 010//import java.awt.geom.AffineTransform; 011//import java.awt.geom.PathIterator; 012//import java.awt.geom.Point2D; 013//import java.awt.geom.Rectangle2D; 014//import sun.awt.geom.Crossings; 015import android.graphics.Path; 016import android.graphics.RectF; 017import java.util.Arrays; 018 019/** 020 * 021 * @author Michael Deutch 022 */ 023public class Polygon { 024 /** 025 * The total number of points. The value of <code>npoints</code> 026 * represents the number of valid points in this <code>Polygon</code> 027 * and might be less than the number of elements in 028 * {@link #xpoints xpoints} or {@link #ypoints ypoints}. 029 * This value can be NULL. 030 * 031 * @serial 032 * @see #addPoint(int, int) 033 * @since 1.0 034 */ 035 public int npoints; 036 037 /** 038 * The array of X coordinates. The number of elements in 039 * this array might be more than the number of X coordinates 040 * in this <code>Polygon</code>. The extra elements allow new points 041 * to be added to this <code>Polygon</code> without re-creating this 042 * array. The value of {@link #npoints npoints} is equal to the 043 * number of valid points in this <code>Polygon</code>. 044 * 045 * @serial 046 * @see #addPoint(int, int) 047 * @since 1.0 048 */ 049 public int xpoints[]; 050 051 /** 052 * The array of Y coordinates. The number of elements in 053 * this array might be more than the number of Y coordinates 054 * in this <code>Polygon</code>. The extra elements allow new points 055 * to be added to this <code>Polygon</code> without re-creating this 056 * array. The value of <code>npoints</code> is equal to the 057 * number of valid points in this <code>Polygon</code>. 058 * 059 * @serial 060 * @see #addPoint(int, int) 061 * @since 1.0 062 */ 063 public int ypoints[]; 064 065 /** 066 * The bounds of this {@code Polygon}. 067 * This value can be null. 068 * 069 * @serial 070 * @see #getBoundingBox() 071 * @see #getBounds() 072 * @since 1.0 073 */ 074 protected Rectangle bounds; 075 076 /* 077 * JDK 1.1 serialVersionUID 078 */ 079 private static final long serialVersionUID = -6460061437900069969L; 080 081 /* 082 * Default length for xpoints and ypoints. 083 */ 084 private static final int MIN_LENGTH = 4; 085 086 /** 087 * Creates an empty polygon. 088 * @since 1.0 089 */ 090 public Polygon() { 091 xpoints = new int[MIN_LENGTH]; 092 ypoints = new int[MIN_LENGTH]; 093 } 094 095 /** 096 * Constructs and initializes a <code>Polygon</code> from the specified 097 * parameters. 098 * @param xpoints an array of X coordinates 099 * @param ypoints an array of Y coordinates 100 * @param npoints the total number of points in the 101 * <code>Polygon</code> 102 * @exception NegativeArraySizeException if the value of 103 * <code>npoints</code> is negative. 104 * @exception IndexOutOfBoundsException if <code>npoints</code> is 105 * greater than the length of <code>xpoints</code> 106 * or the length of <code>ypoints</code>. 107 * @exception NullPointerException if <code>xpoints</code> or 108 * <code>ypoints</code> is <code>null</code>. 109 * @since 1.0 110 */ 111 public Polygon(int xpoints[], int ypoints[], int npoints) { 112 // Fix 4489009: should throw IndexOutofBoundsException instead 113 // of OutofMemoryException if npoints is huge and > {x,y}points.length 114 if (npoints > xpoints.length || npoints > ypoints.length) { 115 throw new IndexOutOfBoundsException("npoints > xpoints.length || "+ 116 "npoints > ypoints.length"); 117 } 118 // Fix 6191114: should throw NegativeArraySizeException with 119 // negative npoints 120 if (npoints < 0) { 121 throw new NegativeArraySizeException("npoints < 0"); 122 } 123 // Fix 6343431: Applet compatibility problems if arrays are not 124 // exactly npoints in length 125 this.npoints = npoints; 126 this.xpoints = Arrays.copyOf(xpoints, npoints); 127 this.ypoints = Arrays.copyOf(ypoints, npoints); 128 } 129 130 /** 131 * Resets this <code>Polygon</code> object to an empty polygon. 132 * The coordinate arrays and the data in them are left untouched 133 * but the number of points is reset to zero to mark the old 134 * vertex data as invalid and to start accumulating new vertex 135 * data at the beginning. 136 * All internally-cached data relating to the old vertices 137 * are discarded. 138 * Note that since the coordinate arrays from before the reset 139 * are reused, creating a new empty <code>Polygon</code> might 140 * be more memory efficient than resetting the current one if 141 * the number of vertices in the new polygon data is significantly 142 * smaller than the number of vertices in the data from before the 143 * reset. 144 * @since 1.4 145 */ 146 public void reset() { 147 npoints = 0; 148 bounds = null; 149 } 150 151 /** 152 * Invalidates or flushes any internally-cached data that depends 153 * on the vertex coordinates of this <code>Polygon</code>. 154 * This method should be called after any direct manipulation 155 * of the coordinates in the <code>xpoints</code> or 156 * <code>ypoints</code> arrays to avoid inconsistent results 157 * from methods such as <code>getBounds</code> or <code>contains</code> 158 * that might cache data from earlier computations relating to 159 * the vertex coordinates. 160 * @since 1.4 161 */ 162 public void invalidate() { 163 bounds = null; 164 } 165 166 167 168 /* 169 * Calculates the bounding box of the points passed to the constructor. 170 * Sets <code>bounds</code> to the result. 171 * @param xpoints[] array of <i>x</i> coordinates 172 * @param ypoints[] array of <i>y</i> coordinates 173 * @param npoints the total number of points 174 */ 175 void calculateBounds(int xpoints[], int ypoints[], int npoints) { 176 int boundsMinX = Integer.MAX_VALUE; 177 int boundsMinY = Integer.MAX_VALUE; 178 int boundsMaxX = Integer.MIN_VALUE; 179 int boundsMaxY = Integer.MIN_VALUE; 180 181 for (int i = 0; i < npoints; i++) { 182 int x = xpoints[i]; 183 boundsMinX = Math.min(boundsMinX, x); 184 boundsMaxX = Math.max(boundsMaxX, x); 185 int y = ypoints[i]; 186 boundsMinY = Math.min(boundsMinY, y); 187 boundsMaxY = Math.max(boundsMaxY, y); 188 } 189 bounds = new Rectangle(boundsMinX, boundsMinY, 190 boundsMaxX - boundsMinX, 191 boundsMaxY - boundsMinY); 192 } 193 194 /* 195 * Resizes the bounding box to accommodate the specified coordinates. 196 * @param x, y the specified coordinates 197 */ 198 void updateBounds(int x, int y) { 199 if (x < bounds.x) { 200 bounds.width = bounds.width + (bounds.x - x); 201 bounds.x = x; 202 } 203 else { 204 bounds.width = Math.max(bounds.width, x - bounds.x); 205 // bounds.x = bounds.x; 206 } 207 208 if (y < bounds.y) { 209 bounds.height = bounds.height + (bounds.y - y); 210 bounds.y = y; 211 } 212 else { 213 bounds.height = Math.max(bounds.height, y - bounds.y); 214 // bounds.y = bounds.y; 215 } 216 } 217 218 /** 219 * Appends the specified coordinates to this <code>Polygon</code>. 220 * <p> 221 * If an operation that calculates the bounding box of this 222 * <code>Polygon</code> has already been performed, such as 223 * <code>getBounds</code> or <code>contains</code>, then this 224 * method updates the bounding box. 225 * @param x the specified X coordinate 226 * @param y the specified Y coordinate 227 228 * @since 1.0 229 */ 230 public void addPoint(int x, int y) { 231 if (npoints >= xpoints.length || npoints >= ypoints.length) { 232 int newLength = npoints * 2; 233 // Make sure that newLength will be greater than MIN_LENGTH and 234 // aligned to the power of 2 235 if (newLength < MIN_LENGTH) { 236 newLength = MIN_LENGTH; 237 } else if ((newLength & (newLength - 1)) != 0) { 238 newLength = Integer.highestOneBit(newLength); 239 } 240 241 xpoints = Arrays.copyOf(xpoints, newLength); 242 ypoints = Arrays.copyOf(ypoints, newLength); 243 } 244 xpoints[npoints] = x; 245 ypoints[npoints] = y; 246 npoints++; 247 if (bounds != null) { 248 updateBounds(x, y); 249 } 250 } 251 252 /** 253 * Gets the bounding box of this <code>Polygon</code>. 254 * The bounding box is the smallest {@link Rectangle} whose 255 * sides are parallel to the x and y axes of the 256 * coordinate space, and can completely contain the <code>Polygon</code>. 257 * @return a <code>Rectangle</code> that defines the bounds of this 258 * <code>Polygon</code>. 259 * @since 1.1 260 */ 261 public Rectangle getBounds() { 262 return getBoundingBox(); 263 } 264 265 /** 266 * Returns the bounds of this <code>Polygon</code>. 267 * @return the bounds of this <code>Polygon</code>. 268 * @deprecated As of JDK version 1.1, 269 * replaced by <code>getBounds()</code>. 270 * @since 1.0 271 */ 272 @Deprecated 273 public Rectangle getBoundingBox() { 274 if (npoints == 0) { 275 return new Rectangle(); 276 } 277 if (bounds == null) { 278 calculateBounds(xpoints, ypoints, npoints); 279 } 280 return bounds.getBounds(); 281 } 282 283 /** 284 * Determines whether the specified {@link Point} is inside this 285 * <code>Polygon</code>. 286 * @param p the specified <code>Point</code> to be tested 287 * @return <code>true</code> if the <code>Polygon</code> contains the 288 * <code>Point</code>; <code>false</code> otherwise. 289 * @see #contains(double, double) 290 * @since 1.0 291 */ 292 public boolean contains(Point p) { 293 return contains(p.x, p.y); 294 } 295 296 /** 297 * Determines whether the specified coordinates are inside this 298 * <code>Polygon</code>. 299 * <p> 300 * @param x the specified X coordinate to be tested 301 * @param y the specified Y coordinate to be tested 302 * @return {@code true} if this {@code Polygon} contains 303 * the specified coordinates {@code (x,y)}; 304 * {@code false} otherwise. 305 * @see #contains(double, double) 306 * @since 1.1 307 */ 308 public boolean contains(int x, int y) { 309 return contains((double) x, (double) y); 310 } 311 312 /** 313 * Determines whether the specified coordinates are contained in this 314 * <code>Polygon</code>. 315 * @param x the specified X coordinate to be tested 316 * @param y the specified Y coordinate to be tested 317 * @return {@code true} if this {@code Polygon} contains 318 * the specified coordinates {@code (x,y)}; 319 * {@code false} otherwise. 320 * @see #contains(double, double) 321 * @deprecated As of JDK version 1.1, 322 * replaced by <code>contains(int, int)</code>. 323 * @since 1.0 324 */ 325 @Deprecated 326 public boolean inside(int x, int y) { 327 return contains((double) x, (double) y); 328 } 329 330 /** 331 * {@inheritDoc} 332 * @since 1.2 333 */ 334 public Rectangle2D getBounds2D() { 335 //return getBounds(); 336 return null; 337 } 338 339 /** 340 * {@inheritDoc} 341 * @since 1.2 342 */ 343 public boolean contains(double x, double y) { 344 if (npoints <= 2 || !getBoundingBox().contains((int)x, (int)y)) { 345 return false; 346 } 347 int hits = 0; 348 349 int lastx = xpoints[npoints - 1]; 350 int lasty = ypoints[npoints - 1]; 351 int curx, cury; 352 353 // Walk the edges of the polygon 354 for (int i = 0; i < npoints; lastx = curx, lasty = cury, i++) { 355 curx = xpoints[i]; 356 cury = ypoints[i]; 357 358 if (cury == lasty) { 359 continue; 360 } 361 362 int leftx; 363 if (curx < lastx) { 364 if (x >= lastx) { 365 continue; 366 } 367 leftx = curx; 368 } else { 369 if (x >= curx) { 370 continue; 371 } 372 leftx = lastx; 373 } 374 375 double test1, test2; 376 if (cury < lasty) { 377 if (y < cury || y >= lasty) { 378 continue; 379 } 380 if (x < leftx) { 381 hits++; 382 continue; 383 } 384 test1 = x - curx; 385 test2 = y - cury; 386 } else { 387 if (y < lasty || y >= cury) { 388 continue; 389 } 390 if (x < leftx) { 391 hits++; 392 continue; 393 } 394 test1 = x - lastx; 395 test2 = y - lasty; 396 } 397 398 if (test1 < (test2 / (lasty - cury) * (lastx - curx))) { 399 hits++; 400 } 401 } 402 403 return ((hits & 1) != 0); 404 } 405 406// private Crossings getCrossings(double xlo, double ylo, 407// double xhi, double yhi) 408// { 409// Crossings cross = new Crossings.EvenOdd(xlo, ylo, xhi, yhi); 410// int lastx = xpoints[npoints - 1]; 411// int lasty = ypoints[npoints - 1]; 412// int curx, cury; 413// 414// // Walk the edges of the polygon 415// for (int i = 0; i < npoints; i++) { 416// curx = xpoints[i]; 417// cury = ypoints[i]; 418// if (cross.accumulateLine(lastx, lasty, curx, cury)) { 419// return null; 420// } 421// lastx = curx; 422// lasty = cury; 423// } 424// 425// return cross; 426// } 427 428 /** 429 * {@inheritDoc} 430 * @since 1.2 431 */ 432 public boolean contains(Point2D p) { 433 return contains(p.getX(), p.getY()); 434 } 435 436 /** 437 * {@inheritDoc} 438 * @since 1.2 439 */ 440 public boolean intersects(double x, double y, double w, double h) { 441 if (npoints <= 0 || !getBoundingBox().intersects(x, y, w, h)) { 442 return false; 443 } 444 445 //Crossings cross = getCrossings(x, y, x+w, y+h); 446 //return (cross == null || !cross.isEmpty()); 447 if (bounds != null) { 448 float fx = (float) x; 449 float fy = (float) y; 450 float fw = (float) w; 451 float fh = (float) h; 452//not sure if math is correct here 453 Path that = new Path(); 454//start 455 that.moveTo(fx, fy); 456//go right 457 that.lineTo(fx + fw, fy); 458//go down 459 that.lineTo(fx + fw, fy - fh); 460//go left 461 that.lineTo(fx, fy - fh); 462//close 463 that.close(); 464//bounds holder 465 RectF thatBounds = new RectF(); 466 RectF rectf=new RectF((float)bounds.x,(float)bounds.y,(float)bounds.x+(float)bounds.width,(float)bounds.y+(float)bounds.height); 467 return RectF.intersects(rectf, thatBounds); 468 } 469 else 470 { 471 return false; 472 } 473 } 474 475 /** 476 * {@inheritDoc} 477 * @since 1.2 478 */ 479 public boolean intersects(Rectangle2D r) { 480 return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 481 } 482 483 /** 484 * {@inheritDoc} 485 * @since 1.2 486 */ 487 public boolean contains(double x, double y, double w, double h) { 488 if (npoints <= 0 || !getBoundingBox().intersects(x, y, w, h)) { 489 return false; 490 } 491 492 //Crossings cross = getCrossings(x, y, x+w, y+h); 493 //return (cross != null && cross.covers(y, y+h)); 494 return false; 495 } 496 497 /** 498 * {@inheritDoc} 499 * @since 1.2 500 */ 501 public boolean contains(Rectangle2D r) { 502 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 503 } 504 505 /** 506 * Returns an iterator object that iterates along the boundary of this 507 * <code>Polygon</code> and provides access to the geometry 508 * of the outline of this <code>Polygon</code>. An optional 509 * {@link AffineTransform} can be specified so that the coordinates 510 * returned in the iteration are transformed accordingly. 511 * @param at an optional <code>AffineTransform</code> to be applied to the 512 * coordinates as they are returned in the iteration, or 513 * <code>null</code> if untransformed coordinates are desired 514 * @return a {@link PathIterator} object that provides access to the 515 * geometry of this <code>Polygon</code>. 516 * @since 1.2 517 */ 518 public PathIterator getPathIterator(AffineTransform at) { 519 //return new PolygonPathIterator(this, at); 520 PathIterator pi=new PathIterator(null); 521 int j=0; 522 if(npoints>0) 523 { 524 pi.moveTo(xpoints[0], ypoints[0]); 525 for(j=1;j<npoints;j++) 526 { 527 pi.lineTo(xpoints[j], ypoints[j]); 528 } 529 } 530 pi.reset(); 531 return pi; 532 } 533 534 /** 535 * Returns an iterator object that iterates along the boundary of 536 * the <code>Shape</code> and provides access to the geometry of the 537 * outline of the <code>Shape</code>. Only SEG_MOVETO, SEG_LINETO, and 538 * SEG_CLOSE point types are returned by the iterator. 539 * Since polygons are already flat, the <code>flatness</code> parameter 540 * is ignored. An optional <code>AffineTransform</code> can be specified 541 * in which case the coordinates returned in the iteration are transformed 542 * accordingly. 543 * @param at an optional <code>AffineTransform</code> to be applied to the 544 * coordinates as they are returned in the iteration, or 545 * <code>null</code> if untransformed coordinates are desired 546 * @param flatness the maximum amount that the control points 547 * for a given curve can vary from colinear before a subdivided 548 * curve is replaced by a straight line connecting the 549 * endpoints. Since polygons are already flat the 550 * <code>flatness</code> parameter is ignored. 551 * @return a <code>PathIterator</code> object that provides access to the 552 * <code>Shape</code> object's geometry. 553 * @since 1.2 554 */ 555 public PathIterator getPathIterator(AffineTransform at, double flatness) { 556 return getPathIterator(at); 557 } 558 559// class PolygonPathIterator implements PathIterator { 560// Polygon poly; 561// AffineTransform transform; 562// int index; 563// 564// public PolygonPathIterator(Polygon pg, AffineTransform at) { 565// poly = pg; 566// transform = at; 567// if (pg.npoints == 0) { 568// // Prevent a spurious SEG_CLOSE segment 569// index = 1; 570// } 571// } 572// 573// /** 574// * Returns the winding rule for determining the interior of the 575// * path. 576// * @return an integer representing the current winding rule. 577// * @see PathIterator#WIND_NON_ZERO 578// */ 579//// public int getWindingRule() { 580//// return WIND_EVEN_ODD; 581//// } 582// 583// /** 584// * Tests if there are more points to read. 585// * @return <code>true</code> if there are more points to read; 586// * <code>false</code> otherwise. 587// */ 588// public boolean isDone() { 589// return index > poly.npoints; 590// } 591// 592// /** 593// * Moves the iterator forwards, along the primary direction of 594// * traversal, to the next segment of the path when there are 595// * more points in that direction. 596// */ 597// public void next() { 598// index++; 599// } 600// 601// /** 602// * Returns the coordinates and type of the current path segment in 603// * the iteration. 604// * The return value is the path segment type: 605// * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE. 606// * A <code>float</code> array of length 2 must be passed in and 607// * can be used to store the coordinates of the point(s). 608// * Each point is stored as a pair of <code>float</code> x, y 609// * coordinates. SEG_MOVETO and SEG_LINETO types return one 610// * point, and SEG_CLOSE does not return any points. 611// * @param coords a <code>float</code> array that specifies the 612// * coordinates of the point(s) 613// * @return an integer representing the type and coordinates of the 614// * current path segment. 615// * @see PathIterator#SEG_MOVETO 616// * @see PathIterator#SEG_LINETO 617// * @see PathIterator#SEG_CLOSE 618// */ 619// public int currentSegment(float[] coords) { 620// if (index >= poly.npoints) { 621// return SEG_CLOSE; 622// } 623// coords[0] = poly.xpoints[index]; 624// coords[1] = poly.ypoints[index]; 625// if (transform != null) { 626// transform.transform(coords, 0, coords, 0, 1); 627// } 628// return (index == 0 ? SEG_MOVETO : SEG_LINETO); 629// } 630// 631// /** 632// * Returns the coordinates and type of the current path segment in 633// * the iteration. 634// * The return value is the path segment type: 635// * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE. 636// * A <code>double</code> array of length 2 must be passed in and 637// * can be used to store the coordinates of the point(s). 638// * Each point is stored as a pair of <code>double</code> x, y 639// * coordinates. 640// * SEG_MOVETO and SEG_LINETO types return one point, 641// * and SEG_CLOSE does not return any points. 642// * @param coords a <code>double</code> array that specifies the 643// * coordinates of the point(s) 644// * @return an integer representing the type and coordinates of the 645// * current path segment. 646// * @see PathIterator#SEG_MOVETO 647// * @see PathIterator#SEG_LINETO 648// * @see PathIterator#SEG_CLOSE 649// */ 650// public int currentSegment(double[] coords) 651// { 652// if (index >= poly.npoints) { 653// return SEG_CLOSE; 654// } 655// coords[0] = poly.xpoints[index]; 656// coords[1] = poly.ypoints[index]; 657// if (transform != null) { 658// transform.transform(coords, 0, coords, 0, 1); 659// } 660// return (index == 0 ? SEG_MOVETO : SEG_LINETO); 661// } 662// } 663}