ExtendedGeneralPath.java Source code

Java tutorial

Introduction

Here is the source code for ExtendedGeneralPath.java

Source

/*
    
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at
    
   http://www.apache.org/licenses/LICENSE-2.0
    
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
    
 */

import java.awt.Shape;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;

/**
 * The <code>ExtendedGeneralPath</code> class represents a geometric
 * path constructed from straight lines, quadratic and cubic (Bezier)
 * curves and elliptical arc. This class delegates lines and curves to
 * an enclosed <code>GeneralPath</code>. Elliptical arc is implemented
 * using an <code>Arc2D</code> in float precision.
 *
 * <p><b>Warning</b> : An elliptical arc may be composed of several
 * path segments. For futher details, see the SVG Appendix&nbsp;F.6
 *
 * @author <a href="mailto:Thierry.Kormann@sophia.inria.fr">Thierry Kormann</a>
 * @version $Id: ExtendedGeneralPath.java 594018 2007-11-12 04:17:41Z cam $
 */
public class ExtendedGeneralPath implements ExtendedShape, Cloneable {

    /** The enclosed general path. */
    protected GeneralPath path;

    int numVals = 0;
    int numSeg = 0;
    float[] values = null;
    int[] types = null;

    float mx, my, cx, cy;

    /**
      * Constructs a new <code>ExtendedGeneralPath</code>.
      */
    public ExtendedGeneralPath() {
        path = new GeneralPath();
    }

    /**
     * Constructs a new <code>ExtendedGeneralPath</code> with the
     * specified winding rule to control operations that require the
     * interior of the path to be defined.
     */
    public ExtendedGeneralPath(int rule) {
        path = new GeneralPath(rule);
    }

    /**
     * Constructs a new <code>ExtendedGeneralPath</code> object with
     * the specified winding rule and the specified initial capacity
     * to store path coordinates.
     */
    public ExtendedGeneralPath(int rule, int initialCapacity) {
        path = new GeneralPath(rule, initialCapacity);
    }

    /**
     * Constructs a new <code>ExtendedGeneralPath</code> object from
     * an arbitrary <code>Shape</code> object.
     */
    public ExtendedGeneralPath(Shape s) {
        this();
        append(s, false);
    }

    /**
     * Adds an elliptical arc, defined by two radii, an angle from the
     * x-axis, a flag to choose the large arc or not, a flag to
     * indicate if we increase or decrease the angles and the final
     * point of the arc.
     *
     * @param rx the x radius of the ellipse
     * @param ry the y radius of the ellipse
     *
     * @param angle the angle from the x-axis of the current
     * coordinate system to the x-axis of the ellipse in degrees.
     *
     * @param largeArcFlag the large arc flag. If true the arc
     * spanning less than or equal to 180 degrees is chosen, otherwise
     * the arc spanning greater than 180 degrees is chosen
     *
     * @param sweepFlag the sweep flag. If true the line joining
     * center to arc sweeps through decreasing angles otherwise it
     * sweeps through increasing angles
     *
     * @param x the absolute x coordinate of the final point of the arc.
     * @param y the absolute y coordinate of the final point of the arc.
     */
    public synchronized void arcTo(float rx, float ry, float angle, boolean largeArcFlag, boolean sweepFlag,
            float x, float y) {

        // Ensure radii are valid
        if (rx == 0 || ry == 0) {
            lineTo(x, y);
            return;
        }

        checkMoveTo(); // check if prev command was moveto

        // Get the current (x, y) coordinates of the path
        double x0 = cx;
        double y0 = cy;
        if (x0 == x && y0 == y) {
            // If the endpoints (x, y) and (x0, y0) are identical, then this
            // is equivalent to omitting the elliptical arc segment entirely.
            return;
        }

        Arc2D arc = computeArc(x0, y0, rx, ry, angle, largeArcFlag, sweepFlag, x, y);
        if (arc == null)
            return;

        AffineTransform t = AffineTransform.getRotateInstance(Math.toRadians(angle), arc.getCenterX(),
                arc.getCenterY());
        Shape s = t.createTransformedShape(arc);
        path.append(s, true);

        makeRoom(7);
        types[numSeg++] = ExtendedPathIterator.SEG_ARCTO;
        values[numVals++] = rx;
        values[numVals++] = ry;
        values[numVals++] = angle;
        values[numVals++] = largeArcFlag ? 1 : 0;
        values[numVals++] = sweepFlag ? 1 : 0;
        cx = values[numVals++] = x;
        cy = values[numVals++] = y;
    }

