mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			580 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			580 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* FlatteningPathIterator.java -- Approximates curves by straight lines
 | |
|    Copyright (C) 2003 Free Software Foundation
 | |
| 
 | |
| This file is part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2, or (at your option)
 | |
| any later version.
 | |
| 
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; see the file COPYING.  If not, write to the
 | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | |
| 02110-1301 USA.
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| obligated to do so.  If you do not wish to do so, delete this
 | |
| exception statement from your version. */
 | |
| 
 | |
| 
 | |
| package java.awt.geom;
 | |
| 
 | |
| import java.util.NoSuchElementException;
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * A PathIterator for approximating curved path segments by sequences
 | |
|  * of straight lines. Instances of this class will only return
 | |
|  * segments of type {@link PathIterator#SEG_MOVETO}, {@link
 | |
|  * PathIterator#SEG_LINETO}, and {@link PathIterator#SEG_CLOSE}.
 | |
|  *
 | |
|  * <p>The accuracy of the approximation is determined by two
 | |
|  * parameters:
 | |
|  *
 | |
|  * <ul><li>The <i>flatness</i> is a threshold value for deciding when
 | |
|  * a curved segment is consided flat enough for being approximated by
 | |
|  * a single straight line. Flatness is defined as the maximal distance
 | |
|  * of a curve control point to the straight line that connects the
 | |
|  * curve start and end. A lower flatness threshold means a closer
 | |
|  * approximation.  See {@link QuadCurve2D#getFlatness()} and {@link
 | |
|  * CubicCurve2D#getFlatness()} for drawings which illustrate the
 | |
|  * meaning of flatness.</li>
 | |
|  *
 | |
|  * <li>The <i>recursion limit</i> imposes an upper bound for how often
 | |
|  * a curved segment gets subdivided. A limit of <i>n</i> means that
 | |
|  * for each individual quadratic and cubic Bézier spline
 | |
|  * segment, at most 2<sup><small><i>n</i></small></sup> {@link
 | |
|  * PathIterator#SEG_LINETO} segments will be created.</li></ul>
 | |
|  *
 | |
|  * <p><b>Memory Efficiency:</b> The memory consumption grows linearly
 | |
|  * with the recursion limit. Neither the <i>flatness</i> parameter nor
 | |
|  * the number of segments in the flattened path will affect the memory
 | |
|  * consumption.
 | |
|  *
 | |
|  * <p><b>Thread Safety:</b> Multiple threads can safely work on
 | |
|  * separate instances of this class. However, multiple threads should
 | |
|  * not concurrently access the same instance, as no synchronization is
 | |
|  * performed.
 | |
|  *
 | |
|  * @see <a href="doc-files/FlatteningPathIterator-1.html"
 | |
|  * >Implementation Note</a>
 | |
|  *
 | |
|  * @author Sascha Brawer (brawer@dandelis.ch)
 | |
|  *
 | |
|  * @since 1.2
 | |
|  */
 | |
| public class FlatteningPathIterator
 | |
|   implements PathIterator
 | |
| {
 | |
|   /**
 | |
|    * The PathIterator whose curved segments are being approximated.
 | |
|    */
 | |
|   private final PathIterator srcIter;
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * The square of the flatness threshold value, which determines when
 | |
|    * a curve segment is considered flat enough that no further
 | |
|    * subdivision is needed.
 | |
|    *
 | |
|    * <p>Calculating flatness actually produces the squared flatness
 | |
|    * value. To avoid the relatively expensive calculation of a square
 | |
|    * root for each curve segment, we perform all flatness comparisons
 | |
|    * on squared values.
 | |
|    *
 | |
|    * @see QuadCurve2D#getFlatnessSq()
 | |
|    * @see CubicCurve2D#getFlatnessSq()
 | |
|    */
 | |
|   private final double flatnessSq;
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * The maximal number of subdivions that are performed to
 | |
|    * approximate a quadratic or cubic curve segment.
 | |
|    */
 | |
|   private final int recursionLimit;
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * A stack for holding the coordinates of subdivided segments.
 | |
|    *
 | |
|    * @see <a href="doc-files/FlatteningPathIterator-1.html"
 | |
|    * >Implementation Note</a>
 | |
|    */
 | |
|   private double[] stack;
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * The current stack size.
 | |
|    *
 | |
|    * @see <a href="doc-files/FlatteningPathIterator-1.html"
 | |
|    * >Implementation Note</a>
 | |
|    */
 | |
|   private int stackSize;
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * The number of recursions that were performed to arrive at
 | |
|    * a segment on the stack.
 | |
|    *
 | |
|    * @see <a href="doc-files/FlatteningPathIterator-1.html"
 | |
|    * >Implementation Note</a>
 | |
|    */
 | |
|   private int[] recLevel;
 | |
| 
 | |
| 
 | |
| 
 | |
|   private final double[] scratch = new double[6];
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * The segment type of the last segment that was returned by
 | |
|    * the source iterator.
 | |
|    */
 | |
|   private int srcSegType;
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * The current <i>x</i> position of the source iterator.
 | |
|    */
 | |
|   private double srcPosX;
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * The current <i>y</i> position of the source iterator.
 | |
|    */
 | |
|   private double srcPosY;
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * A flag that indicates when this path iterator has finished its
 | |
|    * iteration over path segments.
 | |
|    */
 | |
|   private boolean done;
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Constructs a new PathIterator for approximating an input
 | |
|    * PathIterator with straight lines. The approximation works by
 | |
|    * recursive subdivisons, until the specified flatness threshold is
 | |
|    * not exceeded.
 | |
|    *
 | |
|    * <p>There will not be more than 10 nested recursion steps, which
 | |
|    * means that a single <code>SEG_QUADTO</code> or
 | |
|    * <code>SEG_CUBICTO</code> segment is approximated by at most
 | |
|    * 2<sup><small>10</small></sup> = 1024 straight lines.
 | |
|    */
 | |
|   public FlatteningPathIterator(PathIterator src, double flatness)
 | |
|   {
 | |
|     this(src, flatness, 10);
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Constructs a new PathIterator for approximating an input
 | |
|    * PathIterator with straight lines. The approximation works by
 | |
|    * recursive subdivisons, until the specified flatness threshold is
 | |
|    * not exceeded.  Additionally, the number of recursions is also
 | |
|    * bound by the specified recursion limit.
 | |
|    */
 | |
|   public FlatteningPathIterator(PathIterator src, double flatness,
 | |
|                                 int limit)
 | |
|   {
 | |
|     if (flatness < 0 || limit < 0)
 | |
|       throw new IllegalArgumentException();
 | |
| 
 | |
|     srcIter = src;
 | |
|     flatnessSq = flatness * flatness;
 | |
|     recursionLimit = limit;
 | |
|     fetchSegment();
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Returns the maximally acceptable flatness.
 | |
|    *
 | |
|    * @see QuadCurve2D#getFlatness()
 | |
|    * @see CubicCurve2D#getFlatness()
 | |
|    */
 | |
|   public double getFlatness()
 | |
|   {
 | |
|     return Math.sqrt(flatnessSq);
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Returns the maximum number of recursive curve subdivisions.
 | |
|    */
 | |
|   public int getRecursionLimit()
 | |
|   {
 | |
|     return recursionLimit;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // Documentation will be copied from PathIterator.
 | |
|   public int getWindingRule()
 | |
|   {
 | |
|     return srcIter.getWindingRule();
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // Documentation will be copied from PathIterator.
 | |
|   public boolean isDone()
 | |
|   {
 | |
|     return done;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // Documentation will be copied from PathIterator.
 | |
|   public void next()
 | |
|   {
 | |
|     if (stackSize > 0)
 | |
|     {
 | |
|       --stackSize;
 | |
|       if (stackSize > 0)
 | |
|       {
 | |
|         switch (srcSegType)
 | |
|         {
 | |
|         case PathIterator.SEG_QUADTO:
 | |
|           subdivideQuadratic();
 | |
|           return;
 | |
| 
 | |
|         case PathIterator.SEG_CUBICTO:
 | |
|           subdivideCubic();
 | |
|           return;
 | |
| 
 | |
|         default:
 | |
|           throw new IllegalStateException();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     srcIter.next();
 | |
|     fetchSegment();
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // Documentation will be copied from PathIterator.
 | |
|   public int currentSegment(double[] coords)
 | |
|   {
 | |
|     if (done)
 | |
|       throw new NoSuchElementException();
 | |
| 
 | |
|     switch (srcSegType)
 | |
|     {
 | |
|     case PathIterator.SEG_CLOSE:
 | |
|       return srcSegType;
 | |
| 
 | |
|     case PathIterator.SEG_MOVETO:
 | |
|     case PathIterator.SEG_LINETO:
 | |
|       coords[0] = srcPosX;
 | |
|       coords[1] = srcPosY;
 | |
|       return srcSegType;
 | |
| 
 | |
|     case PathIterator.SEG_QUADTO:
 | |
|       if (stackSize == 0)
 | |
|       {
 | |
|         coords[0] = srcPosX;
 | |
|         coords[1] = srcPosY;
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         int sp = stack.length - 4 * stackSize;
 | |
|         coords[0] = stack[sp + 2];
 | |
|         coords[1] = stack[sp + 3];
 | |
|       }
 | |
|       return PathIterator.SEG_LINETO;
 | |
| 
 | |
|     case PathIterator.SEG_CUBICTO:
 | |
|       if (stackSize == 0)
 | |
|       {
 | |
|         coords[0] = srcPosX;
 | |
|         coords[1] = srcPosY;
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         int sp = stack.length - 6 * stackSize;
 | |
|         coords[0] = stack[sp + 4];
 | |
|         coords[1] = stack[sp + 5];
 | |
|       }
 | |
|       return PathIterator.SEG_LINETO;
 | |
|     }
 | |
| 
 | |
|     throw new IllegalStateException();
 | |
|   }
 | |
| 
 | |
| 
 | |
|   // Documentation will be copied from PathIterator.
 | |
|   public int currentSegment(float[] coords)
 | |
|   {
 | |
|     if (done)
 | |
|       throw new NoSuchElementException();
 | |
| 
 | |
|     switch (srcSegType)
 | |
|     {
 | |
|     case PathIterator.SEG_CLOSE:
 | |
|       return srcSegType;
 | |
| 
 | |
|     case PathIterator.SEG_MOVETO:
 | |
|     case PathIterator.SEG_LINETO:
 | |
|       coords[0] = (float) srcPosX;
 | |
|       coords[1] = (float) srcPosY;
 | |
|       return srcSegType;
 | |
| 
 | |
|     case PathIterator.SEG_QUADTO:
 | |
|       if (stackSize == 0)
 | |
|       {
 | |
|         coords[0] = (float) srcPosX;
 | |
|         coords[1] = (float) srcPosY;
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         int sp = stack.length - 4 * stackSize;
 | |
|         coords[0] = (float) stack[sp + 2];
 | |
|         coords[1] = (float) stack[sp + 3];
 | |
|       }
 | |
|       return PathIterator.SEG_LINETO;
 | |
| 
 | |
|     case PathIterator.SEG_CUBICTO:
 | |
|       if (stackSize == 0)
 | |
|       {
 | |
|         coords[0] = (float) srcPosX;
 | |
|         coords[1] = (float) srcPosY;
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         int sp = stack.length - 6 * stackSize;
 | |
|         coords[0] = (float) stack[sp + 4];
 | |
|         coords[1] = (float) stack[sp + 5];
 | |
|       }
 | |
|       return PathIterator.SEG_LINETO;
 | |
|     }
 | |
| 
 | |
|     throw new IllegalStateException();
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Fetches the next segment from the source iterator.
 | |
|    */
 | |
|   private void fetchSegment()
 | |
|   {
 | |
|     int sp;
 | |
| 
 | |
|     if (srcIter.isDone())
 | |
|     {
 | |
|       done = true;
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     srcSegType = srcIter.currentSegment(scratch);
 | |
| 
 | |
|     switch (srcSegType)
 | |
|     {
 | |
|     case PathIterator.SEG_CLOSE:
 | |
|       return;
 | |
| 
 | |
|     case PathIterator.SEG_MOVETO:
 | |
|     case PathIterator.SEG_LINETO:
 | |
|       srcPosX = scratch[0];
 | |
|       srcPosY = scratch[1];
 | |
|       return;
 | |
| 
 | |
|     case PathIterator.SEG_QUADTO:
 | |
|       if (recursionLimit == 0)
 | |
|       {
 | |
|         srcPosX = scratch[2];
 | |
|         srcPosY = scratch[3];
 | |
|         stackSize = 0;
 | |
|         return;
 | |
|       }
 | |
|       sp = 4 * recursionLimit;
 | |
|       stackSize = 1;
 | |
|       if (stack == null)
 | |
|       {
 | |
|         stack = new double[sp + /* 4 + 2 */ 6];
 | |
|         recLevel = new int[recursionLimit + 1];
 | |
|       }
 | |
|       recLevel[0] = 0;
 | |
|       stack[sp] = srcPosX;                  // P1.x
 | |
|       stack[sp + 1] = srcPosY;              // P1.y
 | |
|       stack[sp + 2] = scratch[0];           // C.x
 | |
|       stack[sp + 3] = scratch[1];           // C.y
 | |
|       srcPosX = stack[sp + 4] = scratch[2]; // P2.x
 | |
|       srcPosY = stack[sp + 5] = scratch[3]; // P2.y
 | |
|       subdivideQuadratic();
 | |
|       break;
 | |
| 
 | |
|     case PathIterator.SEG_CUBICTO:
 | |
|       if (recursionLimit == 0)
 | |
|       {
 | |
|         srcPosX = scratch[4];
 | |
|         srcPosY = scratch[5];
 | |
|         stackSize = 0;
 | |
|         return;
 | |
|       }
 | |
|       sp = 6 * recursionLimit;
 | |
|       stackSize = 1;
 | |
|       if ((stack == null) || (stack.length < sp + 8))
 | |
|       {
 | |
|         stack = new double[sp + /* 6 + 2 */ 8];
 | |
|         recLevel = new int[recursionLimit + 1];
 | |
|       }
 | |
|       recLevel[0] = 0;
 | |
|       stack[sp] = srcPosX;                  // P1.x
 | |
|       stack[sp + 1] = srcPosY;              // P1.y
 | |
|       stack[sp + 2] = scratch[0];           // C1.x
 | |
|       stack[sp + 3] = scratch[1];           // C1.y
 | |
|       stack[sp + 4] = scratch[2];           // C2.x
 | |
|       stack[sp + 5] = scratch[3];           // C2.y
 | |
|       srcPosX = stack[sp + 6] = scratch[4]; // P2.x
 | |
|       srcPosY = stack[sp + 7] = scratch[5]; // P2.y
 | |
|       subdivideCubic();
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Repeatedly subdivides the quadratic curve segment that is on top
 | |
|    * of the stack. The iteration terminates when the recursion limit
 | |
|    * has been reached, or when the resulting segment is flat enough.
 | |
|    */
 | |
|   private void subdivideQuadratic()
 | |
|   {
 | |
|     int sp;
 | |
|     int level;
 | |
| 
 | |
|     sp = stack.length - 4 * stackSize - 2;
 | |
|     level = recLevel[stackSize - 1];
 | |
|     while ((level < recursionLimit)
 | |
|            && (QuadCurve2D.getFlatnessSq(stack, sp) >= flatnessSq))
 | |
|     {
 | |
|       recLevel[stackSize] = recLevel[stackSize - 1] = ++level;
 | |
|       QuadCurve2D.subdivide(stack, sp, stack, sp - 4, stack, sp);
 | |
|       ++stackSize;
 | |
|       sp -= 4;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Repeatedly subdivides the cubic curve segment that is on top
 | |
|    * of the stack. The iteration terminates when the recursion limit
 | |
|    * has been reached, or when the resulting segment is flat enough.
 | |
|    */
 | |
|   private void subdivideCubic()
 | |
|   {
 | |
|     int sp;
 | |
|     int level;
 | |
| 
 | |
|     sp = stack.length - 6 * stackSize - 2;
 | |
|     level = recLevel[stackSize - 1];
 | |
|     while ((level < recursionLimit)
 | |
|            && (CubicCurve2D.getFlatnessSq(stack, sp) >= flatnessSq))
 | |
|     {
 | |
|       recLevel[stackSize] = recLevel[stackSize - 1] = ++level;
 | |
| 
 | |
|       CubicCurve2D.subdivide(stack, sp, stack, sp - 6, stack, sp);
 | |
|       ++stackSize;
 | |
|       sp -= 6;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* These routines were useful for debugging. Since they would
 | |
|    * just bloat the implementation, they are commented out.
 | |
|    *
 | |
|    *
 | |
| 
 | |
|   private static String segToString(int segType, double[] d, int offset)
 | |
|   {
 | |
|     String s;
 | |
| 
 | |
|     switch (segType)
 | |
|     {
 | |
|     case PathIterator.SEG_CLOSE:
 | |
|       return "SEG_CLOSE";
 | |
| 
 | |
|     case PathIterator.SEG_MOVETO:
 | |
|       return "SEG_MOVETO (" + d[offset] + ", " + d[offset + 1] + ")";
 | |
| 
 | |
|     case PathIterator.SEG_LINETO:
 | |
|       return "SEG_LINETO (" + d[offset] + ", " + d[offset + 1] + ")";
 | |
| 
 | |
|     case PathIterator.SEG_QUADTO:
 | |
|       return "SEG_QUADTO (" + d[offset] + ", " + d[offset + 1]
 | |
|         + ") (" + d[offset + 2] + ", " + d[offset + 3] + ")";
 | |
| 
 | |
|     case PathIterator.SEG_CUBICTO:
 | |
|       return "SEG_CUBICTO (" + d[offset] + ", " + d[offset + 1]
 | |
|         + ") (" + d[offset + 2] + ", " + d[offset + 3]
 | |
|         + ") (" + d[offset + 4] + ", " + d[offset + 5] + ")";
 | |
|     }
 | |
| 
 | |
|     throw new IllegalStateException();
 | |
|   }
 | |
| 
 | |
| 
 | |
|   private void dumpQuadraticStack(String msg)
 | |
|   {
 | |
|     int sp = stack.length - 4 * stackSize - 2;
 | |
|     int i = 0;
 | |
|     System.err.print("    " + msg + ":");
 | |
|     while (sp < stack.length)
 | |
|     {
 | |
|       System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")");
 | |
|       if (i < recLevel.length)
 | |
|         System.out.print("/" + recLevel[i++]);
 | |
|       if (sp + 3 < stack.length)
 | |
|         System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]");
 | |
|       sp += 4;
 | |
|     }
 | |
|     System.err.println();
 | |
|   }
 | |
| 
 | |
| 
 | |
|   private void dumpCubicStack(String msg)
 | |
|   {
 | |
|     int sp = stack.length - 6 * stackSize - 2;
 | |
|     int i = 0;
 | |
|     System.err.print("    " + msg + ":");
 | |
|     while (sp < stack.length)
 | |
|     {
 | |
|       System.err.print(" (" + stack[sp] + ", " + stack[sp+1] + ")");
 | |
|       if (i < recLevel.length)
 | |
|         System.out.print("/" + recLevel[i++]);
 | |
|       if (sp + 3 < stack.length)
 | |
|       {
 | |
|         System.err.print(" [" + stack[sp+2] + ", " + stack[sp+3] + "]");
 | |
|         System.err.print(" [" + stack[sp+4] + ", " + stack[sp+5] + "]");
 | |
|       }
 | |
|       sp += 6;
 | |
|     }
 | |
|     System.err.println();
 | |
|   }
 | |
| 
 | |
|   *
 | |
|   *
 | |
|   */
 | |
| }
 |