• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 /**
18  * @author Denis M. Kishenko
19  * @version $Revision$
20  */
21 
22 package java.awt.geom;
23 
24 import java.util.NoSuchElementException;
25 
26 import org.apache.harmony.awt.internal.nls.Messages;
27 
28 /**
29  * The Class FlatteningPathIterator takes a PathIterator for traversing a curved
30  * shape and flattens it by estimating the curve as a series of line segments.
31  * The flattening factor indicates how far the estimating line segments are
32  * allowed to be from the actual curve: the FlatteningPathIterator will keep
33  * dividing each curved segment into smaller and smaller flat segments until
34  * either the segments are within the flattening factor of the curve or until
35  * the buffer limit is reached.
36  *
37  * @since Android 1.0
38  */
39 public class FlatteningPathIterator implements PathIterator {
40 
41     /**
42      * The default points buffer size.
43      */
44     private static final int BUFFER_SIZE = 16;
45 
46     /**
47      * The default curve subdivision limit.
48      */
49     private static final int BUFFER_LIMIT = 16;
50 
51     /**
52      * The points buffer capacity.
53      */
54     private static final int BUFFER_CAPACITY = 16;
55 
56     /**
57      * The type of current segment to be flat.
58      */
59     int bufType;
60 
61     /**
62      * The curve subdivision limit.
63      */
64     int bufLimit;
65 
66     /**
67      * The current points buffer size.
68      */
69     int bufSize;
70 
71     /**
72      * The inner cursor position in points buffer.
73      */
74     int bufIndex;
75 
76     /**
77      * The current subdivision count.
78      */
79     int bufSubdiv;
80 
81     /**
82      * The points buffer.
83      */
84     double buf[];
85 
86     /**
87      * The indicator of empty points buffer.
88      */
89     boolean bufEmpty = true;
90 
91     /**
92      * The source PathIterator.
93      */
94     PathIterator p;
95 
96     /**
97      * The flatness of new path.
98      */
99     double flatness;
100 
101     /**
102      * The square of flatness.
103      */
104     double flatness2;
105 
106     /**
107      * The x coordinate of previous path segment.
108      */
109     double px;
110 
111     /**
112      * The y coordinate of previous path segment.
113      */
114     double py;
115 
116     /**
117      * The temporary buffer for getting points from PathIterator.
118      */
119     double coords[] = new double[6];
120 
121     /**
122      * Instantiates a new flattening path iterator given the path iterator for a
123      * (possibly) curved path and a flattening factor which indicates how close
124      * together the points on the curve should be chosen. The buffer limit
125      * defaults to 16 which means that each curve will be divided into no more
126      * than 16 segments regardless of the flattening factor.
127      *
128      * @param path
129      *            the path iterator of the original curve.
130      * @param flatness
131      *            the flattening factor that indicates how far the flat path is
132      *            allowed to be from the actual curve in order to decide when to
133      *            stop dividing the path into smaller and smaller segments.
134      * @throws IllegalArgumentException
135      *             if the flatness is less than zero.
136      * @throws NullPointerException
137      *             if the path is null.
138      */
FlatteningPathIterator(PathIterator path, double flatness)139     public FlatteningPathIterator(PathIterator path, double flatness) {
140         this(path, flatness, BUFFER_LIMIT);
141     }
142 
143     /**
144      * Instantiates a new flattening path iterator given the path iterator for a
145      * (possibly) curved path and a flattening factor and a buffer limit. The
146      * FlatteningPathIterator will keep dividing each curved segment into
147      * smaller and smaller flat segments until either the segments are within
148      * the flattening factor of the curve or until the buffer limit is reached.
149      *
150      * @param path
151      *            the path iterator of the original curve.
152      * @param flatness
153      *            the flattening factor that indicates how far the flat path is
154      *            allowed to be from the actual curve in order to decide when to
155      *            stop dividing the path into smaller and smaller segments.
156      * @param limit
157      *            the maximum number of flat segments to divide each curve into.
158      * @throws IllegalArgumentException
159      *             if the flatness or limit is less than zero.
160      * @throws NullPointerException
161      *             if the path is null.
162      */
FlatteningPathIterator(PathIterator path, double flatness, int limit)163     public FlatteningPathIterator(PathIterator path, double flatness, int limit) {
164         if (flatness < 0.0) {
165             // awt.206=Flatness is less then zero
166             throw new IllegalArgumentException(Messages.getString("awt.206")); //$NON-NLS-1$
167         }
168         if (limit < 0) {
169             // awt.207=Limit is less then zero
170             throw new IllegalArgumentException(Messages.getString("awt.207")); //$NON-NLS-1$
171         }
172         if (path == null) {
173             // awt.208=Path is null
174             throw new NullPointerException(Messages.getString("awt.208")); //$NON-NLS-1$
175         }
176         this.p = path;
177         this.flatness = flatness;
178         this.flatness2 = flatness * flatness;
179         this.bufLimit = limit;
180         this.bufSize = Math.min(bufLimit, BUFFER_SIZE);
181         this.buf = new double[bufSize];
182         this.bufIndex = bufSize;
183     }
184 
185     /**
186      * Gets the flattening factor.
187      *
188      * @return the flattening factor.
189      */
getFlatness()190     public double getFlatness() {
191         return flatness;
192     }
193 
194     /**
195      * Gets the maximum number of subdivisions per curved segment.
196      *
197      * @return the maximum number of subdivisions per curved segment.
198      */
getRecursionLimit()199     public int getRecursionLimit() {
200         return bufLimit;
201     }
202 
getWindingRule()203     public int getWindingRule() {
204         return p.getWindingRule();
205     }
206 
isDone()207     public boolean isDone() {
208         return bufEmpty && p.isDone();
209     }
210 
211     /**
212      * Calculates flat path points for current segment of the source shape. Line
213      * segment is flat by itself. Flatness of quad and cubic curves evaluated by
214      * getFlatnessSq() method. Curves subdivided until current flatness is
215      * bigger than user defined and subdivision limit isn't exhausted. Single
216      * source segment translated to series of buffer points. The less flatness
217      * the bigger series. Every currentSegment() call extract one point from the
218      * buffer. When series completed evaluate() takes next source shape segment.
219      */
evaluate()220     void evaluate() {
221         if (bufEmpty) {
222             bufType = p.currentSegment(coords);
223         }
224 
225         switch (bufType) {
226             case SEG_MOVETO:
227             case SEG_LINETO:
228                 px = coords[0];
229                 py = coords[1];
230                 break;
231             case SEG_QUADTO:
232                 if (bufEmpty) {
233                     bufIndex -= 6;
234                     buf[bufIndex + 0] = px;
235                     buf[bufIndex + 1] = py;
236                     System.arraycopy(coords, 0, buf, bufIndex + 2, 4);
237                     bufSubdiv = 0;
238                 }
239 
240                 while (bufSubdiv < bufLimit) {
241                     if (QuadCurve2D.getFlatnessSq(buf, bufIndex) < flatness2) {
242                         break;
243                     }
244 
245                     // Realloc buffer
246                     if (bufIndex <= 4) {
247                         double tmp[] = new double[bufSize + BUFFER_CAPACITY];
248                         System.arraycopy(buf, bufIndex, tmp, bufIndex + BUFFER_CAPACITY, bufSize
249                                 - bufIndex);
250                         buf = tmp;
251                         bufSize += BUFFER_CAPACITY;
252                         bufIndex += BUFFER_CAPACITY;
253                     }
254 
255                     QuadCurve2D.subdivide(buf, bufIndex, buf, bufIndex - 4, buf, bufIndex);
256 
257                     bufIndex -= 4;
258                     bufSubdiv++;
259                 }
260 
261                 bufIndex += 4;
262                 px = buf[bufIndex];
263                 py = buf[bufIndex + 1];
264 
265                 bufEmpty = (bufIndex == bufSize - 2);
266                 if (bufEmpty) {
267                     bufIndex = bufSize;
268                     bufType = SEG_LINETO;
269                 } else {
270                     bufSubdiv--;
271                 }
272                 break;
273             case SEG_CUBICTO:
274                 if (bufEmpty) {
275                     bufIndex -= 8;
276                     buf[bufIndex + 0] = px;
277                     buf[bufIndex + 1] = py;
278                     System.arraycopy(coords, 0, buf, bufIndex + 2, 6);
279                     bufSubdiv = 0;
280                 }
281 
282                 while (bufSubdiv < bufLimit) {
283                     if (CubicCurve2D.getFlatnessSq(buf, bufIndex) < flatness2) {
284                         break;
285                     }
286 
287                     // Realloc buffer
288                     if (bufIndex <= 6) {
289                         double tmp[] = new double[bufSize + BUFFER_CAPACITY];
290                         System.arraycopy(buf, bufIndex, tmp, bufIndex + BUFFER_CAPACITY, bufSize
291                                 - bufIndex);
292                         buf = tmp;
293                         bufSize += BUFFER_CAPACITY;
294                         bufIndex += BUFFER_CAPACITY;
295                     }
296 
297                     CubicCurve2D.subdivide(buf, bufIndex, buf, bufIndex - 6, buf, bufIndex);
298 
299                     bufIndex -= 6;
300                     bufSubdiv++;
301                 }
302 
303                 bufIndex += 6;
304                 px = buf[bufIndex];
305                 py = buf[bufIndex + 1];
306 
307                 bufEmpty = (bufIndex == bufSize - 2);
308                 if (bufEmpty) {
309                     bufIndex = bufSize;
310                     bufType = SEG_LINETO;
311                 } else {
312                     bufSubdiv--;
313                 }
314                 break;
315         }
316 
317     }
318 
next()319     public void next() {
320         if (bufEmpty) {
321             p.next();
322         }
323     }
324 
currentSegment(float[] coords)325     public int currentSegment(float[] coords) {
326         if (isDone()) {
327             // awt.4B=Iterator out of bounds
328             throw new NoSuchElementException(Messages.getString("awt.4Bx")); //$NON-NLS-1$
329         }
330         evaluate();
331         int type = bufType;
332         if (type != SEG_CLOSE) {
333             coords[0] = (float)px;
334             coords[1] = (float)py;
335             if (type != SEG_MOVETO) {
336                 type = SEG_LINETO;
337             }
338         }
339         return type;
340     }
341 
currentSegment(double[] coords)342     public int currentSegment(double[] coords) {
343         if (isDone()) {
344             // awt.4B=Iterator out of bounds
345             throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
346         }
347         evaluate();
348         int type = bufType;
349         if (type != SEG_CLOSE) {
350             coords[0] = px;
351             coords[1] = py;
352             if (type != SEG_MOVETO) {
353                 type = SEG_LINETO;
354             }
355         }
356         return type;
357     }
358 }
359