    /**
     * This constructs an unrotated Arc2D from the SVG specification of an
     * Elliptical arc.  To get the final arc you need to apply a rotation
     * transform such as:
     *
     * AffineTransform.getRotateInstance
     *     (angle, arc.getX()+arc.getWidth()/2, arc.getY()+arc.getHeight()/2);
     */
    public static Arc2D computeArc(double x0, double y0, double rx, double ry, double angle, boolean largeArcFlag,
            boolean sweepFlag, double x, double y) {
        //
        // Elliptical arc implementation based on the SVG specification notes
        //

        // Compute the half distance between the current and the final point
        double dx2 = (x0 - x) / 2.0;
        double dy2 = (y0 - y) / 2.0;
        // Convert angle from degrees to radians
        angle = Math.toRadians(angle % 360.0);
        double cosAngle = Math.cos(angle);
        double sinAngle = Math.sin(angle);

        //
        // Step 1 : Compute (x1, y1)
        //
        double x1 = (cosAngle * dx2 + sinAngle * dy2);
        double y1 = (-sinAngle * dx2 + cosAngle * dy2);
        // Ensure radii are large enough
        rx = Math.abs(rx);
        ry = Math.abs(ry);
        double Prx = rx * rx;
        double Pry = ry * ry;
        double Px1 = x1 * x1;
        double Py1 = y1 * y1;
        // check that radii are large enough
        double radiiCheck = Px1 / Prx + Py1 / Pry;
        if (radiiCheck > 1) {
            rx = Math.sqrt(radiiCheck) * rx;
            ry = Math.sqrt(radiiCheck) * ry;
            Prx = rx * rx;
            Pry = ry * ry;
        }

        //
        // Step 2 : Compute (cx1, cy1)
        //
        double sign = (largeArcFlag == sweepFlag) ? -1 : 1;
        double sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1));
        sq = (sq < 0) ? 0 : sq;
        double coef = (sign * Math.sqrt(sq));
        double cx1 = coef * ((rx * y1) / ry);
        double cy1 = coef * -((ry * x1) / rx);

        //
        // Step 3 : Compute (cx, cy) from (cx1, cy1)
        //
        double sx2 = (x0 + x) / 2.0;
        double sy2 = (y0 + y) / 2.0;
        double cx = sx2 + (cosAngle * cx1 - sinAngle * cy1);
        double cy = sy2 + (sinAngle * cx1 + cosAngle * cy1);

        //
        // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle)
        //
        double ux = (x1 - cx1) / rx;
        double uy = (y1 - cy1) / ry;
        double vx = (-x1 - cx1) / rx;
        double vy = (-y1 - cy1) / ry;
        double p, n;
        // Compute the angle start
        n = Math.sqrt((ux * ux) + (uy * uy));
        p = ux; // (1 * ux) + (0 * uy)
        sign = (uy < 0) ? -1.0 : 1.0;
        double angleStart = Math.toDegrees(sign * Math.acos(p / n));

        // Compute the angle extent
        n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
        p = ux * vx + uy * vy;
        sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0;
        double angleExtent = Math.toDegrees(sign * Math.acos(p / n));
        if (!sweepFlag && angleExtent > 0) {
            angleExtent -= 360f;
        } else if (sweepFlag && angleExtent < 0) {
            angleExtent += 360f;
        }
        angleExtent %= 360f;
        angleStart %= 360f;

        //
        // We can now build the resulting Arc2D in double precision
        //
        Arc2D.Double arc = new Arc2D.Double();
        arc.x = cx - rx;
        arc.y = cy - ry;
        arc.width = rx * 2.0;
        arc.height = ry * 2.0;
        arc.start = -angleStart;
        arc.extent = -angleExtent;

        return arc;
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public synchronized void moveTo(float x, float y) {
        // Don't add moveto to general path unless there is a reason.
        makeRoom(2);
        types[numSeg++] = PathIterator.SEG_MOVETO;
        cx = mx = values[numVals++] = x;
        cy = my = values[numVals++] = y;

    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public synchronized void lineTo(float x, float y) {
        checkMoveTo(); // check if prev command was moveto
        path.lineTo(x, y);

        makeRoom(2);
        types[numSeg++] = PathIterator.SEG_LINETO;
        cx = values[numVals++] = x;
        cy = values[numVals++] = y;
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public synchronized void quadTo(float x1, float y1, float x2, float y2) {
        checkMoveTo(); // check if prev command was moveto
        path.quadTo(x1, y1, x2, y2);

        makeRoom(4);
        types[numSeg++] = PathIterator.SEG_QUADTO;
        values[numVals++] = x1;
        values[numVals++] = y1;
        cx = values[numVals++] = x2;
        cy = values[numVals++] = y2;
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public synchronized void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) {
        checkMoveTo(); // check if prev command was moveto
        path.curveTo(x1, y1, x2, y2, x3, y3);

        makeRoom(6);
        types[numSeg++] = PathIterator.SEG_CUBICTO;
        values[numVals++] = x1;
        values[numVals++] = y1;
        values[numVals++] = x2;
        values[numVals++] = y2;
        cx = values[numVals++] = x3;
        cy = values[numVals++] = y3;
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public synchronized void closePath() {
        // Don't double close path.
        if ((numSeg != 0) && (types[numSeg - 1] == PathIterator.SEG_CLOSE))
            return;

        // Only close path if the previous command wasn't a moveto
        if ((numSeg != 0) && (types[numSeg - 1] != PathIterator.SEG_MOVETO))
            path.closePath();

        makeRoom(0);
        types[numSeg++] = PathIterator.SEG_CLOSE;
        cx = mx;
        cy = my;
    }

    /**
     * Checks if previous command was a moveto command,
     * skipping a close command (if present).
     */
    protected void checkMoveTo() {
        if (numSeg == 0)
            return;

        switch (types[numSeg - 1]) {

        case PathIterator.SEG_MOVETO:
            path.moveTo(values[numVals - 2], values[numVals - 1]);
            break;

        case PathIterator.SEG_CLOSE:
            if (numSeg == 1)
                return;
            if (types[numSeg - 2] == PathIterator.SEG_MOVETO)
                path.moveTo(values[numVals - 2], values[numVals - 1]);
            break;

        default:
            break;
        }
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public void append(Shape s, boolean connect) {
        append(s.getPathIterator(new AffineTransform()), connect);
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public void append(PathIterator pi, boolean connect) {
        double[] vals = new double[6];

        while (!pi.isDone()) {
            Arrays.fill(vals, 0);
            int type = pi.currentSegment(vals);
            pi.next();
            if (connect && (numVals != 0)) {
                if (type == PathIterator.SEG_MOVETO) {
                    double x = vals[0];
                    double y = vals[1];
                    if ((x != cx) || (y != cy)) {
                        // Change MOVETO to LINETO.
                        type = PathIterator.SEG_LINETO;
                    } else {
                        // Redundent segment (move to current loc) drop it...
                        if (pi.isDone())
                            break; // Nothing interesting
                        type = pi.currentSegment(vals);
                        pi.next();
                    }
                }
                connect = false;
            }

            switch (type) {
            case PathIterator.SEG_CLOSE:
                closePath();
                break;
            case PathIterator.SEG_MOVETO:
                moveTo((float) vals[0], (float) vals[1]);
                break;
            case PathIterator.SEG_LINETO:
                lineTo((float) vals[0], (float) vals[1]);
                break;
            case PathIterator.SEG_QUADTO:
                quadTo((float) vals[0], (float) vals[1], (float) vals[2], (float) vals[3]);
                break;
            case PathIterator.SEG_CUBICTO:
                curveTo((float) vals[0], (float) vals[1], (float) vals[2], (float) vals[3], (float) vals[4],
                        (float) vals[5]);
                break;
            }
        }
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public void append(ExtendedPathIterator epi, boolean connect) {
        float[] vals = new float[7];
        while (!epi.isDone()) {
            Arrays.fill(vals, 0);
            int type = epi.currentSegment(vals);
            epi.next();
            if (connect && (numVals != 0)) {
                if (type == PathIterator.SEG_MOVETO) {
                    float x = vals[0];
                    float y = vals[1];
                    if ((x != cx) || (y != cy)) {
                        // Change MOVETO to LINETO.
                        type = PathIterator.SEG_LINETO;
                    } else {
                        // Redundant segment (move to current loc) drop it...
                        if (epi.isDone())
                            break; // Nothing interesting
                        type = epi.currentSegment(vals);
                        epi.next();
                    }
                }
                connect = false;
            }

            switch (type) {
            case PathIterator.SEG_CLOSE:
                closePath();
                break;
            case PathIterator.SEG_MOVETO:
                moveTo(vals[0], vals[1]);
                break;
            case PathIterator.SEG_LINETO:
                lineTo(vals[0], vals[1]);
                break;
            case PathIterator.SEG_QUADTO:
                quadTo(vals[0], vals[1], vals[2], vals[3]);
                break;
            case PathIterator.SEG_CUBICTO:
                curveTo(vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
                break;
            case ExtendedPathIterator.SEG_ARCTO:
                arcTo(vals[0], vals[1], vals[2], (vals[3] != 0), (vals[4] != 0), vals[5], vals[6]);
                break;
            }
        }
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public synchronized int getWindingRule() {
        return path.getWindingRule();
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public void setWindingRule(int rule) {
        path.setWindingRule(rule);
    }

    /**
     * get the current position or <code>null</code>.
     */
    public synchronized Point2D getCurrentPoint() {
        if (numVals == 0)
            return null;
        return new Point2D.Double(cx, cy);
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public synchronized void reset() {
        path.reset();

        numSeg = 0;
        numVals = 0;
        values = null;
        types = null;
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public void transform(AffineTransform at) {
        if (at.getType() != AffineTransform.TYPE_IDENTITY)
            throw new IllegalArgumentException("ExtendedGeneralPaths can not be transformed");
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public synchronized Shape createTransformedShape(AffineTransform at) {
        return path.createTransformedShape(at);
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public synchronized Rectangle getBounds() {
        return path.getBounds();
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public synchronized Rectangle2D getBounds2D() {
        return path.getBounds2D();
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public boolean contains(double x, double y) {
        return path.contains(x, y);
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public boolean contains(Point2D p) {
        return path.contains(p);
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public boolean contains(double x, double y, double w, double h) {
        return path.contains(x, y, w, h);
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public boolean contains(Rectangle2D r) {
        return path.contains(r);
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public boolean intersects(double x, double y, double w, double h) {
        return path.intersects(x, y, w, h);
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public boolean intersects(Rectangle2D r) {
        return path.intersects(r);
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public PathIterator getPathIterator(AffineTransform at) {
        return path.getPathIterator(at);
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public PathIterator getPathIterator(AffineTransform at, double flatness) {
        return path.getPathIterator(at, flatness);
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public ExtendedPathIterator getExtendedPathIterator() {
        return new EPI();
    }

    class EPI implements ExtendedPathIterator {
        int segNum = 0;
        int valsIdx = 0;

        public int currentSegment() {
            return types[segNum];
        }

        public int currentSegment(double[] coords) {
            int ret = types[segNum];
            switch (ret) {
            case SEG_CLOSE:
                break;
            case SEG_MOVETO:
            case SEG_LINETO:
                coords[0] = values[valsIdx];
                coords[1] = values[valsIdx + 1];
                break;
            case SEG_QUADTO:
                coords[0] = values[valsIdx];
                coords[1] = values[valsIdx + 1];
                coords[2] = values[valsIdx + 2];
                coords[3] = values[valsIdx + 3];
                break;
            case SEG_CUBICTO:
                coords[0] = values[valsIdx];
                coords[1] = values[valsIdx + 1];
                coords[2] = values[valsIdx + 2];
                coords[3] = values[valsIdx + 3];
                coords[4] = values[valsIdx + 4];
                coords[5] = values[valsIdx + 5];
                break;
            case SEG_ARCTO:
                coords[0] = values[valsIdx];
                coords[1] = values[valsIdx + 1];
                coords[2] = values[valsIdx + 2];
                coords[3] = values[valsIdx + 3];
                coords[4] = values[valsIdx + 4];
                coords[5] = values[valsIdx + 5];
                coords[6] = values[valsIdx + 6];
                break;
            }
            // System.out.println("Seg: [" + segNum + "] type: " + ret +
            //                    " vals: [" + coords[0] + ", " + coords[1] +
            //                    "]");
            return ret;
        }

        public int currentSegment(float[] coords) {
            int ret = types[segNum];
            switch (ret) {
            case SEG_CLOSE:
                break;
            case SEG_MOVETO:
            case SEG_LINETO:
                coords[0] = values[valsIdx];
                coords[1] = values[valsIdx + 1];
                break;
            case SEG_QUADTO:
                System.arraycopy(values, valsIdx, coords, 0, 4);
                break;
            case SEG_CUBICTO:
                System.arraycopy(values, valsIdx, coords, 0, 6);
                break;
            case SEG_ARCTO:
                System.arraycopy(values, valsIdx, coords, 0, 7);
                break;
            }
            return ret;
        }

        public int getWindingRule() {
            return path.getWindingRule();
        }

        public boolean isDone() {
            return segNum == numSeg;
        }

        public void next() {
            int type = types[segNum++];
            switch (type) {
            case SEG_CLOSE:
                break;
            case SEG_MOVETO: // fallthrough is intended
            case SEG_LINETO:
                valsIdx += 2;
                break;
            case SEG_QUADTO:
                valsIdx += 4;
                break;
            case SEG_CUBICTO:
                valsIdx += 6;
                break;
            case SEG_ARCTO:
                valsIdx += 7;
                break;
            }
        }
    }

    /**
     * Delegates to the enclosed <code>GeneralPath</code>.
     */
    public Object clone() {
        try {
            ExtendedGeneralPath result = (ExtendedGeneralPath) super.clone();
            result.path = (GeneralPath) path.clone();

            if (values != null) {
                result.values = new float[values.length];
                System.arraycopy(values, 0, result.values, 0, values.length);
            }
            result.numVals = numVals;

            if (types != null) {
                result.types = new int[types.length];
                System.arraycopy(types, 0, result.types, 0, types.length);
            }
            result.numSeg = numSeg;

            return result;
        } catch (CloneNotSupportedException ex) {
        }
        return null;
    }

    /**
     * Make sure, that the requested number of slots in vales[] are available.
     * Must be called even for numValues = 0, because it is also
     * used for initialization of those arrays.
     *
     * @param numValues number of requested coordinates
     */
    private void makeRoom(int numValues) {
        if (values == null) {
            values = new float[2 * numValues];
            types = new int[2];
            numVals = 0;
            numSeg = 0;
            return;
        }

        int newSize = numVals + numValues;
        if (newSize > values.length) {
            int nlen = values.length * 2;
            if (nlen < newSize)
                nlen = newSize;

            float[] nvals = new float[nlen];
            System.arraycopy(values, 0, nvals, 0, numVals);
            values = nvals;
        }

        if (numSeg == types.length) {
            int[] ntypes = new int[types.length * 2];
            System.arraycopy(types, 0, ntypes, 0, types.length);
            types = ntypes;
        }
    }
}

/*
    
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at
    
http://www.apache.org/licenses/LICENSE-2.0
    
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
    
*/

/**
* The <code>ExtendedShape</code> class represents a geometric
* path constructed from straight lines, quadratic and cubic (Bezier)
* curves and elliptical arcs.
* @author <a href="mailto:deweese@apache.org">Thomas DeWeese</a>
* @version $Id: ExtendedShape.java 478249 2006-11-22 17:29:37Z dvholten $
*/
interface ExtendedShape extends Shape {
    /**
     * Get an extended Path iterator that may return SEG_ARCTO commands
     */
    ExtendedPathIterator getExtendedPathIterator();

}

/*
    
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at
    
http://www.apache.org/licenses/LICENSE-2.0
    
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
    
*/

/**
* The <code>ExtendedPathIterator</code> class represents a geometric
* path constructed from straight lines, quadratic and cubic (Bezier)
* curves and elliptical arcs.  This interface is identical to that of
* PathIterator except it can return SEG_ARCTO from currentSegment,
* also the array of values passed to currentSegment must be of length
* 7 or an error will be thrown.
* 
* This does not extend PathIterator as it would break the interface
* contract for that class.
*
* @author <a href="mailto:deweese@apache.org">Thomas DeWeese</a>
* @version $Id: ExtendedPathIterator.java 475477 2006-11-15 22:44:28Z cam $
*/
interface ExtendedPathIterator {

    /**
     * The segment type constant that specifies that the preceding
     * subpath should be closed by appending a line segment back to
     * the point corresponding to the most recent SEG_MOVETO.
     */
    int SEG_CLOSE = PathIterator.SEG_CLOSE;

    /** 
     * The segment type constant for a point that specifies the end
     * point of a line to be drawn from the most recently specified
     * point.  */
    int SEG_MOVETO = PathIterator.SEG_MOVETO;

    /**
     * The segment type constant for a point that specifies the end
     * point of a line to be drawn from the most recently specified
     * point.
     */
    int SEG_LINETO = PathIterator.SEG_LINETO;

    /**
     * The segment type constant for the pair of points that specify a
     * quadratic parametric curve to be drawn from the most recently
     * specified point. The curve is interpolated by solving the
     * parametric control equation in the range (t=[0..1]) using the
     * most recently specified (current) point (CP), the first control
     * point (P1), and the final interpolated control point (P2). 
     */
    int SEG_QUADTO = PathIterator.SEG_QUADTO;

    /**
     * The segment type constant for the set of 3 points that specify
     * a cubic parametric curve to be drawn from the most recently
     * specified point. The curve is interpolated by solving the
     * parametric control equation in the range (t=[0..1]) using the
     * most recently specified (current) point (CP), the first control
     * point (P1), the second control point (P2), and the final
     * interpolated control point (P3).
     */
    int SEG_CUBICTO = PathIterator.SEG_CUBICTO;

    /** The segment type constant for an elliptical arc.  This consists of
     *  Seven values [rx, ry, angle, largeArcFlag, sweepFlag, x, y].
     *  rx, ry are the radious of the ellipse.
     *  angle is angle of the x axis of the ellipse.
     *  largeArcFlag is zero if the smaller of the two arcs are to be used.
     *  sweepFlag is zero if the 'left' branch is taken one otherwise.
     *  x and y are the destination for the ellipse.  */
    int SEG_ARCTO = 4321;

    /** The winding rule constant for specifying an even-odd rule for
     * determining the interior of a path. The even-odd rule specifies
     * that a point lies inside the path if a ray drawn in any
     * direction from that point to infinity is crossed by path
     * segments an odd number of times.  
     */
    int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;

    /**
     * The winding rule constant for specifying a non-zero rule for
     * determining the interior of a path. The non-zero rule specifies
     * that a point lies inside the path if a ray drawn in any
     * direction from that point to infinity is crossed by path
     * segments a different number of times in the counter-clockwise
     * direction than the clockwise direction.
     */
    int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;

    int currentSegment();

    int currentSegment(double[] coords);

    int currentSegment(float[] coords);

    int getWindingRule();

    boolean isDone();

    void next();
}