Polygon.java 24 KB

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