1. /*
  2. * @(#)FlatteningPathIterator.java 1.13 00/02/02
  3. *
  4. * Copyright 1997-2000 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.geom;
  11. import java.util.*;
  12. /**
  13. * The <code>FlatteningPathIterator</code> class returns a flattened view of
  14. * another {@link PathIterator} object. Other {@link java.awt.Shape Shape}
  15. * classes can use this class to provide flattening behavior for their paths
  16. * without having to perform the interpolation calculations themselves.
  17. *
  18. * @version 1.6 06/29/98
  19. * @author Jim Graham
  20. */
  21. public class FlatteningPathIterator implements PathIterator {
  22. static final int GROW_SIZE = 24; // Multiple of cubic & quad curve size
  23. PathIterator src; // The source iterator
  24. double squareflat; // Square of the flatness parameter
  25. // for testing against squared lengths
  26. int limit; // Maximum number of recursion levels
  27. double hold[] = new double[14]; // The cache of interpolated coords
  28. // Note that this must be long enough
  29. // to store a full cubic segment and
  30. // a relative cubic segment to avoid
  31. // aliasing when copying the coords
  32. // of a curve to the end of the array.
  33. // This is also serendipitously equal
  34. // to the size of a full quad segment
  35. // and 2 relative quad segments.
  36. double curx, cury; // The ending x,y of the last segment
  37. double movx, movy; // The x,y of the last move segment
  38. int holdType; // The type of the curve being held
  39. // for interpolation
  40. int holdEnd; // The index of the last curve segment
  41. // being held for interpolation
  42. int holdIndex; // The index of the curve segment
  43. // that was last interpolated. This
  44. // is the curve segment ready to be
  45. // returned in the next call to
  46. // currentSegment().
  47. int levels[]; // The recursion level at which
  48. // each curve being held in storage
  49. // was generated.
  50. int levelIndex; // The index of the entry in the
  51. // levels array of the curve segment
  52. // at the holdIndex
  53. boolean done; // True when iteration is done
  54. /**
  55. * Constructs a new <code>FlatteningPathIterator</code> object that
  56. * flattens a path as it iterates over it. The iterator does not
  57. * subdivide any curve read from the source iterator to more than
  58. * 10 levels of subdivision which yields a maximum of 1024 line
  59. * segments per curve.
  60. * @param src the original unflattened path being iterated over
  61. * @param flatness the maximum allowable distance between the
  62. * control points and the flattened curve
  63. */
  64. public FlatteningPathIterator(PathIterator src, double flatness) {
  65. this(src, flatness, 10);
  66. }
  67. /**
  68. * Constructs a new <code>FlatteningPathIterator</code> object
  69. * that flattens a path as it iterates over it.
  70. * The <code>limit</code> parameter allows you to control the
  71. * maximum number of recursive subdivisions that the iterator
  72. * can make before it assumes that the curve is flat enough
  73. * without measuring against the <code>flatness</code> parameter.
  74. * The flattened iteration therefore never generates more than
  75. * a maximum of <code>(2^limit)</code> line segments per curve.
  76. * @param src the original unflattened path being iterated over
  77. * @param flatness the maximum allowable distance between the
  78. * control points and the flattened curve
  79. * @param limit the maximum number of recursive subdivisions
  80. * allowed for any curved segment
  81. * @exception <code>IllegalArgumentException</code> if
  82. * <code>flatness</code> or <code>limit</code>
  83. * is less than zero
  84. */
  85. public FlatteningPathIterator(PathIterator src, double flatness,
  86. int limit) {
  87. if (flatness < 0.0) {
  88. throw new IllegalArgumentException("flatness must be >= 0");
  89. }
  90. if (limit < 0) {
  91. throw new IllegalArgumentException("limit must be >= 0");
  92. }
  93. this.src = src;
  94. this.squareflat = flatness * flatness;
  95. this.limit = limit;
  96. this.levels = new int[limit + 1];
  97. // prime the first path segment
  98. next(false);
  99. }
  100. /**
  101. * Returns the flatness of this iterator.
  102. * @return the flatness of this <code>FlatteningPathIterator</code>.
  103. */
  104. public double getFlatness() {
  105. return Math.sqrt(squareflat);
  106. }
  107. /**
  108. * Returns the recursion limit of this iterator.
  109. * @return the recursion limit of this
  110. * <code>FlatteningPathIterator</code>.
  111. */
  112. public int getRecursionLimit() {
  113. return limit;
  114. }
  115. /**
  116. * Returns the winding rule for determining the interior of the
  117. * path.
  118. * @return the winding rule of the original unflattened path being
  119. * iterated over.
  120. * @see PathIterator#WIND_EVEN_ODD
  121. * @see PathIterator#WIND_NON_ZERO
  122. */
  123. public int getWindingRule() {
  124. return src.getWindingRule();
  125. }
  126. /**
  127. * Tests if the iteration is complete.
  128. * @return <code>true</code> if all the segments have
  129. * been read; <code>false</code> otherwise.
  130. */
  131. public boolean isDone() {
  132. return done;
  133. }
  134. /*
  135. * Ensures that the hold array can hold up to (want) more values.
  136. * It is currently holding (hold.length - holdIndex) values.
  137. */
  138. void ensureHoldCapacity(int want) {
  139. if (holdIndex - want < 0) {
  140. int have = hold.length - holdIndex;
  141. int newsize = hold.length + GROW_SIZE;
  142. double newhold[] = new double[newsize];
  143. System.arraycopy(hold, holdIndex,
  144. newhold, holdIndex + GROW_SIZE,
  145. have);
  146. hold = newhold;
  147. holdIndex += GROW_SIZE;
  148. holdEnd += GROW_SIZE;
  149. }
  150. }
  151. /**
  152. * Moves the iterator to the next segment of the path forwards
  153. * along the primary direction of traversal as long as there are
  154. * more points in that direction.
  155. */
  156. public void next() {
  157. next(true);
  158. }
  159. private void next(boolean doNext) {
  160. int level;
  161. if (holdIndex >= holdEnd) {
  162. if (doNext) {
  163. src.next();
  164. }
  165. if (src.isDone()) {
  166. done = true;
  167. return;
  168. }
  169. holdType = src.currentSegment(hold);
  170. levelIndex = 0;
  171. levels[0] = 0;
  172. }
  173. switch (holdType) {
  174. case SEG_MOVETO:
  175. case SEG_LINETO:
  176. curx = hold[0];
  177. cury = hold[1];
  178. if (holdType == SEG_MOVETO) {
  179. movx = curx;
  180. movy = cury;
  181. }
  182. holdIndex = 0;
  183. holdEnd = 0;
  184. break;
  185. case SEG_CLOSE:
  186. curx = movx;
  187. cury = movy;
  188. holdIndex = 0;
  189. holdEnd = 0;
  190. break;
  191. case SEG_QUADTO:
  192. if (holdIndex >= holdEnd) {
  193. // Move the coordinates to the end of the array.
  194. holdIndex = hold.length - 6;
  195. holdEnd = hold.length - 2;
  196. hold[holdIndex + 0] = curx;
  197. hold[holdIndex + 1] = cury;
  198. hold[holdIndex + 2] = hold[0];
  199. hold[holdIndex + 3] = hold[1];
  200. hold[holdIndex + 4] = curx = hold[2];
  201. hold[holdIndex + 5] = cury = hold[3];
  202. }
  203. level = levels[levelIndex];
  204. while (level < limit) {
  205. if (QuadCurve2D.getFlatnessSq(hold, holdIndex) < squareflat) {
  206. break;
  207. }
  208. ensureHoldCapacity(4);
  209. QuadCurve2D.subdivide(hold, holdIndex,
  210. hold, holdIndex - 4,
  211. hold, holdIndex);
  212. holdIndex -= 4;
  213. // Now that we have subdivided, we have constructed
  214. // two curves of one depth lower than the original
  215. // curve. One of those curves is in the place of
  216. // the former curve and one of them is in the next
  217. // set of held coordinate slots. We now set both
  218. // curves level values to the next higher level.
  219. level++;
  220. levels[levelIndex] = level;
  221. levelIndex++;
  222. levels[levelIndex] = level;
  223. }
  224. // This curve segment is flat enough, or it is too deep
  225. // in recursion levels to try to flatten any more. The
  226. // two coordinates at holdIndex+4 and holdIndex+5 now
  227. // contain the endpoint of the curve which can be the
  228. // endpoint of an approximating line segment.
  229. holdIndex += 4;
  230. levelIndex--;
  231. break;
  232. case SEG_CUBICTO:
  233. if (holdIndex >= holdEnd) {
  234. // Move the coordinates to the end of the array.
  235. holdIndex = hold.length - 8;
  236. holdEnd = hold.length - 2;
  237. hold[holdIndex + 0] = curx;
  238. hold[holdIndex + 1] = cury;
  239. hold[holdIndex + 2] = hold[0];
  240. hold[holdIndex + 3] = hold[1];
  241. hold[holdIndex + 4] = hold[2];
  242. hold[holdIndex + 5] = hold[3];
  243. hold[holdIndex + 6] = curx = hold[4];
  244. hold[holdIndex + 7] = cury = hold[5];
  245. }
  246. level = levels[levelIndex];
  247. while (level < limit) {
  248. if (CubicCurve2D.getFlatnessSq(hold, holdIndex) < squareflat) {
  249. break;
  250. }
  251. ensureHoldCapacity(6);
  252. CubicCurve2D.subdivide(hold, holdIndex,
  253. hold, holdIndex - 6,
  254. hold, holdIndex);
  255. holdIndex -= 6;
  256. // Now that we have subdivided, we have constructed
  257. // two curves of one depth lower than the original
  258. // curve. One of those curves is in the place of
  259. // the former curve and one of them is in the next
  260. // set of held coordinate slots. We now set both
  261. // curves level values to the next higher level.
  262. level++;
  263. levels[levelIndex] = level;
  264. levelIndex++;
  265. levels[levelIndex] = level;
  266. }
  267. // This curve segment is flat enough, or it is too deep
  268. // in recursion levels to try to flatten any more. The
  269. // two coordinates at holdIndex+6 and holdIndex+7 now
  270. // contain the endpoint of the curve which can be the
  271. // endpoint of an approximating line segment.
  272. holdIndex += 6;
  273. levelIndex--;
  274. break;
  275. }
  276. }
  277. /**
  278. * Returns the coordinates and type of the current path segment in
  279. * the iteration.
  280. * The return value is the path segment type:
  281. * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE.
  282. * A float array of length 6 must be passed in and can be used to
  283. * store the coordinates of the point(s).
  284. * Each point is stored as a pair of float x,y coordinates.
  285. * SEG_MOVETO and SEG_LINETO types return one point,
  286. * and SEG_CLOSE does not return any points.
  287. * @param coords an array that holds the data returned from
  288. * this method
  289. * @return the path segment type of the current path segment.
  290. * @exception <code>NoSuchElementException</code> if there
  291. * are no more elements in the flattening path to be
  292. * returned.
  293. * @see PathIterator#SEG_MOVETO
  294. * @see PathIterator#SEG_LINETO
  295. * @see PathIterator#SEG_CLOSE
  296. */
  297. public int currentSegment(float[] coords) {
  298. if (isDone()) {
  299. throw new NoSuchElementException("flattening iterator out of bounds");
  300. }
  301. int type = holdType;
  302. if (type != SEG_CLOSE) {
  303. coords[0] = (float) hold[holdIndex + 0];
  304. coords[1] = (float) hold[holdIndex + 1];
  305. if (type != SEG_MOVETO) {
  306. type = SEG_LINETO;
  307. }
  308. }
  309. return type;
  310. }
  311. /**
  312. * Returns the coordinates and type of the current path segment in
  313. * the iteration.
  314. * The return value is the path segment type:
  315. * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE.
  316. * A double array of length 6 must be passed in and can be used to
  317. * store the coordinates of the point(s).
  318. * Each point is stored as a pair of double x,y coordinates.
  319. * SEG_MOVETO and SEG_LINETO types return one point,
  320. * and SEG_CLOSE does not return any points.
  321. * @param coords an array that holds the data returned from
  322. * this method
  323. * @return the path segment type of the current path segment.
  324. * @exception <code>NoSuchElementException</code> if there
  325. * are no more elements in the flattening path to be
  326. * returned.
  327. * @see PathIterator#SEG_MOVETO
  328. * @see PathIterator#SEG_LINETO
  329. * @see PathIterator#SEG_CLOSE
  330. */
  331. public int currentSegment(double[] coords) {
  332. if (isDone()) {
  333. throw new NoSuchElementException("flattening iterator out of bounds");
  334. }
  335. int type = holdType;
  336. if (type != SEG_CLOSE) {
  337. coords[0] = hold[holdIndex + 0];
  338. coords[1] = hold[holdIndex + 1];
  339. if (type != SEG_MOVETO) {
  340. type = SEG_LINETO;
  341. }
  342. }
  343. return type;
  344. }
  345. }