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