0001 /*
0002 * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package java.awt.geom;
0027
0028 import java.awt.Shape;
0029 import java.awt.Rectangle;
0030 import sun.awt.geom.Curve;
0031 import java.io.Serializable;
0032 import java.io.StreamCorruptedException;
0033 import java.util.Arrays;
0034
0035 /**
0036 * The {@code Path2D} class provides a simple, yet flexible
0037 * shape which represents an arbitrary geometric path.
0038 * It can fully represent any path which can be iterated by the
0039 * {@link PathIterator} interface including all of its segment
0040 * types and winding rules and it implements all of the
0041 * basic hit testing methods of the {@link Shape} interface.
0042 * <p>
0043 * Use {@link Path2D.Float} when dealing with data that can be represented
0044 * and used with floating point precision. Use {@link Path2D.Double}
0045 * for data that requires the accuracy or range of double precision.
0046 * <p>
0047 * {@code Path2D} provides exactly those facilities required for
0048 * basic construction and management of a geometric path and
0049 * implementation of the above interfaces with little added
0050 * interpretation.
0051 * If it is useful to manipulate the interiors of closed
0052 * geometric shapes beyond simple hit testing then the
0053 * {@link Area} class provides additional capabilities
0054 * specifically targeted at closed figures.
0055 * While both classes nominally implement the {@code Shape}
0056 * interface, they differ in purpose and together they provide
0057 * two useful views of a geometric shape where {@code Path2D}
0058 * deals primarily with a trajectory formed by path segments
0059 * and {@code Area} deals more with interpretation and manipulation
0060 * of enclosed regions of 2D geometric space.
0061 * <p>
0062 * The {@link PathIterator} interface has more detailed descriptions
0063 * of the types of segments that make up a path and the winding rules
0064 * that control how to determine which regions are inside or outside
0065 * the path.
0066 *
0067 * @version 1.10, 05/05/07
0068 * @author Jim Graham
0069 * @since 1.6
0070 */
0071 public abstract class Path2D implements Shape, Cloneable {
0072 /**
0073 * An even-odd winding rule for determining the interior of
0074 * a path.
0075 *
0076 * @see PathIterator#WIND_EVEN_ODD
0077 * @since 1.6
0078 */
0079 public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
0080
0081 /**
0082 * A non-zero winding rule for determining the interior of a
0083 * path.
0084 *
0085 * @see PathIterator#WIND_NON_ZERO
0086 * @since 1.6
0087 */
0088 public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
0089
0090 // For code simplicity, copy these constants to our namespace
0091 // and cast them to byte constants for easy storage.
0092 private static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO;
0093 private static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO;
0094 private static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO;
0095 private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
0096 private static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE;
0097
0098 transient byte[] pointTypes;
0099 transient int numTypes;
0100 transient int numCoords;
0101 transient int windingRule;
0102
0103 static final int INIT_SIZE = 20;
0104 static final int EXPAND_MAX = 500;
0105
0106 /**
0107 * Constructs a new empty {@code Path2D} object.
0108 * It is assumed that the package sibling subclass that is
0109 * defaulting to this constructor will fill in all values.
0110 *
0111 * @since 1.6
0112 */
0113 /* private protected */
0114 Path2D() {
0115 }
0116
0117 /**
0118 * Constructs a new {@code Path2D} object from the given
0119 * specified initial values.
0120 * This method is only intended for internal use and should
0121 * not be made public if the other constructors for this class
0122 * are ever exposed.
0123 *
0124 * @param rule the winding rule
0125 * @param initialTypes the size to make the initial array to
0126 * store the path segment types
0127 * @since 1.6
0128 */
0129 /* private protected */
0130 Path2D(int rule, int initialTypes) {
0131 setWindingRule(rule);
0132 this .pointTypes = new byte[initialTypes];
0133 }
0134
0135 abstract float[] cloneCoordsFloat(AffineTransform at);
0136
0137 abstract double[] cloneCoordsDouble(AffineTransform at);
0138
0139 abstract void append(float x, float y);
0140
0141 abstract void append(double x, double y);
0142
0143 abstract Point2D getPoint(int coordindex);
0144
0145 abstract void needRoom(boolean needMove, int newCoords);
0146
0147 abstract int pointCrossings(double px, double py);
0148
0149 abstract int rectCrossings(double rxmin, double rymin,
0150 double rxmax, double rymax);
0151
0152 /**
0153 * The {@code Float} class defines a geometric path with
0154 * coordinates stored in single precision floating point.
0155 *
0156 * @since 1.6
0157 */
0158 public static class Float extends Path2D implements Serializable {
0159 transient float floatCoords[];
0160
0161 /**
0162 * Constructs a new empty single precision {@code Path2D} object
0163 * with a default winding rule of {@link #WIND_NON_ZERO}.
0164 *
0165 * @since 1.6
0166 */
0167 public Float() {
0168 this (WIND_NON_ZERO, INIT_SIZE);
0169 }
0170
0171 /**
0172 * Constructs a new empty single precision {@code Path2D} object
0173 * with the specified winding rule to control operations that
0174 * require the interior of the path to be defined.
0175 *
0176 * @param rule the winding rule
0177 * @see #WIND_EVEN_ODD
0178 * @see #WIND_NON_ZERO
0179 * @since 1.6
0180 */
0181 public Float(int rule) {
0182 this (rule, INIT_SIZE);
0183 }
0184
0185 /**
0186 * Constructs a new empty single precision {@code Path2D} object
0187 * with the specified winding rule and the specified initial
0188 * capacity to store path segments.
0189 * This number is an initial guess as to how many path segments
0190 * will be added to the path, but the storage is expanded as
0191 * needed to store whatever path segments are added.
0192 *
0193 * @param rule the winding rule
0194 * @param initialCapacity the estimate for the number of path segments
0195 * in the path
0196 * @see #WIND_EVEN_ODD
0197 * @see #WIND_NON_ZERO
0198 * @since 1.6
0199 */
0200 public Float(int rule, int initialCapacity) {
0201 super (rule, initialCapacity);
0202 floatCoords = new float[initialCapacity * 2];
0203 }
0204
0205 /**
0206 * Constructs a new single precision {@code Path2D} object
0207 * from an arbitrary {@link Shape} object.
0208 * All of the initial geometry and the winding rule for this path are
0209 * taken from the specified {@code Shape} object.
0210 *
0211 * @param s the specified {@code Shape} object
0212 * @since 1.6
0213 */
0214 public Float(Shape s) {
0215 this (s, null);
0216 }
0217
0218 /**
0219 * Constructs a new single precision {@code Path2D} object
0220 * from an arbitrary {@link Shape} object, transformed by an
0221 * {@link AffineTransform} object.
0222 * All of the initial geometry and the winding rule for this path are
0223 * taken from the specified {@code Shape} object and transformed
0224 * by the specified {@code AffineTransform} object.
0225 *
0226 * @param s the specified {@code Shape} object
0227 * @param at the specified {@code AffineTransform} object
0228 * @since 1.6
0229 */
0230 public Float(Shape s, AffineTransform at) {
0231 if (s instanceof Path2D) {
0232 Path2D p2d = (Path2D) s;
0233 setWindingRule(p2d.windingRule);
0234 this .numTypes = p2d.numTypes;
0235 this .pointTypes = Arrays.copyOf(p2d.pointTypes,
0236 p2d.pointTypes.length);
0237 this .numCoords = p2d.numCoords;
0238 this .floatCoords = p2d.cloneCoordsFloat(at);
0239 } else {
0240 PathIterator pi = s.getPathIterator(at);
0241 setWindingRule(pi.getWindingRule());
0242 this .pointTypes = new byte[INIT_SIZE];
0243 this .floatCoords = new float[INIT_SIZE * 2];
0244 append(pi, false);
0245 }
0246 }
0247
0248 float[] cloneCoordsFloat(AffineTransform at) {
0249 float ret[];
0250 if (at == null) {
0251 ret = Arrays.copyOf(this .floatCoords,
0252 this .floatCoords.length);
0253 } else {
0254 ret = new float[floatCoords.length];
0255 at.transform(floatCoords, 0, ret, 0, numCoords / 2);
0256 }
0257 return ret;
0258 }
0259
0260 double[] cloneCoordsDouble(AffineTransform at) {
0261 double ret[] = new double[floatCoords.length];
0262 if (at == null) {
0263 for (int i = 0; i < numCoords; i++) {
0264 ret[i] = floatCoords[i];
0265 }
0266 } else {
0267 at.transform(floatCoords, 0, ret, 0, numCoords / 2);
0268 }
0269 return ret;
0270 }
0271
0272 void append(float x, float y) {
0273 floatCoords[numCoords++] = x;
0274 floatCoords[numCoords++] = y;
0275 }
0276
0277 void append(double x, double y) {
0278 floatCoords[numCoords++] = (float) x;
0279 floatCoords[numCoords++] = (float) y;
0280 }
0281
0282 Point2D getPoint(int coordindex) {
0283 return new Point2D.Float(floatCoords[coordindex],
0284 floatCoords[coordindex + 1]);
0285 }
0286
0287 void needRoom(boolean needMove, int newCoords) {
0288 if (needMove && numTypes == 0) {
0289 throw new IllegalPathStateException(
0290 "missing initial moveto "
0291 + "in path definition");
0292 }
0293 int size = pointTypes.length;
0294 if (numTypes >= size) {
0295 int grow = size;
0296 if (grow > EXPAND_MAX) {
0297 grow = EXPAND_MAX;
0298 }
0299 pointTypes = Arrays.copyOf(pointTypes, size + grow);
0300 }
0301 size = floatCoords.length;
0302 if (numCoords + newCoords > size) {
0303 int grow = size;
0304 if (grow > EXPAND_MAX * 2) {
0305 grow = EXPAND_MAX * 2;
0306 }
0307 if (grow < newCoords) {
0308 grow = newCoords;
0309 }
0310 floatCoords = Arrays.copyOf(floatCoords, size + grow);
0311 }
0312 }
0313
0314 /**
0315 * {@inheritDoc}
0316 * @since 1.6
0317 */
0318 public final synchronized void moveTo(double x, double y) {
0319 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
0320 floatCoords[numCoords - 2] = (float) x;
0321 floatCoords[numCoords - 1] = (float) y;
0322 } else {
0323 needRoom(false, 2);
0324 pointTypes[numTypes++] = SEG_MOVETO;
0325 floatCoords[numCoords++] = (float) x;
0326 floatCoords[numCoords++] = (float) y;
0327 }
0328 }
0329
0330 /**
0331 * Adds a point to the path by moving to the specified
0332 * coordinates specified in float precision.
0333 * <p>
0334 * This method provides a single precision variant of
0335 * the double precision {@code moveTo()} method on the
0336 * base {@code Path2D} class.
0337 *
0338 * @param x the specified X coordinate
0339 * @param y the specified Y coordinate
0340 * @see Path2D#moveTo
0341 * @since 1.6
0342 */
0343 public final synchronized void moveTo(float x, float y) {
0344 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
0345 floatCoords[numCoords - 2] = x;
0346 floatCoords[numCoords - 1] = y;
0347 } else {
0348 needRoom(false, 2);
0349 pointTypes[numTypes++] = SEG_MOVETO;
0350 floatCoords[numCoords++] = x;
0351 floatCoords[numCoords++] = y;
0352 }
0353 }
0354
0355 /**
0356 * {@inheritDoc}
0357 * @since 1.6
0358 */
0359 public final synchronized void lineTo(double x, double y) {
0360 needRoom(true, 2);
0361 pointTypes[numTypes++] = SEG_LINETO;
0362 floatCoords[numCoords++] = (float) x;
0363 floatCoords[numCoords++] = (float) y;
0364 }
0365
0366 /**
0367 * Adds a point to the path by drawing a straight line from the
0368 * current coordinates to the new specified coordinates
0369 * specified in float precision.
0370 * <p>
0371 * This method provides a single precision variant of
0372 * the double precision {@code lineTo()} method on the
0373 * base {@code Path2D} class.
0374 *
0375 * @param x the specified X coordinate
0376 * @param y the specified Y coordinate
0377 * @see Path2D#lineTo
0378 * @since 1.6
0379 */
0380 public final synchronized void lineTo(float x, float y) {
0381 needRoom(true, 2);
0382 pointTypes[numTypes++] = SEG_LINETO;
0383 floatCoords[numCoords++] = x;
0384 floatCoords[numCoords++] = y;
0385 }
0386
0387 /**
0388 * {@inheritDoc}
0389 * @since 1.6
0390 */
0391 public final synchronized void quadTo(double x1, double y1,
0392 double x2, double y2) {
0393 needRoom(true, 4);
0394 pointTypes[numTypes++] = SEG_QUADTO;
0395 floatCoords[numCoords++] = (float) x1;
0396 floatCoords[numCoords++] = (float) y1;
0397 floatCoords[numCoords++] = (float) x2;
0398 floatCoords[numCoords++] = (float) y2;
0399 }
0400
0401 /**
0402 * Adds a curved segment, defined by two new points, to the path by
0403 * drawing a Quadratic curve that intersects both the current
0404 * coordinates and the specified coordinates {@code (x2,y2)},
0405 * using the specified point {@code (x1,y1)} as a quadratic
0406 * parametric control point.
0407 * All coordinates are specified in float precision.
0408 * <p>
0409 * This method provides a single precision variant of
0410 * the double precision {@code quadTo()} method on the
0411 * base {@code Path2D} class.
0412 *
0413 * @param x1 the X coordinate of the quadratic control point
0414 * @param y1 the Y coordinate of the quadratic control point
0415 * @param x2 the X coordinate of the final end point
0416 * @param y2 the Y coordinate of the final end point
0417 * @see Path2D#quadTo
0418 * @since 1.6
0419 */
0420 public final synchronized void quadTo(float x1, float y1,
0421 float x2, float y2) {
0422 needRoom(true, 4);
0423 pointTypes[numTypes++] = SEG_QUADTO;
0424 floatCoords[numCoords++] = x1;
0425 floatCoords[numCoords++] = y1;
0426 floatCoords[numCoords++] = x2;
0427 floatCoords[numCoords++] = y2;
0428 }
0429
0430 /**
0431 * {@inheritDoc}
0432 * @since 1.6
0433 */
0434 public final synchronized void curveTo(double x1, double y1,
0435 double x2, double y2, double x3, double y3) {
0436 needRoom(true, 6);
0437 pointTypes[numTypes++] = SEG_CUBICTO;
0438 floatCoords[numCoords++] = (float) x1;
0439 floatCoords[numCoords++] = (float) y1;
0440 floatCoords[numCoords++] = (float) x2;
0441 floatCoords[numCoords++] = (float) y2;
0442 floatCoords[numCoords++] = (float) x3;
0443 floatCoords[numCoords++] = (float) y3;
0444 }
0445
0446 /**
0447 * Adds a curved segment, defined by three new points, to the path by
0448 * drawing a Bézier curve that intersects both the current
0449 * coordinates and the specified coordinates {@code (x3,y3)},
0450 * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
0451 * Bézier control points.
0452 * All coordinates are specified in float precision.
0453 * <p>
0454 * This method provides a single precision variant of
0455 * the double precision {@code curveTo()} method on the
0456 * base {@code Path2D} class.
0457 *
0458 * @param x1 the X coordinate of the first Bézier control point
0459 * @param y1 the Y coordinate of the first Bézier control point
0460 * @param x2 the X coordinate of the second Bézier control point
0461 * @param y2 the Y coordinate of the second Bézier control point
0462 * @param x3 the X coordinate of the final end point
0463 * @param y3 the Y coordinate of the final end point
0464 * @see Path2D#curveTo
0465 * @since 1.6
0466 */
0467 public final synchronized void curveTo(float x1, float y1,
0468 float x2, float y2, float x3, float y3) {
0469 needRoom(true, 6);
0470 pointTypes[numTypes++] = SEG_CUBICTO;
0471 floatCoords[numCoords++] = x1;
0472 floatCoords[numCoords++] = y1;
0473 floatCoords[numCoords++] = x2;
0474 floatCoords[numCoords++] = y2;
0475 floatCoords[numCoords++] = x3;
0476 floatCoords[numCoords++] = y3;
0477 }
0478
0479 int pointCrossings(double px, double py) {
0480 double movx, movy, curx, cury, endx, endy;
0481 float coords[] = floatCoords;
0482 curx = movx = coords[0];
0483 cury = movy = coords[1];
0484 int crossings = 0;
0485 int ci = 2;
0486 for (int i = 1; i < numTypes; i++) {
0487 switch (pointTypes[i]) {
0488 case PathIterator.SEG_MOVETO:
0489 if (cury != movy) {
0490 crossings += Curve.pointCrossingsForLine(px,
0491 py, curx, cury, movx, movy);
0492 }
0493 movx = curx = coords[ci++];
0494 movy = cury = coords[ci++];
0495 break;
0496 case PathIterator.SEG_LINETO:
0497 crossings += Curve.pointCrossingsForLine(px, py,
0498 curx, cury, endx = coords[ci++],
0499 endy = coords[ci++]);
0500 curx = endx;
0501 cury = endy;
0502 break;
0503 case PathIterator.SEG_QUADTO:
0504 crossings += Curve
0505 .pointCrossingsForQuad(px, py, curx, cury,
0506 coords[ci++], coords[ci++],
0507 endx = coords[ci++],
0508 endy = coords[ci++], 0);
0509 curx = endx;
0510 cury = endy;
0511 break;
0512 case PathIterator.SEG_CUBICTO:
0513 crossings += Curve
0514 .pointCrossingsForCubic(px, py, curx, cury,
0515 coords[ci++], coords[ci++],
0516 coords[ci++], coords[ci++],
0517 endx = coords[ci++],
0518 endy = coords[ci++], 0);
0519 curx = endx;
0520 cury = endy;
0521 break;
0522 case PathIterator.SEG_CLOSE:
0523 if (cury != movy) {
0524 crossings += Curve.pointCrossingsForLine(px,
0525 py, curx, cury, movx, movy);
0526 }
0527 curx = movx;
0528 cury = movy;
0529 break;
0530 }
0531 }
0532 if (cury != movy) {
0533 crossings += Curve.pointCrossingsForLine(px, py, curx,
0534 cury, movx, movy);
0535 }
0536 return crossings;
0537 }
0538
0539 int rectCrossings(double rxmin, double rymin, double rxmax,
0540 double rymax) {
0541 float coords[] = floatCoords;
0542 double curx, cury, movx, movy, endx, endy;
0543 curx = movx = coords[0];
0544 cury = movy = coords[1];
0545 int crossings = 0;
0546 int ci = 2;
0547 for (int i = 1; crossings != Curve.RECT_INTERSECTS
0548 && i < numTypes; i++) {
0549 switch (pointTypes[i]) {
0550 case PathIterator.SEG_MOVETO:
0551 if (curx != movx || cury != movy) {
0552 crossings = Curve.rectCrossingsForLine(
0553 crossings, rxmin, rymin, rxmax, rymax,
0554 curx, cury, movx, movy);
0555 }
0556 // Count should always be a multiple of 2 here.
0557 // assert((crossings & 1) != 0);
0558 movx = curx = coords[ci++];
0559 movy = cury = coords[ci++];
0560 break;
0561 case PathIterator.SEG_LINETO:
0562 crossings = Curve.rectCrossingsForLine(crossings,
0563 rxmin, rymin, rxmax, rymax, curx, cury,
0564 endx = coords[ci++], endy = coords[ci++]);
0565 curx = endx;
0566 cury = endy;
0567 break;
0568 case PathIterator.SEG_QUADTO:
0569 crossings = Curve
0570 .rectCrossingsForQuad(crossings, rxmin,
0571 rymin, rxmax, rymax, curx, cury,
0572 coords[ci++], coords[ci++],
0573 endx = coords[ci++],
0574 endy = coords[ci++], 0);
0575 curx = endx;
0576 cury = endy;
0577 break;
0578 case PathIterator.SEG_CUBICTO:
0579 crossings = Curve.rectCrossingsForCubic(crossings,
0580 rxmin, rymin, rxmax, rymax, curx, cury,
0581 coords[ci++], coords[ci++], coords[ci++],
0582 coords[ci++], endx = coords[ci++],
0583 endy = coords[ci++], 0);
0584 curx = endx;
0585 cury = endy;
0586 break;
0587 case PathIterator.SEG_CLOSE:
0588 if (curx != movx || cury != movy) {
0589 crossings = Curve.rectCrossingsForLine(
0590 crossings, rxmin, rymin, rxmax, rymax,
0591 curx, cury, movx, movy);
0592 }
0593 curx = movx;
0594 cury = movy;
0595 // Count should always be a multiple of 2 here.
0596 // assert((crossings & 1) != 0);
0597 break;
0598 }
0599 }
0600 if (crossings != Curve.RECT_INTERSECTS
0601 && (curx != movx || cury != movy)) {
0602 crossings = Curve.rectCrossingsForLine(crossings,
0603 rxmin, rymin, rxmax, rymax, curx, cury, movx,
0604 movy);
0605 }
0606 // Count should always be a multiple of 2 here.
0607 // assert((crossings & 1) != 0);
0608 return crossings;
0609 }
0610
0611 /**
0612 * {@inheritDoc}
0613 * @since 1.6
0614 */
0615 public final void append(PathIterator pi, boolean connect) {
0616 float coords[] = new float[6];
0617 while (!pi.isDone()) {
0618 switch (pi.currentSegment(coords)) {
0619 case SEG_MOVETO:
0620 if (!connect || numTypes < 1 || numCoords < 1) {
0621 moveTo(coords[0], coords[1]);
0622 break;
0623 }
0624 if (pointTypes[numTypes - 1] != SEG_CLOSE
0625 && floatCoords[numCoords - 2] == coords[0]
0626 && floatCoords[numCoords - 1] == coords[1]) {
0627 // Collapse out initial moveto/lineto
0628 break;
0629 }
0630 // NO BREAK;
0631 case SEG_LINETO:
0632 lineTo(coords[0], coords[1]);
0633 break;
0634 case SEG_QUADTO:
0635 quadTo(coords[0], coords[1], coords[2], coords[3]);
0636 break;
0637 case SEG_CUBICTO:
0638 curveTo(coords[0], coords[1], coords[2], coords[3],
0639 coords[4], coords[5]);
0640 break;
0641 case SEG_CLOSE:
0642 closePath();
0643 break;
0644 }
0645 pi.next();
0646 connect = false;
0647 }
0648 }
0649
0650 /**
0651 * {@inheritDoc}
0652 * @since 1.6
0653 */
0654 public final void transform(AffineTransform at) {
0655 at.transform(floatCoords, 0, floatCoords, 0, numCoords / 2);
0656 }
0657
0658 /**
0659 * {@inheritDoc}
0660 * @since 1.6
0661 */
0662 public final synchronized Rectangle2D getBounds2D() {
0663 float x1, y1, x2, y2;
0664 int i = numCoords;
0665 if (i > 0) {
0666 y1 = y2 = floatCoords[--i];
0667 x1 = x2 = floatCoords[--i];
0668 while (i > 0) {
0669 float y = floatCoords[--i];
0670 float x = floatCoords[--i];
0671 if (x < x1)
0672 x1 = x;
0673 if (y < y1)
0674 y1 = y;
0675 if (x > x2)
0676 x2 = x;
0677 if (y > y2)
0678 y2 = y;
0679 }
0680 } else {
0681 x1 = y1 = x2 = y2 = 0.0f;
0682 }
0683 return new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1);
0684 }
0685
0686 /**
0687 * {@inheritDoc}
0688 * <p>
0689 * The iterator for this class is not multi-threaded safe,
0690 * which means that the {@code Path2D} class does not
0691 * guarantee that modifications to the geometry of this
0692 * {@code Path2D} object do not affect any iterations of
0693 * that geometry that are already in process.
0694 *
0695 * @since 1.6
0696 */
0697 public PathIterator getPathIterator(AffineTransform at) {
0698 if (at == null) {
0699 return new CopyIterator(this );
0700 } else {
0701 return new TxIterator(this , at);
0702 }
0703 }
0704
0705 /**
0706 * Creates a new object of the same class as this object.
0707 *
0708 * @return a clone of this instance.
0709 * @exception OutOfMemoryError if there is not enough memory.
0710 * @see java.lang.Cloneable
0711 * @since 1.6
0712 */
0713 public final Object clone() {
0714 // Note: It would be nice to have this return Path2D
0715 // but one of our subclasses (GeneralPath) needs to
0716 // offer "public Object clone()" for backwards
0717 // compatibility so we cannot restrict it further.
0718 // REMIND: Can we do both somehow?
0719 if (this instanceof GeneralPath) {
0720 return new GeneralPath(this );
0721 } else {
0722 return new Path2D.Float(this );
0723 }
0724 }
0725
0726 /*
0727 * JDK 1.6 serialVersionUID
0728 */
0729 private static final long serialVersionUID = 6990832515060788886L;
0730
0731 /**
0732 * Writes the default serializable fields to the
0733 * {@code ObjectOutputStream} followed by an explicit
0734 * serialization of the path segments stored in this
0735 * path.
0736 *
0737 * @serialData
0738 * <a name="Path2DSerialData"><!-- --></a>
0739 * <ol>
0740 * <li>The default serializable fields.
0741 * There are no default serializable fields as of 1.6.
0742 * <li>followed by
0743 * a byte indicating the storage type of the original object
0744 * as a hint (SERIAL_STORAGE_FLT_ARRAY)
0745 * <li>followed by
0746 * an integer indicating the number of path segments to follow (NP)
0747 * or -1 to indicate an unknown number of path segments follows
0748 * <li>followed by
0749 * an integer indicating the total number of coordinates to follow (NC)
0750 * or -1 to indicate an unknown number of coordinates follows
0751 * (NC should always be even since coordinates always appear in pairs
0752 * representing an x,y pair)
0753 * <li>followed by
0754 * a byte indicating the winding rule
0755 * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or
0756 * {@link #WIND_NON_ZERO WIND_NON_ZERO})
0757 * <li>followed by
0758 * NP (or unlimited if NP < 0) sets of values consisting of
0759 * a single byte indicating a path segment type
0760 * followed by one or more pairs of float or double
0761 * values representing the coordinates of the path segment
0762 * <li>followed by
0763 * a byte indicating the end of the path (SERIAL_PATH_END).
0764 * </ol>
0765 * <p>
0766 * The following byte value constants are used in the serialized form
0767 * of {@code Path2D} objects:
0768 * <table>
0769 * <tr>
0770 * <th>Constant Name</th>
0771 * <th>Byte Value</th>
0772 * <th>Followed by</th>
0773 * <th>Description</th>
0774 * </tr>
0775 * <tr>
0776 * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
0777 * <td>0x30</td>
0778 * <td></td>
0779 * <td>A hint that the original {@code Path2D} object stored
0780 * the coordinates in a Java array of floats.</td>
0781 * </tr>
0782 * <tr>
0783 * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
0784 * <td>0x31</td>
0785 * <td></td>
0786 * <td>A hint that the original {@code Path2D} object stored
0787 * the coordinates in a Java array of doubles.</td>
0788 * </tr>
0789 * <tr>
0790 * <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
0791 * <td>0x40</td>
0792 * <td>2 floats</td>
0793 * <td>A {@link #moveTo moveTo} path segment follows.</td>
0794 * </tr>
0795 * <tr>
0796 * <td>{@code SERIAL_SEG_FLT_LINETO}</td>
0797 * <td>0x41</td>
0798 * <td>2 floats</td>
0799 * <td>A {@link #lineTo lineTo} path segment follows.</td>
0800 * </tr>
0801 * <tr>
0802 * <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
0803 * <td>0x42</td>
0804 * <td>4 floats</td>
0805 * <td>A {@link #quadTo quadTo} path segment follows.</td>
0806 * </tr>
0807 * <tr>
0808 * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
0809 * <td>0x43</td>
0810 * <td>6 floats</td>
0811 * <td>A {@link #curveTo curveTo} path segment follows.</td>
0812 * </tr>
0813 * <tr>
0814 * <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
0815 * <td>0x50</td>
0816 * <td>2 doubles</td>
0817 * <td>A {@link #moveTo moveTo} path segment follows.</td>
0818 * </tr>
0819 * <tr>
0820 * <td>{@code SERIAL_SEG_DBL_LINETO}</td>
0821 * <td>0x51</td>
0822 * <td>2 doubles</td>
0823 * <td>A {@link #lineTo lineTo} path segment follows.</td>
0824 * </tr>
0825 * <tr>
0826 * <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
0827 * <td>0x52</td>
0828 * <td>4 doubles</td>
0829 * <td>A {@link #curveTo curveTo} path segment follows.</td>
0830 * </tr>
0831 * <tr>
0832 * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
0833 * <td>0x53</td>
0834 * <td>6 doubles</td>
0835 * <td>A {@link #curveTo curveTo} path segment follows.</td>
0836 * </tr>
0837 * <tr>
0838 * <td>{@code SERIAL_SEG_CLOSE}</td>
0839 * <td>0x60</td>
0840 * <td></td>
0841 * <td>A {@link #closePath closePath} path segment.</td>
0842 * </tr>
0843 * <tr>
0844 * <td>{@code SERIAL_PATH_END}</td>
0845 * <td>0x61</td>
0846 * <td></td>
0847 * <td>There are no more path segments following.</td>
0848 * </table>
0849 *
0850 * @since 1.6
0851 */
0852 private void writeObject(java.io.ObjectOutputStream s)
0853 throws java.io.IOException {
0854 super .writeObject(s, false);
0855 }
0856
0857 /**
0858 * Reads the default serializable fields from the
0859 * {@code ObjectInputStream} followed by an explicit
0860 * serialization of the path segments stored in this
0861 * path.
0862 * <p>
0863 * There are no default serializable fields as of 1.6.
0864 * <p>
0865 * The serial data for this object is described in the
0866 * writeObject method.
0867 *
0868 * @since 1.6
0869 */
0870 private void readObject(java.io.ObjectInputStream s)
0871 throws java.lang.ClassNotFoundException,
0872 java.io.IOException {
0873 super .readObject(s, false);
0874 }
0875
0876 static class CopyIterator extends Path2D.Iterator {
0877 float floatCoords[];
0878
0879 CopyIterator(Path2D.Float p2df) {
0880 super (p2df);
0881 this .floatCoords = p2df.floatCoords;
0882 }
0883
0884 public int currentSegment(float[] coords) {
0885 int type = path.pointTypes[typeIdx];
0886 int numCoords = curvecoords[type];
0887 if (numCoords > 0) {
0888 System.arraycopy(floatCoords, pointIdx, coords, 0,
0889 numCoords);
0890 }
0891 return type;
0892 }
0893
0894 public int currentSegment(double[] coords) {
0895 int type = path.pointTypes[typeIdx];
0896 int numCoords = curvecoords[type];
0897 if (numCoords > 0) {
0898 for (int i = 0; i < numCoords; i++) {
0899 coords[i] = floatCoords[pointIdx + i];
0900 }
0901 }
0902 return type;
0903 }
0904 }
0905
0906 static class TxIterator extends Path2D.Iterator {
0907 float floatCoords[];
0908 AffineTransform affine;
0909
0910 TxIterator(Path2D.Float p2df, AffineTransform at) {
0911 super (p2df);
0912 this .floatCoords = p2df.floatCoords;
0913 this .affine = at;
0914 }
0915
0916 public int currentSegment(float[] coords) {
0917 int type = path.pointTypes[typeIdx];
0918 int numCoords = curvecoords[type];
0919 if (numCoords > 0) {
0920 affine.transform(floatCoords, pointIdx, coords, 0,
0921 numCoords / 2);
0922 }
0923 return type;
0924 }
0925
0926 public int currentSegment(double[] coords) {
0927 int type = path.pointTypes[typeIdx];
0928 int numCoords = curvecoords[type];
0929 if (numCoords > 0) {
0930 affine.transform(floatCoords, pointIdx, coords, 0,
0931 numCoords / 2);
0932 }
0933 return type;
0934 }
0935 }
0936
0937 }
0938
0939 /**
0940 * The {@code Double} class defines a geometric path with
0941 * coordinates stored in double precision floating point.
0942 *
0943 * @since 1.6
0944 */
0945 public static class Double extends Path2D implements Serializable {
0946 transient double doubleCoords[];
0947
0948 /**
0949 * Constructs a new empty double precision {@code Path2D} object
0950 * with a default winding rule of {@link #WIND_NON_ZERO}.
0951 *
0952 * @since 1.6
0953 */
0954 public Double() {
0955 this (WIND_NON_ZERO, INIT_SIZE);
0956 }
0957
0958 /**
0959 * Constructs a new empty double precision {@code Path2D} object
0960 * with the specified winding rule to control operations that
0961 * require the interior of the path to be defined.
0962 *
0963 * @param rule the winding rule
0964 * @see #WIND_EVEN_ODD
0965 * @see #WIND_NON_ZERO
0966 * @since 1.6
0967 */
0968 public Double(int rule) {
0969 this (rule, INIT_SIZE);
0970 }
0971
0972 /**
0973 * Constructs a new empty double precision {@code Path2D} object
0974 * with the specified winding rule and the specified initial
0975 * capacity to store path segments.
0976 * This number is an initial guess as to how many path segments
0977 * are in the path, but the storage is expanded as needed to store
0978 * whatever path segments are added to this path.
0979 *
0980 * @param rule the winding rule
0981 * @param initialCapacity the estimate for the number of path segments
0982 * in the path
0983 * @see #WIND_EVEN_ODD
0984 * @see #WIND_NON_ZERO
0985 * @since 1.6
0986 */
0987 public Double(int rule, int initialCapacity) {
0988 super (rule, initialCapacity);
0989 doubleCoords = new double[initialCapacity * 2];
0990 }
0991
0992 /**
0993 * Constructs a new double precision {@code Path2D} object
0994 * from an arbitrary {@link Shape} object.
0995 * All of the initial geometry and the winding rule for this path are
0996 * taken from the specified {@code Shape} object.
0997 *
0998 * @param s the specified {@code Shape} object
0999 * @since 1.6
1000 */
1001 public Double(Shape s) {
1002 this (s, null);
1003 }
1004
1005 /**
1006 * Constructs a new double precision {@code Path2D} object
1007 * from an arbitrary {@link Shape} object, transformed by an
1008 * {@link AffineTransform} object.
1009 * All of the initial geometry and the winding rule for this path are
1010 * taken from the specified {@code Shape} object and transformed
1011 * by the specified {@code AffineTransform} object.
1012 *
1013 * @param s the specified {@code Shape} object
1014 * @param at the specified {@code AffineTransform} object
1015 * @since 1.6
1016 */
1017 public Double(Shape s, AffineTransform at) {
1018 if (s instanceof Path2D) {
1019 Path2D p2d = (Path2D) s;
1020 setWindingRule(p2d.windingRule);
1021 this .numTypes = p2d.numTypes;
1022 this .pointTypes = Arrays.copyOf(p2d.pointTypes,
1023 p2d.pointTypes.length);
1024 this .numCoords = p2d.numCoords;
1025 this .doubleCoords = p2d.cloneCoordsDouble(at);
1026 } else {
1027 PathIterator pi = s.getPathIterator(at);
1028 setWindingRule(pi.getWindingRule());
1029 this .pointTypes = new byte[INIT_SIZE];
1030 this .doubleCoords = new double[INIT_SIZE * 2];
1031 append(pi, false);
1032 }
1033 }
1034
1035 float[] cloneCoordsFloat(AffineTransform at) {
1036 float ret[] = new float[doubleCoords.length];
1037 if (at == null) {
1038 for (int i = 0; i < numCoords; i++) {
1039 ret[i] = (float) doubleCoords[i];
1040 }
1041 } else {
1042 at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
1043 }
1044 return ret;
1045 }
1046
1047 double[] cloneCoordsDouble(AffineTransform at) {
1048 double ret[];
1049 if (at == null) {
1050 ret = Arrays.copyOf(this .doubleCoords,
1051 this .doubleCoords.length);
1052 } else {
1053 ret = new double[doubleCoords.length];
1054 at.transform(doubleCoords, 0, ret, 0, numCoords / 2);
1055 }
1056 return ret;
1057 }
1058
1059 void append(float x, float y) {
1060 doubleCoords[numCoords++] = x;
1061 doubleCoords[numCoords++] = y;
1062 }
1063
1064 void append(double x, double y) {
1065 doubleCoords[numCoords++] = x;
1066 doubleCoords[numCoords++] = y;
1067 }
1068
1069 Point2D getPoint(int coordindex) {
1070 return new Point2D.Double(doubleCoords[coordindex],
1071 doubleCoords[coordindex + 1]);
1072 }
1073
1074 void needRoom(boolean needMove, int newCoords) {
1075 if (needMove && numTypes == 0) {
1076 throw new IllegalPathStateException(
1077 "missing initial moveto "
1078 + "in path definition");
1079 }
1080 int size = pointTypes.length;
1081 if (numTypes >= size) {
1082 int grow = size;
1083 if (grow > EXPAND_MAX) {
1084 grow = EXPAND_MAX;
1085 }
1086 pointTypes = Arrays.copyOf(pointTypes, size + grow);
1087 }
1088 size = doubleCoords.length;
1089 if (numCoords + newCoords > size) {
1090 int grow = size;
1091 if (grow > EXPAND_MAX * 2) {
1092 grow = EXPAND_MAX * 2;
1093 }
1094 if (grow < newCoords) {
1095 grow = newCoords;
1096 }
1097 doubleCoords = Arrays.copyOf(doubleCoords, size + grow);
1098 }
1099 }
1100
1101 /**
1102 * {@inheritDoc}
1103 * @since 1.6
1104 */
1105 public final synchronized void moveTo(double x, double y) {
1106 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
1107 doubleCoords[numCoords - 2] = x;
1108 doubleCoords[numCoords - 1] = y;
1109 } else {
1110 needRoom(false, 2);
1111 pointTypes[numTypes++] = SEG_MOVETO;
1112 doubleCoords[numCoords++] = x;
1113 doubleCoords[numCoords++] = y;
1114 }
1115 }
1116
1117 /**
1118 * {@inheritDoc}
1119 * @since 1.6
1120 */
1121 public final synchronized void lineTo(double x, double y) {
1122 needRoom(true, 2);
1123 pointTypes[numTypes++] = SEG_LINETO;
1124 doubleCoords[numCoords++] = x;
1125 doubleCoords[numCoords++] = y;
1126 }
1127
1128 /**
1129 * {@inheritDoc}
1130 * @since 1.6
1131 */
1132 public final synchronized void quadTo(double x1, double y1,
1133 double x2, double y2) {
1134 needRoom(true, 4);
1135 pointTypes[numTypes++] = SEG_QUADTO;
1136 doubleCoords[numCoords++] = x1;
1137 doubleCoords[numCoords++] = y1;
1138 doubleCoords[numCoords++] = x2;
1139 doubleCoords[numCoords++] = y2;
1140 }
1141
1142 /**
1143 * {@inheritDoc}
1144 * @since 1.6
1145 */
1146 public final synchronized void curveTo(double x1, double y1,
1147 double x2, double y2, double x3, double y3) {
1148 needRoom(true, 6);
1149 pointTypes[numTypes++] = SEG_CUBICTO;
1150 doubleCoords[numCoords++] = x1;
1151 doubleCoords[numCoords++] = y1;
1152 doubleCoords[numCoords++] = x2;
1153 doubleCoords[numCoords++] = y2;
1154 doubleCoords[numCoords++] = x3;
1155 doubleCoords[numCoords++] = y3;
1156 }
1157
1158 int pointCrossings(double px, double py) {
1159 double movx, movy, curx, cury, endx, endy;
1160 double coords[] = doubleCoords;
1161 curx = movx = coords[0];
1162 cury = movy = coords[1];
1163 int crossings = 0;
1164 int ci = 2;
1165 for (int i = 1; i < numTypes; i++) {
1166 switch (pointTypes[i]) {
1167 case PathIterator.SEG_MOVETO:
1168 if (cury != movy) {
1169 crossings += Curve.pointCrossingsForLine(px,
1170 py, curx, cury, movx, movy);
1171 }
1172 movx = curx = coords[ci++];
1173 movy = cury = coords[ci++];
1174 break;
1175 case PathIterator.SEG_LINETO:
1176 crossings += Curve.pointCrossingsForLine(px, py,
1177 curx, cury, endx = coords[ci++],
1178 endy = coords[ci++]);
1179 curx = endx;
1180 cury = endy;
1181 break;
1182 case PathIterator.SEG_QUADTO:
1183 crossings += Curve
1184 .pointCrossingsForQuad(px, py, curx, cury,
1185 coords[ci++], coords[ci++],
1186 endx = coords[ci++],
1187 endy = coords[ci++], 0);
1188 curx = endx;
1189 cury = endy;
1190 break;
1191 case PathIterator.SEG_CUBICTO:
1192 crossings += Curve
1193 .pointCrossingsForCubic(px, py, curx, cury,
1194 coords[ci++], coords[ci++],
1195 coords[ci++], coords[ci++],
1196 endx = coords[ci++],
1197 endy = coords[ci++], 0);
1198 curx = endx;
1199 cury = endy;
1200 break;
1201 case PathIterator.SEG_CLOSE:
1202 if (cury != movy) {
1203 crossings += Curve.pointCrossingsForLine(px,
1204 py, curx, cury, movx, movy);
1205 }
1206 curx = movx;
1207 cury = movy;
1208 break;
1209 }
1210 }
1211 if (cury != movy) {
1212 crossings += Curve.pointCrossingsForLine(px, py, curx,
1213 cury, movx, movy);
1214 }
1215 return crossings;
1216 }
1217
1218 int rectCrossings(double rxmin, double rymin, double rxmax,
1219 double rymax) {
1220 double coords[] = doubleCoords;
1221 double curx, cury, movx, movy, endx, endy;
1222 curx = movx = coords[0];
1223 cury = movy = coords[1];
1224 int crossings = 0;
1225 int ci = 2;
1226 for (int i = 1; crossings != Curve.RECT_INTERSECTS
1227 && i < numTypes; i++) {
1228 switch (pointTypes[i]) {
1229 case PathIterator.SEG_MOVETO:
1230 if (curx != movx || cury != movy) {
1231 crossings = Curve.rectCrossingsForLine(
1232 crossings, rxmin, rymin, rxmax, rymax,
1233 curx, cury, movx, movy);
1234 }
1235 // Count should always be a multiple of 2 here.
1236 // assert((crossings & 1) != 0);
1237 movx = curx = coords[ci++];
1238 movy = cury = coords[ci++];
1239 break;
1240 case PathIterator.SEG_LINETO:
1241 endx = coords[ci++];
1242 endy = coords[ci++];
1243 crossings = Curve.rectCrossingsForLine(crossings,
1244 rxmin, rymin, rxmax, rymax, curx, cury,
1245 endx, endy);
1246 curx = endx;
1247 cury = endy;
1248 break;
1249 case PathIterator.SEG_QUADTO:
1250 crossings = Curve
1251 .rectCrossingsForQuad(crossings, rxmin,
1252 rymin, rxmax, rymax, curx, cury,
1253 coords[ci++], coords[ci++],
1254 endx = coords[ci++],
1255 endy = coords[ci++], 0);
1256 curx = endx;
1257 cury = endy;
1258 break;
1259 case PathIterator.SEG_CUBICTO:
1260 crossings = Curve.rectCrossingsForCubic(crossings,
1261 rxmin, rymin, rxmax, rymax, curx, cury,
1262 coords[ci++], coords[ci++], coords[ci++],
1263 coords[ci++], endx = coords[ci++],
1264 endy = coords[ci++], 0);
1265 curx = endx;
1266 cury = endy;
1267 break;
1268 case PathIterator.SEG_CLOSE:
1269 if (curx != movx || cury != movy) {
1270 crossings = Curve.rectCrossingsForLine(
1271 crossings, rxmin, rymin, rxmax, rymax,
1272 curx, cury, movx, movy);
1273 }
1274 curx = movx;
1275 cury = movy;
1276 // Count should always be a multiple of 2 here.
1277 // assert((crossings & 1) != 0);
1278 break;
1279 }
1280 }
1281 if (crossings != Curve.RECT_INTERSECTS
1282 && (curx != movx || cury != movy)) {
1283 crossings = Curve.rectCrossingsForLine(crossings,
1284 rxmin, rymin, rxmax, rymax, curx, cury, movx,
1285 movy);
1286 }
1287 // Count should always be a multiple of 2 here.
1288 // assert((crossings & 1) != 0);
1289 return crossings;
1290 }
1291
1292 /**
1293 * {@inheritDoc}
1294 * @since 1.6
1295 */
1296 public final void append(PathIterator pi, boolean connect) {
1297 double coords[] = new double[6];
1298 while (!pi.isDone()) {
1299 switch (pi.currentSegment(coords)) {
1300 case SEG_MOVETO:
1301 if (!connect || numTypes < 1 || numCoords < 1) {
1302 moveTo(coords[0], coords[1]);
1303 break;
1304 }
1305 if (pointTypes[numTypes - 1] != SEG_CLOSE
1306 && doubleCoords[numCoords - 2] == coords[0]
1307 && doubleCoords[numCoords - 1] == coords[1]) {
1308 // Collapse out initial moveto/lineto
1309 break;
1310 }
1311 // NO BREAK;
1312 case SEG_LINETO:
1313 lineTo(coords[0], coords[1]);
1314 break;
1315 case SEG_QUADTO:
1316 quadTo(coords[0], coords[1], coords[2], coords[3]);
1317 break;
1318 case SEG_CUBICTO:
1319 curveTo(coords[0], coords[1], coords[2], coords[3],
1320 coords[4], coords[5]);
1321 break;
1322 case SEG_CLOSE:
1323 closePath();
1324 break;
1325 }
1326 pi.next();
1327 connect = false;
1328 }
1329 }
1330
1331 /**
1332 * {@inheritDoc}
1333 * @since 1.6
1334 */
1335 public final void transform(AffineTransform at) {
1336 at.transform(doubleCoords, 0, doubleCoords, 0,
1337 numCoords / 2);
1338 }
1339
1340 /**
1341 * {@inheritDoc}
1342 * @since 1.6
1343 */
1344 public final synchronized Rectangle2D getBounds2D() {
1345 double x1, y1, x2, y2;
1346 int i = numCoords;
1347 if (i > 0) {
1348 y1 = y2 = doubleCoords[--i];
1349 x1 = x2 = doubleCoords[--i];
1350 while (i > 0) {
1351 double y = doubleCoords[--i];
1352 double x = doubleCoords[--i];
1353 if (x < x1)
1354 x1 = x;
1355 if (y < y1)
1356 y1 = y;
1357 if (x > x2)
1358 x2 = x;
1359 if (y > y2)
1360 y2 = y;
1361 }
1362 } else {
1363 x1 = y1 = x2 = y2 = 0.0;
1364 }
1365 return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
1366 }
1367
1368 /**
1369 * {@inheritDoc}
1370 * <p>
1371 * The iterator for this class is not multi-threaded safe,
1372 * which means that the {@code Path2D} class does not
1373 * guarantee that modifications to the geometry of this
1374 * {@code Path2D} object do not affect any iterations of
1375 * that geometry that are already in process.
1376 *
1377 * @param at an {@code AffineTransform}
1378 * @return a new {@code PathIterator} that iterates along the boundary
1379 * of this {@code Shape} and provides access to the geometry
1380 * of this {@code Shape}'s outline
1381 * @since 1.6
1382 */
1383 public PathIterator getPathIterator(AffineTransform at) {
1384 if (at == null) {
1385 return new CopyIterator(this );
1386 } else {
1387 return new TxIterator(this , at);
1388 }
1389 }
1390
1391 /**
1392 * Creates a new object of the same class as this object.
1393 *
1394 * @return a clone of this instance.
1395 * @exception OutOfMemoryError if there is not enough memory.
1396 * @see java.lang.Cloneable
1397 * @since 1.6
1398 */
1399 public final Object clone() {
1400 // Note: It would be nice to have this return Path2D
1401 // but one of our subclasses (GeneralPath) needs to
1402 // offer "public Object clone()" for backwards
1403 // compatibility so we cannot restrict it further.
1404 // REMIND: Can we do both somehow?
1405 return new Path2D.Double(this );
1406 }
1407
1408 /*
1409 * JDK 1.6 serialVersionUID
1410 */
1411 private static final long serialVersionUID = 1826762518450014216L;
1412
1413 /**
1414 * Writes the default serializable fields to the
1415 * {@code ObjectOutputStream} followed by an explicit
1416 * serialization of the path segments stored in this
1417 * path.
1418 *
1419 * @serialData
1420 * <a name="Path2DSerialData"><!-- --></a>
1421 * <ol>
1422 * <li>The default serializable fields.
1423 * There are no default serializable fields as of 1.6.
1424 * <li>followed by
1425 * a byte indicating the storage type of the original object
1426 * as a hint (SERIAL_STORAGE_DBL_ARRAY)
1427 * <li>followed by
1428 * an integer indicating the number of path segments to follow (NP)
1429 * or -1 to indicate an unknown number of path segments follows
1430 * <li>followed by
1431 * an integer indicating the total number of coordinates to follow (NC)
1432 * or -1 to indicate an unknown number of coordinates follows
1433 * (NC should always be even since coordinates always appear in pairs
1434 * representing an x,y pair)
1435 * <li>followed by
1436 * a byte indicating the winding rule
1437 * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or
1438 * {@link #WIND_NON_ZERO WIND_NON_ZERO})
1439 * <li>followed by
1440 * NP (or unlimited if NP < 0) sets of values consisting of
1441 * a single byte indicating a path segment type
1442 * followed by one or more pairs of float or double
1443 * values representing the coordinates of the path segment
1444 * <li>followed by
1445 * a byte indicating the end of the path (SERIAL_PATH_END).
1446 * </ol>
1447 * <p>
1448 * The following byte value constants are used in the serialized form
1449 * of {@code Path2D} objects:
1450 * <table>
1451 * <tr>
1452 * <th>Constant Name</th>
1453 * <th>Byte Value</th>
1454 * <th>Followed by</th>
1455 * <th>Description</th>
1456 * </tr>
1457 * <tr>
1458 * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td>
1459 * <td>0x30</td>
1460 * <td></td>
1461 * <td>A hint that the original {@code Path2D} object stored
1462 * the coordinates in a Java array of floats.</td>
1463 * </tr>
1464 * <tr>
1465 * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td>
1466 * <td>0x31</td>
1467 * <td></td>
1468 * <td>A hint that the original {@code Path2D} object stored
1469 * the coordinates in a Java array of doubles.</td>
1470 * </tr>
1471 * <tr>
1472 * <td>{@code SERIAL_SEG_FLT_MOVETO}</td>
1473 * <td>0x40</td>
1474 * <td>2 floats</td>
1475 * <td>A {@link #moveTo moveTo} path segment follows.</td>
1476 * </tr>
1477 * <tr>
1478 * <td>{@code SERIAL_SEG_FLT_LINETO}</td>
1479 * <td>0x41</td>
1480 * <td>2 floats</td>
1481 * <td>A {@link #lineTo lineTo} path segment follows.</td>
1482 * </tr>
1483 * <tr>
1484 * <td>{@code SERIAL_SEG_FLT_QUADTO}</td>
1485 * <td>0x42</td>
1486 * <td>4 floats</td>
1487 * <td>A {@link #quadTo quadTo} path segment follows.</td>
1488 * </tr>
1489 * <tr>
1490 * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td>
1491 * <td>0x43</td>
1492 * <td>6 floats</td>
1493 * <td>A {@link #curveTo curveTo} path segment follows.</td>
1494 * </tr>
1495 * <tr>
1496 * <td>{@code SERIAL_SEG_DBL_MOVETO}</td>
1497 * <td>0x50</td>
1498 * <td>2 doubles</td>
1499 * <td>A {@link #moveTo moveTo} path segment follows.</td>
1500 * </tr>
1501 * <tr>
1502 * <td>{@code SERIAL_SEG_DBL_LINETO}</td>
1503 * <td>0x51</td>
1504 * <td>2 doubles</td>
1505 * <td>A {@link #lineTo lineTo} path segment follows.</td>
1506 * </tr>
1507 * <tr>
1508 * <td>{@code SERIAL_SEG_DBL_QUADTO}</td>
1509 * <td>0x52</td>
1510 * <td>4 doubles</td>
1511 * <td>A {@link #curveTo curveTo} path segment follows.</td>
1512 * </tr>
1513 * <tr>
1514 * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td>
1515 * <td>0x53</td>
1516 * <td>6 doubles</td>
1517 * <td>A {@link #curveTo curveTo} path segment follows.</td>
1518 * </tr>
1519 * <tr>
1520 * <td>{@code SERIAL_SEG_CLOSE}</td>
1521 * <td>0x60</td>
1522 * <td></td>
1523 * <td>A {@link #closePath closePath} path segment.</td>
1524 * </tr>
1525 * <tr>
1526 * <td>{@code SERIAL_PATH_END}</td>
1527 * <td>0x61</td>
1528 * <td></td>
1529 * <td>There are no more path segments following.</td>
1530 * </table>
1531 *
1532 * @since 1.6
1533 */
1534 private void writeObject(java.io.ObjectOutputStream s)
1535 throws java.io.IOException {
1536 super .writeObject(s, true);
1537 }
1538
1539 /**
1540 * Reads the default serializable fields from the
1541 * {@code ObjectInputStream} followed by an explicit
1542 * serialization of the path segments stored in this
1543 * path.
1544 * <p>
1545 * There are no default serializable fields as of 1.6.
1546 * <p>
1547 * The serial data for this object is described in the
1548 * writeObject method.
1549 *
1550 * @since 1.6
1551 */
1552 private void readObject(java.io.ObjectInputStream s)
1553 throws java.lang.ClassNotFoundException,
1554 java.io.IOException {
1555 super .readObject(s, true);
1556 }
1557
1558 static class CopyIterator extends Path2D.Iterator {
1559 double doubleCoords[];
1560
1561 CopyIterator(Path2D.Double p2dd) {
1562 super (p2dd);
1563 this .doubleCoords = p2dd.doubleCoords;
1564 }
1565
1566 public int currentSegment(float[] coords) {
1567 int type = path.pointTypes[typeIdx];
1568 int numCoords = curvecoords[type];
1569 if (numCoords > 0) {
1570 for (int i = 0; i < numCoords; i++) {
1571 coords[i] = (float) doubleCoords[pointIdx + i];
1572 }
1573 }
1574 return type;
1575 }
1576
1577 public int currentSegment(double[] coords) {
1578 int type = path.pointTypes[typeIdx];
1579 int numCoords = curvecoords[type];
1580 if (numCoords > 0) {
1581 System.arraycopy(doubleCoords, pointIdx, coords, 0,
1582 numCoords);
1583 }
1584 return type;
1585 }
1586 }
1587
1588 static class TxIterator extends Path2D.Iterator {
1589 double doubleCoords[];
1590 AffineTransform affine;
1591
1592 TxIterator(Path2D.Double p2dd, AffineTransform at) {
1593 super (p2dd);
1594 this .doubleCoords = p2dd.doubleCoords;
1595 this .affine = at;
1596 }
1597
1598 public int currentSegment(float[] coords) {
1599 int type = path.pointTypes[typeIdx];
1600 int numCoords = curvecoords[type];
1601 if (numCoords > 0) {
1602 affine.transform(doubleCoords, pointIdx, coords, 0,
1603 numCoords / 2);
1604 }
1605 return type;
1606 }
1607
1608 public int currentSegment(double[] coords) {
1609 int type = path.pointTypes[typeIdx];
1610 int numCoords = curvecoords[type];
1611 if (numCoords > 0) {
1612 affine.transform(doubleCoords, pointIdx, coords, 0,
1613 numCoords / 2);
1614 }
1615 return type;
1616 }
1617 }
1618 }
1619
1620 /**
1621 * Adds a point to the path by moving to the specified
1622 * coordinates specified in double precision.
1623 *
1624 * @param x the specified X coordinate
1625 * @param y the specified Y coordinate
1626 * @since 1.6
1627 */
1628 public abstract void moveTo(double x, double y);
1629
1630 /**
1631 * Adds a point to the path by drawing a straight line from the
1632 * current coordinates to the new specified coordinates
1633 * specified in double precision.
1634 *
1635 * @param x the specified X coordinate
1636 * @param y the specified Y coordinate
1637 * @since 1.6
1638 */
1639 public abstract void lineTo(double x, double y);
1640
1641 /**
1642 * Adds a curved segment, defined by two new points, to the path by
1643 * drawing a Quadratic curve that intersects both the current
1644 * coordinates and the specified coordinates {@code (x2,y2)},
1645 * using the specified point {@code (x1,y1)} as a quadratic
1646 * parametric control point.
1647 * All coordinates are specified in double precision.
1648 *
1649 * @param x1 the X coordinate of the quadratic control point
1650 * @param y1 the Y coordinate of the quadratic control point
1651 * @param x2 the X coordinate of the final end point
1652 * @param y2 the Y coordinate of the final end point
1653 * @since 1.6
1654 */
1655 public abstract void quadTo(double x1, double y1, double x2,
1656 double y2);
1657
1658 /**
1659 * Adds a curved segment, defined by three new points, to the path by
1660 * drawing a Bézier curve that intersects both the current
1661 * coordinates and the specified coordinates {@code (x3,y3)},
1662 * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
1663 * Bézier control points.
1664 * All coordinates are specified in double precision.
1665 *
1666 * @param x1 the X coordinate of the first Bézier control point
1667 * @param y1 the Y coordinate of the first Bézier control point
1668 * @param x2 the X coordinate of the second Bézier control point
1669 * @param y2 the Y coordinate of the second Bézier control point
1670 * @param x3 the X coordinate of the final end point
1671 * @param y3 the Y coordinate of the final end point
1672 * @since 1.6
1673 */
1674 public abstract void curveTo(double x1, double y1, double x2,
1675 double y2, double x3, double y3);
1676
1677 /**
1678 * Closes the current subpath by drawing a straight line back to
1679 * the coordinates of the last {@code moveTo}. If the path is already
1680 * closed then this method has no effect.
1681 *
1682 * @since 1.6
1683 */
1684 public final synchronized void closePath() {
1685 if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
1686 needRoom(true, 0);
1687 pointTypes[numTypes++] = SEG_CLOSE;
1688 }
1689 }
1690
1691 /**
1692 * Appends the geometry of the specified {@code Shape} object to the
1693 * path, possibly connecting the new geometry to the existing path
1694 * segments with a line segment.
1695 * If the {@code connect} parameter is {@code true} and the
1696 * path is not empty then any initial {@code moveTo} in the
1697 * geometry of the appended {@code Shape}
1698 * is turned into a {@code lineTo} segment.
1699 * If the destination coordinates of such a connecting {@code lineTo}
1700 * segment match the ending coordinates of a currently open
1701 * subpath then the segment is omitted as superfluous.
1702 * The winding rule of the specified {@code Shape} is ignored
1703 * and the appended geometry is governed by the winding
1704 * rule specified for this path.
1705 *
1706 * @param s the {@code Shape} whose geometry is appended
1707 * to this path
1708 * @param connect a boolean to control whether or not to turn an initial
1709 * {@code moveTo} segment into a {@code lineTo} segment
1710 * to connect the new geometry to the existing path
1711 * @since 1.6
1712 */
1713 public final void append(Shape s, boolean connect) {
1714 append(s.getPathIterator(null), connect);
1715 }
1716
1717 /**
1718 * Appends the geometry of the specified
1719 * {@link PathIterator} object
1720 * to the path, possibly connecting the new geometry to the existing
1721 * path segments with a line segment.
1722 * If the {@code connect} parameter is {@code true} and the
1723 * path is not empty then any initial {@code moveTo} in the
1724 * geometry of the appended {@code Shape} is turned into a
1725 * {@code lineTo} segment.
1726 * If the destination coordinates of such a connecting {@code lineTo}
1727 * segment match the ending coordinates of a currently open
1728 * subpath then the segment is omitted as superfluous.
1729 * The winding rule of the specified {@code Shape} is ignored
1730 * and the appended geometry is governed by the winding
1731 * rule specified for this path.
1732 *
1733 * @param pi the {@code PathIterator} whose geometry is appended to
1734 * this path
1735 * @param connect a boolean to control whether or not to turn an initial
1736 * {@code moveTo} segment into a {@code lineTo} segment
1737 * to connect the new geometry to the existing path
1738 * @since 1.6
1739 */
1740 public abstract void append(PathIterator pi, boolean connect);
1741
1742 /**
1743 * Returns the fill style winding rule.
1744 *
1745 * @return an integer representing the current winding rule.
1746 * @see #WIND_EVEN_ODD
1747 * @see #WIND_NON_ZERO
1748 * @see #setWindingRule
1749 * @since 1.6
1750 */
1751 public final synchronized int getWindingRule() {
1752 return windingRule;
1753 }
1754
1755 /**
1756 * Sets the winding rule for this path to the specified value.
1757 *
1758 * @param rule an integer representing the specified
1759 * winding rule
1760 * @exception IllegalArgumentException if
1761 * {@code rule} is not either
1762 * {@link #WIND_EVEN_ODD} or
1763 * {@link #WIND_NON_ZERO}
1764 * @see #getWindingRule
1765 * @since 1.6
1766 */
1767 public final void setWindingRule(int rule) {
1768 if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
1769 throw new IllegalArgumentException("winding rule must be "
1770 + "WIND_EVEN_ODD or " + "WIND_NON_ZERO");
1771 }
1772 windingRule = rule;
1773 }
1774
1775 /**
1776 * Returns the coordinates most recently added to the end of the path
1777 * as a {@link Point2D} object.
1778 *
1779 * @return a {@code Point2D} object containing the ending coordinates of
1780 * the path or {@code null} if there are no points in the path.
1781 * @since 1.6
1782 */
1783 public final synchronized Point2D getCurrentPoint() {
1784 int index = numCoords;
1785 if (numTypes < 1 || index < 1) {
1786 return null;
1787 }
1788 if (pointTypes[numTypes - 1] == SEG_CLOSE) {
1789 loop: for (int i = numTypes - 2; i > 0; i--) {
1790 switch (pointTypes[i]) {
1791 case SEG_MOVETO:
1792 break loop;
1793 case SEG_LINETO:
1794 index -= 2;
1795 break;
1796 case SEG_QUADTO:
1797 index -= 4;
1798 break;
1799 case SEG_CUBICTO:
1800 index -= 6;
1801 break;
1802 case SEG_CLOSE:
1803 break;
1804 }
1805 }
1806 }
1807 return getPoint(index - 2);
1808 }
1809
1810 /**
1811 * Resets the path to empty. The append position is set back to the
1812 * beginning of the path and all coordinates and point types are
1813 * forgotten.
1814 *
1815 * @since 1.6
1816 */
1817 public final synchronized void reset() {
1818 numTypes = numCoords = 0;
1819 }
1820
1821 /**
1822 * Transforms the geometry of this path using the specified
1823 * {@link AffineTransform}.
1824 * The geometry is transformed in place, which permanently changes the
1825 * boundary defined by this object.
1826 *
1827 * @param at the {@code AffineTransform} used to transform the area
1828 * @since 1.6
1829 */
1830 public abstract void transform(AffineTransform at);
1831
1832 /**
1833 * Returns a new {@code Shape} representing a transformed version
1834 * of this {@code Path2D}.
1835 * Note that the exact type and coordinate precision of the return
1836 * value is not specified for this method.
1837 * The method will return a Shape that contains no less precision
1838 * for the transformed geometry than this {@code Path2D} currently
1839 * maintains, but it may contain no more precision either.
1840 * If the tradeoff of precision vs. storage size in the result is
1841 * important then the convenience constructors in the
1842 * {@link Path2D.Float#Path2D.Float(Shape, AffineTransform) Path2D.Float}
1843 * and
1844 * {@link Path2D.Double#Path2D.Double(Shape, AffineTransform) Path2D.Double}
1845 * subclasses should be used to make the choice explicit.
1846 *
1847 * @param at the {@code AffineTransform} used to transform a
1848 * new {@code Shape}.
1849 * @return a new {@code Shape}, transformed with the specified
1850 * {@code AffineTransform}.
1851 * @since 1.6
1852 */
1853 public final synchronized Shape createTransformedShape(
1854 AffineTransform at) {
1855 Path2D p2d = (Path2D) clone();
1856 if (at != null) {
1857 p2d.transform(at);
1858 }
1859 return p2d;
1860 }
1861
1862 /**
1863 * {@inheritDoc}
1864 * @since 1.6
1865 */
1866 public final Rectangle getBounds() {
1867 return getBounds2D().getBounds();
1868 }
1869
1870 /**
1871 * Tests if the specified coordinates are inside the closed
1872 * boundary of the specified {@link PathIterator}.
1873 * <p>
1874 * This method provides a basic facility for implementors of
1875 * the {@link Shape} interface to implement support for the
1876 * {@link Shape#contains(double, double)} method.
1877 *
1878 * @param pi the specified {@code PathIterator}
1879 * @param x the specified X coordinate
1880 * @param y the specified Y coordinate
1881 * @return {@code true} if the specified coordinates are inside the
1882 * specified {@code PathIterator}; {@code false} otherwise
1883 * @since 1.6
1884 */
1885 public static boolean contains(PathIterator pi, double x, double y) {
1886 if (x * 0.0 + y * 0.0 == 0.0) {
1887 /* N * 0.0 is 0.0 only if N is finite.
1888 * Here we know that both x and y are finite.
1889 */
1890 int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 1);
1891 int cross = Curve.pointCrossingsForPath(pi, x, y);
1892 return ((cross & mask) != 0);
1893 } else {
1894 /* Either x or y was infinite or NaN.
1895 * A NaN always produces a negative response to any test
1896 * and Infinity values cannot be "inside" any path so
1897 * they should return false as well.
1898 */
1899 return false;
1900 }
1901 }
1902
1903 /**
1904 * Tests if the specified {@link Point2D} is inside the closed
1905 * boundary of the specified {@link PathIterator}.
1906 * <p>
1907 * This method provides a basic facility for implementors of
1908 * the {@link Shape} interface to implement support for the
1909 * {@link Shape#contains(Point2D)} method.
1910 *
1911 * @param pi the specified {@code PathIterator}
1912 * @param p the specified {@code Point2D}
1913 * @return {@code true} if the specified coordinates are inside the
1914 * specified {@code PathIterator}; {@code false} otherwise
1915 * @since 1.6
1916 */
1917 public static boolean contains(PathIterator pi, Point2D p) {
1918 return contains(pi, p.getX(), p.getY());
1919 }
1920
1921 /**
1922 * {@inheritDoc}
1923 * @since 1.6
1924 */
1925 public final boolean contains(double x, double y) {
1926 if (x * 0.0 + y * 0.0 == 0.0) {
1927 /* N * 0.0 is 0.0 only if N is finite.
1928 * Here we know that both x and y are finite.
1929 */
1930 if (numTypes < 2) {
1931 return false;
1932 }
1933 int mask = (windingRule == WIND_NON_ZERO ? -1 : 1);
1934 return ((pointCrossings(x, y) & mask) != 0);
1935 } else {
1936 /* Either x or y was infinite or NaN.
1937 * A NaN always produces a negative response to any test
1938 * and Infinity values cannot be "inside" any path so
1939 * they should return false as well.
1940 */
1941 return false;
1942 }
1943 }
1944
1945 /**
1946 * {@inheritDoc}
1947 * @since 1.6
1948 */
1949 public final boolean contains(Point2D p) {
1950 return contains(p.getX(), p.getY());
1951 }
1952
1953 /**
1954 * Tests if the specified rectangular area is entirely inside the
1955 * closed boundary of the specified {@link PathIterator}.
1956 * <p>
1957 * This method provides a basic facility for implementors of
1958 * the {@link Shape} interface to implement support for the
1959 * {@link Shape#contains(double, double, double, double)} method.
1960 * <p>
1961 * This method object may conservatively return false in
1962 * cases where the specified rectangular area intersects a
1963 * segment of the path, but that segment does not represent a
1964 * boundary between the interior and exterior of the path.
1965 * Such segments could lie entirely within the interior of the
1966 * path if they are part of a path with a {@link #WIND_NON_ZERO}
1967 * winding rule or if the segments are retraced in the reverse
1968 * direction such that the two sets of segments cancel each
1969 * other out without any exterior area falling between them.
1970 * To determine whether segments represent true boundaries of
1971 * the interior of the path would require extensive calculations
1972 * involving all of the segments of the path and the winding
1973 * rule and are thus beyond the scope of this implementation.
1974 *
1975 * @param pi the specified {@code PathIterator}
1976 * @param x the specified X coordinate
1977 * @param y the specified Y coordinate
1978 * @param w the width of the specified rectangular area
1979 * @param h the height of the specified rectangular area
1980 * @return {@code true} if the specified {@code PathIterator} contains
1981 * the specified rectangluar area; {@code false} otherwise.
1982 * @since 1.6
1983 */
1984 public static boolean contains(PathIterator pi, double x, double y,
1985 double w, double h) {
1986 if (java.lang.Double.isNaN(x + w)
1987 || java.lang.Double.isNaN(y + h)) {
1988 /* [xy]+[wh] is NaN if any of those values are NaN,
1989 * or if adding the two together would produce NaN
1990 * by virtue of adding opposing Infinte values.
1991 * Since we need to add them below, their sum must
1992 * not be NaN.
1993 * We return false because NaN always produces a
1994 * negative response to tests
1995 */
1996 return false;
1997 }
1998 if (w <= 0 || h <= 0) {
1999 return false;
2000 }
2001 int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
2002 int crossings = Curve.rectCrossingsForPath(pi, x, y, x + w, y
2003 + h);
2004 return (crossings != Curve.RECT_INTERSECTS && (crossings & mask) != 0);
2005 }
2006
2007 /**
2008 * Tests if the specified {@link Rectangle2D} is entirely inside the
2009 * closed boundary of the specified {@link PathIterator}.
2010 * <p>
2011 * This method provides a basic facility for implementors of
2012 * the {@link Shape} interface to implement support for the
2013 * {@link Shape#contains(Rectangle2D)} method.
2014 * <p>
2015 * This method object may conservatively return false in
2016 * cases where the specified rectangular area intersects a
2017 * segment of the path, but that segment does not represent a
2018 * boundary between the interior and exterior of the path.
2019 * Such segments could lie entirely within the interior of the
2020 * path if they are part of a path with a {@link #WIND_NON_ZERO}
2021 * winding rule or if the segments are retraced in the reverse
2022 * direction such that the two sets of segments cancel each
2023 * other out without any exterior area falling between them.
2024 * To determine whether segments represent true boundaries of
2025 * the interior of the path would require extensive calculations
2026 * involving all of the segments of the path and the winding
2027 * rule and are thus beyond the scope of this implementation.
2028 *
2029 * @param pi the specified {@code PathIterator}
2030 * @param r a specified {@code Rectangle2D}
2031 * @return {@code true} if the specified {@code PathIterator} contains
2032 * the specified {@code Rectangle2D}; {@code false} otherwise.
2033 * @since 1.6
2034 */
2035 public static boolean contains(PathIterator pi, Rectangle2D r) {
2036 return contains(pi, r.getX(), r.getY(), r.getWidth(), r
2037 .getHeight());
2038 }
2039
2040 /**
2041 * {@inheritDoc}
2042 * <p>
2043 * This method object may conservatively return false in
2044 * cases where the specified rectangular area intersects a
2045 * segment of the path, but that segment does not represent a
2046 * boundary between the interior and exterior of the path.
2047 * Such segments could lie entirely within the interior of the
2048 * path if they are part of a path with a {@link #WIND_NON_ZERO}
2049 * winding rule or if the segments are retraced in the reverse
2050 * direction such that the two sets of segments cancel each
2051 * other out without any exterior area falling between them.
2052 * To determine whether segments represent true boundaries of
2053 * the interior of the path would require extensive calculations
2054 * involving all of the segments of the path and the winding
2055 * rule and are thus beyond the scope of this implementation.
2056 *
2057 * @since 1.6
2058 */
2059 public final boolean contains(double x, double y, double w, double h) {
2060 if (java.lang.Double.isNaN(x + w)
2061 || java.lang.Double.isNaN(y + h)) {
2062 /* [xy]+[wh] is NaN if any of those values are NaN,
2063 * or if adding the two together would produce NaN
2064 * by virtue of adding opposing Infinte values.
2065 * Since we need to add them below, their sum must
2066 * not be NaN.
2067 * We return false because NaN always produces a
2068 * negative response to tests
2069 */
2070 return false;
2071 }
2072 if (w <= 0 || h <= 0) {
2073 return false;
2074 }
2075 int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
2076 int crossings = rectCrossings(x, y, x + w, y + h);
2077 return (crossings != Curve.RECT_INTERSECTS && (crossings & mask) != 0);
2078 }
2079
2080 /**
2081 * {@inheritDoc}
2082 * <p>
2083 * This method object may conservatively return false in
2084 * cases where the specified rectangular area intersects a
2085 * segment of the path, but that segment does not represent a
2086 * boundary between the interior and exterior of the path.
2087 * Such segments could lie entirely within the interior of the
2088 * path if they are part of a path with a {@link #WIND_NON_ZERO}
2089 * winding rule or if the segments are retraced in the reverse
2090 * direction such that the two sets of segments cancel each
2091 * other out without any exterior area falling between them.
2092 * To determine whether segments represent true boundaries of
2093 * the interior of the path would require extensive calculations
2094 * involving all of the segments of the path and the winding
2095 * rule and are thus beyond the scope of this implementation.
2096 *
2097 * @since 1.6
2098 */
2099 public final boolean contains(Rectangle2D r) {
2100 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
2101 }
2102
2103 /**
2104 * Tests if the interior of the specified {@link PathIterator}
2105 * intersects the interior of a specified set of rectangular
2106 * coordinates.
2107 * <p>
2108 * This method provides a basic facility for implementors of
2109 * the {@link Shape} interface to implement support for the
2110 * {@link Shape#intersects(double, double, double, double)} method.
2111 * <p>
2112 * This method object may conservatively return true in
2113 * cases where the specified rectangular area intersects a
2114 * segment of the path, but that segment does not represent a
2115 * boundary between the interior and exterior of the path.
2116 * Such a case may occur if some set of segments of the
2117 * path are retraced in the reverse direction such that the
2118 * two sets of segments cancel each other out without any
2119 * interior area between them.
2120 * To determine whether segments represent true boundaries of
2121 * the interior of the path would require extensive calculations
2122 * involving all of the segments of the path and the winding
2123 * rule and are thus beyond the scope of this implementation.
2124 *
2125 * @param pi the specified {@code PathIterator}
2126 * @param x the specified X coordinate
2127 * @param y the specified Y coordinate
2128 * @param w the width of the specified rectangular coordinates
2129 * @param h the height of the specified rectangular coordinates
2130 * @return {@code true} if the specified {@code PathIterator} and
2131 * the interior of the specified set of rectangular
2132 * coordinates intersect each other; {@code false} otherwise.
2133 * @since 1.6
2134 */
2135 public static boolean intersects(PathIterator pi, double x,
2136 double y, double w, double h) {
2137 if (java.lang.Double.isNaN(x + w)
2138 || java.lang.Double.isNaN(y + h)) {
2139 /* [xy]+[wh] is NaN if any of those values are NaN,
2140 * or if adding the two together would produce NaN
2141 * by virtue of adding opposing Infinte values.
2142 * Since we need to add them below, their sum must
2143 * not be NaN.
2144 * We return false because NaN always produces a
2145 * negative response to tests
2146 */
2147 return false;
2148 }
2149 if (w <= 0 || h <= 0) {
2150 return false;
2151 }
2152 int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
2153 int crossings = Curve.rectCrossingsForPath(pi, x, y, x + w, y
2154 + h);
2155 return (crossings == Curve.RECT_INTERSECTS || (crossings & mask) != 0);
2156 }
2157
2158 /**
2159 * Tests if the interior of the specified {@link PathIterator}
2160 * intersects the interior of a specified {@link Rectangle2D}.
2161 * <p>
2162 * This method provides a basic facility for implementors of
2163 * the {@link Shape} interface to implement support for the
2164 * {@link Shape#intersects(Rectangle2D)} method.
2165 * <p>
2166 * This method object may conservatively return true in
2167 * cases where the specified rectangular area intersects a
2168 * segment of the path, but that segment does not represent a
2169 * boundary between the interior and exterior of the path.
2170 * Such a case may occur if some set of segments of the
2171 * path are retraced in the reverse direction such that the
2172 * two sets of segments cancel each other out without any
2173 * interior area between them.
2174 * To determine whether segments represent true boundaries of
2175 * the interior of the path would require extensive calculations
2176 * involving all of the segments of the path and the winding
2177 * rule and are thus beyond the scope of this implementation.
2178 *
2179 * @param pi the specified {@code PathIterator}
2180 * @param r the specified {@code Rectangle2D}
2181 * @return {@code true} if the specified {@code PathIterator} and
2182 * the interior of the specified {@code Rectangle2D}
2183 * intersect each other; {@code false} otherwise.
2184 * @since 1.6
2185 */
2186 public static boolean intersects(PathIterator pi, Rectangle2D r) {
2187 return intersects(pi, r.getX(), r.getY(), r.getWidth(), r
2188 .getHeight());
2189 }
2190
2191 /**
2192 * {@inheritDoc}
2193 * <p>
2194 * This method object may conservatively return true in
2195 * cases where the specified rectangular area intersects a
2196 * segment of the path, but that segment does not represent a
2197 * boundary between the interior and exterior of the path.
2198 * Such a case may occur if some set of segments of the
2199 * path are retraced in the reverse direction such that the
2200 * two sets of segments cancel each other out without any
2201 * interior area between them.
2202 * To determine whether segments represent true boundaries of
2203 * the interior of the path would require extensive calculations
2204 * involving all of the segments of the path and the winding
2205 * rule and are thus beyond the scope of this implementation.
2206 *
2207 * @since 1.6
2208 */
2209 public final boolean intersects(double x, double y, double w,
2210 double h) {
2211 if (java.lang.Double.isNaN(x + w)
2212 || java.lang.Double.isNaN(y + h)) {
2213 /* [xy]+[wh] is NaN if any of those values are NaN,
2214 * or if adding the two together would produce NaN
2215 * by virtue of adding opposing Infinte values.
2216 * Since we need to add them below, their sum must
2217 * not be NaN.
2218 * We return false because NaN always produces a
2219 * negative response to tests
2220 */
2221 return false;
2222 }
2223 if (w <= 0 || h <= 0) {
2224 return false;
2225 }
2226 int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
2227 int crossings = rectCrossings(x, y, x + w, y + h);
2228 return (crossings == Curve.RECT_INTERSECTS || (crossings & mask) != 0);
2229 }
2230
2231 /**
2232 * {@inheritDoc}
2233 * <p>
2234 * This method object may conservatively return true in
2235 * cases where the specified rectangular area intersects a
2236 * segment of the path, but that segment does not represent a
2237 * boundary between the interior and exterior of the path.
2238 * Such a case may occur if some set of segments of the
2239 * path are retraced in the reverse direction such that the
2240 * two sets of segments cancel each other out without any
2241 * interior area between them.
2242 * To determine whether segments represent true boundaries of
2243 * the interior of the path would require extensive calculations
2244 * involving all of the segments of the path and the winding
2245 * rule and are thus beyond the scope of this implementation.
2246 *
2247 * @since 1.6
2248 */
2249 public final boolean intersects(Rectangle2D r) {
2250 return intersects(r.getX(), r.getY(), r.getWidth(), r
2251 .getHeight());
2252 }
2253
2254 /**
2255 * {@inheritDoc}
2256 * <p>
2257 * The iterator for this class is not multi-threaded safe,
2258 * which means that this {@code Path2D} class does not
2259 * guarantee that modifications to the geometry of this
2260 * {@code Path2D} object do not affect any iterations of
2261 * that geometry that are already in process.
2262 *
2263 * @since 1.6
2264 */
2265 public PathIterator getPathIterator(AffineTransform at,
2266 double flatness) {
2267 return new FlatteningPathIterator(getPathIterator(at), flatness);
2268 }
2269
2270 /**
2271 * Creates a new object of the same class as this object.
2272 *
2273 * @return a clone of this instance.
2274 * @exception OutOfMemoryError if there is not enough memory.
2275 * @see java.lang.Cloneable
2276 * @since 1.6
2277 */
2278 public abstract Object clone();
2279
2280 // Note: It would be nice to have this return Path2D
2281 // but one of our subclasses (GeneralPath) needs to
2282 // offer "public Object clone()" for backwards
2283 // compatibility so we cannot restrict it further.
2284 // REMIND: Can we do both somehow?
2285
2286 /*
2287 * Support fields and methods for serializing the subclasses.
2288 */
2289 private static final byte SERIAL_STORAGE_FLT_ARRAY = 0x30;
2290 private static final byte SERIAL_STORAGE_DBL_ARRAY = 0x31;
2291
2292 private static final byte SERIAL_SEG_FLT_MOVETO = 0x40;
2293 private static final byte SERIAL_SEG_FLT_LINETO = 0x41;
2294 private static final byte SERIAL_SEG_FLT_QUADTO = 0x42;
2295 private static final byte SERIAL_SEG_FLT_CUBICTO = 0x43;
2296
2297 private static final byte SERIAL_SEG_DBL_MOVETO = 0x50;
2298 private static final byte SERIAL_SEG_DBL_LINETO = 0x51;
2299 private static final byte SERIAL_SEG_DBL_QUADTO = 0x52;
2300 private static final byte SERIAL_SEG_DBL_CUBICTO = 0x53;
2301
2302 private static final byte SERIAL_SEG_CLOSE = 0x60;
2303 private static final byte SERIAL_PATH_END = 0x61;
2304
2305 final void writeObject(java.io.ObjectOutputStream s, boolean isdbl)
2306 throws java.io.IOException {
2307 s.defaultWriteObject();
2308
2309 float fCoords[];
2310 double dCoords[];
2311
2312 if (isdbl) {
2313 dCoords = ((Path2D.Double) this ).doubleCoords;
2314 fCoords = null;
2315 } else {
2316 fCoords = ((Path2D.Float) this ).floatCoords;
2317 dCoords = null;
2318 }
2319
2320 int numTypes = this .numTypes;
2321
2322 s.writeByte(isdbl ? SERIAL_STORAGE_DBL_ARRAY
2323 : SERIAL_STORAGE_FLT_ARRAY);
2324 s.writeInt(numTypes);
2325 s.writeInt(numCoords);
2326 s.writeByte((byte) windingRule);
2327
2328 int cindex = 0;
2329 for (int i = 0; i < numTypes; i++) {
2330 int npoints;
2331 byte serialtype;
2332 switch (pointTypes[i]) {
2333 case SEG_MOVETO:
2334 npoints = 1;
2335 serialtype = (isdbl ? SERIAL_SEG_DBL_MOVETO
2336 : SERIAL_SEG_FLT_MOVETO);
2337 break;
2338 case SEG_LINETO:
2339 npoints = 1;
2340 serialtype = (isdbl ? SERIAL_SEG_DBL_LINETO
2341 : SERIAL_SEG_FLT_LINETO);
2342 break;
2343 case SEG_QUADTO:
2344 npoints = 2;
2345 serialtype = (isdbl ? SERIAL_SEG_DBL_QUADTO
2346 : SERIAL_SEG_FLT_QUADTO);
2347 break;
2348 case SEG_CUBICTO:
2349 npoints = 3;
2350 serialtype = (isdbl ? SERIAL_SEG_DBL_CUBICTO
2351 : SERIAL_SEG_FLT_CUBICTO);
2352 break;
2353 case SEG_CLOSE:
2354 npoints = 0;
2355 serialtype = SERIAL_SEG_CLOSE;
2356 break;
2357
2358 default:
2359 // Should never happen
2360 throw new InternalError("unrecognized path type");
2361 }
2362 s.writeByte(serialtype);
2363 while (--npoints >= 0) {
2364 if (isdbl) {
2365 s.writeDouble(dCoords[cindex++]);
2366 s.writeDouble(dCoords[cindex++]);
2367 } else {
2368 s.writeFloat(fCoords[cindex++]);
2369 s.writeFloat(fCoords[cindex++]);
2370 }
2371 }
2372 }
2373 s.writeByte((byte) SERIAL_PATH_END);
2374 }
2375
2376 final void readObject(java.io.ObjectInputStream s, boolean storedbl)
2377 throws java.lang.ClassNotFoundException,
2378 java.io.IOException {
2379 s.defaultReadObject();
2380
2381 // The subclass calls this method with the storage type that
2382 // they want us to use (storedbl) so we ignore the storage
2383 // method hint from the stream.
2384 s.readByte();
2385 int nT = s.readInt();
2386 int nC = s.readInt();
2387 try {
2388 setWindingRule(s.readByte());
2389 } catch (IllegalArgumentException iae) {
2390 throw new java.io.InvalidObjectException(iae.getMessage());
2391 }
2392
2393 pointTypes = new byte[(nT < 0) ? INIT_SIZE : nT];
2394 if (nC < 0) {
2395 nC = INIT_SIZE * 2;
2396 }
2397 if (storedbl) {
2398 ((Path2D.Double) this ).doubleCoords = new double[nC];
2399 } else {
2400 ((Path2D.Float) this ).floatCoords = new float[nC];
2401 }
2402
2403 PATHDONE: for (int i = 0; nT < 0 || i < nT; i++) {
2404 boolean isdbl;
2405 int npoints;
2406 byte segtype;
2407
2408 byte serialtype = s.readByte();
2409 switch (serialtype) {
2410 case SERIAL_SEG_FLT_MOVETO:
2411 isdbl = false;
2412 npoints = 1;
2413 segtype = SEG_MOVETO;
2414 break;
2415 case SERIAL_SEG_FLT_LINETO:
2416 isdbl = false;
2417 npoints = 1;
2418 segtype = SEG_LINETO;
2419 break;
2420 case SERIAL_SEG_FLT_QUADTO:
2421 isdbl = false;
2422 npoints = 2;
2423 segtype = SEG_QUADTO;
2424 break;
2425 case SERIAL_SEG_FLT_CUBICTO:
2426 isdbl = false;
2427 npoints = 3;
2428 segtype = SEG_CUBICTO;
2429 break;
2430
2431 case SERIAL_SEG_DBL_MOVETO:
2432 isdbl = true;
2433 npoints = 1;
2434 segtype = SEG_MOVETO;
2435 break;
2436 case SERIAL_SEG_DBL_LINETO:
2437 isdbl = true;
2438 npoints = 1;
2439 segtype = SEG_LINETO;
2440 break;
2441 case SERIAL_SEG_DBL_QUADTO:
2442 isdbl = true;
2443 npoints = 2;
2444 segtype = SEG_QUADTO;
2445 break;
2446 case SERIAL_SEG_DBL_CUBICTO:
2447 isdbl = true;
2448 npoints = 3;
2449 segtype = SEG_CUBICTO;
2450 break;
2451
2452 case SERIAL_SEG_CLOSE:
2453 isdbl = false;
2454 npoints = 0;
2455 segtype = SEG_CLOSE;
2456 break;
2457
2458 case SERIAL_PATH_END:
2459 if (nT < 0) {
2460 break PATHDONE;
2461 }
2462 throw new StreamCorruptedException(
2463 "unexpected PATH_END");
2464
2465 default:
2466 throw new StreamCorruptedException(
2467 "unrecognized path type");
2468 }
2469 needRoom(segtype != SEG_MOVETO, npoints * 2);
2470 if (isdbl) {
2471 while (--npoints >= 0) {
2472 append(s.readDouble(), s.readDouble());
2473 }
2474 } else {
2475 while (--npoints >= 0) {
2476 append(s.readFloat(), s.readFloat());
2477 }
2478 }
2479 pointTypes[numTypes++] = segtype;
2480 }
2481 if (nT >= 0 && s.readByte() != SERIAL_PATH_END) {
2482 throw new StreamCorruptedException("missing PATH_END");
2483 }
2484 }
2485
2486 static abstract class Iterator implements PathIterator {
2487 int typeIdx;
2488 int pointIdx;
2489 Path2D path;
2490
2491 static final int curvecoords[] = { 2, 2, 4, 6, 0 };
2492
2493 Iterator(Path2D path) {
2494 this .path = path;
2495 }
2496
2497 public int getWindingRule() {
2498 return path.getWindingRule();
2499 }
2500
2501 public boolean isDone() {
2502 return (typeIdx >= path.numTypes);
2503 }
2504
2505 public void next() {
2506 int type = path.pointTypes[typeIdx++];
2507 pointIdx += curvecoords[type];
2508 }
2509 }
2510 }
|