1. /*
  2. * @(#)ArcIterator.java 1.11 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. * A utility class to iterate over the path segments of an arc
  14. * through the PathIterator interface.
  15. *
  16. * @version 10 Feb 1997
  17. * @author Jim Graham
  18. */
  19. class ArcIterator implements PathIterator {
  20. double x, y, w, h, angStRad, angExtDeg;
  21. AffineTransform affine;
  22. int index;
  23. int arcSegs;
  24. int lineSegs;
  25. ArcIterator(Arc2D a, AffineTransform at) {
  26. this.w = a.getWidth() / 2;
  27. this.h = a.getHeight() / 2;
  28. this.x = a.getX() + w;
  29. this.y = a.getY() + h;
  30. this.angStRad = -Math.toRadians(a.getAngleStart());
  31. this.angExtDeg = -a.getAngleExtent();
  32. this.affine = at;
  33. double ext = Math.abs(angExtDeg);
  34. if (ext >= 360.0) {
  35. arcSegs = 4;
  36. } else {
  37. arcSegs = (int) Math.ceil(ext / 90.0);
  38. }
  39. switch (a.getArcType()) {
  40. case Arc2D.OPEN:
  41. lineSegs = 0;
  42. break;
  43. case Arc2D.CHORD:
  44. lineSegs = 1;
  45. break;
  46. case Arc2D.PIE:
  47. lineSegs = 2;
  48. break;
  49. }
  50. if (w < 0 || h < 0) {
  51. arcSegs = lineSegs = -1;
  52. }
  53. }
  54. /**
  55. * Return the winding rule for determining the insideness of the
  56. * path.
  57. * @see #WIND_EVEN_ODD
  58. * @see #WIND_NON_ZERO
  59. */
  60. public int getWindingRule() {
  61. return WIND_NON_ZERO;
  62. }
  63. /**
  64. * Tests if there are more points to read.
  65. * @return true if there are more points to read
  66. */
  67. public boolean isDone() {
  68. return index > arcSegs + lineSegs;
  69. }
  70. /**
  71. * Moves the iterator to the next segment of the path forwards
  72. * along the primary direction of traversal as long as there are
  73. * more points in that direction.
  74. */
  75. public void next() {
  76. index++;
  77. }
  78. private static double btan(double increment) {
  79. increment /= 2.0;
  80. double a = 1.0 - Math.cos(increment);
  81. double b = Math.tan(increment);
  82. double c = Math.sqrt(1.0 + b * b) - 1.0 + a;
  83. return 4.0 / 3.0 * a * b / c;
  84. }
  85. /**
  86. * Returns the coordinates and type of the current path segment in
  87. * the iteration.
  88. * The return value is the path segment type:
  89. * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE.
  90. * A float array of length 6 must be passed in and may be used to
  91. * store the coordinates of the point(s).
  92. * Each point is stored as a pair of float x,y coordinates.
  93. * SEG_MOVETO and SEG_LINETO types will return one point,
  94. * SEG_QUADTO will return two points,
  95. * SEG_CUBICTO will return 3 points
  96. * and SEG_CLOSE will not return any points.
  97. * @see #SEG_MOVETO
  98. * @see #SEG_LINETO
  99. * @see #SEG_QUADTO
  100. * @see #SEG_CUBICTO
  101. * @see #SEG_CLOSE
  102. */
  103. public int currentSegment(float[] coords) {
  104. if (isDone()) {
  105. throw new NoSuchElementException("arc iterator out of bounds");
  106. }
  107. double angle = angStRad;
  108. if (index == 0) {
  109. coords[0] = (float) (x + Math.cos(angle) * w);
  110. coords[1] = (float) (y + Math.sin(angle) * h);
  111. if (affine != null) {
  112. affine.transform(coords, 0, coords, 0, 1);
  113. }
  114. return SEG_MOVETO;
  115. }
  116. if (index > arcSegs) {
  117. if (index == arcSegs + lineSegs) {
  118. return SEG_CLOSE;
  119. }
  120. coords[0] = (float) x;
  121. coords[1] = (float) y;
  122. if (affine != null) {
  123. affine.transform(coords, 0, coords, 0, 1);
  124. }
  125. return SEG_LINETO;
  126. }
  127. double increment = angExtDeg;
  128. if (increment > 360.0) {
  129. increment = 360.0;
  130. } else if (increment < -360.0) {
  131. increment = -360.0;
  132. }
  133. increment /= arcSegs;
  134. increment = Math.toRadians(increment);
  135. angle += increment * (index - 1);
  136. double relx = Math.cos(angle);
  137. double rely = Math.sin(angle);
  138. double z = btan(increment);
  139. coords[0] = (float) (x + (relx - z * rely) * w);
  140. coords[1] = (float) (y + (rely + z * relx) * h);
  141. angle += increment;
  142. relx = Math.cos(angle);
  143. rely = Math.sin(angle);
  144. coords[2] = (float) (x + (relx + z * rely) * w);
  145. coords[3] = (float) (y + (rely - z * relx) * h);
  146. coords[4] = (float) (x + relx * w);
  147. coords[5] = (float) (y + rely * h);
  148. if (affine != null) {
  149. affine.transform(coords, 0, coords, 0, 3);
  150. }
  151. return SEG_CUBICTO;
  152. }
  153. /**
  154. * Returns the coordinates and type of the current path segment in
  155. * the iteration.
  156. * The return value is the path segment type:
  157. * SEG_MOVETO, SEG_LINETO, SEG_QUADTO, SEG_CUBICTO, or SEG_CLOSE.
  158. * A double array of length 6 must be passed in and may be used to
  159. * store the coordinates of the point(s).
  160. * Each point is stored as a pair of double x,y coordinates.
  161. * SEG_MOVETO and SEG_LINETO types will return one point,
  162. * SEG_QUADTO will return two points,
  163. * SEG_CUBICTO will return 3 points
  164. * and SEG_CLOSE will not return any points.
  165. * @see #SEG_MOVETO
  166. * @see #SEG_LINETO
  167. * @see #SEG_QUADTO
  168. * @see #SEG_CUBICTO
  169. * @see #SEG_CLOSE
  170. */
  171. public int currentSegment(double[] coords) {
  172. if (isDone()) {
  173. throw new NoSuchElementException("arc iterator out of bounds");
  174. }
  175. double angle = angStRad;
  176. if (index == 0) {
  177. coords[0] = x + Math.cos(angle) * w;
  178. coords[1] = y + Math.sin(angle) * h;
  179. if (affine != null) {
  180. affine.transform(coords, 0, coords, 0, 1);
  181. }
  182. return SEG_MOVETO;
  183. }
  184. if (index > arcSegs) {
  185. if (index == arcSegs + lineSegs) {
  186. return SEG_CLOSE;
  187. }
  188. coords[0] = x;
  189. coords[1] = y;
  190. if (affine != null) {
  191. affine.transform(coords, 0, coords, 0, 1);
  192. }
  193. return SEG_LINETO;
  194. }
  195. double increment = angExtDeg;
  196. if (increment > 360.0) {
  197. increment = 360.0;
  198. } else if (increment < -360.0) {
  199. increment = -360.0;
  200. }
  201. increment /= arcSegs;
  202. increment = Math.toRadians(increment);
  203. angle += increment * (index - 1);
  204. double relx = Math.cos(angle);
  205. double rely = Math.sin(angle);
  206. double z = btan(increment);
  207. coords[0] = x + (relx - z * rely) * w;
  208. coords[1] = y + (rely + z * relx) * h;
  209. angle += increment;
  210. relx = Math.cos(angle);
  211. rely = Math.sin(angle);
  212. coords[2] = x + (relx + z * rely) * w;
  213. coords[3] = y + (rely - z * relx) * h;
  214. coords[4] = x + relx * w;
  215. coords[5] = y + rely * h;
  216. if (affine != null) {
  217. affine.transform(coords, 0, coords, 0, 3);
  218. }
  219. return SEG_CUBICTO;
  220. }
  221. }