/*
* Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.awt.geom;
import java.awt.
Rectangle;
import java.awt.
Shape;
import java.io.
Serializable;
import java.io.
StreamCorruptedException;
import java.util.
Arrays;
import sun.awt.geom.
Curve;
/**
* 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;
static final int
EXPAND_MAX_COORDS =
EXPAND_MAX * 2;
static final int
EXPAND_MIN = 10; // ensure > 6 (cubics)
/**
* 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);
static byte[]
expandPointTypes(byte[]
oldPointTypes, int
needed) {
final int
oldSize =
oldPointTypes.length;
final int
newSizeMin =
oldSize +
needed;
if (
newSizeMin <
oldSize) {
// hard overflow failure - we can't even accommodate
// new items without overflowing
throw new
ArrayIndexOutOfBoundsException(
"pointTypes exceeds maximum capacity !");
}
// growth algorithm computation
int
grow =
oldSize;
if (
grow >
EXPAND_MAX) {
grow =
Math.
max(
EXPAND_MAX,
oldSize >> 3); // 1/8th min
} else if (
grow <
EXPAND_MIN) {
grow =
EXPAND_MIN;
}
assert
grow > 0;
int
newSize =
oldSize +
grow;
if (
newSize <
newSizeMin) {
// overflow in growth algorithm computation
newSize =
Integer.
MAX_VALUE;
}
while (true) {
try {
// try allocating the larger array
return
Arrays.
copyOf(
oldPointTypes,
newSize);
} catch (
OutOfMemoryError oome) {
if (
newSize ==
newSizeMin) {
throw
oome;
}
}
newSize =
newSizeMin + (
newSize -
newSizeMin) / 2;
}
}
/**
* 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;
// trim arrays:
this.
pointTypes =
Arrays.
copyOf(
p2d.
pointTypes,
p2d.
numTypes);
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);
}
}
@
Override
float[]
cloneCoordsFloat(
AffineTransform at) {
// trim arrays:
float
ret[];
if (
at == null) {
ret =
Arrays.
copyOf(
floatCoords,
numCoords);
} else {
ret = new float[
numCoords];
at.
transform(
floatCoords, 0,
ret, 0,
numCoords / 2);
}
return
ret;
}
@
Override
double[]
cloneCoordsDouble(
AffineTransform at) {
// trim arrays:
double
ret[] = new double[
numCoords];
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]);
}
@
Override
void
needRoom(boolean
needMove, int
newCoords) {
if ((
numTypes == 0) &&
needMove) {
throw new
IllegalPathStateException("missing initial moveto "+
"in path definition");
}
if (
numTypes >=
pointTypes.length) {
pointTypes =
expandPointTypes(
pointTypes, 1);
}
if (
numCoords > (
floatCoords.length -
newCoords)) {
floatCoords =
expandCoords(
floatCoords,
newCoords);
}
}
static float[]
expandCoords(float[]
oldCoords, int
needed) {
final int
oldSize =
oldCoords.length;
final int
newSizeMin =
oldSize +
needed;
if (
newSizeMin <
oldSize) {
// hard overflow failure - we can't even accommodate
// new items without overflowing
throw new
ArrayIndexOutOfBoundsException(
"coords exceeds maximum capacity !");
}
// growth algorithm computation
int
grow =
oldSize;
if (
grow >
EXPAND_MAX_COORDS) {
grow =
Math.
max(
EXPAND_MAX_COORDS,
oldSize >> 3); // 1/8th min
} else if (
grow <
EXPAND_MIN) {
grow =
EXPAND_MIN;
}
assert
grow >
needed;
int
newSize =
oldSize +
grow;
if (
newSize <
newSizeMin) {
// overflow in growth algorithm computation
newSize =
Integer.
MAX_VALUE;
}
while (true) {
try {
// try allocating the larger array
return
Arrays.
copyOf(
oldCoords,
newSize);
} catch (
OutOfMemoryError oome) {
if (
newSize ==
newSizeMin) {
throw
oome;
}
}
newSize =
newSizeMin + (
newSize -
newSizeMin) / 2;
}
}
/**
* {@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é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é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ézier control point
* @param y1 the Y coordinate of the first Bézier control point
* @param x2 the X coordinate of the second Bézier control point
* @param y2 the Y coordinate of the second Bé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) {
if (
numTypes == 0) {
return 0;
}
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)
{
if (
numTypes == 0) {
return 0;
}
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;
}
lineTo(
coords[0],
coords[1]);
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
* {@code NP} (or unlimited if {@code 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;
// trim arrays:
this.
pointTypes =
Arrays.
copyOf(
p2d.
pointTypes,
p2d.
numTypes);
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);
}
}
@
Override
float[]
cloneCoordsFloat(
AffineTransform at) {
// trim arrays:
float
ret[] = new float[
numCoords];
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;
}
@
Override
double[]
cloneCoordsDouble(
AffineTransform at) {
// trim arrays:
double
ret[];
if (
at == null) {
ret =
Arrays.
copyOf(
doubleCoords,
numCoords);
} else {
ret = new double[
numCoords];
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]);
}
@
Override
void
needRoom(boolean
needMove, int
newCoords) {
if ((
numTypes == 0) &&
needMove) {
throw new
IllegalPathStateException("missing initial moveto "+
"in path definition");
}
if (
numTypes >=
pointTypes.length) {
pointTypes =
expandPointTypes(
pointTypes, 1);
}
if (
numCoords > (
doubleCoords.length -
newCoords)) {
doubleCoords =
expandCoords(
doubleCoords,
newCoords);
}
}
static double[]
expandCoords(double[]
oldCoords, int
needed) {
final int
oldSize =
oldCoords.length;
final int
newSizeMin =
oldSize +
needed;
if (
newSizeMin <
oldSize) {
// hard overflow failure - we can't even accommodate
// new items without overflowing
throw new
ArrayIndexOutOfBoundsException(
"coords exceeds maximum capacity !");
}
// growth algorithm computation
int
grow =
oldSize;
if (
grow >
EXPAND_MAX_COORDS) {
grow =
Math.
max(
EXPAND_MAX_COORDS,
oldSize >> 3); // 1/8th min
} else if (
grow <
EXPAND_MIN) {
grow =
EXPAND_MIN;
}
assert
grow >
needed;
int
newSize =
oldSize +
grow;
if (
newSize <
newSizeMin) {
// overflow in growth algorithm computation
newSize =
Integer.
MAX_VALUE;
}
while (true) {
try {
// try allocating the larger array
return
Arrays.
copyOf(
oldCoords,
newSize);
} catch (
OutOfMemoryError oome) {
if (
newSize ==
newSizeMin) {
throw
oome;
}
}
newSize =
newSizeMin + (
newSize -
newSizeMin) / 2;
}
}
/**
* {@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) {
if (
numTypes == 0) {
return 0;
}
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)
{
if (
numTypes == 0) {
return 0;
}
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;
}
lineTo(
coords[0],
coords[1]);
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
* {@code NP} (or unlimited if {@code 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é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ézier control points.
* All coordinates are specified in double precision.
*
* @param x1 the X coordinate of the first Bézier control point
* @param y1 the Y coordinate of the first Bézier control point
* @param x2 the X coordinate of the second Bézier control point
* @param y2 the Y coordinate of the second Bé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 rectangular 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 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(
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());
}
// Accept the size from the stream only if it is less than INIT_SIZE
// otherwise the size will be based on the real data in the stream
pointTypes = new byte[(
nT < 0 ||
nT >
INIT_SIZE) ?
INIT_SIZE :
nT];
final int
initX2 =
INIT_SIZE * 2;
if (
nC < 0 ||
nC >
initX2) {
nC =
initX2;
}
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];
}
}
}