Path2D.java :  » Chart » droidcharts » net » droidsolutions » droidcharts » awt » Android Open Source

Android Open Source » Chart » droidcharts 
droidcharts » net » droidsolutions » droidcharts » awt » Path2D.java
/*
 * Copyright 2006 Sun Microsystems, Inc.  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.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package net.droidsolutions.droidcharts.awt;

import java.io.Serializable;
import java.io.StreamCorruptedException;

/**
 * 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 RuntimeException("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 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 RuntimeException("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 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) {
      /*
       * 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;
    }
    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;
    }
    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;
    }
    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;
    }
    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 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];
    }
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.