2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / libjava / java / awt / geom / FlatteningPathIterator.java
blob94ff145621b49bab46af0322cccd3cec59a5f12a
1 /* FlatteningPathIterator.java -- Approximates curves by straight lines
2 Copyright (C) 2003 Free Software Foundation
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package java.awt.geom;
41 import java.util.NoSuchElementException;
44 /**
45 * A PathIterator for approximating curved path segments by sequences
46 * of straight lines. Instances of this class will only return
47 * segments of type {@link PathIterator#SEG_MOVETO}, {@link
48 * PathIterator#SEG_LINETO}, and {@link PathIterator#SEG_CLOSE}.
50 * <p>The accuracy of the approximation is determined by two
51 * parameters:
53 * <ul><li>The <i>flatness</i> is a threshold value for deciding when
54 * a curved segment is consided flat enough for being approximated by
55 * a single straight line. Flatness is defined as the maximal distance
56 * of a curve control point to the straight line that connects the
57 * curve start and end. A lower flatness threshold means a closer
58 * approximation. See {@link QuadCurve2D#getFlatness()} and {@link
59 * CubicCurve2D#getFlatness()} for drawings which illustrate the
60 * meaning of flatness.</li>
62 * <li>The <i>recursion limit</i> imposes an upper bound for how often
63 * a curved segment gets subdivided. A limit of <i>n</i> means that
64 * for each individual quadratic and cubic B&#xe9;zier spline
65 * segment, at most 2<sup><small><i>n</i></small></sup> {@link
66 * PathIterator#SEG_LINETO} segments will be created.</li></ul>
68 * <p><b>Memory Efficiency:</b> The memory consumption grows linearly
69 * with the recursion limit. Neither the <i>flatness</i> parameter nor
70 * the number of segments in the flattened path will affect the memory
71 * consumption.
73 * <p><b>Thread Safety:</b> Multiple threads can safely work on
74 * separate instances of this class. However, multiple threads should
75 * not concurrently access the same instance, as no synchronization is
76 * performed.
78 * @see <a href="doc-files/FlatteningPathIterator-1.html"
79 * >Implementation Note</a>
81 * @author Sascha Brawer (brawer@dandelis.ch)
83 * @since 1.2
85 public class FlatteningPathIterator
86 implements PathIterator
88 /**
89 * The PathIterator whose curved segments are being approximated.
91 private final PathIterator srcIter;
94 /**
95 * The square of the flatness threshold value, which determines when
96 * a curve segment is considered flat enough that no further
97 * subdivision is needed.
99 * <p>Calculating flatness actually produces the squared flatness
100 * value. To avoid the relatively expensive calculation of a square
101 * root for each curve segment, we perform all flatness comparisons
102 * on squared values.
104 * @see QuadCurve2D#getFlatnessSq()
105 * @see CubicCurve2D#getFlatnessSq()
107 private final double flatnessSq;
111 * The maximal number of subdivions that are performed to
112 * approximate a quadratic or cubic curve segment.
114 private final int recursionLimit;
118 * A stack for holding the coordinates of subdivided segments.
120 * @see <a href="doc-files/FlatteningPathIterator-1.html"
121 * >Implementation Note</a>
123 private double[] stack;
127 * The current stack size.
129 * @see <a href="doc-files/FlatteningPathIterator-1.html"
130 * >Implementation Note</a>
132 private int stackSize;
136 * The number of recursions that were performed to arrive at
137 * a segment on the stack.
139 * @see <a href="doc-files/FlatteningPathIterator-1.html"
140 * >Implementation Note</a>
142 private int[] recLevel;
146 private final double[] scratch = new double[6];
150 * The segment type of the last segment that was returned by
151 * the source iterator.
153 private int srcSegType;
157 * The current <i>x</i> position of the source iterator.
159 private double srcPosX;
163 * The current <i>y</i> position of the source iterator.
165 private double srcPosY;
169 * A flag that indicates when this path iterator has finished its
170 * iteration over path segments.
172 private boolean done;
176 * Constructs a new PathIterator for approximating an input
177 * PathIterator with straight lines. The approximation works by
178 * recursive subdivisons, until the specified flatness threshold is
179 * not exceeded.
181 * <p>There will not be more than 10 nested recursion steps, which
182 * means that a single <code>SEG_QUADTO</code> or
183 * <code>SEG_CUBICTO</code> segment is approximated by at most
184 * 2<sup><small>10</small></sup> = 1024 straight lines.
186 public FlatteningPathIterator(PathIterator src, double flatness)
188 this(src, flatness, 10);
193 * Constructs a new PathIterator for approximating an input
194 * PathIterator with straight lines. The approximation works by
195 * recursive subdivisons, until the specified flatness threshold is
196 * not exceeded. Additionally, the number of recursions is also
197 * bound by the specified recursion limit.
199 public FlatteningPathIterator(PathIterator src, double flatness,
200 int limit)
202 if (flatness < 0 || limit < 0)
203 throw new IllegalArgumentException();
205 srcIter = src;
206 flatnessSq = flatness * flatness;
207 recursionLimit = limit;
208 fetchSegment();
213 * Returns the maximally acceptable flatness.
215 * @see QuadCurve2D#getFlatness()
216 * @see CubicCurve2D#getFlatness()
218 public double getFlatness()
220 return Math.sqrt(flatnessSq);
225 * Returns the maximum number of recursive curve subdivisions.
227 public int getRecursionLimit()
229 return recursionLimit;
233 // Documentation will be copied from PathIterator.
234 public int getWindingRule()
236 return srcIter.getWindingRule();
240 // Documentation will be copied from PathIterator.
241 public boolean isDone()
243 return done;
247 // Documentation will be copied from PathIterator.
248 public void next()
250 if (stackSize > 0)
252 --stackSize;
253 if (stackSize > 0)
255 switch (srcSegType)
257 case PathIterator.SEG_QUADTO:
258 subdivideQuadratic();
259 return;
261 case PathIterator.SEG_CUBICTO:
262 subdivideCubic();
263 return;
265 default:
266 throw new IllegalStateException();
271 srcIter.next();
272 fetchSegment();
276 // Documentation will be copied from PathIterator.
277 public int currentSegment(double[] coords)
279 if (done)
280 throw new NoSuchElementException();
282 switch (srcSegType)
284 case PathIterator.SEG_CLOSE:
285 return srcSegType;
287 case PathIterator.SEG_MOVETO:
288 case PathIterator.SEG_LINETO:
289 coords[0] = srcPosX;
290 coords[1] = srcPosY;
291 return srcSegType;
293 case PathIterator.SEG_QUADTO:
294 if (stackSize == 0)
296 coords[0] = srcPosX;
297 coords[1] = srcPosY;
299 else
301 int sp = stack.length - 4 * stackSize;
302 coords[0] = stack[sp + 2];
303 coords[1] = stack[sp + 3];
305 return PathIterator.SEG_LINETO;
307 case PathIterator.SEG_CUBICTO:
308 if (stackSize == 0)
310 coords[0] = srcPosX;
311 coords[1] = srcPosY;
313 else
315 int sp = stack.length - 6 * stackSize;
316 coords[0] = stack[sp + 4];
317 coords[1] = stack[sp + 5];
319 return PathIterator.SEG_LINETO;
322 throw new IllegalStateException();
326 // Documentation will be copied from PathIterator.
327 public int currentSegment(float[] coords)
329 if (done)
330 throw new NoSuchElementException();
332 switch (srcSegType)
334 case PathIterator.SEG_CLOSE:
335 return srcSegType;
337 case PathIterator.SEG_MOVETO:
338 case PathIterator.SEG_LINETO:
339 coords[0] = (float) srcPosX;
340 coords[1] = (float) srcPosY;
341 return srcSegType;
343 case PathIterator.SEG_QUADTO:
344 if (stackSize == 0)
346 coords[0] = (float) srcPosX;
347 coords[1] = (float) srcPosY;
349 else
351 int sp = stack.length - 4 * stackSize;
352 coords[0] = (float) stack[sp + 2];
353 coords[1] = (float) stack[sp + 3];
355 return PathIterator.SEG_LINETO;
357 case PathIterator.SEG_CUBICTO:
358 if (stackSize == 0)
360 coords[0] = (float) srcPosX;
361 coords[1] = (float) srcPosY;
363 else
365 int sp = stack.length - 6 * stackSize;
366 coords[0] = (float) stack[sp + 4];
367 coords[1] = (float) stack[sp + 5];
369 return PathIterator.SEG_LINETO;
372 throw new IllegalStateException();
377 * Fetches the next segment from the source iterator.
379 private void fetchSegment()
381 int sp;
383 if (srcIter.isDone())
385 done = true;
386 return;
389 srcSegType = srcIter.currentSegment(scratch);
391 switch (srcSegType)
393 case PathIterator.SEG_CLOSE:
394 return;
396 case PathIterator.SEG_MOVETO:
397 case PathIterator.SEG_LINETO:
398 srcPosX = scratch[0];
399 srcPosY = scratch[1];
400 return;
402 case PathIterator.SEG_QUADTO:
403 if (recursionLimit == 0)
405 srcPosX = scratch[2];
406 srcPosY = scratch[3];
407 stackSize = 0;
408 return;
410 sp = 4 * recursionLimit;
411 stackSize = 1;
412 if (stack == null)
414 stack = new double[sp + /* 4 + 2 */ 6];
415 recLevel = new int[recursionLimit + 1];
417 recLevel[0] = 0;
418 stack[sp] = srcPosX; // P1.x
419 stack[sp + 1] = srcPosY; // P1.y
420 stack[sp + 2] = scratch[0]; // C.x
421 stack[sp + 3] = scratch[1]; // C.y
422 srcPosX = stack[sp + 4] = scratch[2]; // P2.x
423 srcPosY = stack[sp + 5] = scratch[3]; // P2.y
424 subdivideQuadratic();
425 break;
427 case PathIterator.SEG_CUBICTO:
428 if (recursionLimit == 0)
430 srcPosX = scratch[4];
431 srcPosY = scratch[5];
432 stackSize = 0;
433 return;
435 sp = 6 * recursionLimit;
436 stackSize = 1;
437 if ((stack == null) || (stack.length < sp + 8))
439 stack = new double[sp + /* 6 + 2 */ 8];
440 recLevel = new int[recursionLimit + 1];
442 recLevel[0] = 0;
443 stack[sp] = srcPosX; // P1.x
444 stack[sp + 1] = srcPosY; // P1.y
445 stack[sp + 2] = scratch[0]; // C1.x
446 stack[sp + 3] = scratch[1]; // C1.y
447 stack[sp + 4] = scratch[2]; // C2.x
448 stack[sp + 5] = scratch[3]; // C2.y
449 srcPosX = stack[sp + 6] = scratch[4]; // P2.x
450 srcPosY = stack[sp + 7] = scratch[5]; // P2.y
451 subdivideCubic();
452 return;
458 * Repeatedly subdivides the quadratic curve segment that is on top
459 * of the stack. The iteration terminates when the recursion limit
460 * has been reached, or when the resulting segment is flat enough.
462 private void subdivideQuadratic()
464 int sp;
465 int level;
467 sp = stack.length - 4 * stackSize - 2;
468 level = recLevel[stackSize - 1];
469 while ((level < recursionLimit)
470 && (QuadCurve2D.getFlatnessSq(stack, sp) >= flatnessSq))
472 recLevel[stackSize] = recLevel[stackSize - 1] = ++level;
473 QuadCurve2D.subdivide(stack, sp, stack, sp - 4, stack, sp);
474 ++stackSize;
475 sp -= 4;
481 * Repeatedly subdivides the cubic curve segment that is on top
482 * of the stack. The iteration terminates when the recursion limit
483 * has been reached, or when the resulting segment is flat enough.
485 private void subdivideCubic()
487 int sp;
488 int level;
490 sp = stack.length - 6 * stackSize - 2;
491 level = recLevel[stackSize - 1];
492 while ((level < recursionLimit)
493 && (CubicCurve2D.getFlatnessSq(stack, sp) >= flatnessSq))
495 recLevel[stackSize] = recLevel[stackSize - 1] = ++level;
497 CubicCurve2D.subdivide(stack, sp, stack, sp - 6, stack, sp);
498 ++stackSize;
499 sp -= 6;
504 /* These routines were useful for debugging. Since they would
505 * just bloat the implementation, they are commented out.
509 private static String segToString(int segType, double[] d, int offset)
511 String s;
513 switch (segType)
515 case PathIterator.SEG_CLOSE:
516 return "SEG_CLOSE";
518 case PathIterator.SEG_MOVETO:
519 return "SEG_MOVETO (" + d[offset] + ", " + d[offset + 1] + ")";
521 case PathIterator.SEG_LINETO:
522 return "SEG_LINETO (" + d[offset] + ", " + d[offset + 1] + ")";
524 case PathIterator.SEG_QUADTO:
525 return "SEG_QUADTO (" + d[offset] + ", " + d[offset + 1]
526 + ") (" + d[offset + 2] + ", " + d[offset + 3] + ")";
528 case PathIterator.SEG_CUBICTO:
529 return "SEG_CUBICTO (" + d[offset] + ", " + d[offset + 1]
530 + ") (" + d[offset + 2] + ", " + d[offset + 3]
531 + ") (" + d[offset + 4] + ", " + d[offset + 5] + ")";
534 throw new IllegalStateException();
538 private void dumpQuadraticStack(String msg)
540 int sp = stack.length - 4 * stackSize - 2;
541 int i = 0;
542 System.err.print(" " + msg + ":");
543 while (sp < stack.length)
545 System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")");
546 if (i < recLevel.length)
547 System.out.print("/" + recLevel[i++]);
548 if (sp + 3 < stack.length)
549 System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]");
550 sp += 4;
552 System.err.println();
556 private void dumpCubicStack(String msg)
558 int sp = stack.length - 6 * stackSize - 2;
559 int i = 0;
560 System.err.print(" " + msg + ":");
561 while (sp < stack.length)
563 System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")");
564 if (i < recLevel.length)
565 System.out.print("/" + recLevel[i++]);
566 if (sp + 3 < stack.length)
568 System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]");
569 System.err.print(" [" + stack[sp+4] + ", " + stack[sp+5] + "]");
571 sp += 6;
573 System.err.println();