001/* 002 * @(#)FlatteningPathIterator.java 1.17 05/11/17 003 * 004 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 005 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 006 */ 007 008package armyc2.c2sd.graphics2d; 009 010//import java.util.*; 011 012/** 013 * The <code>FlatteningPathIterator</code> class returns a flattened view of 014 * another {@link PathIterator} object. Other {@link java.awt.Shape Shape} 015 * classes can use this class to provide flattening behavior for their paths 016 * without having to perform the interpolation calculations themselves. 017 * 018 * @version 1.6 06/29/98 019 * @author Jim Graham 020 */ 021public class FlatteningPathIterator /*implements PathIterator */{ 022 static final int GROW_SIZE = 24; // Multiple of cubic & quad curve size 023 024 ArcIterator src; // The source iterator 025 026 double squareflat; // Square of the flatness parameter 027 // for testing against squared lengths 028 029 int limit; // Maximum number of recursion levels 030 031 double hold[] = new double[14]; // The cache of interpolated coords 032 // Note that this must be long enough 033 // to store a full cubic segment and 034 // a relative cubic segment to avoid 035 // aliasing when copying the coords 036 // of a curve to the end of the array. 037 // This is also serendipitously equal 038 // to the size of a full quad segment 039 // and 2 relative quad segments. 040 041 double curx, cury; // The ending x,y of the last segment 042 043 double movx, movy; // The x,y of the last move segment 044 045 int holdType; // The type of the curve being held 046 // for interpolation 047 048 int holdEnd; // The index of the last curve segment 049 // being held for interpolation 050 051 int holdIndex; // The index of the curve segment 052 // that was last interpolated. This 053 // is the curve segment ready to be 054 // returned in the next call to 055 // currentSegment(). 056 057 int levels[]; // The recursion level at which 058 // each curve being held in storage 059 // was generated. 060 061 int levelIndex; // The index of the entry in the 062 // levels array of the curve segment 063 // at the holdIndex 064 065 boolean done; // True when iteration is done 066 067 /** 068 * Constructs a new <code>FlatteningPathIterator</code> object that 069 * flattens a path as it iterates over it. The iterator does not 070 * subdivide any curve read from the source iterator to more than 071 * 10 levels of subdivision which yields a maximum of 1024 line 072 * segments per curve. 073 * @param src the original unflattened path being iterated over 074 * @param flatness the maximum allowable distance between the 075 * control points and the flattened curve 076 */ 077 public FlatteningPathIterator(ArcIterator src, double flatness) { 078 this(src, flatness, 10); 079 } 080 081 /** 082 * Constructs a new <code>FlatteningPathIterator</code> object 083 * that flattens a path as it iterates over it. 084 * The <code>limit</code> parameter allows you to control the 085 * maximum number of recursive subdivisions that the iterator 086 * can make before it assumes that the curve is flat enough 087 * without measuring against the <code>flatness</code> parameter. 088 * The flattened iteration therefore never generates more than 089 * a maximum of <code>(2^limit)</code> line segments per curve. 090 * @param src the original unflattened path being iterated over 091 * @param flatness the maximum allowable distance between the 092 * control points and the flattened curve 093 * @param limit the maximum number of recursive subdivisions 094 * allowed for any curved segment 095 * @exception <code>IllegalArgumentException</code> if 096 * <code>flatness</code> or <code>limit</code> 097 * is less than zero 098 */ 099 public FlatteningPathIterator(ArcIterator src, double flatness, 100 int limit) { 101 if (flatness < 0.0) { 102 throw new IllegalArgumentException("flatness must be >= 0"); 103 } 104 if (limit < 0) { 105 throw new IllegalArgumentException("limit must be >= 0"); 106 } 107 this.src = src; 108 this.squareflat = flatness * flatness; 109 this.limit = limit; 110 this.levels = new int[limit + 1]; 111 // prime the first path segment 112 next2(false); 113 } 114 115 /** 116 * Returns the flatness of this iterator. 117 * @return the flatness of this <code>FlatteningPathIterator</code>. 118 */ 119 public double getFlatness() { 120 return Math.sqrt(squareflat); 121 } 122 123 /** 124 * Returns the recursion limit of this iterator. 125 * @return the recursion limit of this 126 * <code>FlatteningPathIterator</code>. 127 */ 128 public int getRecursionLimit() { 129 return limit; 130 } 131 132 /** 133 * Returns the winding rule for determining the interior of the 134 * path. 135 * @return the winding rule of the original unflattened path being 136 * iterated over. 137 * @see PathIterator#WIND_EVEN_ODD 138 * @see PathIterator#WIND_NON_ZERO 139 */ 140 public int getWindingRule() { 141 return src.getWindingRule(); 142 } 143 144 /** 145 * Tests if the iteration is complete. 146 * @return <code>true</code> if all the segments have 147 * been read; <code>false</code> otherwise. 148 */ 149 public boolean isDone() { 150 return done; 151 } 152 153 /* 154 * Ensures that the hold array can hold up to (want) more values. 155 * It is currently holding (hold.length - holdIndex) values. 156 */ 157 void ensureHoldCapacity(int want) { 158 if (holdIndex - want < 0) { 159 int have = hold.length - holdIndex; 160 int newsize = hold.length + GROW_SIZE; 161 double newhold[] = new double[newsize]; 162 System.arraycopy(hold, holdIndex, 163 newhold, holdIndex + GROW_SIZE, 164 have); 165 hold = newhold; 166 holdIndex += GROW_SIZE; 167 holdEnd += GROW_SIZE; 168 } 169 } 170 171 /** 172 * Moves the iterator to the next segment of the path forwards 173 * along the primary direction of traversal as long as there are 174 * more points in that direction. 175 */ 176 public void next() { 177 next2(true); 178 } 179 180 private void next2(boolean doNext) { 181 int level; 182 183 if (holdIndex >= holdEnd) { 184 if (doNext) { 185 src.next(); 186 } 187 if (src.isDone()) { 188 done = true; 189 return; 190 } 191 holdType = src.currentSegment(hold); 192 levelIndex = 0; 193 levels[0] = 0; 194 } 195 196 switch (holdType) { 197 case PathIterator.SEG_MOVETO: 198 case PathIterator.SEG_LINETO: 199 curx = hold[0]; 200 cury = hold[1]; 201 if (holdType == PathIterator.SEG_MOVETO) { 202 movx = curx; 203 movy = cury; 204 } 205 holdIndex = 0; 206 holdEnd = 0; 207 break; 208 case PathIterator.SEG_CLOSE: 209 curx = movx; 210 cury = movy; 211 holdIndex = 0; 212 holdEnd = 0; 213 break; 214 case PathIterator.SEG_QUADTO: 215 if (holdIndex >= holdEnd) { 216 // Move the coordinates to the end of the array. 217 holdIndex = hold.length - 6; 218 holdEnd = hold.length - 2; 219 hold[holdIndex + 0] = curx; 220 hold[holdIndex + 1] = cury; 221 hold[holdIndex + 2] = hold[0]; 222 hold[holdIndex + 3] = hold[1]; 223 hold[holdIndex + 4] = curx = hold[2]; 224 hold[holdIndex + 5] = cury = hold[3]; 225 } 226 227 level = levels[levelIndex]; 228 while (level < limit) { 229 if (QuadCurve2D.getFlatnessSq(hold, holdIndex) < squareflat) { 230 break; 231 } 232 233 ensureHoldCapacity(4); 234 QuadCurve2D.subdivide(hold, holdIndex, 235 hold, holdIndex - 4, 236 hold, holdIndex); 237 holdIndex -= 4; 238 239 // Now that we have subdivided, we have constructed 240 // two curves of one depth lower than the original 241 // curve. One of those curves is in the place of 242 // the former curve and one of them is in the next 243 // set of held coordinate slots. We now set both 244 // curves level values to the next higher level. 245 level++; 246 levels[levelIndex] = level; 247 levelIndex++; 248 levels[levelIndex] = level; 249 } 250 251 // This curve segment is flat enough, or it is too deep 252 // in recursion levels to try to flatten any more. The 253 // two coordinates at holdIndex+4 and holdIndex+5 now 254 // contain the endpoint of the curve which can be the 255 // endpoint of an approximating line segment. 256 holdIndex += 4; 257 levelIndex--; 258 break; 259 case PathIterator.SEG_CUBICTO: 260 if (holdIndex >= holdEnd) { 261 // Move the coordinates to the end of the array. 262 holdIndex = hold.length - 8; 263 holdEnd = hold.length - 2; 264 hold[holdIndex + 0] = curx; 265 hold[holdIndex + 1] = cury; 266 hold[holdIndex + 2] = hold[0]; 267 hold[holdIndex + 3] = hold[1]; 268 hold[holdIndex + 4] = hold[2]; 269 hold[holdIndex + 5] = hold[3]; 270 hold[holdIndex + 6] = curx = hold[4]; 271 hold[holdIndex + 7] = cury = hold[5]; 272 } 273 274 level = levels[levelIndex]; 275 while (level < limit) { 276 if (CubicCurve2D.getFlatnessSq(hold, holdIndex) < squareflat) { 277 break; 278 } 279 280 ensureHoldCapacity(6); 281 CubicCurve2D.subdivide(hold, holdIndex, 282 hold, holdIndex - 6, 283 hold, holdIndex); 284 holdIndex -= 6; 285 286 // Now that we have subdivided, we have constructed 287 // two curves of one depth lower than the original 288 // curve. One of those curves is in the place of 289 // the former curve and one of them is in the next 290 // set of held coordinate slots. We now set both 291 // curves level values to the next higher level. 292 level++; 293 levels[levelIndex] = level; 294 levelIndex++; 295 levels[levelIndex] = level; 296 } 297 298 // This curve segment is flat enough, or it is too deep 299 // in recursion levels to try to flatten any more. The 300 // two coordinates at holdIndex+6 and holdIndex+7 now 301 // contain the endpoint of the curve which can be the 302 // endpoint of an approximating line segment. 303 holdIndex += 6; 304 levelIndex--; 305 break; 306 } 307 } 308 309 /** 310 * Returns the coordinates and type of the current path segment in 311 * the iteration. 312 * The return value is the path segment type: 313 * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE. 314 * A float array of length 6 must be passed in and can be used to 315 * store the coordinates of the point(s). 316 * Each point is stored as a pair of float x,y coordinates. 317 * SEG_MOVETO and SEG_LINETO types return one point, 318 * and SEG_CLOSE does not return any points. 319 * @param coords an array that holds the data returned from 320 * this method 321 * @return the path segment type of the current path segment. 322 * @exception <code>NoSuchElementException</code> if there 323 * are no more elements in the flattening path to be 324 * returned. 325 * @see PathIterator#SEG_MOVETO 326 * @see PathIterator#SEG_LINETO 327 * @see PathIterator#SEG_CLOSE 328 */ 329 public int currentSegmentFlt(float[] coords) { 330 if (isDone()) { 331 //throw new NoSuchElementException("flattening iterator out of bounds"); 332 System.out.println("flattening iterator out of bounds"); 333 return -1; 334 } 335 int type = holdType; 336 if (type != PathIterator.SEG_CLOSE) { 337 coords[0] = (float) hold[holdIndex + 0]; 338 coords[1] = (float) hold[holdIndex + 1]; 339 if (type != PathIterator.SEG_MOVETO) { 340 type = PathIterator.SEG_LINETO; 341 } 342 } 343 return type; 344 } 345 346 /** 347 * Returns the coordinates and type of the current path segment in 348 * the iteration. 349 * The return value is the path segment type: 350 * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE. 351 * A double array of length 6 must be passed in and can be used to 352 * store the coordinates of the point(s). 353 * Each point is stored as a pair of double x,y coordinates. 354 * SEG_MOVETO and SEG_LINETO types return one point, 355 * and SEG_CLOSE does not return any points. 356 * @param coords an array that holds the data returned from 357 * this method 358 * @return the path segment type of the current path segment. 359 * @exception <code>NoSuchElementException</code> if there 360 * are no more elements in the flattening path to be 361 * returned. 362 * @see PathIterator#SEG_MOVETO 363 * @see PathIterator#SEG_LINETO 364 * @see PathIterator#SEG_CLOSE 365 */ 366 public int currentSegment(double[] coords) { 367 if (isDone()) { 368 //throw new NoSuchElementException("flattening iterator out of bounds"); 369 System.out.println("flattening iterator out of bounds"); 370 return -1; 371 } 372 int type = holdType; 373 if (type != PathIterator.SEG_CLOSE) { 374 coords[0] = hold[holdIndex + 0]; 375 coords[1] = hold[holdIndex + 1]; 376 if (type != PathIterator.SEG_MOVETO) { 377 type = PathIterator.SEG_LINETO; 378 } 379 } 380 return type; 381 } 382}