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