/[classpath]/classpath/java/awt/geom/FlatteningPathIterator.java
ViewVC logotype

Diff of /classpath/java/awt/geom/FlatteningPathIterator.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.2 by ericb, Fri Mar 22 16:54:31 2002 UTC revision 1.3 by brawer, Tue Nov 11 15:05:07 2003 UTC
# Line 1  Line 1 
1  /* FlatteningPathIterator.java -- performs interpolation of curved paths  /* FlatteningPathIterator.java -- Approximates curves by straight lines
2     Copyright (C) 2002 Free Software Foundation     Copyright (C) 2003 Free Software Foundation
3    
4  This file is part of GNU Classpath.  This file is part of GNU Classpath.
5    
# Line 38  exception statement from your version. * Line 38  exception statement from your version. *
38    
39  package java.awt.geom;  package java.awt.geom;
40    
41    import java.util.NoSuchElementException;
42    
43    
44  /**  /**
45   * This class can be used to perform the flattening required by the Shape   * A PathIterator for approximating curved path segments by sequences
46   * interface. It interpolates a curved path segment into a sequence of flat   * of straight lines. Instances of this class will only return
47   * ones within a certain flatness, up to a recursion limit.   * segments of type {@link PathIterator#SEG_MOVETO}, {@link
48   *   * PathIterator#SEG_LINETO}, and {@link PathIterator#SEG_CLOSE}.
49   * @author Eric Blake <ebb9@email.byu.edu>   *
50   * @see Shape   * <p>The accuracy of the approximation is determined by two
51   * @see RectangularShape#getPathIterator(AffineTransform, double)   * parameters:
52     *
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>
61     *
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>
67     *
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.
72     *
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.
77     *
78     * @see <a href="doc-files/FlatteningPathIterator-1.html"
79     * >Implementation Note</a>
80     *
81     * @author Sascha Brawer (brawer@dandelis.ch)
82     *
83   * @since 1.2   * @since 1.2
  * @status STUBS ONLY  
84   */   */
85  public class FlatteningPathIterator implements PathIterator  public class FlatteningPathIterator
86      implements PathIterator
87  {  {
88    // The iterator we are applied to.    /**
89    private PathIterator subIterator;     * The PathIterator whose curved segments are being approximated.
90    private double flatness;     */
91    private int limit;    private final PathIterator srcIter;
92    
93    
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.
98       *
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.
103       *
104       * @see QuadCurve2D#getFlatnessSq()
105       * @see CubicCurve2D#getFlatnessSq()
106       */
107      private final double flatnessSq;
108    
109    
110      /**
111       * The maximal number of subdivions that are performed to
112       * approximate a quadratic or cubic curve segment.
113       */
114      private final int recursionLimit;
115    
116    
117      /**
118       * A stack for holding the coordinates of subdivided segments.
119       *
120       * @see <a href="doc-files/FlatteningPathIterator-1.html"
121       * >Implementation Note</a>
122       */
123      private double[] stack;
124    
125    
126      /**
127       * The current stack size.
128       *
129       * @see <a href="doc-files/FlatteningPathIterator-1.html"
130       * >Implementation Note</a>
131       */
132      private int stackSize;
133    
134    
135      /**
136       * The number of recursions that were performed to arrive at
137       * a segment on the stack.
138       *
139       * @see <a href="doc-files/FlatteningPathIterator-1.html"
140       * >Implementation Note</a>
141       */
142      private int[] recLevel;
143    
144      
145      
146      private final double[] scratch = new double[6];
147    
148    
149      /**
150       * The segment type of the last segment that was returned by
151       * the source iterator.
152       */
153      private int srcSegType;
154    
155    
156      /**
157       * The current <i>x</i> position of the source iterator.
158       */
159      private double srcPosX;
160    
161    
162      /**
163       * The current <i>y</i> position of the source iterator.
164       */
165      private double srcPosY;
166    
167    
168      /**
169       * A flag that indicates when this path iterator has finished its
170       * iteration over path segments.
171       */
172      private boolean done;
173    
174    
175      /**
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.
180       *
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.
185       */
186    public FlatteningPathIterator(PathIterator src, double flatness)    public FlatteningPathIterator(PathIterator src, double flatness)
187    {    {
188      this(src, flatness, 10);      this(src, flatness, 10);
189    }    }
190    public FlatteningPathIterator(PathIterator src, double flatness, int limit)  
191    
192      /**
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.
198       */
199      public FlatteningPathIterator(PathIterator src, double flatness,
200                                    int limit)
201    {    {
     subIterator = src;  
     this.flatness = flatness;  
     this.limit = limit;  
202      if (flatness < 0 || limit < 0)      if (flatness < 0 || limit < 0)
203        throw new IllegalArgumentException();        throw new IllegalArgumentException();
204    
205        srcIter = src;
206        flatnessSq = flatness * flatness;
207        recursionLimit = limit;
208        fetchSegment();
209    }    }
210    
211    
212      /**
213       * Returns the maximally acceptable flatness.
214       *
215       * @see QuadCurve2D#getFlatness()
216       * @see CubicCurve2D#getFlatness()
217       */
218    public double getFlatness()    public double getFlatness()
219    {    {
220      return flatness;      return Math.sqrt(flatnessSq);
221    }    }
222    
223    
224      /**
225       * Returns the maximum number of recursive curve subdivisions.
226       */
227    public int getRecursionLimit()    public int getRecursionLimit()
228    {    {
229      return limit;      return recursionLimit;
230    }    }
231    
232    
233      // Documentation will be copied from PathIterator.
234    public int getWindingRule()    public int getWindingRule()
235    {    {
236      return subIterator.getWindingRule();      return srcIter.getWindingRule();
237    }    }
238    
239    
240      // Documentation will be copied from PathIterator.
241    public boolean isDone()    public boolean isDone()
242    {    {
243      return subIterator.isDone();      return done;
244    }    }
245    
246    
247      // Documentation will be copied from PathIterator.
248    public void next()    public void next()
249    {    {
250      throw new Error("not implemented");      if (stackSize > 0)
251        {
252          --stackSize;
253          if (stackSize > 0)
254          {
255            switch (srcSegType)
256            {
257            case PathIterator.SEG_QUADTO:
258              subdivideQuadratic();
259              return;
260    
261            case PathIterator.SEG_CUBICTO:
262              subdivideCubic();
263              return;
264    
265            default:
266              throw new IllegalStateException();
267            }
268          }
269        }
270    
271        srcIter.next();
272        fetchSegment();
273    }    }
274    
275    
276      // Documentation will be copied from PathIterator.
277    public int currentSegment(double[] coords)    public int currentSegment(double[] coords)
278    {    {
279      throw new Error("not implemented");      if (done)
280          throw new NoSuchElementException();
281    
282        switch (srcSegType)
283        {
284        case PathIterator.SEG_CLOSE:
285          return srcSegType;
286    
287        case PathIterator.SEG_MOVETO:
288        case PathIterator.SEG_LINETO:
289          coords[0] = srcPosX;
290          coords[1] = srcPosY;
291          return srcSegType;
292    
293        case PathIterator.SEG_QUADTO:
294          if (stackSize == 0)
295          {
296            coords[0] = srcPosX;
297            coords[1] = srcPosY;
298          }
299          else
300          {
301            int sp = stack.length - 4 * stackSize;
302            coords[0] = stack[sp + 2];
303            coords[1] = stack[sp + 3];
304          }
305          return PathIterator.SEG_LINETO;
306    
307        case PathIterator.SEG_CUBICTO:
308          if (stackSize == 0)
309          {
310            coords[0] = srcPosX;
311            coords[1] = srcPosY;
312          }
313          else
314          {
315            int sp = stack.length - 6 * stackSize;
316            coords[0] = stack[sp + 4];
317            coords[1] = stack[sp + 5];
318          }
319          return PathIterator.SEG_LINETO;
320        }
321    
322        throw new IllegalStateException();
323    }    }
324    
325    
326      // Documentation will be copied from PathIterator.
327    public int currentSegment(float[] coords)    public int currentSegment(float[] coords)
328    {    {
329      throw new Error("not implemented");      if (done)
330          throw new NoSuchElementException();
331    
332        switch (srcSegType)
333        {
334        case PathIterator.SEG_CLOSE:
335          return srcSegType;
336    
337        case PathIterator.SEG_MOVETO:
338        case PathIterator.SEG_LINETO:
339          coords[0] = (float) srcPosX;
340          coords[1] = (float) srcPosY;
341          return srcSegType;
342    
343        case PathIterator.SEG_QUADTO:
344          if (stackSize == 0)
345          {
346            coords[0] = (float) srcPosX;
347            coords[1] = (float) srcPosY;
348          }
349          else
350          {
351            int sp = stack.length - 4 * stackSize;
352            coords[0] = (float) stack[sp + 2];
353            coords[1] = (float) stack[sp + 3];
354          }
355          return PathIterator.SEG_LINETO;
356    
357        case PathIterator.SEG_CUBICTO:
358          if (stackSize == 0)
359          {
360            coords[0] = (float) srcPosX;
361            coords[1] = (float) srcPosY;
362          }
363          else
364          {
365            int sp = stack.length - 6 * stackSize;
366            coords[0] = (float) stack[sp + 4];
367            coords[1] = (float) stack[sp + 5];
368          }
369          return PathIterator.SEG_LINETO;
370        }
371    
372        throw new IllegalStateException();
373      }
374    
375    
376      /**
377       * Fetches the next segment from the source iterator.
378       */
379      private void fetchSegment()
380      {
381        int sp;
382    
383        if (srcIter.isDone())
384        {
385          done = true;
386          return;
387        }
388    
389        srcSegType = srcIter.currentSegment(scratch);
390        
391        switch (srcSegType)
392        {
393        case PathIterator.SEG_CLOSE:
394          return;
395    
396        case PathIterator.SEG_MOVETO:
397        case PathIterator.SEG_LINETO:
398          srcPosX = scratch[0];
399          srcPosY = scratch[1];
400          return;
401    
402        case PathIterator.SEG_QUADTO:
403          if (recursionLimit == 0)
404          {
405            srcPosX = scratch[2];
406            srcPosY = scratch[3];
407            stackSize = 0;
408            return;
409          }
410          sp = 4 * recursionLimit;
411          stackSize = 1;
412          if (stack == null)
413          {
414            stack = new double[sp + /* 4 + 2 */ 6];
415            recLevel = new int[recursionLimit + 1];
416          }
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;
426    
427        case PathIterator.SEG_CUBICTO:
428          if (recursionLimit == 0)
429          {
430            srcPosX = scratch[4];
431            srcPosY = scratch[5];
432            stackSize = 0;
433            return;
434          }
435          sp = 6 * recursionLimit;
436          stackSize = 1;
437          if ((stack == null) || (stack.length < sp + 8))
438          {
439            stack = new double[sp + /* 6 + 2 */ 8];
440            recLevel = new int[recursionLimit + 1];
441          }
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;
453        }
454      }
455    
456    
457      /**
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.
461       */
462      private void subdivideQuadratic()
463      {
464        int sp;
465        int level;
466    
467        sp = stack.length - 4 * stackSize - 2;
468        level = recLevel[stackSize - 1];
469        while ((level < recursionLimit)
470               && (QuadCurve2D.getFlatnessSq(stack, sp) >= flatnessSq))
471        {
472          recLevel[stackSize] = recLevel[stackSize - 1] = ++level;
473          QuadCurve2D.subdivide(stack, sp, stack, sp - 4, stack, sp);
474          ++stackSize;
475          sp -= 4;
476        }
477    }    }
478  } // class FlatteningPathIterator  
479    
480      /**
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.
484       */
485      private void subdivideCubic()
486      {
487        int sp;
488        int level;
489    
490        sp = stack.length - 6 * stackSize - 2;
491        level = recLevel[stackSize - 1];
492        while ((level < recursionLimit)
493               && (CubicCurve2D.getFlatnessSq(stack, sp) >= flatnessSq))
494        {
495          recLevel[stackSize] = recLevel[stackSize - 1] = ++level;
496          
497          CubicCurve2D.subdivide(stack, sp, stack, sp - 6, stack, sp);
498          ++stackSize;
499          sp -= 6;
500        }
501      }
502    
503    
504      /* These routines were useful for debugging. Since they would
505       * just bloat the implementation, they are commented out.
506       *
507       *
508    
509      private static String segToString(int segType, double[] d, int offset)
510      {
511        String s;
512    
513        switch (segType)
514        {
515        case PathIterator.SEG_CLOSE:
516          return "SEG_CLOSE";
517    
518        case PathIterator.SEG_MOVETO:
519          return "SEG_MOVETO (" + d[offset] + ", " + d[offset + 1] + ")";
520    
521        case PathIterator.SEG_LINETO:
522          return "SEG_LINETO (" + d[offset] + ", " + d[offset + 1] + ")";
523    
524        case PathIterator.SEG_QUADTO:
525          return "SEG_QUADTO (" + d[offset] + ", " + d[offset + 1]
526            + ") (" + d[offset + 2] + ", " + d[offset + 3] + ")";
527    
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] + ")";
532        }
533    
534        throw new IllegalStateException();
535      }
536    
537    
538      private void dumpQuadraticStack(String msg)
539      {
540        int sp = stack.length - 4 * stackSize - 2;
541        int i = 0;
542        System.err.print("    " + msg + ":");
543        while (sp < stack.length)
544        {
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;
551        }
552        System.err.println();
553      }
554    
555    
556      private void dumpCubicStack(String msg)
557      {
558        int sp = stack.length - 6 * stackSize - 2;
559        int i = 0;
560        System.err.print("    " + msg + ":");
561        while (sp < stack.length)
562        {
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)
567          {
568            System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]");
569            System.err.print(" [" + stack[sp+4] + ", " + stack[sp+5] + "]");
570          }
571          sp += 6;
572        }
573        System.err.println();
574      }
575    
576      *
577      *
578      */
579    }

Legend:
Removed from v.1.2  
changed lines
  Added in v.1.3

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26