1. /*
  2. * @(#)Polygon.java 1.40 01/02/09
  3. *
  4. * Copyright 1995-2001 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.awt;
  11. import java.awt.geom.AffineTransform;
  12. import java.awt.geom.PathIterator;
  13. import java.awt.geom.Point2D;
  14. import java.awt.geom.Rectangle2D;
  15. import sun.awt.geom.Crossings;
  16. /**
  17. * The <code>Polygon</code> class encapsulates a description of a
  18. * closed, two-dimensional region within a coordinate space. This
  19. * region is bounded by an arbitrary number of line segments, each of
  20. * which is one side of the polygon. Internally, a polygon
  21. * comprises of a list of (<i>x</i>, <i>y</i>)
  22. * coordinate pairs, where each pair defines a <i>vertex</i> of the
  23. * polygon, and two successive pairs are the endpoints of a
  24. * line that is a side of the polygon. The first and final
  25. * pairs of (<i>x</i>, <i>y</i>) points are joined by a line segment
  26. * that closes the polygon. This <code>Polygon</code> is defined with
  27. * an even-odd winding rule. See
  28. * {@link java.awt.geom.PathIterator#WIND_EVEN_ODD WIND_EVEN_ODD}
  29. * for a definition of the even-odd winding rule.
  30. * This class's hit-testing methods, which include the
  31. * <code>contains</code>, <code>intersects</code> and <code>inside</code>
  32. * methods, use the <i>insideness</i> definition described in the
  33. * {@link Shape} class comments.
  34. *
  35. * @version 1.26, 07/24/98
  36. * @author Sami Shaio
  37. * @author Herb Jellinek
  38. * @see Shape
  39. * @since JDK1.0
  40. */
  41. public class Polygon implements Shape, java.io.Serializable {
  42. /**
  43. * The total number of points.
  44. * This value can be NULL.
  45. *
  46. * @serial
  47. * @see #addPoint(int, int)
  48. */
  49. public int npoints = 0;
  50. /**
  51. * The array of <i>x</i> coordinates.
  52. *
  53. * @serial
  54. * @see #addPoint(int, int)
  55. */
  56. public int xpoints[] = new int[4];
  57. /**
  58. * The array of <i>y</i> coordinates.
  59. *
  60. * @serial
  61. * @see #addPoint(int, int)
  62. */
  63. public int ypoints[] = new int[4];
  64. /**
  65. * Bounds of the polygon.
  66. * This value can be NULL.
  67. * Please see the javadoc comments getBounds().
  68. *
  69. * @serial
  70. * @see #getBoundingBox()
  71. * @see #getBounds()
  72. */
  73. protected Rectangle bounds = null;
  74. /*
  75. * JDK 1.1 serialVersionUID
  76. */
  77. private static final long serialVersionUID = -6460061437900069969L;
  78. /**
  79. * Creates an empty polygon.
  80. */
  81. public Polygon() {
  82. }
  83. /**
  84. * Constructs and initializes a <code>Polygon</code> from the specified
  85. * parameters.
  86. * @param xpoints an array of <i>x</i> coordinates
  87. * @param ypoints an array of <i>y</i> coordinates
  88. * @param npoints the total number of points in the
  89. * <code>Polygon</code>
  90. * @exception NegativeArraySizeException if the value of
  91. * <code>npoints</code> is negative.
  92. * @exception IndexOutOfBoundsException if <code>npoints</code> is
  93. * greater than the length of <code>xpoints</code>
  94. * or the length of <code>ypoints</code>.
  95. * @exception NullPointerException if <code>xpoints</code> or
  96. * <code>ypoints</code> is <code>null</code>.
  97. */
  98. public Polygon(int xpoints[], int ypoints[], int npoints) {
  99. this.npoints = npoints;
  100. this.xpoints = new int[npoints];
  101. this.ypoints = new int[npoints];
  102. System.arraycopy(xpoints, 0, this.xpoints, 0, npoints);
  103. System.arraycopy(ypoints, 0, this.ypoints, 0, npoints);
  104. }
  105. /**
  106. * Translates the vertices of the <code>Polygon</code> by
  107. * <code>deltaX</code> along the x axis and by
  108. * <code>deltaY</code> along the y axis.
  109. * @param deltaX the amount to translate along the <i>x</i> axis
  110. * @param deltaY the amount to translate along the <i>y</i> axis
  111. * @since JDK1.1
  112. */
  113. public void translate(int deltaX, int deltaY) {
  114. for (int i = 0; i < npoints; i++) {
  115. xpoints[i] += deltaX;
  116. ypoints[i] += deltaY;
  117. }
  118. if (bounds != null) {
  119. bounds.translate(deltaX, deltaY);
  120. }
  121. }
  122. /*
  123. * Calculates the bounding box of the points passed to the constructor.
  124. * Sets <code>bounds</code> to the result.
  125. * @param xpoints[] array of <i>x</i> coordinates
  126. * @param ypoints[] array of <i>y</i> coordinates
  127. * @param npoints the total number of points
  128. */
  129. void calculateBounds(int xpoints[], int ypoints[], int npoints) {
  130. int boundsMinX = Integer.MAX_VALUE;
  131. int boundsMinY = Integer.MAX_VALUE;
  132. int boundsMaxX = Integer.MIN_VALUE;
  133. int boundsMaxY = Integer.MIN_VALUE;
  134. for (int i = 0; i < npoints; i++) {
  135. int x = xpoints[i];
  136. boundsMinX = Math.min(boundsMinX, x);
  137. boundsMaxX = Math.max(boundsMaxX, x);
  138. int y = ypoints[i];
  139. boundsMinY = Math.min(boundsMinY, y);
  140. boundsMaxY = Math.max(boundsMaxY, y);
  141. }
  142. bounds = new Rectangle(boundsMinX, boundsMinY,
  143. boundsMaxX - boundsMinX,
  144. boundsMaxY - boundsMinY);
  145. }
  146. /*
  147. * Resizes the bounding box to accomodate the specified coordinates.
  148. * @param x, y the specified coordinates
  149. */
  150. void updateBounds(int x, int y) {
  151. if (x < bounds.x) {
  152. bounds.width = bounds.width + (bounds.x - x);
  153. bounds.x = x;
  154. }
  155. else {
  156. bounds.width = Math.max(bounds.width, x - bounds.x);
  157. // bounds.x = bounds.x;
  158. }
  159. if (y < bounds.y) {
  160. bounds.height = bounds.height + (bounds.y - y);
  161. bounds.y = y;
  162. }
  163. else {
  164. bounds.height = Math.max(bounds.height, y - bounds.y);
  165. // bounds.y = bounds.y;
  166. }
  167. }
  168. /**
  169. * Appends the specified coordinates to this <code>Polygon</code>.
  170. * <p>
  171. * If an operation that calculates the bounding box of this
  172. * <code>Polygon</code> has already been performed, such as
  173. * <code>getBounds</code> or <code>contains</code>, then this
  174. * method updates the bounding box.
  175. * @param x, y the specified coordinates
  176. * @see java.awt.Polygon#getBounds
  177. * @see java.awt.Polygon#contains
  178. */
  179. public void addPoint(int x, int y) {
  180. if (npoints == xpoints.length) {
  181. int tmp[];
  182. tmp = new int[npoints * 2];
  183. System.arraycopy(xpoints, 0, tmp, 0, npoints);
  184. xpoints = tmp;
  185. tmp = new int[npoints * 2];
  186. System.arraycopy(ypoints, 0, tmp, 0, npoints);
  187. ypoints = tmp;
  188. }
  189. xpoints[npoints] = x;
  190. ypoints[npoints] = y;
  191. npoints++;
  192. if (bounds != null) {
  193. updateBounds(x, y);
  194. }
  195. }
  196. /**
  197. * Gets the bounding box of this <code>Polygon</code>.
  198. * The bounding box is the smallest {@link Rectangle} whose
  199. * sides are parallel to the x and y axes of the
  200. * coordinate space, and can completely contain the <code>Polygon</code>.
  201. * @return a <code>Rectangle</code> that defines the bounds of this
  202. * <code>Polygon</code>.
  203. * @since JDK1.1
  204. */
  205. public Rectangle getBounds() {
  206. return getBoundingBox();
  207. }
  208. /**
  209. * Returns the bounds of this <code>Polygon</code>.
  210. * @return the bounds of this <code>Polygon</code>.
  211. * @deprecated As of JDK version 1.1,
  212. * replaced by <code>getBounds()</code>.
  213. */
  214. public Rectangle getBoundingBox() {
  215. if (bounds == null) {
  216. calculateBounds(xpoints, ypoints, npoints);
  217. }
  218. return bounds;
  219. }
  220. /**
  221. * Determines whether the specified {@link Point} is inside this
  222. * <code>Polygon</code>.
  223. * @param p the specified <code>Point</code> to be tested
  224. * @return <code>true</code> if the <code>Polygon</code> contains the
  225. * <code>Point</code> <code>false</code> otherwise.
  226. * @see #contains(double, double)
  227. */
  228. public boolean contains(Point p) {
  229. return contains(p.x, p.y);
  230. }
  231. /**
  232. * Determines whether the specified coordinates are inside this
  233. * <code>Polygon</code>.
  234. * <p>
  235. * @param x, y the specified coordinates to be tested
  236. * @return <code>true</code> if this <code>Polygon</code> contains
  237. * the specified coordinates, (<i>x</i>, <i>y</i>);
  238. * <code>false</code> otherwise.
  239. * @see #contains(double, double)
  240. * @since JDK1.1
  241. */
  242. public boolean contains(int x, int y) {
  243. return contains((double) x, (double) y);
  244. }
  245. /**
  246. * Determines whether the specified coordinates are contained in this
  247. * <code>Polygon</code>.
  248. * @param x, y the specified coordinates to be tested
  249. * @return <code>true</code> if this <code>Polygon</code> contains
  250. * the specified coordinates, (<i>x</i>, <i>y</i>);
  251. * <code>false</code> otherwise.
  252. * @see #contains(double, double)
  253. * @deprecated As of JDK version 1.1,
  254. * replaced by <code>contains(int, int)</code>.
  255. */
  256. public boolean inside(int x, int y) {
  257. return contains((double) x, (double) y);
  258. }
  259. /**
  260. * Returns the high precision bounding box of the {@link Shape}.
  261. * @return a {@link Rectangle2D} that precisely
  262. * bounds the <code>Shape</code>.
  263. */
  264. public Rectangle2D getBounds2D() {
  265. Rectangle r = getBounds();
  266. return new Rectangle2D.Float(r.x, r.y, r.width, r.height);
  267. }
  268. /**
  269. * Determines whether the specified coordinates are inside this
  270. * <code>Polygon</code>. For the definition of
  271. * <i>insideness</i>, see the class comments of {@link Shape}.
  272. * @param x, y the specified coordinates
  273. * @return <code>true</code> if this <code>Polygon</code> contains the
  274. * specified coordinates; <code>false</code> otherwise.
  275. */
  276. public boolean contains(double x, double y) {
  277. if (npoints <= 2 || !getBoundingBox().contains(x, y)) {
  278. return false;
  279. }
  280. int hits = 0;
  281. int lastx = xpoints[npoints - 1];
  282. int lasty = ypoints[npoints - 1];
  283. int curx, cury;
  284. // Walk the edges of the polygon
  285. for (int i = 0; i < npoints; lastx = curx, lasty = cury, i++) {
  286. curx = xpoints[i];
  287. cury = ypoints[i];
  288. if (cury == lasty) {
  289. continue;
  290. }
  291. int leftx;
  292. if (curx < lastx) {
  293. if (x >= lastx) {
  294. continue;
  295. }
  296. leftx = curx;
  297. } else {
  298. if (x >= curx) {
  299. continue;
  300. }
  301. leftx = lastx;
  302. }
  303. double test1, test2;
  304. if (cury < lasty) {
  305. if (y < cury || y >= lasty) {
  306. continue;
  307. }
  308. if (x < leftx) {
  309. hits++;
  310. continue;
  311. }
  312. test1 = x - curx;
  313. test2 = y - cury;
  314. } else {
  315. if (y < lasty || y >= cury) {
  316. continue;
  317. }
  318. if (x < leftx) {
  319. hits++;
  320. continue;
  321. }
  322. test1 = x - lastx;
  323. test2 = y - lasty;
  324. }
  325. if (test1 < (test2 / (lasty - cury) * (lastx - curx))) {
  326. hits++;
  327. }
  328. }
  329. return ((hits & 1) != 0);
  330. }
  331. private Crossings getCrossings(double xlo, double ylo,
  332. double xhi, double yhi)
  333. {
  334. Crossings cross = new Crossings.EvenOdd(xlo, ylo, xhi, yhi);
  335. int lastx = xpoints[npoints - 1];
  336. int lasty = ypoints[npoints - 1];
  337. int curx, cury;
  338. // Walk the edges of the polygon
  339. for (int i = 0; i < npoints; i++) {
  340. curx = xpoints[i];
  341. cury = ypoints[i];
  342. if (cross.accumulateLine(lastx, lasty, curx, cury)) {
  343. return null;
  344. }
  345. lastx = curx;
  346. lasty = cury;
  347. }
  348. return cross;
  349. }
  350. /**
  351. * Tests if a specified {@link Point2D} is inside the boundary of this
  352. * <code>Polygon</code>.
  353. * @param p a specified <code>Point2D</code>
  354. * @return <code>true</code> if this <code>Polygon</code> contains the
  355. * specified <code>Point2D</code> <code>false</code>
  356. * otherwise.
  357. * @see #contains(double, double)
  358. */
  359. public boolean contains(Point2D p) {
  360. return contains(p.getX(), p.getY());
  361. }
  362. /**
  363. * Tests if the interior of this <code>Polygon</code> intersects the
  364. * interior of a specified set of rectangular coordinates.
  365. * @param x, y the coordinates of the specified rectangular
  366. * shape's top-left corner
  367. * @param w the width of the specified rectangular shape
  368. * @param h the height of the specified rectangular shape
  369. * @return <code>true</code> if the interior of this
  370. * <code>Polygon</code> and the interior of the
  371. * specified set of rectangular
  372. * coordinates intersect each other;
  373. * <code>false</code> otherwise.
  374. */
  375. public boolean intersects(double x, double y, double w, double h) {
  376. if (npoints <= 0 || !getBoundingBox().intersects(x, y, w, h)) {
  377. return false;
  378. }
  379. Crossings cross = getCrossings(x, y, x+w, y+h);
  380. return (cross == null || !cross.isEmpty());
  381. }
  382. /**
  383. * Tests if the interior of this <code>Polygon</code> intersects the
  384. * interior of a specified <code>Rectangle2D</code>.
  385. * @param r a specified <code>Rectangle2D</code>
  386. * @return <code>true</code> if this <code>Polygon</code> and the
  387. * interior of the specified <code>Rectangle2D</code>
  388. * intersect each other; <code>false</code>
  389. * otherwise.
  390. */
  391. public boolean intersects(Rectangle2D r) {
  392. return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  393. }
  394. /**
  395. * Tests if the interior of this <code>Polygon</code> entirely
  396. * contains the specified set of rectangular coordinates.
  397. * @param x, y the coordinate of the top-left corner of the
  398. * specified set of rectangular coordinates
  399. * @param w the width of the set of rectangular coordinates
  400. * @param h the height of the set of rectangular coordinates
  401. * @return <code>true</code> if this <code>Polygon</code> entirely
  402. * contains the specified set of rectangular
  403. * coordinates; <code>false</code> otherwise.
  404. */
  405. public boolean contains(double x, double y, double w, double h) {
  406. if (npoints <= 0 || !getBoundingBox().intersects(x, y, w, h)) {
  407. return false;
  408. }
  409. Crossings cross = getCrossings(x, y, x+w, y+h);
  410. return (cross != null && cross.covers(y, y+h));
  411. }
  412. /**
  413. * Tests if the interior of this <code>Polygon</code> entirely
  414. * contains the specified <code>Rectangle2D</code>.
  415. * @param r the specified <code>Rectangle2D</code>
  416. * @return <code>true</code> if this <code>Polygon</code> entirely
  417. * contains the specified <code>Rectangle2D</code>
  418. * <code>false</code> otherwise.
  419. * @see contains(double, double, double, double)
  420. */
  421. public boolean contains(Rectangle2D r) {
  422. return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  423. }
  424. /**
  425. * Returns an iterator object that iterates along the boundary of this
  426. * <code>Polygon</code> and provides access to the geometry
  427. * of the outline of this <code>Polygon</code>. An optional
  428. * {@link AffineTransform} can be specified so that the coordinates
  429. * returned in the iteration are transformed accordingly.
  430. * @param at an optional <code>AffineTransform</code> to be applied to the
  431. * coordinates as they are returned in the iteration, or
  432. * <code>null</code> if untransformed coordinates are desired
  433. * @return a {@link PathIterator} object that provides access to the
  434. * geometry of this <code>Polygon</code>.
  435. */
  436. public PathIterator getPathIterator(AffineTransform at) {
  437. return new PolygonPathIterator(this, at);
  438. }
  439. /**
  440. * Returns an iterator object that iterates along the boundary of
  441. * the <code>Shape</code> and provides access to the geometry of the
  442. * outline of the <code>Shape</code>. Only SEG_MOVETO, SEG_LINETO, and
  443. * SEG_CLOSE point types are returned by the iterator.
  444. * Since polygons are already flat, the <code>flatness</code> parameter
  445. * is ignored. An optional <code>AffineTransform</code> can be specified
  446. * in which case the coordinates returned in the iteration are transformed
  447. * accordingly.
  448. * @param at an optional <code>AffineTransform</code> to be applied to the
  449. * coordinates as they are returned in the iteration, or
  450. * <code>null</code> if untransformed coordinates are desired
  451. * @param flatness the maximum amount that the control points
  452. * for a given curve can vary from colinear before a subdivided
  453. * curve is replaced by a straight line connecting the
  454. * endpoints. Since polygons are already flat the
  455. * <code>flatness</code> parameter is ignored.
  456. * @return a <code>PathIterator</code> object that provides access to the
  457. * <code>Shape</code> object's geometry.
  458. */
  459. public PathIterator getPathIterator(AffineTransform at, double flatness) {
  460. return getPathIterator(at);
  461. }
  462. class PolygonPathIterator implements PathIterator {
  463. Polygon poly;
  464. AffineTransform transform;
  465. int index;
  466. public PolygonPathIterator(Polygon pg, AffineTransform at) {
  467. poly = pg;
  468. transform = at;
  469. }
  470. /**
  471. * Returns the winding rule for determining the interior of the
  472. * path.
  473. * @return an integer representing the current winding rule.
  474. * @see PathIterator#WIND_NON_ZERO
  475. */
  476. public int getWindingRule() {
  477. return WIND_EVEN_ODD;
  478. }
  479. /**
  480. * Tests if there are more points to read.
  481. * @return <code>true</code> if there are more points to read;
  482. * <code>false</code> otherwise.
  483. */
  484. public boolean isDone() {
  485. return index > poly.npoints;
  486. }
  487. /**
  488. * Moves the iterator forwards, along the primary direction of
  489. * traversal, to the next segment of the path when there are
  490. * more points in that direction.
  491. */
  492. public void next() {
  493. index++;
  494. }
  495. /**
  496. * Returns the coordinates and type of the current path segment in
  497. * the iteration.
  498. * The return value is the path segment type:
  499. * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE.
  500. * A <code>float</code> array of length 2 must be passed in and
  501. * can be used to store the coordinates of the point(s).
  502. * Each point is stored as a pair of <code>float</code> x, y
  503. * coordinates. SEG_MOVETO and SEG_LINETO types return one
  504. * point, and SEG_CLOSE does not return any points.
  505. * @param coords a <code>float</code> array that specifies the
  506. * coordinates of the point(s)
  507. * @return an integer representing the type and coordinates of the
  508. * current path segment.
  509. * @see PathIterator#SEG_MOVETO
  510. * @see PathIterator#SEG_LINETO
  511. * @see PathIterator#SEG_CLOSE
  512. */
  513. public int currentSegment(float[] coords) {
  514. if (index >= poly.npoints) {
  515. return SEG_CLOSE;
  516. }
  517. coords[0] = poly.xpoints[index];
  518. coords[1] = poly.ypoints[index];
  519. if (transform != null) {
  520. transform.transform(coords, 0, coords, 0, 1);
  521. }
  522. return (index == 0 ? SEG_MOVETO : SEG_LINETO);
  523. }
  524. /**
  525. * Returns the coordinates and type of the current path segment in
  526. * the iteration.
  527. * The return value is the path segment type:
  528. * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE.
  529. * A <code>double</code> array of length 2 must be passed in and
  530. * can be used to store the coordinates of the point(s).
  531. * Each point is stored as a pair of <code>double</code> x, y
  532. * coordinates.
  533. * SEG_MOVETO and SEG_LINETO types return one point,
  534. * and SEG_CLOSE does not return any points.
  535. * @param coords a <code>double</code> array that specifies the
  536. * coordinates of the point(s)
  537. * @return an integer representing the type and coordinates of the
  538. * current path segment.
  539. * @see PathIterator#SEG_MOVETO
  540. * @see PathIterator#SEG_LINETO
  541. * @see PathIterator#SEG_CLOSE
  542. */
  543. public int currentSegment(double[] coords) {
  544. if (index >= poly.npoints) {
  545. return SEG_CLOSE;
  546. }
  547. coords[0] = poly.xpoints[index];
  548. coords[1] = poly.ypoints[index];
  549. if (transform != null) {
  550. transform.transform(coords, 0, coords, 0, 1);
  551. }
  552. return (index == 0 ? SEG_MOVETO : SEG_LINETO);
  553. }
  554. }
  555. }