/* |
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code 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 |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package java.awt.geom; |
|
import java.util.*; |
|
/** |
|
* The <code>FlatteningPathIterator</code> class returns a flattened view of |
|
* another {@link PathIterator} object. Other {@link java.awt.Shape Shape} |
|
* classes can use this class to provide flattening behavior for their paths |
|
* without having to perform the interpolation calculations themselves. |
|
* |
|
* @author Jim Graham |
|
*/ |
|
public class FlatteningPathIterator implements PathIterator { |
|
static final int GROW_SIZE = 24; // Multiple of cubic & quad curve size |
|
PathIterator src; // The source iterator |
|
double squareflat; // Square of the flatness parameter |
|
// for testing against squared lengths |
|
int limit; // Maximum number of recursion levels |
|
double hold[] = new double[14]; // The cache of interpolated coords |
|
// Note that this must be long enough |
|
// to store a full cubic segment and |
|
// a relative cubic segment to avoid |
|
// aliasing when copying the coords |
|
// of a curve to the end of the array. |
|
// This is also serendipitously equal |
|
// to the size of a full quad segment |
|
// and 2 relative quad segments. |
|
double curx, cury; // The ending x,y of the last segment |
|
double movx, movy; // The x,y of the last move segment |
|
int holdType; // The type of the curve being held |
|
// for interpolation |
|
int holdEnd; // The index of the last curve segment |
|
// being held for interpolation |
|
int holdIndex; // The index of the curve segment |
|
// that was last interpolated. This |
|
// is the curve segment ready to be |
|
// returned in the next call to |
|
// currentSegment(). |
|
int levels[]; // The recursion level at which |
|
// each curve being held in storage |
|
// was generated. |
|
int levelIndex; // The index of the entry in the |
|
// levels array of the curve segment |
|
// at the holdIndex |
|
boolean done; // True when iteration is done |
|
/** |
|
* Constructs a new <code>FlatteningPathIterator</code> object that |
|
* flattens a path as it iterates over it. The iterator does not |
|
* subdivide any curve read from the source iterator to more than |
|
* 10 levels of subdivision which yields a maximum of 1024 line |
|
* segments per curve. |
|
* @param src the original unflattened path being iterated over |
|
* @param flatness the maximum allowable distance between the |
|
* control points and the flattened curve |
|
*/ |
|
public FlatteningPathIterator(PathIterator src, double flatness) { |
|
this(src, flatness, 10); |
|
} |
|
/** |
|
* Constructs a new <code>FlatteningPathIterator</code> object |
|
* that flattens a path as it iterates over it. |
|
* The <code>limit</code> parameter allows you to control the |
|
* maximum number of recursive subdivisions that the iterator |
|
* can make before it assumes that the curve is flat enough |
|
* without measuring against the <code>flatness</code> parameter. |
|
* The flattened iteration therefore never generates more than |
|
* a maximum of <code>(2^limit)</code> line segments per curve. |
|
* @param src the original unflattened path being iterated over |
|
* @param flatness the maximum allowable distance between the |
|
* control points and the flattened curve |
|
* @param limit the maximum number of recursive subdivisions |
|
* allowed for any curved segment |
|
* @exception IllegalArgumentException if |
|
* <code>flatness</code> or <code>limit</code> |
|
* is less than zero |
|
*/ |
|
public FlatteningPathIterator(PathIterator src, double flatness, |
|
int limit) { |
|
if (flatness < 0.0) { |
|
throw new IllegalArgumentException("flatness must be >= 0"); |
|
} |
|
if (limit < 0) { |
|
throw new IllegalArgumentException("limit must be >= 0"); |
|
} |
|
this.src = src; |
|
this.squareflat = flatness * flatness; |
|
this.limit = limit; |
|
this.levels = new int[limit + 1]; |
|
// prime the first path segment |
|
next(false); |
|
} |
|
/** |
|
* Returns the flatness of this iterator. |
|
* @return the flatness of this <code>FlatteningPathIterator</code>. |
|
*/ |
|
public double getFlatness() { |
|
return Math.sqrt(squareflat); |
|
} |
|
/** |
|
* Returns the recursion limit of this iterator. |
|
* @return the recursion limit of this |
|
* <code>FlatteningPathIterator</code>. |
|
*/ |
|
public int getRecursionLimit() { |
|
return limit; |
|
} |
|
/** |
|
* Returns the winding rule for determining the interior of the |
|
* path. |
|
* @return the winding rule of the original unflattened path being |
|
* iterated over. |
|
* @see PathIterator#WIND_EVEN_ODD |
|
* @see PathIterator#WIND_NON_ZERO |
|
*/ |
|
public int getWindingRule() { |
|
return src.getWindingRule(); |
|
} |
|
/** |
|
* Tests if the iteration is complete. |
|
* @return <code>true</code> if all the segments have |
|
* been read; <code>false</code> otherwise. |
|
*/ |
|
public boolean isDone() { |
|
return done; |
|
} |
|
/* |
|
* Ensures that the hold array can hold up to (want) more values. |
|
* It is currently holding (hold.length - holdIndex) values. |
|
*/ |
|
void ensureHoldCapacity(int want) { |
|
if (holdIndex - want < 0) { |
|
int have = hold.length - holdIndex; |
|
int newsize = hold.length + GROW_SIZE; |
|
double newhold[] = new double[newsize]; |
|
System.arraycopy(hold, holdIndex, |
|
newhold, holdIndex + GROW_SIZE, |
|
have); |
|
hold = newhold; |
|
holdIndex += GROW_SIZE; |
|
holdEnd += GROW_SIZE; |
|
} |
|
} |
|
/** |
|
* Moves the iterator to the next segment of the path forwards |
|
* along the primary direction of traversal as long as there are |
|
* more points in that direction. |
|
*/ |
|
public void next() { |
|
next(true); |
|
} |
|
private void next(boolean doNext) { |
|
int level; |
|
if (holdIndex >= holdEnd) { |
|
if (doNext) { |
|
src.next(); |
|
} |
|
if (src.isDone()) { |
|
done = true; |
|
return; |
|
} |
|
holdType = src.currentSegment(hold); |
|
levelIndex = 0; |
|
levels[0] = 0; |
|
} |
|
switch (holdType) { |
|
case SEG_MOVETO: |
|
case SEG_LINETO: |
|
curx = hold[0]; |
|
cury = hold[1]; |
|
if (holdType == SEG_MOVETO) { |
|
movx = curx; |
|
movy = cury; |
|
} |
|
holdIndex = 0; |
|
holdEnd = 0; |
|
break; |
|
case SEG_CLOSE: |
|
curx = movx; |
|
cury = movy; |
|
holdIndex = 0; |
|
holdEnd = 0; |
|
break; |
|
case SEG_QUADTO: |
|
if (holdIndex >= holdEnd) { |
|
// Move the coordinates to the end of the array. |
|
holdIndex = hold.length - 6; |
|
holdEnd = hold.length - 2; |
|
hold[holdIndex + 0] = curx; |
|
hold[holdIndex + 1] = cury; |
|
hold[holdIndex + 2] = hold[0]; |
|
hold[holdIndex + 3] = hold[1]; |
|
hold[holdIndex + 4] = curx = hold[2]; |
|
hold[holdIndex + 5] = cury = hold[3]; |
|
} |
|
level = levels[levelIndex]; |
|
while (level < limit) { |
|
if (QuadCurve2D.getFlatnessSq(hold, holdIndex) < squareflat) { |
|
break; |
|
} |
|
ensureHoldCapacity(4); |
|
QuadCurve2D.subdivide(hold, holdIndex, |
|
hold, holdIndex - 4, |
|
hold, holdIndex); |
|
holdIndex -= 4; |
|
// Now that we have subdivided, we have constructed |
|
// two curves of one depth lower than the original |
|
// curve. One of those curves is in the place of |
|
// the former curve and one of them is in the next |
|
// set of held coordinate slots. We now set both |
|
// curves level values to the next higher level. |
|
level++; |
|
levels[levelIndex] = level; |
|
levelIndex++; |
|
levels[levelIndex] = level; |
|
} |
|
// This curve segment is flat enough, or it is too deep |
|
// in recursion levels to try to flatten any more. The |
|
// two coordinates at holdIndex+4 and holdIndex+5 now |
|
// contain the endpoint of the curve which can be the |
|
// endpoint of an approximating line segment. |
|
holdIndex += 4; |
|
levelIndex--; |
|
break; |
|
case SEG_CUBICTO: |
|
if (holdIndex >= holdEnd) { |
|
// Move the coordinates to the end of the array. |
|
holdIndex = hold.length - 8; |
|
holdEnd = hold.length - 2; |
|
hold[holdIndex + 0] = curx; |
|
hold[holdIndex + 1] = cury; |
|
hold[holdIndex + 2] = hold[0]; |
|
hold[holdIndex + 3] = hold[1]; |
|
hold[holdIndex + 4] = hold[2]; |
|
hold[holdIndex + 5] = hold[3]; |
|
hold[holdIndex + 6] = curx = hold[4]; |
|
hold[holdIndex + 7] = cury = hold[5]; |
|
} |
|
level = levels[levelIndex]; |
|
while (level < limit) { |
|
if (CubicCurve2D.getFlatnessSq(hold, holdIndex) < squareflat) { |
|
break; |
|
} |
|
ensureHoldCapacity(6); |
|
CubicCurve2D.subdivide(hold, holdIndex, |
|
hold, holdIndex - 6, |
|
hold, holdIndex); |
|
holdIndex -= 6; |
|
// Now that we have subdivided, we have constructed |
|
// two curves of one depth lower than the original |
|
// curve. One of those curves is in the place of |
|
// the former curve and one of them is in the next |
|
// set of held coordinate slots. We now set both |
|
// curves level values to the next higher level. |
|
level++; |
|
levels[levelIndex] = level; |
|
levelIndex++; |
|
levels[levelIndex] = level; |
|
} |
|
// This curve segment is flat enough, or it is too deep |
|
// in recursion levels to try to flatten any more. The |
|
// two coordinates at holdIndex+6 and holdIndex+7 now |
|
// contain the endpoint of the curve which can be the |
|
// endpoint of an approximating line segment. |
|
holdIndex += 6; |
|
levelIndex--; |
|
break; |
|
} |
|
} |
|
/** |
|
* Returns the coordinates and type of the current path segment in |
|
* the iteration. |
|
* The return value is the path segment type: |
|
* SEG_MOVETO, SEG_LINETO, or SEG_CLOSE. |
|
* A float array of length 6 must be passed in and can be used to |
|
* store the coordinates of the point(s). |
|
* Each point is stored as a pair of float x,y coordinates. |
|
* SEG_MOVETO and SEG_LINETO types return one point, |
|
* and SEG_CLOSE does not return any points. |
|
* @param coords an array that holds the data returned from |
|
* this method |
|
* @return the path segment type of the current path segment. |
|
* @exception NoSuchElementException if there |
|
* are no more elements in the flattening path to be |
|
* returned. |
|
* @see PathIterator#SEG_MOVETO |
|
* @see PathIterator#SEG_LINETO |
|
* @see PathIterator#SEG_CLOSE |
|
*/ |
|
public int currentSegment(float[] coords) { |
|
if (isDone()) { |
|
throw new NoSuchElementException("flattening iterator out of bounds"); |
|
} |
|
int type = holdType; |
|
if (type != SEG_CLOSE) { |
|
coords[0] = (float) hold[holdIndex + 0]; |
|
coords[1] = (float) hold[holdIndex + 1]; |
|
if (type != SEG_MOVETO) { |
|
type = SEG_LINETO; |
|
} |
|
} |
|
return type; |
|
} |
|
/** |
|
* Returns the coordinates and type of the current path segment in |
|
* the iteration. |
|
* The return value is the path segment type: |
|
* SEG_MOVETO, SEG_LINETO, or SEG_CLOSE. |
|
* A double array of length 6 must be passed in and can be used to |
|
* store the coordinates of the point(s). |
|
* Each point is stored as a pair of double x,y coordinates. |
|
* SEG_MOVETO and SEG_LINETO types return one point, |
|
* and SEG_CLOSE does not return any points. |
|
* @param coords an array that holds the data returned from |
|
* this method |
|
* @return the path segment type of the current path segment. |
|
* @exception NoSuchElementException if there |
|
* are no more elements in the flattening path to be |
|
* returned. |
|
* @see PathIterator#SEG_MOVETO |
|
* @see PathIterator#SEG_LINETO |
|
* @see PathIterator#SEG_CLOSE |
|
*/ |
|
public int currentSegment(double[] coords) { |
|
if (isDone()) { |
|
throw new NoSuchElementException("flattening iterator out of bounds"); |
|
} |
|
int type = holdType; |
|
if (type != SEG_CLOSE) { |
|
coords[0] = hold[holdIndex + 0]; |
|
coords[1] = hold[holdIndex + 1]; |
|
if (type != SEG_MOVETO) { |
|
type = SEG_LINETO; |
|
} |
|
} |
|
return type; |
|
} |
|
} |