`
i2534
  • 浏览: 179606 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

svg中的arc转化为java中的arc

    博客分类:
  • util
阅读更多

最近项目需要解析svg中的path.直线和贝塞尔曲线都好办,唯独arc不太好办.

svg中的arc是由弧上两点,角度确定的,而java中的arc是由弧的外接矩形和角度决定的.

所有中间需要一个转换工作.

好吧,我自己是转化不来的,apache的batik提供了全套的svg解析,它肯定转化过来了.于是去拔它的算法.

中间辛苦不用说,找到了相关的计算类.

 

仿照PathIterator做一个遍历接口

 

import java.awt.geom.PathIterator;

/**
 * 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,v 1.1 2011/05/27 03:01:11 zhengll Exp $
 */
public 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();
}

 

再做一个保存信息的中间集合图形

 

import java.awt.Shape;

/**
 * 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,v 1.1 2011/05/27 03:01:11 zhengll Exp $
 */
public interface ExtendedShape extends Shape {
    /**
     * Get an extended Path iterator that may return SEG_ARCTO commands
     */
    ExtendedPathIterator getExtendedPathIterator();

}

 

重头戏来了,计算逻辑

 

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,v 1.1 2011/05/27 03:01:11 zhengll Exp $
 */
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;
        }
    }
}
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics