You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
396 lines
14 KiB
396 lines
14 KiB
/*
|
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
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;
|
|
}
|
|
}
|