/*
 * Copyright (c) 2006, 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.awt.Rectangle;
import java.awt.Shape;
import java.io.Serializable;
import sun.awt.geom.Curve;

import com.levigo.util.gwtawt.client.helper.Arrays;

/**
 * The {@code Path2D} class provides a simple, yet flexible shape which represents an arbitrary
 * geometric path. It can fully represent any path which can be iterated by the {@link PathIterator}
 * interface including all of its segment types and winding rules and it implements all of the basic
 * hit testing methods of the {@link Shape} interface.
 * <p>
 * Use {@link Path2D.Float} when dealing with data that can be represented and used with floating
 * point precision. Use {@link Path2D.Double} for data that requires the accuracy or range of double
 * precision.
 * <p>
 * {@code Path2D} provides exactly those facilities required for basic construction and management
 * of a geometric path and implementation of the above interfaces with little added interpretation.
 * If it is useful to manipulate the interiors of closed geometric shapes beyond simple hit testing
 * then the {@link Area} class provides additional capabilities specifically targeted at closed
 * figures. While both classes nominally implement the {@code Shape} interface, they differ in
 * purpose and together they provide two useful views of a geometric shape where {@code Path2D}
 * deals primarily with a trajectory formed by path segments and {@code Area} deals more with
 * interpretation and manipulation of enclosed regions of 2D geometric space.
 * <p>
 * The {@link PathIterator} interface has more detailed descriptions of the types of segments that
 * make up a path and the winding rules that control how to determine which regions are inside or
 * outside the path.
 * 
 * @author Jim Graham
 * @since 1.6
 */
public abstract class Path2D implements Shape/* , Cloneable */{
  /**
   * An even-odd winding rule for determining the interior of a path.
   * 
   * @see PathIterator#WIND_EVEN_ODD
   * @since 1.6
   */
  public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;

  /**
   * A non-zero winding rule for determining the interior of a path.
   * 
   * @see PathIterator#WIND_NON_ZERO
   * @since 1.6
   */
  public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;

  // For code simplicity, copy these constants to our namespace
  // and cast them to byte constants for easy storage.
  private static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO;
  private static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO;
  private static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO;
  private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
  private static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE;

  transient byte[] pointTypes;
  transient int numTypes;
  transient int numCoords;
  transient int windingRule;

  static final int INIT_SIZE = 20;
  static final int EXPAND_MAX = 500;

  /**
   * Constructs a new empty {@code Path2D} object. It is assumed that the package sibling subclass
   * that is defaulting to this constructor will fill in all values.
   * 
   * @since 1.6
   */
  /* private protected */
  Path2D() {
  }

  /**
   * Constructs a new {@code Path2D} object from the given specified initial values. This method is
   * only intended for internal use and should not be made public if the other constructors for this
   * class are ever exposed.
   * 
   * @param rule the winding rule
   * @param initialTypes the size to make the initial array to store the path segment types
   * @since 1.6
   */
  /* private protected */
  Path2D(int rule, int initialTypes) {
    setWindingRule(rule);
    this.pointTypes = new byte[initialTypes];
  }

  abstract float[] cloneCoordsFloat(AffineTransform at);

  abstract double[] cloneCoordsDouble(AffineTransform at);

  abstract void append(float x, float y);

  abstract void append(double x, double y);

  abstract Point2D getPoint(int coordindex);

  abstract void needRoom(boolean needMove, int newCoords);

  abstract int pointCrossings(double px, double py);

  abstract int rectCrossings(double rxmin, double rymin, double rxmax, double rymax);

  /**
   * The {@code Float} class defines a geometric path with coordinates stored in single precision
   * floating point.
   * 
   * @since 1.6
   */
  public static class Float extends Path2D implements Serializable {
    transient float floatCoords[];

    /**
     * Constructs a new empty single precision {@code Path2D} object with a default winding rule of
     * {@link #WIND_NON_ZERO}.
     * 
     * @since 1.6
     */
    public Float() {
      this(WIND_NON_ZERO, INIT_SIZE);
    }

    /**
     * Constructs a new empty single precision {@code Path2D} object with the specified winding rule
     * to control operations that require the interior of the path to be defined.
     * 
     * @param rule the winding rule
     * @see #WIND_EVEN_ODD
     * @see #WIND_NON_ZERO
     * @since 1.6
     */
    public Float(int rule) {
      this(rule, INIT_SIZE);
    }

    /**
     * Constructs a new empty single precision {@code Path2D} object with the specified winding rule
     * and the specified initial capacity to store path segments. This number is an initial guess as
     * to how many path segments will be added to the path, but the storage is expanded as needed to
     * store whatever path segments are added.
     * 
     * @param rule the winding rule
     * @param initialCapacity the estimate for the number of path segments in the path
     * @see #WIND_EVEN_ODD
     * @see #WIND_NON_ZERO
     * @since 1.6
     */
    public Float(int rule, int initialCapacity) {
      super(rule, initialCapacity);
      floatCoords = new float[initialCapacity * 2];
    }

    /**
     * Constructs a new single precision {@code Path2D} object from an arbitrary {@link Shape}
     * object. All of the initial geometry and the winding rule for this path are taken from the
     * specified {@code Shape} object.
     * 
     * @param s the specified {@code Shape} object
     * @since 1.6
     */
    public Float(Shape s) {
      this(s, null);
    }

    /**
     * Constructs a new single precision {@code Path2D} object from an arbitrary {@link Shape}
     * object, transformed by an {@link AffineTransform} object. All of the initial geometry and the
     * winding rule for this path are taken from the specified {@code Shape} object and transformed
     * by the specified {@code AffineTransform} object.
     * 
     * @param s the specified {@code Shape} object
     * @param at the specified {@code AffineTransform} object
     * @since 1.6
     */
    public Float(Shape s, AffineTransform at) {
      if (s instanceof Path2D) {
        Path2D p2d = (Path2D) s;
        setWindingRule(p2d.windingRule);
        this.numTypes = p2d.numTypes;
        this.pointTypes = Arrays.copyOf(p2d.pointTypes, p2d.pointTypes.length);
        this.numCoords = p2d.numCoords;
        this.floatCoords = p2d.cloneCoordsFloat(at);
      } else {
        PathIterator pi = s.getPathIterator(at);
        setWindingRule(pi.getWindingRule());
        this.pointTypes = new byte[INIT_SIZE];
        this.floatCoords = new float[INIT_SIZE * 2];
        append(pi, false);
      }
    }

    float[] cloneCoordsFloat(AffineTransform at) {
      float ret[];
      if (at == null) {
        ret = Arrays.copyOf(this.floatCoords, this.floatCoords.length);
      } else {
        ret = new float[floatCoords.length];
        at.transform(floatCoords, 0, ret, 0, numCoords / 2);
      }
      return ret;
    }

    double[] cloneCoordsDouble(AffineTransform at) {
      double ret[] = new double[floatCoords.length];
      if (at == null) {
        for (int i = 0; i < numCoords; i++) {
          ret[i] = floatCoords[i];
        }
      } else {
        at.transform(floatCoords, 0, ret, 0, numCoords / 2);
      }
      return ret;
    }

    void append(float x, float y) {
      floatCoords[numCoords++] = x;
      floatCoords[numCoords++] = y;
    }

    void append(double x, double y) {
      floatCoords[numCoords++] = (float) x;
      floatCoords[numCoords++] = (float) y;
    }

    Point2D getPoint(int coordindex) {
      return new Point2D.Float(floatCoords[coordindex], floatCoords[coordindex + 1]);
    }

    void needRoom(boolean needMove, int newCoords) {
      if (needMove && numTypes == 0) {
        throw new IllegalPathStateException("missing initial moveto " + "in path definition");
      }
      int size = pointTypes.length;
      if (numTypes >= size) {
        int grow = size;
        if (grow > EXPAND_MAX) {
          grow = EXPAND_MAX;
        }
        pointTypes = Arrays.copyOf(pointTypes, size + grow);
      }
      size = floatCoords.length;
      if (numCoords + newCoords > size) {
        int grow = size;
        if (grow > EXPAND_MAX * 2) {
          grow = EXPAND_MAX * 2;
        }
        if (grow < newCoords) {
          grow = newCoords;
        }
        floatCoords = Arrays.copyOf(floatCoords, size + grow);
      }
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final synchronized void moveTo(double x, double y) {
      if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
        floatCoords[numCoords - 2] = (float) x;
        floatCoords[numCoords - 1] = (float) y;
      } else {
        needRoom(false, 2);
        pointTypes[numTypes++] = SEG_MOVETO;
        floatCoords[numCoords++] = (float) x;
        floatCoords[numCoords++] = (float) y;
      }
    }

