KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > awt > geom > FlatteningPathIterator


1 /*
2  * @(#)FlatteningPathIterator.java 1.16 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.awt.geom;
9
10 import java.util.*;
11
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 JavaDoc {
22     static final int GROW_SIZE = 24; // Multiple of cubic & quad curve size
23

24     PathIterator JavaDoc src; // The source iterator
25

26     double squareflat; // Square of the flatness parameter
27
// for testing against squared lengths
28

29     int limit; // Maximum number of recursion levels
30

31     double hold[] = new double[14]; // The cache of interpolated coords
32
// Note that this must be long enough
33
// to store a full cubic segment and
34
// a relative cubic segment to avoid
35
// aliasing when copying the coords
36
// of a curve to the end of the array.
37
// This is also serendipitously equal
38
// to the size of a full quad segment
39
// and 2 relative quad segments.
40

41     double curx, cury; // The ending x,y of the last segment
42

43     double movx, movy; // The x,y of the last move segment
44

45     int holdType; // The type of the curve being held
46
// for interpolation
47

48     int holdEnd; // The index of the last curve segment
49
// being held for interpolation
50

51     int holdIndex; // The index of the curve segment
52
// that was last interpolated. This
53
// is the curve segment ready to be
54
// returned in the next call to
55
// currentSegment().
56

57     int levels[]; // The recursion level at which
58
// each curve being held in storage
59
// was generated.
60

61     int levelIndex; // The index of the entry in the
62
// levels array of the curve segment
63
// at the holdIndex
64

65     boolean done; // True when iteration is done
66

67     /**
68      * Constructs a new <code>FlatteningPathIterator</code> object that
69      * flattens a path as it iterates over it. The iterator does not
70      * subdivide any curve read from the source iterator to more than
71      * 10 levels of subdivision which yields a maximum of 1024 line
72      * segments per curve.
73      * @param src the original unflattened path being iterated over
74      * @param flatness the maximum allowable distance between the
75      * control points and the flattened curve
76      */

77     public FlatteningPathIterator(PathIterator JavaDoc src, double flatness) {
78     this(src, flatness, 10);
79     }
80
81     /**
82      * Constructs a new <code>FlatteningPathIterator</code> object
83      * that flattens a path as it iterates over it.
84      * The <code>limit</code> parameter allows you to control the
85      * maximum number of recursive subdivisions that the iterator
86      * can make before it assumes that the curve is flat enough
87      * without measuring against the <code>flatness</code> parameter.
88      * The flattened iteration therefore never generates more than
89      * a maximum of <code>(2^limit)</code> line segments per curve.
90      * @param src the original unflattened path being iterated over
91      * @param flatness the maximum allowable distance between the
92      * control points and the flattened curve
93      * @param limit the maximum number of recursive subdivisions
94      * allowed for any curved segment
95      * @exception <code>IllegalArgumentException</code> if
96      * <code>flatness</code> or <code>limit</code>
97      * is less than zero
98      */

99     public FlatteningPathIterator(PathIterator JavaDoc src, double flatness,
100                   int limit) {
101     if (flatness < 0.0) {
102         throw new IllegalArgumentException JavaDoc("flatness must be >= 0");
103     }
104     if (limit < 0) {
105         throw new IllegalArgumentException JavaDoc("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
next(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     next(true);
178     }
179
180     private void next(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 SEG_MOVETO:
198     case SEG_LINETO:
199         curx = hold[0];
200         cury = hold[1];
201         if (holdType == SEG_MOVETO) {
202         movx = curx;
203         movy = cury;
204         }
205         holdIndex = 0;
206         holdEnd = 0;
207         break;
208     case SEG_CLOSE:
209         curx = movx;
210         cury = movy;
211         holdIndex = 0;
212         holdEnd = 0;
213         break;
214     case 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 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 currentSegment(float[] coords) {
330     if (isDone()) {
331         throw new NoSuchElementException("flattening iterator out of bounds");
332     }
333     int type = holdType;
334     if (type != SEG_CLOSE) {
335         coords[0] = (float) hold[holdIndex + 0];
336         coords[1] = (float) hold[holdIndex + 1];
337         if (type != SEG_MOVETO) {
338         type = SEG_LINETO;
339         }
340     }
341     return type;
342     }
343
344     /**
345      * Returns the coordinates and type of the current path segment in
346      * the iteration.
347      * The return value is the path segment type:
348      * SEG_MOVETO, SEG_LINETO, or SEG_CLOSE.
349      * A double array of length 6 must be passed in and can be used to
350      * store the coordinates of the point(s).
351      * Each point is stored as a pair of double x,y coordinates.
352      * SEG_MOVETO and SEG_LINETO types return one point,
353      * and SEG_CLOSE does not return any points.
354      * @param coords an array that holds the data returned from
355      * this method
356      * @return the path segment type of the current path segment.
357      * @exception <code>NoSuchElementException</code> if there
358      * are no more elements in the flattening path to be
359      * returned.
360      * @see PathIterator#SEG_MOVETO
361      * @see PathIterator#SEG_LINETO
362      * @see PathIterator#SEG_CLOSE
363      */

364     public int currentSegment(double[] coords) {
365     if (isDone()) {
366         throw new NoSuchElementException("flattening iterator out of bounds");
367     }
368     int type = holdType;
369     if (type != SEG_CLOSE) {
370         coords[0] = hold[holdIndex + 0];
371         coords[1] = hold[holdIndex + 1];
372         if (type != SEG_MOVETO) {
373         type = SEG_LINETO;
374         }
375     }
376     return type;
377     }
378 }
379
Popular Tags