    /**
     * Adds a point to the path by moving to the specified coordinates specified in float precision.
     * <p>
     * This method provides a single precision variant of the double precision {@code moveTo()}
     * method on the base {@code Path2D} class.
     * 
     * @param x the specified X coordinate
     * @param y the specified Y coordinate
     * @see Path2D#moveTo
     * @since 1.6
     */
    public final synchronized void moveTo(float x, float y) {
      if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
        floatCoords[numCoords - 2] = x;
        floatCoords[numCoords - 1] = y;
      } else {
        needRoom(false, 2);
        pointTypes[numTypes++] = SEG_MOVETO;
        floatCoords[numCoords++] = x;
        floatCoords[numCoords++] = y;
      }
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final synchronized void lineTo(double x, double y) {
      needRoom(true, 2);
      pointTypes[numTypes++] = SEG_LINETO;
      floatCoords[numCoords++] = (float) x;
      floatCoords[numCoords++] = (float) y;
    }

    /**
     * Adds a point to the path by drawing a straight line from the current coordinates to the new
     * specified coordinates specified in float precision.
     * <p>
     * This method provides a single precision variant of the double precision {@code lineTo()}
     * method on the base {@code Path2D} class.
     * 
     * @param x the specified X coordinate
     * @param y the specified Y coordinate
     * @see Path2D#lineTo
     * @since 1.6
     */
    public final synchronized void lineTo(float x, float y) {
      needRoom(true, 2);
      pointTypes[numTypes++] = SEG_LINETO;
      floatCoords[numCoords++] = x;
      floatCoords[numCoords++] = y;
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final synchronized void quadTo(double x1, double y1, double x2, double y2) {
      needRoom(true, 4);
      pointTypes[numTypes++] = SEG_QUADTO;
      floatCoords[numCoords++] = (float) x1;
      floatCoords[numCoords++] = (float) y1;
      floatCoords[numCoords++] = (float) x2;
      floatCoords[numCoords++] = (float) y2;
    }

    /**
     * Adds a curved segment, defined by two new points, to the path by drawing a Quadratic curve
     * that intersects both the current coordinates and the specified coordinates {@code (x2,y2)},
     * using the specified point {@code (x1,y1)} as a quadratic parametric control point. All
     * coordinates are specified in float precision.
     * <p>
     * This method provides a single precision variant of the double precision {@code quadTo()}
     * method on the base {@code Path2D} class.
     * 
     * @param x1 the X coordinate of the quadratic control point
     * @param y1 the Y coordinate of the quadratic control point
     * @param x2 the X coordinate of the final end point
     * @param y2 the Y coordinate of the final end point
     * @see Path2D#quadTo
     * @since 1.6
     */
    public final synchronized void quadTo(float x1, float y1, float x2, float y2) {
      needRoom(true, 4);
      pointTypes[numTypes++] = SEG_QUADTO;
      floatCoords[numCoords++] = x1;
      floatCoords[numCoords++] = y1;
      floatCoords[numCoords++] = x2;
      floatCoords[numCoords++] = y2;
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final synchronized void curveTo(double x1, double y1, double x2, double y2, double x3, double y3) {
      needRoom(true, 6);
      pointTypes[numTypes++] = SEG_CUBICTO;
      floatCoords[numCoords++] = (float) x1;
      floatCoords[numCoords++] = (float) y1;
      floatCoords[numCoords++] = (float) x2;
      floatCoords[numCoords++] = (float) y2;
      floatCoords[numCoords++] = (float) x3;
      floatCoords[numCoords++] = (float) y3;
    }

    /**
     * Adds a curved segment, defined by three new points, to the path by drawing a B&eacute;zier
     * curve that intersects both the current coordinates and the specified coordinates
     * {@code (x3,y3)}, using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
     * B&eacute;zier control points. All coordinates are specified in float precision.
     * <p>
     * This method provides a single precision variant of the double precision {@code curveTo()}
     * method on the base {@code Path2D} class.
     * 
     * @param x1 the X coordinate of the first B&eacute;zier control point
     * @param y1 the Y coordinate of the first B&eacute;zier control point
     * @param x2 the X coordinate of the second B&eacute;zier control point
     * @param y2 the Y coordinate of the second B&eacute;zier control point
     * @param x3 the X coordinate of the final end point
     * @param y3 the Y coordinate of the final end point
     * @see Path2D#curveTo
     * @since 1.6
     */
    public final synchronized void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) {
      needRoom(true, 6);
      pointTypes[numTypes++] = SEG_CUBICTO;
      floatCoords[numCoords++] = x1;
      floatCoords[numCoords++] = y1;
      floatCoords[numCoords++] = x2;
      floatCoords[numCoords++] = y2;
      floatCoords[numCoords++] = x3;
      floatCoords[numCoords++] = y3;
    }

    int pointCrossings(double px, double py) {
      double movx, movy, curx, cury, endx, endy;
      float coords[] = floatCoords;
      curx = movx = coords[0];
      cury = movy = coords[1];
      int crossings = 0;
      int ci = 2;
      for (int i = 1; i < numTypes; i++) {
        switch (pointTypes[i]){
          case PathIterator.SEG_MOVETO :
            if (cury != movy) {
              crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
            }
            movx = curx = coords[ci++];
            movy = cury = coords[ci++];
            break;
          case PathIterator.SEG_LINETO :
            crossings += Curve.pointCrossingsForLine(px, py, curx, cury, endx = coords[ci++], endy = coords[ci++]);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_QUADTO :
            crossings += Curve.pointCrossingsForQuad(px, py, curx, cury, coords[ci++], coords[ci++],
                endx = coords[ci++], endy = coords[ci++], 0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CUBICTO :
            crossings += Curve.pointCrossingsForCubic(px, py, curx, cury, coords[ci++], coords[ci++], coords[ci++],
                coords[ci++], endx = coords[ci++], endy = coords[ci++], 0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CLOSE :
            if (cury != movy) {
              crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
            }
            curx = movx;
            cury = movy;
            break;
        }
      }
      if (cury != movy) {
        crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
      }
      return crossings;
    }

    int rectCrossings(double rxmin, double rymin, double rxmax, double rymax) {
      float coords[] = floatCoords;
      double curx, cury, movx, movy, endx, endy;
      curx = movx = coords[0];
      cury = movy = coords[1];
      int crossings = 0;
      int ci = 2;
      for (int i = 1; crossings != Curve.RECT_INTERSECTS && i < numTypes; i++) {
        switch (pointTypes[i]){
          case PathIterator.SEG_MOVETO :
            if (curx != movx || cury != movy) {
              crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
            }
            // Count should always be a multiple of 2 here.
            // assert((crossings & 1) != 0);
            movx = curx = coords[ci++];
            movy = cury = coords[ci++];
            break;
          case PathIterator.SEG_LINETO :
            crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury,
                endx = coords[ci++], endy = coords[ci++]);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_QUADTO :
            crossings = Curve.rectCrossingsForQuad(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[ci++],
                coords[ci++], endx = coords[ci++], endy = coords[ci++], 0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CUBICTO :
            crossings = Curve.rectCrossingsForCubic(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[ci++],
                coords[ci++], coords[ci++], coords[ci++], endx = coords[ci++], endy = coords[ci++], 0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CLOSE :
            if (curx != movx || cury != movy) {
              crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
            }
            curx = movx;
            cury = movy;
            // Count should always be a multiple of 2 here.
            // assert((crossings & 1) != 0);
            break;
        }
      }
      if (crossings != Curve.RECT_INTERSECTS && (curx != movx || cury != movy)) {
        crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
      }
      // Count should always be a multiple of 2 here.
      // assert((crossings & 1) != 0);
      return crossings;
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final void append(PathIterator pi, boolean connect) {
      float coords[] = new float[6];
      while (!pi.isDone()) {
        switch (pi.currentSegment(coords)){
          case SEG_MOVETO :
            if (!connect || numTypes < 1 || numCoords < 1) {
              moveTo(coords[0], coords[1]);
              break;
            }
            if (pointTypes[numTypes - 1] != SEG_CLOSE && floatCoords[numCoords - 2] == coords[0]
                && floatCoords[numCoords - 1] == coords[1]) {
              // Collapse out initial moveto/lineto
              break;
            }
            // NO BREAK;
          case SEG_LINETO :
            lineTo(coords[0], coords[1]);
            break;
          case SEG_QUADTO :
            quadTo(coords[0], coords[1], coords[2], coords[3]);
            break;
          case SEG_CUBICTO :
            curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
            break;
          case SEG_CLOSE :
            closePath();
            break;
        }
        pi.next();
        connect = false;
      }
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final void transform(AffineTransform at) {
      at.transform(floatCoords, 0, floatCoords, 0, numCoords / 2);
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final synchronized Rectangle2D getBounds2D() {
      float x1, y1, x2, y2;
      int i = numCoords;
      if (i > 0) {
        y1 = y2 = floatCoords[--i];
        x1 = x2 = floatCoords[--i];
        while (i > 0) {
          float y = floatCoords[--i];
          float x = floatCoords[--i];
          if (x < x1)
            x1 = x;
          if (y < y1)
            y1 = y;
          if (x > x2)
            x2 = x;
          if (y > y2)
            y2 = y;
        }
      } else {
        x1 = y1 = x2 = y2 = 0.0f;
      }
      return new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1);
    }

    /**
     * {@inheritDoc}
     * <p>
     * The iterator for this class is not multi-threaded safe, which means that the {@code Path2D}
     * class does not guarantee that modifications to the geometry of this {@code Path2D} object do
     * not affect any iterations of that geometry that are already in process.
     * 
     * @since 1.6
     */
    public final PathIterator getPathIterator(AffineTransform at) {
      if (at == null) {
        return new CopyIterator(this);
      } else {
        return new TxIterator(this, at);
      }
    }

    /**
     * Creates a new object of the same class as this object.
     * 
     * @return a clone of this instance.
     * @exception OutOfMemoryError if there is not enough memory.
     * @see java.lang.Cloneable
     * @since 1.6
     */
    // public final Object clone() {
    // // Note: It would be nice to have this return Path2D
    // // but one of our subclasses (GeneralPath) needs to
    // // offer "public Object clone()" for backwards
    // // compatibility so we cannot restrict it further.
    // // REMIND: Can we do both somehow?
    // if (this instanceof GeneralPath) {
    // return new GeneralPath(this);
    // } else {
    // return new Path2D.Float(this);
    // }
    // }

    /*
     * JDK 1.6 serialVersionUID
     */
    private static final long serialVersionUID = 6990832515060788886L;

    // /**
    // * Writes the default serializable fields to the {@code ObjectOutputStream} followed by an
    // * explicit serialization of the path segments stored in this path.
    // *
    // * @serialData <a name="Path2DSerialData"><!-- --></a>
    // * <ol>
    // * <li>The default serializable fields. There are no default serializable fields as
    // * of 1.6.
    // * <li>followed by a byte indicating the storage type of the original object as a
    // * hint (SERIAL_STORAGE_FLT_ARRAY)
    // * <li>followed by an integer indicating the number of path segments to follow (NP)
    // * or -1 to indicate an unknown number of path segments follows
    // * <li>followed by an integer indicating the total number of coordinates to follow
    // * (NC) or -1 to indicate an unknown number of coordinates follows (NC should always
    // * be even since coordinates always appear in pairs representing an x,y pair)
    // * <li>followed by a byte indicating the winding rule ({@link #WIND_EVEN_ODD
    // * WIND_EVEN_ODD} or {@link #WIND_NON_ZERO WIND_NON_ZERO})
    // * <li>followed by NP (or unlimited if NP < 0) sets of values consisting of a single
    // * byte indicating a path segment type followed by one or more pairs of float or
    // * double values representing the coordinates of the path segment
    // * <li>followed by a byte indicating the end of the path (SERIAL_PATH_END).
    // * </ol>
    // * <p>
    // * The following byte value constants are used in the serialized form of
    // * {@code Path2D} objects:
    // * <table>
    // * <tr>
    // * <th>Constant Name</th>
    // * <th>Byte Value</th>
    // * <th>Followed by</th>
    // * <th>Description</th>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
    // * <td>0x30</td>
    // * <td></td>
    // * <td>A hint that the original {@code Path2D} object stored the coordinates in a
    // * Java array of floats.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
    // * <td>0x31</td>
    // * <td></td>
    // * <td>A hint that the original {@code Path2D} object stored the coordinates in a
    // * Java array of doubles.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
    // * <td>0x40</td>
    // * <td>2 floats</td>
    // * <td>A {@link #moveTo moveTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_FLT_LINETO}</td>
    // * <td>0x41</td>
    // * <td>2 floats</td>
    // * <td>A {@link #lineTo lineTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
    // * <td>0x42</td>
    // * <td>4 floats</td>
    // * <td>A {@link #quadTo quadTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
    // * <td>0x43</td>
    // * <td>6 floats</td>
    // * <td>A {@link #curveTo curveTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
    // * <td>0x50</td>
    // * <td>2 doubles</td>
    // * <td>A {@link #moveTo moveTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_DBL_LINETO}</td>
    // * <td>0x51</td>
    // * <td>2 doubles</td>
    // * <td>A {@link #lineTo lineTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
    // * <td>0x52</td>
    // * <td>4 doubles</td>
    // * <td>A {@link #curveTo curveTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
    // * <td>0x53</td>
    // * <td>6 doubles</td>
    // * <td>A {@link #curveTo curveTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_CLOSE}</td>
    // * <td>0x60</td>
    // * <td></td>
    // * <td>A {@link #closePath closePath} path segment.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_PATH_END}</td>
    // * <td>0x61</td>
    // * <td></td>
    // * <td>There are no more path segments following.</td>
    // * </table>
    // *
    // * @since 1.6
    // */
    // private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
    // super.writeObject(s, false);
    // }
    //
    // /**
    // * Reads the default serializable fields from the {@code ObjectInputStream} followed by an
    // * explicit serialization of the path segments stored in this path.
    // * <p>
    // * There are no default serializable fields as of 1.6.
    // * <p>
    // * The serial data for this object is described in the writeObject method.
    // *
    // * @since 1.6
    // */
    // private void readObject(java.io.ObjectInputStream s) throws java.lang.ClassNotFoundException,
    // java.io.IOException {
    // super.readObject(s, false);
    // }

    static class CopyIterator extends Path2D.Iterator {
      float floatCoords[];

      CopyIterator(Path2D.Float p2df) {
        super(p2df);
        this.floatCoords = p2df.floatCoords;
      }

      public int currentSegment(float[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          System.arraycopy(floatCoords, pointIdx, coords, 0, numCoords);
        }
        return type;
      }

      public int currentSegment(double[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          for (int i = 0; i < numCoords; i++) {
            coords[i] = floatCoords[pointIdx + i];
          }
        }
        return type;
      }
    }

    static class TxIterator extends Path2D.Iterator {
      float floatCoords[];
      AffineTransform affine;

      TxIterator(Path2D.Float p2df, AffineTransform at) {
        super(p2df);
        this.floatCoords = p2df.floatCoords;
        this.affine = at;
      }

      public int currentSegment(float[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          affine.transform(floatCoords, pointIdx, coords, 0, numCoords / 2);
        }
        return type;
      }

      public int currentSegment(double[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          affine.transform(floatCoords, pointIdx, coords, 0, numCoords / 2);
        }
        return type;
      }
    }
  }

  /**
   * The {@code Double} class defines a geometric path with coordinates stored in double precision
   * floating point.
   * 
   * @since 1.6
   */
  public static class Double extends Path2D implements Serializable {
    transient double doubleCoords[];

    /**
     * Constructs a new empty double precision {@code Path2D} object with a default winding rule of
     * {@link #WIND_NON_ZERO}.
     * 
     * @since 1.6
     */
    public Double() {
      this(WIND_NON_ZERO, INIT_SIZE);
    }

    /**
     * Constructs a new empty double precision {@code Path2D} object with the specified winding rule
     * to control operations that require the interior of the path to be defined.
     * 
     * @param rule the winding rule
     * @see #WIND_EVEN_ODD
     * @see #WIND_NON_ZERO
     * @since 1.6
     */
    public Double(int rule) {
      this(rule, INIT_SIZE);
    }

    /**
     * Constructs a new empty double precision {@code Path2D} object with the specified winding rule
     * and the specified initial capacity to store path segments. This number is an initial guess as
     * to how many path segments are in the path, but the storage is expanded as needed to store
     * whatever path segments are added to this path.
     * 
     * @param rule the winding rule
     * @param initialCapacity the estimate for the number of path segments in the path
     * @see #WIND_EVEN_ODD
     * @see #WIND_NON_ZERO
     * @since 1.6
     */
    public Double(int rule, int initialCapacity) {
      super(rule, initialCapacity);
      doubleCoords = new double[initialCapacity * 2];
    }

    /**
     * Constructs a new double precision {@code Path2D} object from an arbitrary {@link Shape}
     * object. All of the initial geometry and the winding rule for this path are taken from the
     * specified {@code Shape} object.
     * 
     * @param s the specified {@code Shape} object
     * @since 1.6
     */
    public Double(Shape s) {
      this(s, null);
    }

    /**
     * Constructs a new double precision {@code Path2D} object from an arbitrary {@link Shape}
     * object, transformed by an {@link AffineTransform} object. All of the initial geometry and the
     * winding rule for this path are taken from the specified {@code Shape} object and transformed
     * by the specified {@code AffineTransform} object.
     * 
     * @param s the specified {@code Shape} object
     * @param at the specified {@code AffineTransform} object
     * @since 1.6
     */
    public Double(Shape s, AffineTransform at) {
      if (s instanceof Path2D) {
        Path2D p2d = (Path2D) s;
        setWindingRule(p2d.windingRule);
        this.numTypes = p2d.numTypes;
        this.pointTypes = Arrays.copyOf(p2d.pointTypes, p2d.pointTypes.length);
        this.numCoords = p2d.numCoords;
        this.doubleCoords = p2d.cloneCoordsDouble(at);
      } else {
        PathIterator pi = s.getPathIterator(at);
        setWindingRule(pi.getWindingRule());
        this.pointTypes = new byte[INIT_SIZE];
        this.doubleCoords = new double[INIT_SIZE * 2];
        append(pi, false);
      }
    }

    float[] cloneCoordsFloat(AffineTransform at) {
      float ret[] = new float[doubleCoords.length];
      if (at == null) {
        for (int i = 0; i < numCoords; i++) {
          ret[i] = (float) doubleCoords[i];
        }
      } else {
        at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
      }
      return ret;
    }

    double[] cloneCoordsDouble(AffineTransform at) {
      double ret[];
      if (at == null) {
        ret = Arrays.copyOf(this.doubleCoords, this.doubleCoords.length);
      } else {
        ret = new double[doubleCoords.length];
        at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
      }
      return ret;
    }

    void append(float x, float y) {
      doubleCoords[numCoords++] = x;
      doubleCoords[numCoords++] = y;
    }

    void append(double x, double y) {
      doubleCoords[numCoords++] = x;
      doubleCoords[numCoords++] = y;
    }

    Point2D getPoint(int coordindex) {
      return new Point2D.Double(doubleCoords[coordindex], doubleCoords[coordindex + 1]);
    }

    void needRoom(boolean needMove, int newCoords) {
      if (needMove && numTypes == 0) {
        throw new IllegalPathStateException("missing initial moveto " + "in path definition");
      }
      int size = pointTypes.length;
      if (numTypes >= size) {
        int grow = size;
        if (grow > EXPAND_MAX) {
          grow = EXPAND_MAX;
        }
        pointTypes = Arrays.copyOf(pointTypes, size + grow);
      }
      size = doubleCoords.length;
      if (numCoords + newCoords > size) {
        int grow = size;
        if (grow > EXPAND_MAX * 2) {
          grow = EXPAND_MAX * 2;
        }
        if (grow < newCoords) {
          grow = newCoords;
        }
        doubleCoords = Arrays.copyOf(doubleCoords, size + grow);
      }
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final synchronized void moveTo(double x, double y) {
      if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
        doubleCoords[numCoords - 2] = x;
        doubleCoords[numCoords - 1] = y;
      } else {
        needRoom(false, 2);
        pointTypes[numTypes++] = SEG_MOVETO;
        doubleCoords[numCoords++] = x;
        doubleCoords[numCoords++] = y;
      }
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final synchronized void lineTo(double x, double y) {
      needRoom(true, 2);
      pointTypes[numTypes++] = SEG_LINETO;
      doubleCoords[numCoords++] = x;
      doubleCoords[numCoords++] = y;
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final synchronized void quadTo(double x1, double y1, double x2, double y2) {
      needRoom(true, 4);
      pointTypes[numTypes++] = SEG_QUADTO;
      doubleCoords[numCoords++] = x1;
      doubleCoords[numCoords++] = y1;
      doubleCoords[numCoords++] = x2;
      doubleCoords[numCoords++] = y2;
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final synchronized void curveTo(double x1, double y1, double x2, double y2, double x3, double y3) {
      needRoom(true, 6);
      pointTypes[numTypes++] = SEG_CUBICTO;
      doubleCoords[numCoords++] = x1;
      doubleCoords[numCoords++] = y1;
      doubleCoords[numCoords++] = x2;
      doubleCoords[numCoords++] = y2;
      doubleCoords[numCoords++] = x3;
      doubleCoords[numCoords++] = y3;
    }

    int pointCrossings(double px, double py) {
      double movx, movy, curx, cury, endx, endy;
      double coords[] = doubleCoords;
      curx = movx = coords[0];
      cury = movy = coords[1];
      int crossings = 0;
      int ci = 2;
      for (int i = 1; i < numTypes; i++) {
        switch (pointTypes[i]){
          case PathIterator.SEG_MOVETO :
            if (cury != movy) {
              crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
            }
            movx = curx = coords[ci++];
            movy = cury = coords[ci++];
            break;
          case PathIterator.SEG_LINETO :
            crossings += Curve.pointCrossingsForLine(px, py, curx, cury, endx = coords[ci++], endy = coords[ci++]);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_QUADTO :
            crossings += Curve.pointCrossingsForQuad(px, py, curx, cury, coords[ci++], coords[ci++],
                endx = coords[ci++], endy = coords[ci++], 0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CUBICTO :
            crossings += Curve.pointCrossingsForCubic(px, py, curx, cury, coords[ci++], coords[ci++], coords[ci++],
                coords[ci++], endx = coords[ci++], endy = coords[ci++], 0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CLOSE :
            if (cury != movy) {
              crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
            }
            curx = movx;
            cury = movy;
            break;
        }
      }
      if (cury != movy) {
        crossings += Curve.pointCrossingsForLine(px, py, curx, cury, movx, movy);
      }
      return crossings;
    }

    int rectCrossings(double rxmin, double rymin, double rxmax, double rymax) {
      double coords[] = doubleCoords;
      double curx, cury, movx, movy, endx, endy;
      curx = movx = coords[0];
      cury = movy = coords[1];
      int crossings = 0;
      int ci = 2;
      for (int i = 1; crossings != Curve.RECT_INTERSECTS && i < numTypes; i++) {
        switch (pointTypes[i]){
          case PathIterator.SEG_MOVETO :
            if (curx != movx || cury != movy) {
              crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
            }
            // Count should always be a multiple of 2 here.
            // assert((crossings & 1) != 0);
            movx = curx = coords[ci++];
            movy = cury = coords[ci++];
            break;
          case PathIterator.SEG_LINETO :
            endx = coords[ci++];
            endy = coords[ci++];
            crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, endx, endy);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_QUADTO :
            crossings = Curve.rectCrossingsForQuad(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[ci++],
                coords[ci++], endx = coords[ci++], endy = coords[ci++], 0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CUBICTO :
            crossings = Curve.rectCrossingsForCubic(crossings, rxmin, rymin, rxmax, rymax, curx, cury, coords[ci++],
                coords[ci++], coords[ci++], coords[ci++], endx = coords[ci++], endy = coords[ci++], 0);
            curx = endx;
            cury = endy;
            break;
          case PathIterator.SEG_CLOSE :
            if (curx != movx || cury != movy) {
              crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
            }
            curx = movx;
            cury = movy;
            // Count should always be a multiple of 2 here.
            // assert((crossings & 1) != 0);
            break;
        }
      }
      if (crossings != Curve.RECT_INTERSECTS && (curx != movx || cury != movy)) {
        crossings = Curve.rectCrossingsForLine(crossings, rxmin, rymin, rxmax, rymax, curx, cury, movx, movy);
      }
      // Count should always be a multiple of 2 here.
      // assert((crossings & 1) != 0);
      return crossings;
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final void append(PathIterator pi, boolean connect) {
      double coords[] = new double[6];
      while (!pi.isDone()) {
        switch (pi.currentSegment(coords)){
          case SEG_MOVETO :
            if (!connect || numTypes < 1 || numCoords < 1) {
              moveTo(coords[0], coords[1]);
              break;
            }
            if (pointTypes[numTypes - 1] != SEG_CLOSE && doubleCoords[numCoords - 2] == coords[0]
                && doubleCoords[numCoords - 1] == coords[1]) {
              // Collapse out initial moveto/lineto
              break;
            }
            // NO BREAK;
          case SEG_LINETO :
            lineTo(coords[0], coords[1]);
            break;
          case SEG_QUADTO :
            quadTo(coords[0], coords[1], coords[2], coords[3]);
            break;
          case SEG_CUBICTO :
            curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
            break;
          case SEG_CLOSE :
            closePath();
            break;
        }
        pi.next();
        connect = false;
      }
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final void transform(AffineTransform at) {
      at.transform(doubleCoords, 0, doubleCoords, 0, numCoords / 2);
    }

    /**
     * {@inheritDoc}
     * 
     * @since 1.6
     */
    public final synchronized Rectangle2D getBounds2D() {
      double x1, y1, x2, y2;
      int i = numCoords;
      if (i > 0) {
        y1 = y2 = doubleCoords[--i];
        x1 = x2 = doubleCoords[--i];
        while (i > 0) {
          double y = doubleCoords[--i];
          double x = doubleCoords[--i];
          if (x < x1)
            x1 = x;
          if (y < y1)
            y1 = y;
          if (x > x2)
            x2 = x;
          if (y > y2)
            y2 = y;
        }
      } else {
        x1 = y1 = x2 = y2 = 0.0;
      }
      return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
    }

    /**
     * {@inheritDoc}
     * <p>
     * The iterator for this class is not multi-threaded safe, which means that the {@code Path2D}
     * class does not guarantee that modifications to the geometry of this {@code Path2D} object do
     * not affect any iterations of that geometry that are already in process.
     * 
     * @param at an {@code AffineTransform}
     * @return a new {@code PathIterator} that iterates along the boundary of this {@code Shape} and
     *         provides access to the geometry of this {@code Shape}'s outline
     * @since 1.6
     */
    public final PathIterator getPathIterator(AffineTransform at) {
      if (at == null) {
        return new CopyIterator(this);
      } else {
        return new TxIterator(this, at);
      }
    }

    /**
     * Creates a new object of the same class as this object.
     * 
     * @return a clone of this instance.
     * @exception OutOfMemoryError if there is not enough memory.
     * @see java.lang.Cloneable
     * @since 1.6
     */
    // public final Object clone() {
    // // Note: It would be nice to have this return Path2D
    // // but one of our subclasses (GeneralPath) needs to
    // // offer "public Object clone()" for backwards
    // // compatibility so we cannot restrict it further.
    // // REMIND: Can we do both somehow?
    // return new Path2D.Double(this);
    // }

    /*
     * JDK 1.6 serialVersionUID
     */
    private static final long serialVersionUID = 1826762518450014216L;

    // /**
    // * Writes the default serializable fields to the {@code ObjectOutputStream} followed by an
    // * explicit serialization of the path segments stored in this path.
    // *
    // * @serialData <a name="Path2DSerialData"><!-- --></a>
    // * <ol>
    // * <li>The default serializable fields. There are no default serializable fields as
    // * of 1.6.
    // * <li>followed by a byte indicating the storage type of the original object as a
    // * hint (SERIAL_STORAGE_DBL_ARRAY)
    // * <li>followed by an integer indicating the number of path segments to follow (NP)
    // * or -1 to indicate an unknown number of path segments follows
    // * <li>followed by an integer indicating the total number of coordinates to follow
    // * (NC) or -1 to indicate an unknown number of coordinates follows (NC should always
    // * be even since coordinates always appear in pairs representing an x,y pair)
    // * <li>followed by a byte indicating the winding rule ({@link #WIND_EVEN_ODD
    // * WIND_EVEN_ODD} or {@link #WIND_NON_ZERO WIND_NON_ZERO})
    // * <li>followed by NP (or unlimited if NP < 0) sets of values consisting of a single
    // * byte indicating a path segment type followed by one or more pairs of float or
    // * double values representing the coordinates of the path segment
    // * <li>followed by a byte indicating the end of the path (SERIAL_PATH_END).
    // * </ol>
    // * <p>
    // * The following byte value constants are used in the serialized form of
    // * {@code Path2D} objects:
    // * <table>
    // * <tr>
    // * <th>Constant Name</th>
    // * <th>Byte Value</th>
    // * <th>Followed by</th>
    // * <th>Description</th>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
    // * <td>0x30</td>
    // * <td></td>
    // * <td>A hint that the original {@code Path2D} object stored the coordinates in a
    // * Java array of floats.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
    // * <td>0x31</td>
    // * <td></td>
    // * <td>A hint that the original {@code Path2D} object stored the coordinates in a
    // * Java array of doubles.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
    // * <td>0x40</td>
    // * <td>2 floats</td>
    // * <td>A {@link #moveTo moveTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_FLT_LINETO}</td>
    // * <td>0x41</td>
    // * <td>2 floats</td>
    // * <td>A {@link #lineTo lineTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
    // * <td>0x42</td>
    // * <td>4 floats</td>
    // * <td>A {@link #quadTo quadTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
    // * <td>0x43</td>
    // * <td>6 floats</td>
    // * <td>A {@link #curveTo curveTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
    // * <td>0x50</td>
    // * <td>2 doubles</td>
    // * <td>A {@link #moveTo moveTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_DBL_LINETO}</td>
    // * <td>0x51</td>
    // * <td>2 doubles</td>
    // * <td>A {@link #lineTo lineTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
    // * <td>0x52</td>
    // * <td>4 doubles</td>
    // * <td>A {@link #curveTo curveTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
    // * <td>0x53</td>
    // * <td>6 doubles</td>
    // * <td>A {@link #curveTo curveTo} path segment follows.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_SEG_CLOSE}</td>
    // * <td>0x60</td>
    // * <td></td>
    // * <td>A {@link #closePath closePath} path segment.</td>
    // * </tr>
    // * <tr>
    // * <td>{@code SERIAL_PATH_END}</td>
    // * <td>0x61</td>
    // * <td></td>
    // * <td>There are no more path segments following.</td>
    // * </table>
    // *
    // * @since 1.6
    // */
    // private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
    // super.writeObject(s, true);
    // }
    //
    // /**
    // * Reads the default serializable fields from the {@code ObjectInputStream} followed by an
    // * explicit serialization of the path segments stored in this path.
    // * <p>
    // * There are no default serializable fields as of 1.6.
    // * <p>
    // * The serial data for this object is described in the writeObject method.
    // *
    // * @since 1.6
    // */
    // private void readObject(java.io.ObjectInputStream s) throws java.lang.ClassNotFoundException,
    // java.io.IOException {
    // super.readObject(s, true);
    // }

    static class CopyIterator extends Path2D.Iterator {
      double doubleCoords[];

      CopyIterator(Path2D.Double p2dd) {
        super(p2dd);
        this.doubleCoords = p2dd.doubleCoords;
      }

      public int currentSegment(float[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          for (int i = 0; i < numCoords; i++) {
            coords[i] = (float) doubleCoords[pointIdx + i];
          }
        }
        return type;
      }

      public int currentSegment(double[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          System.arraycopy(doubleCoords, pointIdx, coords, 0, numCoords);
        }
        return type;
      }
    }

    static class TxIterator extends Path2D.Iterator {
      double doubleCoords[];
      AffineTransform affine;

      TxIterator(Path2D.Double p2dd, AffineTransform at) {
        super(p2dd);
        this.doubleCoords = p2dd.doubleCoords;
        this.affine = at;
      }

      public int currentSegment(float[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          affine.transform(doubleCoords, pointIdx, coords, 0, numCoords / 2);
        }
        return type;
      }

      public int currentSegment(double[] coords) {
        int type = path.pointTypes[typeIdx];
        int numCoords = curvecoords[type];
        if (numCoords > 0) {
          affine.transform(doubleCoords, pointIdx, coords, 0, numCoords / 2);
        }
        return type;
      }
    }

  }

  /**
   * Adds a point to the path by moving to the specified coordinates specified in double precision.
   * 
   * @param x the specified X coordinate
   * @param y the specified Y coordinate
   * @since 1.6
   */
  public abstract void moveTo(double x, double y);

  /**
   * Adds a point to the path by drawing a straight line from the current coordinates to the new
   * specified coordinates specified in double precision.
   * 
   * @param x the specified X coordinate
   * @param y the specified Y coordinate
   * @since 1.6
   */
  public abstract void lineTo(double x, double y);

  /**
   * Adds a curved segment, defined by two new points, to the path by drawing a Quadratic curve that
   * intersects both the current coordinates and the specified coordinates {@code (x2,y2)}, using
   * the specified point {@code (x1,y1)} as a quadratic parametric control point. All coordinates
   * are specified in double precision.
   * 
   * @param x1 the X coordinate of the quadratic control point
   * @param y1 the Y coordinate of the quadratic control point
   * @param x2 the X coordinate of the final end point
   * @param y2 the Y coordinate of the final end point
   * @since 1.6
   */
  public abstract void quadTo(double x1, double y1, double x2, double y2);

  /**
   * Adds a curved segment, defined by three new points, to the path by drawing a B&eacute;zier
   * curve that intersects both the current coordinates and the specified coordinates
   * {@code (x3,y3)}, using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
   * B&eacute;zier control points. All coordinates are specified in double precision.
   * 
   * @param x1 the X coordinate of the first B&eacute;zier control point
   * @param y1 the Y coordinate of the first B&eacute;zier control point
   * @param x2 the X coordinate of the second B&eacute;zier control point
   * @param y2 the Y coordinate of the second B&eacute;zier control point
   * @param x3 the X coordinate of the final end point
   * @param y3 the Y coordinate of the final end point
   * @since 1.6
   */
  public abstract void curveTo(double x1, double y1, double x2, double y2, double x3, double y3);

  /**
   * Closes the current subpath by drawing a straight line back to the coordinates of the last
   * {@code moveTo}. If the path is already closed then this method has no effect.
   * 
   * @since 1.6
   */
  public final synchronized void closePath() {
    if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
      needRoom(true, 0);
      pointTypes[numTypes++] = SEG_CLOSE;
    }
  }

  /**
   * Appends the geometry of the specified {@code Shape} object to the path, possibly connecting the
   * new geometry to the existing path segments with a line segment. If the {@code connect}
   * parameter is {@code true} and the path is not empty then any initial {@code moveTo} in the
   * geometry of the appended {@code Shape} is turned into a {@code lineTo} segment. If the
   * destination coordinates of such a connecting {@code lineTo} segment match the ending
   * coordinates of a currently open subpath then the segment is omitted as superfluous. The winding
   * rule of the specified {@code Shape} is ignored and the appended geometry is governed by the
   * winding rule specified for this path.
   * 
   * @param s the {@code Shape} whose geometry is appended to this path
   * @param connect a boolean to control whether or not to turn an initial {@code moveTo} segment
   *          into a {@code lineTo} segment to connect the new geometry to the existing path
   * @since 1.6
   */
  public final void append(Shape s, boolean connect) {
    append(s.getPathIterator(null), connect);
  }

  /**
   * Appends the geometry of the specified {@link PathIterator} object to the path, possibly
   * connecting the new geometry to the existing path segments with a line segment. If the
   * {@code connect} parameter is {@code true} and the path is not empty then any initial
   * {@code moveTo} in the geometry of the appended {@code Shape} is turned into a {@code lineTo}
   * segment. If the destination coordinates of such a connecting {@code lineTo} segment match the
   * ending coordinates of a currently open subpath then the segment is omitted as superfluous. The
   * winding rule of the specified {@code Shape} is ignored and the appended geometry is governed by
   * the winding rule specified for this path.
   * 
   * @param pi the {@code PathIterator} whose geometry is appended to this path
   * @param connect a boolean to control whether or not to turn an initial {@code moveTo} segment
   *          into a {@code lineTo} segment to connect the new geometry to the existing path
   * @since 1.6
   */
  public abstract void append(PathIterator pi, boolean connect);

  /**
   * Returns the fill style winding rule.
   * 
   * @return an integer representing the current winding rule.
   * @see #WIND_EVEN_ODD
   * @see #WIND_NON_ZERO
   * @see #setWindingRule
   * @since 1.6
   */
  public final synchronized int getWindingRule() {
    return windingRule;
  }

  /**
   * Sets the winding rule for this path to the specified value.
   * 
   * @param rule an integer representing the specified winding rule
   * @exception IllegalArgumentException if {@code rule} is not either {@link #WIND_EVEN_ODD} or
   *              {@link #WIND_NON_ZERO}
   * @see #getWindingRule
   * @since 1.6
   */
  public final void setWindingRule(int rule) {
    if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
      throw new IllegalArgumentException("winding rule must be " + "WIND_EVEN_ODD or " + "WIND_NON_ZERO");
    }
    windingRule = rule;
  }

  /**
   * Returns the coordinates most recently added to the end of the path as a {@link Point2D} object.
   * 
   * @return a {@code Point2D} object containing the ending coordinates of the path or {@code null}
   *         if there are no points in the path.
   * @since 1.6
   */
  public final synchronized Point2D getCurrentPoint() {
    int index = numCoords;
    if (numTypes < 1 || index < 1) {
      return null;
    }
    if (pointTypes[numTypes - 1] == SEG_CLOSE) {
      loop : for (int i = numTypes - 2; i > 0; i--) {
        switch (pointTypes[i]){
          case SEG_MOVETO :
            break loop;
          case SEG_LINETO :
            index -= 2;
            break;
          case SEG_QUADTO :
            index -= 4;
            break;
          case SEG_CUBICTO :
            index -= 6;
            break;
          case SEG_CLOSE :
            break;
        }
      }
    }
    return getPoint(index - 2);
  }

  /**
   * Resets the path to empty. The append position is set back to the beginning of the path and all
   * coordinates and point types are forgotten.
   * 
   * @since 1.6
   */
  public final synchronized void reset() {
    numTypes = numCoords = 0;
  }

  /**
   * Transforms the geometry of this path using the specified {@link AffineTransform}. The geometry
   * is transformed in place, which permanently changes the boundary defined by this object.
   * 
   * @param at the {@code AffineTransform} used to transform the area
   * @since 1.6
   */
  public abstract void transform(AffineTransform at);

  /**
   * Returns a new {@code Shape} representing a transformed version of this {@code Path2D}. Note
   * that the exact type and coordinate precision of the return value is not specified for this
   * method. The method will return a Shape that contains no less precision for the transformed
   * geometry than this {@code Path2D} currently maintains, but it may contain no more precision
   * either. If the tradeoff of precision vs. storage size in the result is important then the
   * convenience constructors in the {@link Path2D.Float#Path2D.Float(Shape, AffineTransform)
   * Path2D.Float} and {@link Path2D.Double#Path2D.Double(Shape, AffineTransform) Path2D.Double}
   * subclasses should be used to make the choice explicit.
   * 
   * @param at the {@code AffineTransform} used to transform a new {@code Shape}.
   * @return a new {@code Shape}, transformed with the specified {@code AffineTransform}.
   * @since 1.6
   */
  // public final synchronized Shape createTransformedShape(AffineTransform at) {
  // Path2D p2d = (Path2D) clone();
  //
  // if (at != null) {
  // p2d.transform(at);
  // }
  // return p2d;
  // }

  /**
   * {@inheritDoc}
   * 
   * @since 1.6
   */
  public final Rectangle getBounds() {
    return getBounds2D().getBounds();
  }

  /**
   * Tests if the specified coordinates are inside the closed boundary of the specified
   * {@link PathIterator}.
   * <p>
   * This method provides a basic facility for implementors of the {@link Shape} interface to
   * implement support for the {@link Shape#contains(double, double)} method.
   * 
   * @param pi the specified {@code PathIterator}
   * @param x the specified X coordinate
   * @param y the specified Y coordinate
   * @return {@code true} if the specified coordinates are inside the specified {@code PathIterator}
   *         ; {@code false} otherwise
   * @since 1.6
   */
  public static boolean contains(PathIterator pi, double x, double y) {
    if (x * 0.0 + y * 0.0 == 0.0) {
      // TODO
      assert (false) : "Not implemented yet";
      return false;
      // /*
      // * N * 0.0 is 0.0 only if N is finite. Here we know that both x and y are finite.
      // */
      // int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 1);
      //
      // int cross = Curve.pointCrossingsForPath(pi, x, y);
      // return ((cross & mask) != 0);
    } else {
      /*
       * Either x or y was infinite or NaN. A NaN always produces a negative response to any test
       * and Infinity values cannot be "inside" any path so they should return false as well.
       */
      return false;
    }
  }

  /**
   * Tests if the specified {@link Point2D} is inside the closed boundary of the specified
   * {@link PathIterator}.
   * <p>
   * This method provides a basic facility for implementors of the {@link Shape} interface to
   * implement support for the {@link Shape#contains(Point2D)} method.
   * 
   * @param pi the specified {@code PathIterator}
   * @param p the specified {@code Point2D}
   * @return {@code true} if the specified coordinates are inside the specified {@code PathIterator}
   *         ; {@code false} otherwise
   * @since 1.6
   */
  public static boolean contains(PathIterator pi, Point2D p) {
    return contains(pi, p.getX(), p.getY());
  }

  /**
   * {@inheritDoc}
   * 
   * @since 1.6
   */
  public final boolean contains(double x, double y) {
    if (x * 0.0 + y * 0.0 == 0.0) {
      /*
       * N * 0.0 is 0.0 only if N is finite. Here we know that both x and y are finite.
       */
      if (numTypes < 2) {
        return false;
      }
      int mask = (windingRule == WIND_NON_ZERO ? -1 : 1);
      return ((pointCrossings(x, y) & mask) != 0);
    } else {
      /*
       * Either x or y was infinite or NaN. A NaN always produces a negative response to any test
       * and Infinity values cannot be "inside" any path so they should return false as well.
       */
      return false;
    }
  }

  /**
   * {@inheritDoc}
   * 
   * @since 1.6
   */
  public final boolean contains(Point2D p) {
    return contains(p.getX(), p.getY());
  }

  /**
   * Tests if the specified rectangular area is entirely inside the closed boundary of the specified
   * {@link PathIterator}.
   * <p>
   * This method provides a basic facility for implementors of the {@link Shape} interface to
   * implement support for the {@link Shape#contains(double, double, double, double)} method.
   * <p>
   * This method object may conservatively return false in cases where the specified rectangular
   * area intersects a segment of the path, but that segment does not represent a boundary between
   * the interior and exterior of the path. Such segments could lie entirely within the interior of
   * the path if they are part of a path with a {@link #WIND_NON_ZERO} winding rule or if the
   * segments are retraced in the reverse direction such that the two sets of segments cancel each
   * other out without any exterior area falling between them. To determine whether segments
   * represent true boundaries of the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding rule and are thus beyond the scope of
   * this implementation.
   * 
   * @param pi the specified {@code PathIterator}
   * @param x the specified X coordinate
   * @param y the specified Y coordinate
   * @param w the width of the specified rectangular area
   * @param h the height of the specified rectangular area
   * @return {@code true} if the specified {@code PathIterator} contains the specified rectangluar
   *         area; {@code false} otherwise.
   * @since 1.6
   */
  public static boolean contains(PathIterator pi, double x, double y, double w, double h) {
    if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
      /*
       * [xy]+[wh] is NaN if any of those values are NaN, or if adding the two together would
       * produce NaN by virtue of adding opposing Infinte values. Since we need to add them below,
       * their sum must not be NaN. We return false because NaN always produces a negative response
       * to tests
       */
      return false;
    }
    if (w <= 0 || h <= 0) {
      return false;
    }
    // TODO
    assert (false) : "Not implemented yet";
    return false;

    // int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
    // int crossings = Curve.rectCrossingsForPath(pi, x, y, x + w, y + h);
    // return (crossings != Curve.RECT_INTERSECTS && (crossings & mask) != 0);
  }

  /**
   * Tests if the specified {@link Rectangle2D} is entirely inside the closed boundary of the
   * specified {@link PathIterator}.
   * <p>
   * This method provides a basic facility for implementors of the {@link Shape} interface to
   * implement support for the {@link Shape#contains(Rectangle2D)} method.
   * <p>
   * This method object may conservatively return false in cases where the specified rectangular
   * area intersects a segment of the path, but that segment does not represent a boundary between
   * the interior and exterior of the path. Such segments could lie entirely within the interior of
   * the path if they are part of a path with a {@link #WIND_NON_ZERO} winding rule or if the
   * segments are retraced in the reverse direction such that the two sets of segments cancel each
   * other out without any exterior area falling between them. To determine whether segments
   * represent true boundaries of the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding rule and are thus beyond the scope of
   * this implementation.
   * 
   * @param pi the specified {@code PathIterator}
   * @param r a specified {@code Rectangle2D}
   * @return {@code true} if the specified {@code PathIterator} contains the specified
   *         {@code Rectangle2D}; {@code false} otherwise.
   * @since 1.6
   */
  public static boolean contains(PathIterator pi, Rectangle2D r) {
    return contains(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
  }

  /**
   * {@inheritDoc}
   * <p>
   * This method object may conservatively return false in cases where the specified rectangular
   * area intersects a segment of the path, but that segment does not represent a boundary between
   * the interior and exterior of the path. Such segments could lie entirely within the interior of
   * the path if they are part of a path with a {@link #WIND_NON_ZERO} winding rule or if the
   * segments are retraced in the reverse direction such that the two sets of segments cancel each
   * other out without any exterior area falling between them. To determine whether segments
   * represent true boundaries of the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding rule and are thus beyond the scope of
   * this implementation.
   * 
   * @since 1.6
   */
  public final boolean contains(double x, double y, double w, double h) {
    if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
      /*
       * [xy]+[wh] is NaN if any of those values are NaN, or if adding the two together would
       * produce NaN by virtue of adding opposing Infinte values. Since we need to add them below,
       * their sum must not be NaN. We return false because NaN always produces a negative response
       * to tests
       */
      return false;
    }
    if (w <= 0 || h <= 0) {
      return false;
    }
    // TODO
    assert (false) : "Not implemented yet";
    return false;
    // int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
    // int crossings = rectCrossings(x, y, x + w, y + h);
    // return (crossings != Curve.RECT_INTERSECTS && (crossings & mask) != 0);
  }

  /**
   * {@inheritDoc}
   * <p>
   * This method object may conservatively return false in cases where the specified rectangular
   * area intersects a segment of the path, but that segment does not represent a boundary between
   * the interior and exterior of the path. Such segments could lie entirely within the interior of
   * the path if they are part of a path with a {@link #WIND_NON_ZERO} winding rule or if the
   * segments are retraced in the reverse direction such that the two sets of segments cancel each
   * other out without any exterior area falling between them. To determine whether segments
   * represent true boundaries of the interior of the path would require extensive calculations
   * involving all of the segments of the path and the winding rule and are thus beyond the scope of
   * this implementation.
   * 
   * @since 1.6
   */
  public final boolean contains(Rectangle2D r) {
    return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  }

  /**
   * Tests if the interior of the specified {@link PathIterator} intersects the interior of a
   * specified set of rectangular coordinates.
   * <p>
   * This method provides a basic facility for implementors of the {@link Shape} interface to
   * implement support for the {@link Shape#intersects(double, double, double, double)} method.
   * <p>
   * This method object may conservatively return true in cases where the specified rectangular area
   * intersects a segment of the path, but that segment does not represent a boundary between the
   * interior and exterior of the path. Such a case may occur if some set of segments of the path
   * are retraced in the reverse direction such that the two sets of segments cancel each other out
   * without any interior area between them. To determine whether segments represent true boundaries
   * of the interior of the path would require extensive calculations involving all of the segments
   * of the path and the winding rule and are thus beyond the scope of this implementation.
   * 
   * @param pi the specified {@code PathIterator}
   * @param x the specified X coordinate
   * @param y the specified Y coordinate
   * @param w the width of the specified rectangular coordinates
   * @param h the height of the specified rectangular coordinates
   * @return {@code true} if the specified {@code PathIterator} and the interior of the specified
   *         set of rectangular coordinates intersect each other; {@code false} otherwise.
   * @since 1.6
   */
  public static boolean intersects(PathIterator pi, double x, double y, double w, double h) {
    if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
      /*
       * [xy]+[wh] is NaN if any of those values are NaN, or if adding the two together would
       * produce NaN by virtue of adding opposing Infinte values. Since we need to add them below,
       * their sum must not be NaN. We return false because NaN always produces a negative response
       * to tests
       */
      return false;
    }
    if (w <= 0 || h <= 0) {
      return false;
    }
    // TODO
    assert (false) : "Not implemented yet";
    return false;

    // int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
    // int crossings = Curve.rectCrossingsForPath(pi, x, y, x + w, y + h);
    // return (crossings == Curve.RECT_INTERSECTS || (crossings & mask) != 0);
  }

  /**
   * Tests if the interior of the specified {@link PathIterator} intersects the interior of a
   * specified {@link Rectangle2D}.
   * <p>
   * This method provides a basic facility for implementors of the {@link Shape} interface to
   * implement support for the {@link Shape#intersects(Rectangle2D)} method.
   * <p>
   * This method object may conservatively return true in cases where the specified rectangular area
   * intersects a segment of the path, but that segment does not represent a boundary between the
   * interior and exterior of the path. Such a case may occur if some set of segments of the path
   * are retraced in the reverse direction such that the two sets of segments cancel each other out
   * without any interior area between them. To determine whether segments represent true boundaries
   * of the interior of the path would require extensive calculations involving all of the segments
   * of the path and the winding rule and are thus beyond the scope of this implementation.
   * 
   * @param pi the specified {@code PathIterator}
   * @param r the specified {@code Rectangle2D}
   * @return {@code true} if the specified {@code PathIterator} and the interior of the specified
   *         {@code Rectangle2D} intersect each other; {@code false} otherwise.
   * @since 1.6
   */
  public static boolean intersects(PathIterator pi, Rectangle2D r) {
    return intersects(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight());
  }

  /**
   * {@inheritDoc}
   * <p>
   * This method object may conservatively return true in cases where the specified rectangular area
   * intersects a segment of the path, but that segment does not represent a boundary between the
   * interior and exterior of the path. Such a case may occur if some set of segments of the path
   * are retraced in the reverse direction such that the two sets of segments cancel each other out
   * without any interior area between them. To determine whether segments represent true boundaries
   * of the interior of the path would require extensive calculations involving all of the segments
   * of the path and the winding rule and are thus beyond the scope of this implementation.
   * 
   * @since 1.6
   */
  public final boolean intersects(double x, double y, double w, double h) {
    if (java.lang.Double.isNaN(x + w) || java.lang.Double.isNaN(y + h)) {
      /*
       * [xy]+[wh] is NaN if any of those values are NaN, or if adding the two together would
       * produce NaN by virtue of adding opposing Infinte values. Since we need to add them below,
       * their sum must not be NaN. We return false because NaN always produces a negative response
       * to tests
       */
      return false;
    }
    if (w <= 0 || h <= 0) {
      return false;
    }
    // TODO
    assert (false) : "Not implemented yet";
    return false;

    // int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
    // int crossings = rectCrossings(x, y, x + w, y + h);
    // return (crossings == Curve.RECT_INTERSECTS || (crossings & mask) != 0);
  }

  /**
   * {@inheritDoc}
   * <p>
   * This method object may conservatively return true in cases where the specified rectangular area
   * intersects a segment of the path, but that segment does not represent a boundary between the
   * interior and exterior of the path. Such a case may occur if some set of segments of the path
   * are retraced in the reverse direction such that the two sets of segments cancel each other out
   * without any interior area between them. To determine whether segments represent true boundaries
   * of the interior of the path would require extensive calculations involving all of the segments
   * of the path and the winding rule and are thus beyond the scope of this implementation.
   * 
   * @since 1.6
   */
  public final boolean intersects(Rectangle2D r) {
    return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
  }

  /**
   * {@inheritDoc}
   * <p>
   * The iterator for this class is not multi-threaded safe, which means that this {@code Path2D}
   * class does not guarantee that modifications to the geometry of this {@code Path2D} object do
   * not affect any iterations of that geometry that are already in process.
   * 
   * @since 1.6
   */
  // public final PathIterator getPathIterator(AffineTransform at, double flatness) {
  // return new FlatteningPathIterator(getPathIterator(at), flatness);
  // }

  /**
   * Creates a new object of the same class as this object.
   * 
   * @return a clone of this instance.
   * @exception OutOfMemoryError if there is not enough memory.
   * @see java.lang.Cloneable
   * @since 1.6
   */
  // public abstract Object clone();

  // Note: It would be nice to have this return Path2D
  // but one of our subclasses (GeneralPath) needs to
  // offer "public Object clone()" for backwards
  // compatibility so we cannot restrict it further.
  // REMIND: Can we do both somehow?

  // /*
  // * Support fields and methods for serializing the subclasses.
  // */
  // private static final byte SERIAL_STORAGE_FLT_ARRAY = 0x30;
  // private static final byte SERIAL_STORAGE_DBL_ARRAY = 0x31;
  //
  // private static final byte SERIAL_SEG_FLT_MOVETO = 0x40;
  // private static final byte SERIAL_SEG_FLT_LINETO = 0x41;
  // private static final byte SERIAL_SEG_FLT_QUADTO = 0x42;
  // private static final byte SERIAL_SEG_FLT_CUBICTO = 0x43;
  //
  // private static final byte SERIAL_SEG_DBL_MOVETO = 0x50;
  // private static final byte SERIAL_SEG_DBL_LINETO = 0x51;
  // private static final byte SERIAL_SEG_DBL_QUADTO = 0x52;
  // private static final byte SERIAL_SEG_DBL_CUBICTO = 0x53;
  //
  // private static final byte SERIAL_SEG_CLOSE = 0x60;
  // private static final byte SERIAL_PATH_END = 0x61;

  // final void writeObject(java.io.ObjectOutputStream s, boolean isdbl) throws java.io.IOException
  // {
  // s.defaultWriteObject();
  //
  // float fCoords[];
  // double dCoords[];
  //
  // if (isdbl) {
  // dCoords = ((Path2D.Double) this).doubleCoords;
  // fCoords = null;
  // } else {
  // fCoords = ((Path2D.Float) this).floatCoords;
  // dCoords = null;
  // }
  //
  // int numTypes = this.numTypes;
  //
  // s.writeByte(isdbl ? SERIAL_STORAGE_DBL_ARRAY : SERIAL_STORAGE_FLT_ARRAY);
  // s.writeInt(numTypes);
  // s.writeInt(numCoords);
  // s.writeByte((byte) windingRule);
  //
  // int cindex = 0;
  // for (int i = 0; i < numTypes; i++) {
  // int npoints;
  // byte serialtype;
  // switch (pointTypes[i]){
  // case SEG_MOVETO :
  // npoints = 1;
  // serialtype = (isdbl ? SERIAL_SEG_DBL_MOVETO : SERIAL_SEG_FLT_MOVETO);
  // break;
  // case SEG_LINETO :
  // npoints = 1;
  // serialtype = (isdbl ? SERIAL_SEG_DBL_LINETO : SERIAL_SEG_FLT_LINETO);
  // break;
  // case SEG_QUADTO :
  // npoints = 2;
  // serialtype = (isdbl ? SERIAL_SEG_DBL_QUADTO : SERIAL_SEG_FLT_QUADTO);
  // break;
  // case SEG_CUBICTO :
  // npoints = 3;
  // serialtype = (isdbl ? SERIAL_SEG_DBL_CUBICTO : SERIAL_SEG_FLT_CUBICTO);
  // break;
  // case SEG_CLOSE :
  // npoints = 0;
  // serialtype = SERIAL_SEG_CLOSE;
  // break;
  //
  // default :
  // // Should never happen
  // throw new InternalError("unrecognized path type");
  // }
  // s.writeByte(serialtype);
  // while (--npoints >= 0) {
  // if (isdbl) {
  // s.writeDouble(dCoords[cindex++]);
  // s.writeDouble(dCoords[cindex++]);
  // } else {
  // s.writeFloat(fCoords[cindex++]);
  // s.writeFloat(fCoords[cindex++]);
  // }
  // }
  // }
  // s.writeByte((byte) SERIAL_PATH_END);
  // }
  //
  // final void readObject(java.io.ObjectInputStream s, boolean storedbl) throws
  // java.lang.ClassNotFoundException,
  // java.io.IOException {
  // s.defaultReadObject();
  //
  // // The subclass calls this method with the storage type that
  // // they want us to use (storedbl) so we ignore the storage
  // // method hint from the stream.
  // s.readByte();
  // int nT = s.readInt();
  // int nC = s.readInt();
  // try {
  // setWindingRule(s.readByte());
  // } catch (IllegalArgumentException iae) {
  // throw new java.io.InvalidObjectException(iae.getMessage());
  // }
  //
  // pointTypes = new byte[(nT < 0) ? INIT_SIZE : nT];
  // if (nC < 0) {
  // nC = INIT_SIZE * 2;
  // }
  // if (storedbl) {
  // ((Path2D.Double) this).doubleCoords = new double[nC];
  // } else {
  // ((Path2D.Float) this).floatCoords = new float[nC];
  // }
  //
  // PATHDONE : for (int i = 0; nT < 0 || i < nT; i++) {
  // boolean isdbl;
  // int npoints;
  // byte segtype;
  //
  // byte serialtype = s.readByte();
  // switch (serialtype){
  // case SERIAL_SEG_FLT_MOVETO :
  // isdbl = false;
  // npoints = 1;
  // segtype = SEG_MOVETO;
  // break;
  // case SERIAL_SEG_FLT_LINETO :
  // isdbl = false;
  // npoints = 1;
  // segtype = SEG_LINETO;
  // break;
  // case SERIAL_SEG_FLT_QUADTO :
  // isdbl = false;
  // npoints = 2;
  // segtype = SEG_QUADTO;
  // break;
  // case SERIAL_SEG_FLT_CUBICTO :
  // isdbl = false;
  // npoints = 3;
  // segtype = SEG_CUBICTO;
  // break;
  //
  // case SERIAL_SEG_DBL_MOVETO :
  // isdbl = true;
  // npoints = 1;
  // segtype = SEG_MOVETO;
  // break;
  // case SERIAL_SEG_DBL_LINETO :
  // isdbl = true;
  // npoints = 1;
  // segtype = SEG_LINETO;
  // break;
  // case SERIAL_SEG_DBL_QUADTO :
  // isdbl = true;
  // npoints = 2;
  // segtype = SEG_QUADTO;
  // break;
  // case SERIAL_SEG_DBL_CUBICTO :
  // isdbl = true;
  // npoints = 3;
  // segtype = SEG_CUBICTO;
  // break;
  //
  // case SERIAL_SEG_CLOSE :
  // isdbl = false;
  // npoints = 0;
  // segtype = SEG_CLOSE;
  // break;
  //
  // case SERIAL_PATH_END :
  // if (nT < 0) {
  // break PATHDONE;
  // }
  // throw new StreamCorruptedException("unexpected PATH_END");
  //
  // default :
  // throw new StreamCorruptedException("unrecognized path type");
  // }
  // needRoom(segtype != SEG_MOVETO, npoints * 2);
  // if (isdbl) {
  // while (--npoints >= 0) {
  // append(s.readDouble(), s.readDouble());
  // }
  // } else {
  // while (--npoints >= 0) {
  // append(s.readFloat(), s.readFloat());
  // }
  // }
  // pointTypes[numTypes++] = segtype;
  // }
  // if (nT >= 0 && s.readByte() != SERIAL_PATH_END) {
  // throw new StreamCorruptedException("missing PATH_END");
  // }
  // }

  static abstract class Iterator implements PathIterator {
    int typeIdx;
    int pointIdx;
    Path2D path;

    static final int curvecoords[] = {
        2, 2, 4, 6, 0
    };

    Iterator(Path2D path) {
      this.path = path;
    }

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

    public boolean isDone() {
      return (typeIdx >= path.numTypes);
    }

    public void next() {
      int type = path.pointTypes[typeIdx++];
      pointIdx += curvecoords[type];
    }
  }
}
