1 /* 2 * Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt.geom; 27 28 import java.awt.Shape; 29 import java.awt.Rectangle; 30 import sun.awt.geom.Curve; 31 import java.io.Serializable; 32 import java.io.StreamCorruptedException; 33 import java.util.Arrays; 34 35 /** 36 * The {@code Path2D} class provides a simple, yet flexible 37 * shape which represents an arbitrary geometric path. 38 * It can fully represent any path which can be iterated by the 39 * {@link PathIterator} interface including all of its segment 40 * types and winding rules and it implements all of the 41 * basic hit testing methods of the {@link Shape} interface. 42 * <p> 43 * Use {@link Path2D.Float} when dealing with data that can be represented 44 * and used with floating point precision. Use {@link Path2D.Double} 45 * for data that requires the accuracy or range of double precision. 46 * <p> 47 * {@code Path2D} provides exactly those facilities required for 48 * basic construction and management of a geometric path and 49 * implementation of the above interfaces with little added 50 * interpretation. 51 * If it is useful to manipulate the interiors of closed 52 * geometric shapes beyond simple hit testing then the 53 * {@link Area} class provides additional capabilities 54 * specifically targeted at closed figures. 55 * While both classes nominally implement the {@code Shape} 56 * interface, they differ in purpose and together they provide 57 * two useful views of a geometric shape where {@code Path2D} 58 * deals primarily with a trajectory formed by path segments 59 * and {@code Area} deals more with interpretation and manipulation 60 * of enclosed regions of 2D geometric space. 61 * <p> 62 * The {@link PathIterator} interface has more detailed descriptions 63 * of the types of segments that make up a path and the winding rules 64 * that control how to determine which regions are inside or outside 65 * the path. 66 * 67 * @author Jim Graham 68 * @since 1.6 69 */ 70 public abstract class Path2D implements Shape, Cloneable { 71 /** 72 * An even-odd winding rule for determining the interior of 73 * a path. 74 * 75 * @see PathIterator#WIND_EVEN_ODD 76 * @since 1.6 77 */ 78 public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; 79 80 /** 81 * A non-zero winding rule for determining the interior of a 82 * path. 83 * 84 * @see PathIterator#WIND_NON_ZERO 85 * @since 1.6 86 */ 87 public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; 88 89 // For code simplicity, copy these constants to our namespace 90 // and cast them to byte constants for easy storage. 91 private static final byte SEG_MOVETO = (byte) PathIterator.SEG_MOVETO; 92 private static final byte SEG_LINETO = (byte) PathIterator.SEG_LINETO; 93 private static final byte SEG_QUADTO = (byte) PathIterator.SEG_QUADTO; 94 private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO; 95 private static final byte SEG_CLOSE = (byte) PathIterator.SEG_CLOSE; 96 97 transient byte[] pointTypes; 98 transient int numTypes; 99 transient int numCoords; 100 transient int windingRule; 101 102 static final int INIT_SIZE = 20; 103 static final int EXPAND_MAX = 500; 104 105 /** 106 * Constructs a new empty {@code Path2D} object. 107 * It is assumed that the package sibling subclass that is 108 * defaulting to this constructor will fill in all values. 109 * 110 * @since 1.6 111 */ 112 /* private protected */ 113 Path2D() { 114 } 115 116 /** 117 * Constructs a new {@code Path2D} object from the given 118 * specified initial values. 119 * This method is only intended for internal use and should 120 * not be made public if the other constructors for this class 121 * are ever exposed. 122 * 123 * @param rule the winding rule 124 * @param initialTypes the size to make the initial array to 125 * store the path segment types 126 * @since 1.6 127 */ 128 /* private protected */ 129 Path2D(int rule, int initialTypes) { 130 setWindingRule(rule); 131 this.pointTypes = new byte[initialTypes]; 132 } 133 134 abstract float[] cloneCoordsFloat(AffineTransform at); 135 abstract double[] cloneCoordsDouble(AffineTransform at); 136 abstract void append(float x, float y); 137 abstract void append(double x, double y); 138 abstract Point2D getPoint(int coordindex); 139 abstract void needRoom(boolean needMove, int newCoords); 140 abstract int pointCrossings(double px, double py); 141 abstract int rectCrossings(double rxmin, double rymin, 142 double rxmax, double rymax); 143 144 /** 145 * The {@code Float} class defines a geometric path with 146 * coordinates stored in single precision floating point. 147 * 148 * @since 1.6 149 */ 150 public static class Float extends Path2D implements Serializable { 151 transient float floatCoords[]; 152 153 /** 154 * Constructs a new empty single precision {@code Path2D} object 155 * with a default winding rule of {@link #WIND_NON_ZERO}. 156 * 157 * @since 1.6 158 */ 159 public Float() { 160 this(WIND_NON_ZERO, INIT_SIZE); 161 } 162 163 /** 164 * Constructs a new empty single precision {@code Path2D} object 165 * with the specified winding rule to control operations that 166 * require the interior of the path to be defined. 167 * 168 * @param rule the winding rule 169 * @see #WIND_EVEN_ODD 170 * @see #WIND_NON_ZERO 171 * @since 1.6 172 */ 173 public Float(int rule) { 174 this(rule, INIT_SIZE); 175 } 176 177 /** 178 * Constructs a new empty single precision {@code Path2D} object 179 * with the specified winding rule and the specified initial 180 * capacity to store path segments. 181 * This number is an initial guess as to how many path segments 182 * will be added to the path, but the storage is expanded as 183 * needed to store whatever path segments are added. 184 * 185 * @param rule the winding rule 186 * @param initialCapacity the estimate for the number of path segments 187 * in the path 188 * @see #WIND_EVEN_ODD 189 * @see #WIND_NON_ZERO 190 * @since 1.6 191 */ 192 public Float(int rule, int initialCapacity) { 193 super(rule, initialCapacity); 194 floatCoords = new float[initialCapacity * 2]; 195 } 196 197 /** 198 * Constructs a new single precision {@code Path2D} object 199 * from an arbitrary {@link Shape} object. 200 * All of the initial geometry and the winding rule for this path are 201 * taken from the specified {@code Shape} object. 202 * 203 * @param s the specified {@code Shape} object 204 * @since 1.6 205 */ 206 public Float(Shape s) { 207 this(s, null); 208 } 209 210 /** 211 * Constructs a new single precision {@code Path2D} object 212 * from an arbitrary {@link Shape} object, transformed by an 213 * {@link AffineTransform} object. 214 * All of the initial geometry and the winding rule for this path are 215 * taken from the specified {@code Shape} object and transformed 216 * by the specified {@code AffineTransform} object. 217 * 218 * @param s the specified {@code Shape} object 219 * @param at the specified {@code AffineTransform} object 220 * @since 1.6 221 */ 222 public Float(Shape s, AffineTransform at) { 223 super(); 224 if (s instanceof Path2D) { 225 Path2D p2d = (Path2D) s; 226 setWindingRule(p2d.windingRule); 227 this.numTypes = p2d.numTypes; 228 // trim arrays: 229 this.pointTypes = Arrays.copyOf(p2d.pointTypes, this.numTypes); 230 this.numCoords = p2d.numCoords; 231 this.floatCoords = p2d.cloneCoordsFloat(at); 232 } else { 233 PathIterator pi = s.getPathIterator(at); 234 setWindingRule(pi.getWindingRule()); 235 this.pointTypes = new byte[INIT_SIZE]; 236 this.floatCoords = new float[INIT_SIZE * 2]; 237 append(pi, false); 238 } 239 } 240 241 @Override 242 float[] cloneCoordsFloat(AffineTransform at) { 243 // trim arrays: 244 float ret[]; 245 if (at == null) { 246 ret = Arrays.copyOf(floatCoords, numCoords); 247 } else { 248 ret = new float[numCoords]; 249 at.transform(floatCoords, 0, ret, 0, numCoords / 2); 250 } 251 return ret; 252 } 253 254 @Override 255 double[] cloneCoordsDouble(AffineTransform at) { 256 // trim arrays: 257 double ret[] = new double[numCoords]; 258 if (at == null) { 259 for (int i = 0; i < numCoords; i++) { 260 ret[i] = floatCoords[i]; 261 } 262 } else { 263 at.transform(floatCoords, 0, ret, 0, numCoords / 2); 264 } 265 return ret; 266 } 267 268 void append(float x, float y) { 269 floatCoords[numCoords++] = x; 270 floatCoords[numCoords++] = y; 271 } 272 273 void append(double x, double y) { 274 floatCoords[numCoords++] = (float) x; 275 floatCoords[numCoords++] = (float) y; 276 } 277 278 Point2D getPoint(int coordindex) { 279 return new Point2D.Float(floatCoords[coordindex], 280 floatCoords[coordindex+1]); 281 } 282 283 void needRoom(boolean needMove, int newCoords) { 284 if (needMove && numTypes == 0) { 285 throw new IllegalPathStateException("missing initial moveto "+ 286 "in path definition"); 287 } 288 int size = pointTypes.length; 289 if (numTypes >= size) { 290 int grow = size; 291 if (grow > EXPAND_MAX) { 292 grow = EXPAND_MAX; 293 } else if (grow == 0) { 294 grow = 1; 295 } 296 pointTypes = Arrays.copyOf(pointTypes, size+grow); 297 } 298 size = floatCoords.length; 299 if (numCoords + newCoords > size) { 300 int grow = size; 301 if (grow > EXPAND_MAX * 2) { 302 grow = EXPAND_MAX * 2; 303 } 304 if (grow < newCoords) { 305 grow = newCoords; 306 } 307 floatCoords = Arrays.copyOf(floatCoords, size+grow); 308 } 309 } 310 311 /** 312 * {@inheritDoc} 313 * @since 1.6 314 */ 315 public final synchronized void moveTo(double x, double y) { 316 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) { 317 floatCoords[numCoords-2] = (float) x; 318 floatCoords[numCoords-1] = (float) y; 319 } else { 320 needRoom(false, 2); 321 pointTypes[numTypes++] = SEG_MOVETO; 322 floatCoords[numCoords++] = (float) x; 323 floatCoords[numCoords++] = (float) y; 324 } 325 } 326 327 /** 328 * Adds a point to the path by moving to the specified 329 * coordinates specified in float precision. 330 * <p> 331 * This method provides a single precision variant of 332 * the double precision {@code moveTo()} method on the 333 * base {@code Path2D} class. 334 * 335 * @param x the specified X coordinate 336 * @param y the specified Y coordinate 337 * @see Path2D#moveTo 338 * @since 1.6 339 */ 340 public final synchronized void moveTo(float x, float y) { 341 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) { 342 floatCoords[numCoords-2] = x; 343 floatCoords[numCoords-1] = y; 344 } else { 345 needRoom(false, 2); 346 pointTypes[numTypes++] = SEG_MOVETO; 347 floatCoords[numCoords++] = x; 348 floatCoords[numCoords++] = y; 349 } 350 } 351 352 /** 353 * {@inheritDoc} 354 * @since 1.6 355 */ 356 public final synchronized void lineTo(double x, double y) { 357 needRoom(true, 2); 358 pointTypes[numTypes++] = SEG_LINETO; 359 floatCoords[numCoords++] = (float) x; 360 floatCoords[numCoords++] = (float) y; 361 } 362 363 /** 364 * Adds a point to the path by drawing a straight line from the 365 * current coordinates to the new specified coordinates 366 * specified in float precision. 367 * <p> 368 * This method provides a single precision variant of 369 * the double precision {@code lineTo()} method on the 370 * base {@code Path2D} class. 371 * 372 * @param x the specified X coordinate 373 * @param y the specified Y coordinate 374 * @see Path2D#lineTo 375 * @since 1.6 376 */ 377 public final synchronized void lineTo(float x, float y) { 378 needRoom(true, 2); 379 pointTypes[numTypes++] = SEG_LINETO; 380 floatCoords[numCoords++] = x; 381 floatCoords[numCoords++] = y; 382 } 383 384 /** 385 * {@inheritDoc} 386 * @since 1.6 387 */ 388 public final synchronized void quadTo(double x1, double y1, 389 double x2, double y2) 390 { 391 needRoom(true, 4); 392 pointTypes[numTypes++] = SEG_QUADTO; 393 floatCoords[numCoords++] = (float) x1; 394 floatCoords[numCoords++] = (float) y1; 395 floatCoords[numCoords++] = (float) x2; 396 floatCoords[numCoords++] = (float) y2; 397 } 398 399 /** 400 * Adds a curved segment, defined by two new points, to the path by 401 * drawing a Quadratic curve that intersects both the current 402 * coordinates and the specified coordinates {@code (x2,y2)}, 403 * using the specified point {@code (x1,y1)} as a quadratic 404 * parametric control point. 405 * All coordinates are specified in float precision. 406 * <p> 407 * This method provides a single precision variant of 408 * the double precision {@code quadTo()} method on the 409 * base {@code Path2D} class. 410 * 411 * @param x1 the X coordinate of the quadratic control point 412 * @param y1 the Y coordinate of the quadratic control point 413 * @param x2 the X coordinate of the final end point 414 * @param y2 the Y coordinate of the final end point 415 * @see Path2D#quadTo 416 * @since 1.6 417 */ 418 public final synchronized void quadTo(float x1, float y1, 419 float x2, float y2) 420 { 421 needRoom(true, 4); 422 pointTypes[numTypes++] = SEG_QUADTO; 423 floatCoords[numCoords++] = x1; 424 floatCoords[numCoords++] = y1; 425 floatCoords[numCoords++] = x2; 426 floatCoords[numCoords++] = y2; 427 } 428 429 /** 430 * {@inheritDoc} 431 * @since 1.6 432 */ 433 public final synchronized void curveTo(double x1, double y1, 434 double x2, double y2, 435 double x3, double y3) 436 { 437 needRoom(true, 6); 438 pointTypes[numTypes++] = SEG_CUBICTO; 439 floatCoords[numCoords++] = (float) x1; 440 floatCoords[numCoords++] = (float) y1; 441 floatCoords[numCoords++] = (float) x2; 442 floatCoords[numCoords++] = (float) y2; 443 floatCoords[numCoords++] = (float) x3; 444 floatCoords[numCoords++] = (float) y3; 445 } 446 447 /** 448 * Adds a curved segment, defined by three new points, to the path by 449 * drawing a Bézier curve that intersects both the current 450 * coordinates and the specified coordinates {@code (x3,y3)}, 451 * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as 452 * Bézier control points. 453 * All coordinates are specified in float precision. 454 * <p> 455 * This method provides a single precision variant of 456 * the double precision {@code curveTo()} method on the 457 * base {@code Path2D} class. 458 * 459 * @param x1 the X coordinate of the first Bézier control point 460 * @param y1 the Y coordinate of the first Bézier control point 461 * @param x2 the X coordinate of the second Bézier control point 462 * @param y2 the Y coordinate of the second Bézier control point 463 * @param x3 the X coordinate of the final end point 464 * @param y3 the Y coordinate of the final end point 465 * @see Path2D#curveTo 466 * @since 1.6 467 */ 468 public final synchronized void curveTo(float x1, float y1, 469 float x2, float y2, 470 float x3, float y3) 471 { 472 needRoom(true, 6); 473 pointTypes[numTypes++] = SEG_CUBICTO; 474 floatCoords[numCoords++] = x1; 475 floatCoords[numCoords++] = y1; 476 floatCoords[numCoords++] = x2; 477 floatCoords[numCoords++] = y2; 478 floatCoords[numCoords++] = x3; 479 floatCoords[numCoords++] = y3; 480 } 481 482 int pointCrossings(double px, double py) { 483 double movx, movy, curx, cury, endx, endy; 484 float coords[] = floatCoords; 485 curx = movx = coords[0]; 486 cury = movy = coords[1]; 487 int crossings = 0; 488 int ci = 2; 489 for (int i = 1; i < numTypes; i++) { 490 switch (pointTypes[i]) { 491 case PathIterator.SEG_MOVETO: 492 if (cury != movy) { 493 crossings += 494 Curve.pointCrossingsForLine(px, py, 495 curx, cury, 496 movx, movy); 497 } 498 movx = curx = coords[ci++]; 499 movy = cury = coords[ci++]; 500 break; 501 case PathIterator.SEG_LINETO: 502 crossings += 503 Curve.pointCrossingsForLine(px, py, 504 curx, cury, 505 endx = coords[ci++], 506 endy = coords[ci++]); 507 curx = endx; 508 cury = endy; 509 break; 510 case PathIterator.SEG_QUADTO: 511 crossings += 512 Curve.pointCrossingsForQuad(px, py, 513 curx, cury, 514 coords[ci++], 515 coords[ci++], 516 endx = coords[ci++], 517 endy = coords[ci++], 518 0); 519 curx = endx; 520 cury = endy; 521 break; 522 case PathIterator.SEG_CUBICTO: 523 crossings += 524 Curve.pointCrossingsForCubic(px, py, 525 curx, cury, 526 coords[ci++], 527 coords[ci++], 528 coords[ci++], 529 coords[ci++], 530 endx = coords[ci++], 531 endy = coords[ci++], 532 0); 533 curx = endx; 534 cury = endy; 535 break; 536 case PathIterator.SEG_CLOSE: 537 if (cury != movy) { 538 crossings += 539 Curve.pointCrossingsForLine(px, py, 540 curx, cury, 541 movx, movy); 542 } 543 curx = movx; 544 cury = movy; 545 break; 546 } 547 } 548 if (cury != movy) { 549 crossings += 550 Curve.pointCrossingsForLine(px, py, 551 curx, cury, 552 movx, movy); 553 } 554 return crossings; 555 } 556 557 int rectCrossings(double rxmin, double rymin, 558 double rxmax, double rymax) 559 { 560 float coords[] = floatCoords; 561 double curx, cury, movx, movy, endx, endy; 562 curx = movx = coords[0]; 563 cury = movy = coords[1]; 564 int crossings = 0; 565 int ci = 2; 566 for (int i = 1; 567 crossings != Curve.RECT_INTERSECTS && i < numTypes; 568 i++) 569 { 570 switch (pointTypes[i]) { 571 case PathIterator.SEG_MOVETO: 572 if (curx != movx || cury != movy) { 573 crossings = 574 Curve.rectCrossingsForLine(crossings, 575 rxmin, rymin, 576 rxmax, rymax, 577 curx, cury, 578 movx, movy); 579 } 580 // Count should always be a multiple of 2 here. 581 // assert((crossings & 1) != 0); 582 movx = curx = coords[ci++]; 583 movy = cury = coords[ci++]; 584 break; 585 case PathIterator.SEG_LINETO: 586 crossings = 587 Curve.rectCrossingsForLine(crossings, 588 rxmin, rymin, 589 rxmax, rymax, 590 curx, cury, 591 endx = coords[ci++], 592 endy = coords[ci++]); 593 curx = endx; 594 cury = endy; 595 break; 596 case PathIterator.SEG_QUADTO: 597 crossings = 598 Curve.rectCrossingsForQuad(crossings, 599 rxmin, rymin, 600 rxmax, rymax, 601 curx, cury, 602 coords[ci++], 603 coords[ci++], 604 endx = coords[ci++], 605 endy = coords[ci++], 606 0); 607 curx = endx; 608 cury = endy; 609 break; 610 case PathIterator.SEG_CUBICTO: 611 crossings = 612 Curve.rectCrossingsForCubic(crossings, 613 rxmin, rymin, 614 rxmax, rymax, 615 curx, cury, 616 coords[ci++], 617 coords[ci++], 618 coords[ci++], 619 coords[ci++], 620 endx = coords[ci++], 621 endy = coords[ci++], 622 0); 623 curx = endx; 624 cury = endy; 625 break; 626 case PathIterator.SEG_CLOSE: 627 if (curx != movx || cury != movy) { 628 crossings = 629 Curve.rectCrossingsForLine(crossings, 630 rxmin, rymin, 631 rxmax, rymax, 632 curx, cury, 633 movx, movy); 634 } 635 curx = movx; 636 cury = movy; 637 // Count should always be a multiple of 2 here. 638 // assert((crossings & 1) != 0); 639 break; 640 } 641 } 642 if (crossings != Curve.RECT_INTERSECTS && 643 (curx != movx || cury != movy)) 644 { 645 crossings = 646 Curve.rectCrossingsForLine(crossings, 647 rxmin, rymin, 648 rxmax, rymax, 649 curx, cury, 650 movx, movy); 651 } 652 // Count should always be a multiple of 2 here. 653 // assert((crossings & 1) != 0); 654 return crossings; 655 } 656 657 /** 658 * {@inheritDoc} 659 * @since 1.6 660 */ 661 public final void append(PathIterator pi, boolean connect) { 662 float coords[] = new float[6]; 663 while (!pi.isDone()) { 664 switch (pi.currentSegment(coords)) { 665 case SEG_MOVETO: 666 if (!connect || numTypes < 1 || numCoords < 1) { 667 moveTo(coords[0], coords[1]); 668 break; 669 } 670 if (pointTypes[numTypes - 1] != SEG_CLOSE && 671 floatCoords[numCoords-2] == coords[0] && 672 floatCoords[numCoords-1] == coords[1]) 673 { 674 // Collapse out initial moveto/lineto 675 break; 676 } 677 lineTo(coords[0], coords[1]); 678 break; 679 case SEG_LINETO: 680 lineTo(coords[0], coords[1]); 681 break; 682 case SEG_QUADTO: 683 quadTo(coords[0], coords[1], 684 coords[2], coords[3]); 685 break; 686 case SEG_CUBICTO: 687 curveTo(coords[0], coords[1], 688 coords[2], coords[3], 689 coords[4], coords[5]); 690 break; 691 case SEG_CLOSE: 692 closePath(); 693 break; 694 } 695 pi.next(); 696 connect = false; 697 } 698 } 699 700 /** 701 * {@inheritDoc} 702 * @since 1.6 703 */ 704 public final void transform(AffineTransform at) { 705 at.transform(floatCoords, 0, floatCoords, 0, numCoords / 2); 706 } 707 708 /** 709 * {@inheritDoc} 710 * @since 1.6 711 */ 712 public final synchronized Rectangle2D getBounds2D() { 713 float x1, y1, x2, y2; 714 int i = numCoords; 715 if (i > 0) { 716 y1 = y2 = floatCoords[--i]; 717 x1 = x2 = floatCoords[--i]; 718 while (i > 0) { 719 float y = floatCoords[--i]; 720 float x = floatCoords[--i]; 721 if (x < x1) x1 = x; 722 if (y < y1) y1 = y; 723 if (x > x2) x2 = x; 724 if (y > y2) y2 = y; 725 } 726 } else { 727 x1 = y1 = x2 = y2 = 0.0f; 728 } 729 return new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1); 730 } 731 732 /** 733 * {@inheritDoc} 734 * <p> 735 * The iterator for this class is not multi-threaded safe, 736 * which means that the {@code Path2D} class does not 737 * guarantee that modifications to the geometry of this 738 * {@code Path2D} object do not affect any iterations of 739 * that geometry that are already in process. 740 * 741 * @since 1.6 742 */ 743 public final PathIterator getPathIterator(AffineTransform at) { 744 if (at == null) { 745 return new CopyIterator(this); 746 } else { 747 return new TxIterator(this, at); 748 } 749 } 750 751 /** 752 * Creates a new object of the same class as this object. 753 * 754 * @return a clone of this instance. 755 * @exception OutOfMemoryError if there is not enough memory. 756 * @see java.lang.Cloneable 757 * @since 1.6 758 */ 759 public final Object clone() { 760 // Note: It would be nice to have this return Path2D 761 // but one of our subclasses (GeneralPath) needs to 762 // offer "public Object clone()" for backwards 763 // compatibility so we cannot restrict it further. 764 // REMIND: Can we do both somehow? 765 if (this instanceof GeneralPath) { 766 return new GeneralPath(this); 767 } else { 768 return new Path2D.Float(this); 769 } 770 } 771 772 /* 773 * JDK 1.6 serialVersionUID 774 */ 775 private static final long serialVersionUID = 6990832515060788886L; 776 777 /** 778 * Writes the default serializable fields to the 779 * {@code ObjectOutputStream} followed by an explicit 780 * serialization of the path segments stored in this 781 * path. 782 * 783 * @serialData 784 * <a name="Path2DSerialData"><!-- --></a> 785 * <ol> 786 * <li>The default serializable fields. 787 * There are no default serializable fields as of 1.6. 788 * <li>followed by 789 * a byte indicating the storage type of the original object 790 * as a hint (SERIAL_STORAGE_FLT_ARRAY) 791 * <li>followed by 792 * an integer indicating the number of path segments to follow (NP) 793 * or -1 to indicate an unknown number of path segments follows 794 * <li>followed by 795 * an integer indicating the total number of coordinates to follow (NC) 796 * or -1 to indicate an unknown number of coordinates follows 797 * (NC should always be even since coordinates always appear in pairs 798 * representing an x,y pair) 799 * <li>followed by 800 * a byte indicating the winding rule 801 * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or 802 * {@link #WIND_NON_ZERO WIND_NON_ZERO}) 803 * <li>followed by 804 * {@code NP} (or unlimited if {@code NP < 0}) sets of values consisting of 805 * a single byte indicating a path segment type 806 * followed by one or more pairs of float or double 807 * values representing the coordinates of the path segment 808 * <li>followed by 809 * a byte indicating the end of the path (SERIAL_PATH_END). 810 * </ol> 811 * <p> 812 * The following byte value constants are used in the serialized form 813 * of {@code Path2D} objects: 814 * <table> 815 * <tr> 816 * <th>Constant Name</th> 817 * <th>Byte Value</th> 818 * <th>Followed by</th> 819 * <th>Description</th> 820 * </tr> 821 * <tr> 822 * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td> 823 * <td>0x30</td> 824 * <td></td> 825 * <td>A hint that the original {@code Path2D} object stored 826 * the coordinates in a Java array of floats.</td> 827 * </tr> 828 * <tr> 829 * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td> 830 * <td>0x31</td> 831 * <td></td> 832 * <td>A hint that the original {@code Path2D} object stored 833 * the coordinates in a Java array of doubles.</td> 834 * </tr> 835 * <tr> 836 * <td>{@code SERIAL_SEG_FLT_MOVETO}</td> 837 * <td>0x40</td> 838 * <td>2 floats</td> 839 * <td>A {@link #moveTo moveTo} path segment follows.</td> 840 * </tr> 841 * <tr> 842 * <td>{@code SERIAL_SEG_FLT_LINETO}</td> 843 * <td>0x41</td> 844 * <td>2 floats</td> 845 * <td>A {@link #lineTo lineTo} path segment follows.</td> 846 * </tr> 847 * <tr> 848 * <td>{@code SERIAL_SEG_FLT_QUADTO}</td> 849 * <td>0x42</td> 850 * <td>4 floats</td> 851 * <td>A {@link #quadTo quadTo} path segment follows.</td> 852 * </tr> 853 * <tr> 854 * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td> 855 * <td>0x43</td> 856 * <td>6 floats</td> 857 * <td>A {@link #curveTo curveTo} path segment follows.</td> 858 * </tr> 859 * <tr> 860 * <td>{@code SERIAL_SEG_DBL_MOVETO}</td> 861 * <td>0x50</td> 862 * <td>2 doubles</td> 863 * <td>A {@link #moveTo moveTo} path segment follows.</td> 864 * </tr> 865 * <tr> 866 * <td>{@code SERIAL_SEG_DBL_LINETO}</td> 867 * <td>0x51</td> 868 * <td>2 doubles</td> 869 * <td>A {@link #lineTo lineTo} path segment follows.</td> 870 * </tr> 871 * <tr> 872 * <td>{@code SERIAL_SEG_DBL_QUADTO}</td> 873 * <td>0x52</td> 874 * <td>4 doubles</td> 875 * <td>A {@link #curveTo curveTo} path segment follows.</td> 876 * </tr> 877 * <tr> 878 * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td> 879 * <td>0x53</td> 880 * <td>6 doubles</td> 881 * <td>A {@link #curveTo curveTo} path segment follows.</td> 882 * </tr> 883 * <tr> 884 * <td>{@code SERIAL_SEG_CLOSE}</td> 885 * <td>0x60</td> 886 * <td></td> 887 * <td>A {@link #closePath closePath} path segment.</td> 888 * </tr> 889 * <tr> 890 * <td>{@code SERIAL_PATH_END}</td> 891 * <td>0x61</td> 892 * <td></td> 893 * <td>There are no more path segments following.</td> 894 * </table> 895 * 896 * @since 1.6 897 */ 898 private void writeObject(java.io.ObjectOutputStream s) 899 throws java.io.IOException 900 { 901 super.writeObject(s, false); 902 } 903 904 /** 905 * Reads the default serializable fields from the 906 * {@code ObjectInputStream} followed by an explicit 907 * serialization of the path segments stored in this 908 * path. 909 * <p> 910 * There are no default serializable fields as of 1.6. 911 * <p> 912 * The serial data for this object is described in the 913 * writeObject method. 914 * 915 * @since 1.6 916 */ 917 private void readObject(java.io.ObjectInputStream s) 918 throws java.lang.ClassNotFoundException, java.io.IOException 919 { 920 super.readObject(s, false); 921 } 922 923 static class CopyIterator extends Path2D.Iterator { 924 float floatCoords[]; 925 926 CopyIterator(Path2D.Float p2df) { 927 super(p2df); 928 this.floatCoords = p2df.floatCoords; 929 } 930 931 public int currentSegment(float[] coords) { 932 int type = path.pointTypes[typeIdx]; 933 int numCoords = curvecoords[type]; 934 if (numCoords > 0) { 935 System.arraycopy(floatCoords, pointIdx, 936 coords, 0, numCoords); 937 } 938 return type; 939 } 940 941 public int currentSegment(double[] coords) { 942 int type = path.pointTypes[typeIdx]; 943 int numCoords = curvecoords[type]; 944 if (numCoords > 0) { 945 for (int i = 0; i < numCoords; i++) { 946 coords[i] = floatCoords[pointIdx + i]; 947 } 948 } 949 return type; 950 } 951 } 952 953 static class TxIterator extends Path2D.Iterator { 954 float floatCoords[]; 955 AffineTransform affine; 956 957 TxIterator(Path2D.Float p2df, AffineTransform at) { 958 super(p2df); 959 this.floatCoords = p2df.floatCoords; 960 this.affine = at; 961 } 962 963 public int currentSegment(float[] coords) { 964 int type = path.pointTypes[typeIdx]; 965 int numCoords = curvecoords[type]; 966 if (numCoords > 0) { 967 affine.transform(floatCoords, pointIdx, 968 coords, 0, numCoords / 2); 969 } 970 return type; 971 } 972 973 public int currentSegment(double[] coords) { 974 int type = path.pointTypes[typeIdx]; 975 int numCoords = curvecoords[type]; 976 if (numCoords > 0) { 977 affine.transform(floatCoords, pointIdx, 978 coords, 0, numCoords / 2); 979 } 980 return type; 981 } 982 } 983 984 } 985 986 /** 987 * The {@code Double} class defines a geometric path with 988 * coordinates stored in double precision floating point. 989 * 990 * @since 1.6 991 */ 992 public static class Double extends Path2D implements Serializable { 993 transient double doubleCoords[]; 994 995 /** 996 * Constructs a new empty double precision {@code Path2D} object 997 * with a default winding rule of {@link #WIND_NON_ZERO}. 998 * 999 * @since 1.6 1000 */ 1001 public Double() { 1002 this(WIND_NON_ZERO, INIT_SIZE); 1003 } 1004 1005 /** 1006 * Constructs a new empty double precision {@code Path2D} object 1007 * with the specified winding rule to control operations that 1008 * require the interior of the path to be defined. 1009 * 1010 * @param rule the winding rule 1011 * @see #WIND_EVEN_ODD 1012 * @see #WIND_NON_ZERO 1013 * @since 1.6 1014 */ 1015 public Double(int rule) { 1016 this(rule, INIT_SIZE); 1017 } 1018 1019 /** 1020 * Constructs a new empty double precision {@code Path2D} object 1021 * with the specified winding rule and the specified initial 1022 * capacity to store path segments. 1023 * This number is an initial guess as to how many path segments 1024 * are in the path, but the storage is expanded as needed to store 1025 * whatever path segments are added to this path. 1026 * 1027 * @param rule the winding rule 1028 * @param initialCapacity the estimate for the number of path segments 1029 * in the path 1030 * @see #WIND_EVEN_ODD 1031 * @see #WIND_NON_ZERO 1032 * @since 1.6 1033 */ 1034 public Double(int rule, int initialCapacity) { 1035 super(rule, initialCapacity); 1036 doubleCoords = new double[initialCapacity * 2]; 1037 } 1038 1039 /** 1040 * Constructs a new double precision {@code Path2D} object 1041 * from an arbitrary {@link Shape} object. 1042 * All of the initial geometry and the winding rule for this path are 1043 * taken from the specified {@code Shape} object. 1044 * 1045 * @param s the specified {@code Shape} object 1046 * @since 1.6 1047 */ 1048 public Double(Shape s) { 1049 this(s, null); 1050 } 1051 1052 /** 1053 * Constructs a new double precision {@code Path2D} object 1054 * from an arbitrary {@link Shape} object, transformed by an 1055 * {@link AffineTransform} object. 1056 * All of the initial geometry and the winding rule for this path are 1057 * taken from the specified {@code Shape} object and transformed 1058 * by the specified {@code AffineTransform} object. 1059 * 1060 * @param s the specified {@code Shape} object 1061 * @param at the specified {@code AffineTransform} object 1062 * @since 1.6 1063 */ 1064 public Double(Shape s, AffineTransform at) { 1065 super(); 1066 if (s instanceof Path2D) { 1067 Path2D p2d = (Path2D) s; 1068 setWindingRule(p2d.windingRule); 1069 this.numTypes = p2d.numTypes; 1070 // trim arrays: 1071 this.pointTypes = Arrays.copyOf(p2d.pointTypes, this.numTypes); 1072 this.numCoords = p2d.numCoords; 1073 this.doubleCoords = p2d.cloneCoordsDouble(at); 1074 } else { 1075 PathIterator pi = s.getPathIterator(at); 1076 setWindingRule(pi.getWindingRule()); 1077 this.pointTypes = new byte[INIT_SIZE]; 1078 this.doubleCoords = new double[INIT_SIZE * 2]; 1079 append(pi, false); 1080 } 1081 } 1082 1083 @Override 1084 float[] cloneCoordsFloat(AffineTransform at) { 1085 // trim arrays: 1086 float ret[] = new float[numCoords]; 1087 if (at == null) { 1088 for (int i = 0; i < numCoords; i++) { 1089 ret[i] = (float) doubleCoords[i]; 1090 } 1091 } else { 1092 at.transform(doubleCoords, 0, ret, 0, numCoords / 2); 1093 } 1094 return ret; 1095 } 1096 1097 @Override 1098 double[] cloneCoordsDouble(AffineTransform at) { 1099 // trim arrays: 1100 double ret[]; 1101 if (at == null) { 1102 ret = Arrays.copyOf(doubleCoords, numCoords); 1103 } else { 1104 ret = new double[numCoords]; 1105 at.transform(doubleCoords, 0, ret, 0, numCoords / 2); 1106 } 1107 return ret; 1108 } 1109 1110 void append(float x, float y) { 1111 doubleCoords[numCoords++] = x; 1112 doubleCoords[numCoords++] = y; 1113 } 1114 1115 void append(double x, double y) { 1116 doubleCoords[numCoords++] = x; 1117 doubleCoords[numCoords++] = y; 1118 } 1119 1120 Point2D getPoint(int coordindex) { 1121 return new Point2D.Double(doubleCoords[coordindex], 1122 doubleCoords[coordindex+1]); 1123 } 1124 1125 void needRoom(boolean needMove, int newCoords) { 1126 if (needMove && numTypes == 0) { 1127 throw new IllegalPathStateException("missing initial moveto "+ 1128 "in path definition"); 1129 } 1130 int size = pointTypes.length; 1131 if (numTypes >= size) { 1132 int grow = size; 1133 if (grow > EXPAND_MAX) { 1134 grow = EXPAND_MAX; 1135 } else if (grow == 0) { 1136 grow = 1; 1137 } 1138 pointTypes = Arrays.copyOf(pointTypes, size+grow); 1139 } 1140 size = doubleCoords.length; 1141 if (numCoords + newCoords > size) { 1142 int grow = size; 1143 if (grow > EXPAND_MAX * 2) { 1144 grow = EXPAND_MAX * 2; 1145 } 1146 if (grow < newCoords) { 1147 grow = newCoords; 1148 } 1149 doubleCoords = Arrays.copyOf(doubleCoords, size+grow); 1150 } 1151 } 1152 1153 /** 1154 * {@inheritDoc} 1155 * @since 1.6 1156 */ 1157 public final synchronized void moveTo(double x, double y) { 1158 if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) { 1159 doubleCoords[numCoords-2] = x; 1160 doubleCoords[numCoords-1] = y; 1161 } else { 1162 needRoom(false, 2); 1163 pointTypes[numTypes++] = SEG_MOVETO; 1164 doubleCoords[numCoords++] = x; 1165 doubleCoords[numCoords++] = y; 1166 } 1167 } 1168 1169 /** 1170 * {@inheritDoc} 1171 * @since 1.6 1172 */ 1173 public final synchronized void lineTo(double x, double y) { 1174 needRoom(true, 2); 1175 pointTypes[numTypes++] = SEG_LINETO; 1176 doubleCoords[numCoords++] = x; 1177 doubleCoords[numCoords++] = y; 1178 } 1179 1180 /** 1181 * {@inheritDoc} 1182 * @since 1.6 1183 */ 1184 public final synchronized void quadTo(double x1, double y1, 1185 double x2, double y2) 1186 { 1187 needRoom(true, 4); 1188 pointTypes[numTypes++] = SEG_QUADTO; 1189 doubleCoords[numCoords++] = x1; 1190 doubleCoords[numCoords++] = y1; 1191 doubleCoords[numCoords++] = x2; 1192 doubleCoords[numCoords++] = y2; 1193 } 1194 1195 /** 1196 * {@inheritDoc} 1197 * @since 1.6 1198 */ 1199 public final synchronized void curveTo(double x1, double y1, 1200 double x2, double y2, 1201 double x3, double y3) 1202 { 1203 needRoom(true, 6); 1204 pointTypes[numTypes++] = SEG_CUBICTO; 1205 doubleCoords[numCoords++] = x1; 1206 doubleCoords[numCoords++] = y1; 1207 doubleCoords[numCoords++] = x2; 1208 doubleCoords[numCoords++] = y2; 1209 doubleCoords[numCoords++] = x3; 1210 doubleCoords[numCoords++] = y3; 1211 } 1212 1213 int pointCrossings(double px, double py) { 1214 double movx, movy, curx, cury, endx, endy; 1215 double coords[] = doubleCoords; 1216 curx = movx = coords[0]; 1217 cury = movy = coords[1]; 1218 int crossings = 0; 1219 int ci = 2; 1220 for (int i = 1; i < numTypes; i++) { 1221 switch (pointTypes[i]) { 1222 case PathIterator.SEG_MOVETO: 1223 if (cury != movy) { 1224 crossings += 1225 Curve.pointCrossingsForLine(px, py, 1226 curx, cury, 1227 movx, movy); 1228 } 1229 movx = curx = coords[ci++]; 1230 movy = cury = coords[ci++]; 1231 break; 1232 case PathIterator.SEG_LINETO: 1233 crossings += 1234 Curve.pointCrossingsForLine(px, py, 1235 curx, cury, 1236 endx = coords[ci++], 1237 endy = coords[ci++]); 1238 curx = endx; 1239 cury = endy; 1240 break; 1241 case PathIterator.SEG_QUADTO: 1242 crossings += 1243 Curve.pointCrossingsForQuad(px, py, 1244 curx, cury, 1245 coords[ci++], 1246 coords[ci++], 1247 endx = coords[ci++], 1248 endy = coords[ci++], 1249 0); 1250 curx = endx; 1251 cury = endy; 1252 break; 1253 case PathIterator.SEG_CUBICTO: 1254 crossings += 1255 Curve.pointCrossingsForCubic(px, py, 1256 curx, cury, 1257 coords[ci++], 1258 coords[ci++], 1259 coords[ci++], 1260 coords[ci++], 1261 endx = coords[ci++], 1262 endy = coords[ci++], 1263 0); 1264 curx = endx; 1265 cury = endy; 1266 break; 1267 case PathIterator.SEG_CLOSE: 1268 if (cury != movy) { 1269 crossings += 1270 Curve.pointCrossingsForLine(px, py, 1271 curx, cury, 1272 movx, movy); 1273 } 1274 curx = movx; 1275 cury = movy; 1276 break; 1277 } 1278 } 1279 if (cury != movy) { 1280 crossings += 1281 Curve.pointCrossingsForLine(px, py, 1282 curx, cury, 1283 movx, movy); 1284 } 1285 return crossings; 1286 } 1287 1288 int rectCrossings(double rxmin, double rymin, 1289 double rxmax, double rymax) 1290 { 1291 double coords[] = doubleCoords; 1292 double curx, cury, movx, movy, endx, endy; 1293 curx = movx = coords[0]; 1294 cury = movy = coords[1]; 1295 int crossings = 0; 1296 int ci = 2; 1297 for (int i = 1; 1298 crossings != Curve.RECT_INTERSECTS && i < numTypes; 1299 i++) 1300 { 1301 switch (pointTypes[i]) { 1302 case PathIterator.SEG_MOVETO: 1303 if (curx != movx || cury != movy) { 1304 crossings = 1305 Curve.rectCrossingsForLine(crossings, 1306 rxmin, rymin, 1307 rxmax, rymax, 1308 curx, cury, 1309 movx, movy); 1310 } 1311 // Count should always be a multiple of 2 here. 1312 // assert((crossings & 1) != 0); 1313 movx = curx = coords[ci++]; 1314 movy = cury = coords[ci++]; 1315 break; 1316 case PathIterator.SEG_LINETO: 1317 endx = coords[ci++]; 1318 endy = coords[ci++]; 1319 crossings = 1320 Curve.rectCrossingsForLine(crossings, 1321 rxmin, rymin, 1322 rxmax, rymax, 1323 curx, cury, 1324 endx, endy); 1325 curx = endx; 1326 cury = endy; 1327 break; 1328 case PathIterator.SEG_QUADTO: 1329 crossings = 1330 Curve.rectCrossingsForQuad(crossings, 1331 rxmin, rymin, 1332 rxmax, rymax, 1333 curx, cury, 1334 coords[ci++], 1335 coords[ci++], 1336 endx = coords[ci++], 1337 endy = coords[ci++], 1338 0); 1339 curx = endx; 1340 cury = endy; 1341 break; 1342 case PathIterator.SEG_CUBICTO: 1343 crossings = 1344 Curve.rectCrossingsForCubic(crossings, 1345 rxmin, rymin, 1346 rxmax, rymax, 1347 curx, cury, 1348 coords[ci++], 1349 coords[ci++], 1350 coords[ci++], 1351 coords[ci++], 1352 endx = coords[ci++], 1353 endy = coords[ci++], 1354 0); 1355 curx = endx; 1356 cury = endy; 1357 break; 1358 case PathIterator.SEG_CLOSE: 1359 if (curx != movx || cury != movy) { 1360 crossings = 1361 Curve.rectCrossingsForLine(crossings, 1362 rxmin, rymin, 1363 rxmax, rymax, 1364 curx, cury, 1365 movx, movy); 1366 } 1367 curx = movx; 1368 cury = movy; 1369 // Count should always be a multiple of 2 here. 1370 // assert((crossings & 1) != 0); 1371 break; 1372 } 1373 } 1374 if (crossings != Curve.RECT_INTERSECTS && 1375 (curx != movx || cury != movy)) 1376 { 1377 crossings = 1378 Curve.rectCrossingsForLine(crossings, 1379 rxmin, rymin, 1380 rxmax, rymax, 1381 curx, cury, 1382 movx, movy); 1383 } 1384 // Count should always be a multiple of 2 here. 1385 // assert((crossings & 1) != 0); 1386 return crossings; 1387 } 1388 1389 /** 1390 * {@inheritDoc} 1391 * @since 1.6 1392 */ 1393 public final void append(PathIterator pi, boolean connect) { 1394 double coords[] = new double[6]; 1395 while (!pi.isDone()) { 1396 switch (pi.currentSegment(coords)) { 1397 case SEG_MOVETO: 1398 if (!connect || numTypes < 1 || numCoords < 1) { 1399 moveTo(coords[0], coords[1]); 1400 break; 1401 } 1402 if (pointTypes[numTypes - 1] != SEG_CLOSE && 1403 doubleCoords[numCoords-2] == coords[0] && 1404 doubleCoords[numCoords-1] == coords[1]) 1405 { 1406 // Collapse out initial moveto/lineto 1407 break; 1408 } 1409 lineTo(coords[0], coords[1]); 1410 break; 1411 case SEG_LINETO: 1412 lineTo(coords[0], coords[1]); 1413 break; 1414 case SEG_QUADTO: 1415 quadTo(coords[0], coords[1], 1416 coords[2], coords[3]); 1417 break; 1418 case SEG_CUBICTO: 1419 curveTo(coords[0], coords[1], 1420 coords[2], coords[3], 1421 coords[4], coords[5]); 1422 break; 1423 case SEG_CLOSE: 1424 closePath(); 1425 break; 1426 } 1427 pi.next(); 1428 connect = false; 1429 } 1430 } 1431 1432 /** 1433 * {@inheritDoc} 1434 * @since 1.6 1435 */ 1436 public final void transform(AffineTransform at) { 1437 at.transform(doubleCoords, 0, doubleCoords, 0, numCoords / 2); 1438 } 1439 1440 /** 1441 * {@inheritDoc} 1442 * @since 1.6 1443 */ 1444 public final synchronized Rectangle2D getBounds2D() { 1445 double x1, y1, x2, y2; 1446 int i = numCoords; 1447 if (i > 0) { 1448 y1 = y2 = doubleCoords[--i]; 1449 x1 = x2 = doubleCoords[--i]; 1450 while (i > 0) { 1451 double y = doubleCoords[--i]; 1452 double x = doubleCoords[--i]; 1453 if (x < x1) x1 = x; 1454 if (y < y1) y1 = y; 1455 if (x > x2) x2 = x; 1456 if (y > y2) y2 = y; 1457 } 1458 } else { 1459 x1 = y1 = x2 = y2 = 0.0; 1460 } 1461 return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1); 1462 } 1463 1464 /** 1465 * {@inheritDoc} 1466 * <p> 1467 * The iterator for this class is not multi-threaded safe, 1468 * which means that the {@code Path2D} class does not 1469 * guarantee that modifications to the geometry of this 1470 * {@code Path2D} object do not affect any iterations of 1471 * that geometry that are already in process. 1472 * 1473 * @param at an {@code AffineTransform} 1474 * @return a new {@code PathIterator} that iterates along the boundary 1475 * of this {@code Shape} and provides access to the geometry 1476 * of this {@code Shape}'s outline 1477 * @since 1.6 1478 */ 1479 public final PathIterator getPathIterator(AffineTransform at) { 1480 if (at == null) { 1481 return new CopyIterator(this); 1482 } else { 1483 return new TxIterator(this, at); 1484 } 1485 } 1486 1487 /** 1488 * Creates a new object of the same class as this object. 1489 * 1490 * @return a clone of this instance. 1491 * @exception OutOfMemoryError if there is not enough memory. 1492 * @see java.lang.Cloneable 1493 * @since 1.6 1494 */ 1495 public final Object clone() { 1496 // Note: It would be nice to have this return Path2D 1497 // but one of our subclasses (GeneralPath) needs to 1498 // offer "public Object clone()" for backwards 1499 // compatibility so we cannot restrict it further. 1500 // REMIND: Can we do both somehow? 1501 return new Path2D.Double(this); 1502 } 1503 1504 /* 1505 * JDK 1.6 serialVersionUID 1506 */ 1507 private static final long serialVersionUID = 1826762518450014216L; 1508 1509 /** 1510 * Writes the default serializable fields to the 1511 * {@code ObjectOutputStream} followed by an explicit 1512 * serialization of the path segments stored in this 1513 * path. 1514 * 1515 * @serialData 1516 * <a name="Path2DSerialData"><!-- --></a> 1517 * <ol> 1518 * <li>The default serializable fields. 1519 * There are no default serializable fields as of 1.6. 1520 * <li>followed by 1521 * a byte indicating the storage type of the original object 1522 * as a hint (SERIAL_STORAGE_DBL_ARRAY) 1523 * <li>followed by 1524 * an integer indicating the number of path segments to follow (NP) 1525 * or -1 to indicate an unknown number of path segments follows 1526 * <li>followed by 1527 * an integer indicating the total number of coordinates to follow (NC) 1528 * or -1 to indicate an unknown number of coordinates follows 1529 * (NC should always be even since coordinates always appear in pairs 1530 * representing an x,y pair) 1531 * <li>followed by 1532 * a byte indicating the winding rule 1533 * ({@link #WIND_EVEN_ODD WIND_EVEN_ODD} or 1534 * {@link #WIND_NON_ZERO WIND_NON_ZERO}) 1535 * <li>followed by 1536 * {@code NP} (or unlimited if {@code NP < 0}) sets of values consisting of 1537 * a single byte indicating a path segment type 1538 * followed by one or more pairs of float or double 1539 * values representing the coordinates of the path segment 1540 * <li>followed by 1541 * a byte indicating the end of the path (SERIAL_PATH_END). 1542 * </ol> 1543 * <p> 1544 * The following byte value constants are used in the serialized form 1545 * of {@code Path2D} objects: 1546 * <table> 1547 * <tr> 1548 * <th>Constant Name</th> 1549 * <th>Byte Value</th> 1550 * <th>Followed by</th> 1551 * <th>Description</th> 1552 * </tr> 1553 * <tr> 1554 * <td>{@code SERIAL_STORAGE_FLT_ARRAY}</td> 1555 * <td>0x30</td> 1556 * <td></td> 1557 * <td>A hint that the original {@code Path2D} object stored 1558 * the coordinates in a Java array of floats.</td> 1559 * </tr> 1560 * <tr> 1561 * <td>{@code SERIAL_STORAGE_DBL_ARRAY}</td> 1562 * <td>0x31</td> 1563 * <td></td> 1564 * <td>A hint that the original {@code Path2D} object stored 1565 * the coordinates in a Java array of doubles.</td> 1566 * </tr> 1567 * <tr> 1568 * <td>{@code SERIAL_SEG_FLT_MOVETO}</td> 1569 * <td>0x40</td> 1570 * <td>2 floats</td> 1571 * <td>A {@link #moveTo moveTo} path segment follows.</td> 1572 * </tr> 1573 * <tr> 1574 * <td>{@code SERIAL_SEG_FLT_LINETO}</td> 1575 * <td>0x41</td> 1576 * <td>2 floats</td> 1577 * <td>A {@link #lineTo lineTo} path segment follows.</td> 1578 * </tr> 1579 * <tr> 1580 * <td>{@code SERIAL_SEG_FLT_QUADTO}</td> 1581 * <td>0x42</td> 1582 * <td>4 floats</td> 1583 * <td>A {@link #quadTo quadTo} path segment follows.</td> 1584 * </tr> 1585 * <tr> 1586 * <td>{@code SERIAL_SEG_FLT_CUBICTO}</td> 1587 * <td>0x43</td> 1588 * <td>6 floats</td> 1589 * <td>A {@link #curveTo curveTo} path segment follows.</td> 1590 * </tr> 1591 * <tr> 1592 * <td>{@code SERIAL_SEG_DBL_MOVETO}</td> 1593 * <td>0x50</td> 1594 * <td>2 doubles</td> 1595 * <td>A {@link #moveTo moveTo} path segment follows.</td> 1596 * </tr> 1597 * <tr> 1598 * <td>{@code SERIAL_SEG_DBL_LINETO}</td> 1599 * <td>0x51</td> 1600 * <td>2 doubles</td> 1601 * <td>A {@link #lineTo lineTo} path segment follows.</td> 1602 * </tr> 1603 * <tr> 1604 * <td>{@code SERIAL_SEG_DBL_QUADTO}</td> 1605 * <td>0x52</td> 1606 * <td>4 doubles</td> 1607 * <td>A {@link #curveTo curveTo} path segment follows.</td> 1608 * </tr> 1609 * <tr> 1610 * <td>{@code SERIAL_SEG_DBL_CUBICTO}</td> 1611 * <td>0x53</td> 1612 * <td>6 doubles</td> 1613 * <td>A {@link #curveTo curveTo} path segment follows.</td> 1614 * </tr> 1615 * <tr> 1616 * <td>{@code SERIAL_SEG_CLOSE}</td> 1617 * <td>0x60</td> 1618 * <td></td> 1619 * <td>A {@link #closePath closePath} path segment.</td> 1620 * </tr> 1621 * <tr> 1622 * <td>{@code SERIAL_PATH_END}</td> 1623 * <td>0x61</td> 1624 * <td></td> 1625 * <td>There are no more path segments following.</td> 1626 * </table> 1627 * 1628 * @since 1.6 1629 */ 1630 private void writeObject(java.io.ObjectOutputStream s) 1631 throws java.io.IOException 1632 { 1633 super.writeObject(s, true); 1634 } 1635 1636 /** 1637 * Reads the default serializable fields from the 1638 * {@code ObjectInputStream} followed by an explicit 1639 * serialization of the path segments stored in this 1640 * path. 1641 * <p> 1642 * There are no default serializable fields as of 1.6. 1643 * <p> 1644 * The serial data for this object is described in the 1645 * writeObject method. 1646 * 1647 * @since 1.6 1648 */ 1649 private void readObject(java.io.ObjectInputStream s) 1650 throws java.lang.ClassNotFoundException, java.io.IOException 1651 { 1652 super.readObject(s, true); 1653 } 1654 1655 static class CopyIterator extends Path2D.Iterator { 1656 double doubleCoords[]; 1657 1658 CopyIterator(Path2D.Double p2dd) { 1659 super(p2dd); 1660 this.doubleCoords = p2dd.doubleCoords; 1661 } 1662 1663 public int currentSegment(float[] coords) { 1664 int type = path.pointTypes[typeIdx]; 1665 int numCoords = curvecoords[type]; 1666 if (numCoords > 0) { 1667 for (int i = 0; i < numCoords; i++) { 1668 coords[i] = (float) doubleCoords[pointIdx + i]; 1669 } 1670 } 1671 return type; 1672 } 1673 1674 public int currentSegment(double[] coords) { 1675 int type = path.pointTypes[typeIdx]; 1676 int numCoords = curvecoords[type]; 1677 if (numCoords > 0) { 1678 System.arraycopy(doubleCoords, pointIdx, 1679 coords, 0, numCoords); 1680 } 1681 return type; 1682 } 1683 } 1684 1685 static class TxIterator extends Path2D.Iterator { 1686 double doubleCoords[]; 1687 AffineTransform affine; 1688 1689 TxIterator(Path2D.Double p2dd, AffineTransform at) { 1690 super(p2dd); 1691 this.doubleCoords = p2dd.doubleCoords; 1692 this.affine = at; 1693 } 1694 1695 public int currentSegment(float[] coords) { 1696 int type = path.pointTypes[typeIdx]; 1697 int numCoords = curvecoords[type]; 1698 if (numCoords > 0) { 1699 affine.transform(doubleCoords, pointIdx, 1700 coords, 0, numCoords / 2); 1701 } 1702 return type; 1703 } 1704 1705 public int currentSegment(double[] coords) { 1706 int type = path.pointTypes[typeIdx]; 1707 int numCoords = curvecoords[type]; 1708 if (numCoords > 0) { 1709 affine.transform(doubleCoords, pointIdx, 1710 coords, 0, numCoords / 2); 1711 } 1712 return type; 1713 } 1714 } 1715 } 1716 1717 /** 1718 * Adds a point to the path by moving to the specified 1719 * coordinates specified in double precision. 1720 * 1721 * @param x the specified X coordinate 1722 * @param y the specified Y coordinate 1723 * @since 1.6 1724 */ 1725 public abstract void moveTo(double x, double y); 1726 1727 /** 1728 * Adds a point to the path by drawing a straight line from the 1729 * current coordinates to the new specified coordinates 1730 * specified in double precision. 1731 * 1732 * @param x the specified X coordinate 1733 * @param y the specified Y coordinate 1734 * @since 1.6 1735 */ 1736 public abstract void lineTo(double x, double y); 1737 1738 /** 1739 * Adds a curved segment, defined by two new points, to the path by 1740 * drawing a Quadratic curve that intersects both the current 1741 * coordinates and the specified coordinates {@code (x2,y2)}, 1742 * using the specified point {@code (x1,y1)} as a quadratic 1743 * parametric control point. 1744 * All coordinates are specified in double precision. 1745 * 1746 * @param x1 the X coordinate of the quadratic control point 1747 * @param y1 the Y coordinate of the quadratic control point 1748 * @param x2 the X coordinate of the final end point 1749 * @param y2 the Y coordinate of the final end point 1750 * @since 1.6 1751 */ 1752 public abstract void quadTo(double x1, double y1, 1753 double x2, double y2); 1754 1755 /** 1756 * Adds a curved segment, defined by three new points, to the path by 1757 * drawing a Bézier curve that intersects both the current 1758 * coordinates and the specified coordinates {@code (x3,y3)}, 1759 * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as 1760 * Bézier control points. 1761 * All coordinates are specified in double precision. 1762 * 1763 * @param x1 the X coordinate of the first Bézier control point 1764 * @param y1 the Y coordinate of the first Bézier control point 1765 * @param x2 the X coordinate of the second Bézier control point 1766 * @param y2 the Y coordinate of the second Bézier control point 1767 * @param x3 the X coordinate of the final end point 1768 * @param y3 the Y coordinate of the final end point 1769 * @since 1.6 1770 */ 1771 public abstract void curveTo(double x1, double y1, 1772 double x2, double y2, 1773 double x3, double y3); 1774 1775 /** 1776 * Closes the current subpath by drawing a straight line back to 1777 * the coordinates of the last {@code moveTo}. If the path is already 1778 * closed then this method has no effect. 1779 * 1780 * @since 1.6 1781 */ 1782 public final synchronized void closePath() { 1783 if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) { 1784 needRoom(true, 0); 1785 pointTypes[numTypes++] = SEG_CLOSE; 1786 } 1787 } 1788 1789 /** 1790 * Appends the geometry of the specified {@code Shape} object to the 1791 * path, possibly connecting the new geometry to the existing path 1792 * segments with a line segment. 1793 * If the {@code connect} parameter is {@code true} and the 1794 * path is not empty then any initial {@code moveTo} in the 1795 * geometry of the appended {@code Shape} 1796 * is turned into a {@code lineTo} segment. 1797 * If the destination coordinates of such a connecting {@code lineTo} 1798 * segment match the ending coordinates of a currently open 1799 * subpath then the segment is omitted as superfluous. 1800 * The winding rule of the specified {@code Shape} is ignored 1801 * and the appended geometry is governed by the winding 1802 * rule specified for this path. 1803 * 1804 * @param s the {@code Shape} whose geometry is appended 1805 * to this path 1806 * @param connect a boolean to control whether or not to turn an initial 1807 * {@code moveTo} segment into a {@code lineTo} segment 1808 * to connect the new geometry to the existing path 1809 * @since 1.6 1810 */ 1811 public final void append(Shape s, boolean connect) { 1812 append(s.getPathIterator(null), connect); 1813 } 1814 1815 /** 1816 * Appends the geometry of the specified 1817 * {@link PathIterator} object 1818 * to the path, possibly connecting the new geometry to the existing 1819 * path segments with a line segment. 1820 * If the {@code connect} parameter is {@code true} and the 1821 * path is not empty then any initial {@code moveTo} in the 1822 * geometry of the appended {@code Shape} is turned into a 1823 * {@code lineTo} segment. 1824 * If the destination coordinates of such a connecting {@code lineTo} 1825 * segment match the ending coordinates of a currently open 1826 * subpath then the segment is omitted as superfluous. 1827 * The winding rule of the specified {@code Shape} is ignored 1828 * and the appended geometry is governed by the winding 1829 * rule specified for this path. 1830 * 1831 * @param pi the {@code PathIterator} whose geometry is appended to 1832 * this path 1833 * @param connect a boolean to control whether or not to turn an initial 1834 * {@code moveTo} segment into a {@code lineTo} segment 1835 * to connect the new geometry to the existing path 1836 * @since 1.6 1837 */ 1838 public abstract void append(PathIterator pi, boolean connect); 1839 1840 /** 1841 * Returns the fill style winding rule. 1842 * 1843 * @return an integer representing the current winding rule. 1844 * @see #WIND_EVEN_ODD 1845 * @see #WIND_NON_ZERO 1846 * @see #setWindingRule 1847 * @since 1.6 1848 */ 1849 public final synchronized int getWindingRule() { 1850 return windingRule; 1851 } 1852 1853 /** 1854 * Sets the winding rule for this path to the specified value. 1855 * 1856 * @param rule an integer representing the specified 1857 * winding rule 1858 * @exception IllegalArgumentException if 1859 * {@code rule} is not either 1860 * {@link #WIND_EVEN_ODD} or 1861 * {@link #WIND_NON_ZERO} 1862 * @see #getWindingRule 1863 * @since 1.6 1864 */ 1865 public final void setWindingRule(int rule) { 1866 if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) { 1867 throw new IllegalArgumentException("winding rule must be "+ 1868 "WIND_EVEN_ODD or "+ 1869 "WIND_NON_ZERO"); 1870 } 1871 windingRule = rule; 1872 } 1873 1874 /** 1875 * Returns the coordinates most recently added to the end of the path 1876 * as a {@link Point2D} object. 1877 * 1878 * @return a {@code Point2D} object containing the ending coordinates of 1879 * the path or {@code null} if there are no points in the path. 1880 * @since 1.6 1881 */ 1882 public final synchronized Point2D getCurrentPoint() { 1883 int index = numCoords; 1884 if (numTypes < 1 || index < 1) { 1885 return null; 1886 } 1887 if (pointTypes[numTypes - 1] == SEG_CLOSE) { 1888 loop: 1889 for (int i = numTypes - 2; i > 0; i--) { 1890 switch (pointTypes[i]) { 1891 case SEG_MOVETO: 1892 break loop; 1893 case SEG_LINETO: 1894 index -= 2; 1895 break; 1896 case SEG_QUADTO: 1897 index -= 4; 1898 break; 1899 case SEG_CUBICTO: 1900 index -= 6; 1901 break; 1902 case SEG_CLOSE: 1903 break; 1904 } 1905 } 1906 } 1907 return getPoint(index - 2); 1908 } 1909 1910 /** 1911 * Resets the path to empty. The append position is set back to the 1912 * beginning of the path and all coordinates and point types are 1913 * forgotten. 1914 * 1915 * @since 1.6 1916 */ 1917 public final synchronized void reset() { 1918 numTypes = numCoords = 0; 1919 } 1920 1921 /** 1922 * Transforms the geometry of this path using the specified 1923 * {@link AffineTransform}. 1924 * The geometry is transformed in place, which permanently changes the 1925 * boundary defined by this object. 1926 * 1927 * @param at the {@code AffineTransform} used to transform the area 1928 * @since 1.6 1929 */ 1930 public abstract void transform(AffineTransform at); 1931 1932 /** 1933 * Returns a new {@code Shape} representing a transformed version 1934 * of this {@code Path2D}. 1935 * Note that the exact type and coordinate precision of the return 1936 * value is not specified for this method. 1937 * The method will return a Shape that contains no less precision 1938 * for the transformed geometry than this {@code Path2D} currently 1939 * maintains, but it may contain no more precision either. 1940 * If the tradeoff of precision vs. storage size in the result is 1941 * important then the convenience constructors in the 1942 * {@link Path2D.Float#Float(Shape, AffineTransform) Path2D.Float} 1943 * and 1944 * {@link Path2D.Double#Double(Shape, AffineTransform) Path2D.Double} 1945 * subclasses should be used to make the choice explicit. 1946 * 1947 * @param at the {@code AffineTransform} used to transform a 1948 * new {@code Shape}. 1949 * @return a new {@code Shape}, transformed with the specified 1950 * {@code AffineTransform}. 1951 * @since 1.6 1952 */ 1953 public final synchronized Shape createTransformedShape(AffineTransform at) { 1954 Path2D p2d = (Path2D) clone(); 1955 if (at != null) { 1956 p2d.transform(at); 1957 } 1958 return p2d; 1959 } 1960 1961 /** 1962 * {@inheritDoc} 1963 * @since 1.6 1964 */ 1965 public final Rectangle getBounds() { 1966 return getBounds2D().getBounds(); 1967 } 1968 1969 /** 1970 * Tests if the specified coordinates are inside the closed 1971 * boundary of the specified {@link PathIterator}. 1972 * <p> 1973 * This method provides a basic facility for implementors of 1974 * the {@link Shape} interface to implement support for the 1975 * {@link Shape#contains(double, double)} method. 1976 * 1977 * @param pi the specified {@code PathIterator} 1978 * @param x the specified X coordinate 1979 * @param y the specified Y coordinate 1980 * @return {@code true} if the specified coordinates are inside the 1981 * specified {@code PathIterator}; {@code false} otherwise 1982 * @since 1.6 1983 */ 1984 public static boolean contains(PathIterator pi, double x, double y) { 1985 if (x * 0.0 + y * 0.0 == 0.0) { 1986 /* N * 0.0 is 0.0 only if N is finite. 1987 * Here we know that both x and y are finite. 1988 */ 1989 int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 1); 1990 int cross = Curve.pointCrossingsForPath(pi, x, y); 1991 return ((cross & mask) != 0); 1992 } else { 1993 /* Either x or y was infinite or NaN. 1994 * A NaN always produces a negative response to any test 1995 * and Infinity values cannot be "inside" any path so 1996 * they should return false as well. 1997 */ 1998 return false; 1999 } 2000 } 2001 2002 /** 2003 * Tests if the specified {@link Point2D} is inside the closed 2004 * boundary of the specified {@link PathIterator}. 2005 * <p> 2006 * This method provides a basic facility for implementors of 2007 * the {@link Shape} interface to implement support for the 2008 * {@link Shape#contains(Point2D)} method. 2009 * 2010 * @param pi the specified {@code PathIterator} 2011 * @param p the specified {@code Point2D} 2012 * @return {@code true} if the specified coordinates are inside the 2013 * specified {@code PathIterator}; {@code false} otherwise 2014 * @since 1.6 2015 */ 2016 public static boolean contains(PathIterator pi, Point2D p) { 2017 return contains(pi, p.getX(), p.getY()); 2018 } 2019 2020 /** 2021 * {@inheritDoc} 2022 * @since 1.6 2023 */ 2024 public final boolean contains(double x, double y) { 2025 if (x * 0.0 + y * 0.0 == 0.0) { 2026 /* N * 0.0 is 0.0 only if N is finite. 2027 * Here we know that both x and y are finite. 2028 */ 2029 if (numTypes < 2) { 2030 return false; 2031 } 2032 int mask = (windingRule == WIND_NON_ZERO ? -1 : 1); 2033 return ((pointCrossings(x, y) & mask) != 0); 2034 } else { 2035 /* Either x or y was infinite or NaN. 2036 * A NaN always produces a negative response to any test 2037 * and Infinity values cannot be "inside" any path so 2038 * they should return false as well. 2039 */ 2040 return false; 2041 } 2042 } 2043 2044 /** 2045 * {@inheritDoc} 2046 * @since 1.6 2047 */ 2048 public final boolean contains(Point2D p) { 2049 return contains(p.getX(), p.getY()); 2050 } 2051 2052 /** 2053 * Tests if the specified rectangular area is entirely inside the 2054 * closed boundary of the specified {@link PathIterator}. 2055 * <p> 2056 * This method provides a basic facility for implementors of 2057 * the {@link Shape} interface to implement support for the 2058 * {@link Shape#contains(double, double, double, double)} method. 2059 * <p> 2060 * This method object may conservatively return false in 2061 * cases where the specified rectangular area intersects a 2062 * segment of the path, but that segment does not represent a 2063 * boundary between the interior and exterior of the path. 2064 * Such segments could lie entirely within the interior of the 2065 * path if they are part of a path with a {@link #WIND_NON_ZERO} 2066 * winding rule or if the segments are retraced in the reverse 2067 * direction such that the two sets of segments cancel each 2068 * other out without any exterior area falling between them. 2069 * To determine whether segments represent true boundaries of 2070 * the interior of the path would require extensive calculations 2071 * involving all of the segments of the path and the winding 2072 * rule and are thus beyond the scope of this implementation. 2073 * 2074 * @param pi the specified {@code PathIterator} 2075 * @param x the specified X coordinate 2076 * @param y the specified Y coordinate 2077 * @param w the width of the specified rectangular area 2078 * @param h the height of the specified rectangular area 2079 * @return {@code true} if the specified {@code PathIterator} contains 2080 * the specified rectangular area; {@code false} otherwise. 2081 * @since 1.6 2082 */ 2083 public static boolean contains(PathIterator pi, 2084 double x, double y, double w, double h) 2085 { 2086 if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) { 2087 /* [xy]+[wh] is NaN if any of those values are NaN, 2088 * or if adding the two together would produce NaN 2089 * by virtue of adding opposing Infinte values. 2090 * Since we need to add them below, their sum must 2091 * not be NaN. 2092 * We return false because NaN always produces a 2093 * negative response to tests 2094 */ 2095 return false; 2096 } 2097 if (w <= 0 || h <= 0) { 2098 return false; 2099 } 2100 int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2); 2101 int crossings = Curve.rectCrossingsForPath(pi, x, y, x+w, y+h); 2102 return (crossings != Curve.RECT_INTERSECTS && 2103 (crossings & mask) != 0); 2104 } 2105 2106 /** 2107 * Tests if the specified {@link Rectangle2D} is entirely inside the 2108 * closed boundary of the specified {@link PathIterator}. 2109 * <p> 2110 * This method provides a basic facility for implementors of 2111 * the {@link Shape} interface to implement support for the 2112 * {@link Shape#contains(Rectangle2D)} method. 2113 * <p> 2114 * This method object may conservatively return false in 2115 * cases where the specified rectangular area intersects a 2116 * segment of the path, but that segment does not represent a 2117 * boundary between the interior and exterior of the path. 2118 * Such segments could lie entirely within the interior of the 2119 * path if they are part of a path with a {@link #WIND_NON_ZERO} 2120 * winding rule or if the segments are retraced in the reverse 2121 * direction such that the two sets of segments cancel each 2122 * other out without any exterior area falling between them. 2123 * To determine whether segments represent true boundaries of 2124 * the interior of the path would require extensive calculations 2125 * involving all of the segments of the path and the winding 2126 * rule and are thus beyond the scope of this implementation. 2127 * 2128 * @param pi the specified {@code PathIterator} 2129 * @param r a specified {@code Rectangle2D} 2130 * @return {@code true} if the specified {@code PathIterator} contains 2131 * the specified {@code Rectangle2D}; {@code false} otherwise. 2132 * @since 1.6 2133 */ 2134 public static boolean contains(PathIterator pi, Rectangle2D r) { 2135 return contains(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight()); 2136 } 2137 2138 /** 2139 * {@inheritDoc} 2140 * <p> 2141 * This method object may conservatively return false in 2142 * cases where the specified rectangular area intersects a 2143 * segment of the path, but that segment does not represent a 2144 * boundary between the interior and exterior of the path. 2145 * Such segments could lie entirely within the interior of the 2146 * path if they are part of a path with a {@link #WIND_NON_ZERO} 2147 * winding rule or if the segments are retraced in the reverse 2148 * direction such that the two sets of segments cancel each 2149 * other out without any exterior area falling between them. 2150 * To determine whether segments represent true boundaries of 2151 * the interior of the path would require extensive calculations 2152 * involving all of the segments of the path and the winding 2153 * rule and are thus beyond the scope of this implementation. 2154 * 2155 * @since 1.6 2156 */ 2157 public final boolean contains(double x, double y, double w, double h) { 2158 if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) { 2159 /* [xy]+[wh] is NaN if any of those values are NaN, 2160 * or if adding the two together would produce NaN 2161 * by virtue of adding opposing Infinte values. 2162 * Since we need to add them below, their sum must 2163 * not be NaN. 2164 * We return false because NaN always produces a 2165 * negative response to tests 2166 */ 2167 return false; 2168 } 2169 if (w <= 0 || h <= 0) { 2170 return false; 2171 } 2172 int mask = (windingRule == WIND_NON_ZERO ? -1 : 2); 2173 int crossings = rectCrossings(x, y, x+w, y+h); 2174 return (crossings != Curve.RECT_INTERSECTS && 2175 (crossings & mask) != 0); 2176 } 2177 2178 /** 2179 * {@inheritDoc} 2180 * <p> 2181 * This method object may conservatively return false in 2182 * cases where the specified rectangular area intersects a 2183 * segment of the path, but that segment does not represent a 2184 * boundary between the interior and exterior of the path. 2185 * Such segments could lie entirely within the interior of the 2186 * path if they are part of a path with a {@link #WIND_NON_ZERO} 2187 * winding rule or if the segments are retraced in the reverse 2188 * direction such that the two sets of segments cancel each 2189 * other out without any exterior area falling between them. 2190 * To determine whether segments represent true boundaries of 2191 * the interior of the path would require extensive calculations 2192 * involving all of the segments of the path and the winding 2193 * rule and are thus beyond the scope of this implementation. 2194 * 2195 * @since 1.6 2196 */ 2197 public final boolean contains(Rectangle2D r) { 2198 return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 2199 } 2200 2201 /** 2202 * Tests if the interior of the specified {@link PathIterator} 2203 * intersects the interior of a specified set of rectangular 2204 * coordinates. 2205 * <p> 2206 * This method provides a basic facility for implementors of 2207 * the {@link Shape} interface to implement support for the 2208 * {@link Shape#intersects(double, double, double, double)} method. 2209 * <p> 2210 * This method object may conservatively return true in 2211 * cases where the specified rectangular area intersects a 2212 * segment of the path, but that segment does not represent a 2213 * boundary between the interior and exterior of the path. 2214 * Such a case may occur if some set of segments of the 2215 * path are retraced in the reverse direction such that the 2216 * two sets of segments cancel each other out without any 2217 * interior area between them. 2218 * To determine whether segments represent true boundaries of 2219 * the interior of the path would require extensive calculations 2220 * involving all of the segments of the path and the winding 2221 * rule and are thus beyond the scope of this implementation. 2222 * 2223 * @param pi the specified {@code PathIterator} 2224 * @param x the specified X coordinate 2225 * @param y the specified Y coordinate 2226 * @param w the width of the specified rectangular coordinates 2227 * @param h the height of the specified rectangular coordinates 2228 * @return {@code true} if the specified {@code PathIterator} and 2229 * the interior of the specified set of rectangular 2230 * coordinates intersect each other; {@code false} otherwise. 2231 * @since 1.6 2232 */ 2233 public static boolean intersects(PathIterator pi, 2234 double x, double y, double w, double h) 2235 { 2236 if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) { 2237 /* [xy]+[wh] is NaN if any of those values are NaN, 2238 * or if adding the two together would produce NaN 2239 * by virtue of adding opposing Infinte values. 2240 * Since we need to add them below, their sum must 2241 * not be NaN. 2242 * We return false because NaN always produces a 2243 * negative response to tests 2244 */ 2245 return false; 2246 } 2247 if (w <= 0 || h <= 0) { 2248 return false; 2249 } 2250 int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2); 2251 int crossings = Curve.rectCrossingsForPath(pi, x, y, x+w, y+h); 2252 return (crossings == Curve.RECT_INTERSECTS || 2253 (crossings & mask) != 0); 2254 } 2255 2256 /** 2257 * Tests if the interior of the specified {@link PathIterator} 2258 * intersects the interior of a specified {@link Rectangle2D}. 2259 * <p> 2260 * This method provides a basic facility for implementors of 2261 * the {@link Shape} interface to implement support for the 2262 * {@link Shape#intersects(Rectangle2D)} method. 2263 * <p> 2264 * This method object may conservatively return true in 2265 * cases where the specified rectangular area intersects a 2266 * segment of the path, but that segment does not represent a 2267 * boundary between the interior and exterior of the path. 2268 * Such a case may occur if some set of segments of the 2269 * path are retraced in the reverse direction such that the 2270 * two sets of segments cancel each other out without any 2271 * interior area between them. 2272 * To determine whether segments represent true boundaries of 2273 * the interior of the path would require extensive calculations 2274 * involving all of the segments of the path and the winding 2275 * rule and are thus beyond the scope of this implementation. 2276 * 2277 * @param pi the specified {@code PathIterator} 2278 * @param r the specified {@code Rectangle2D} 2279 * @return {@code true} if the specified {@code PathIterator} and 2280 * the interior of the specified {@code Rectangle2D} 2281 * intersect each other; {@code false} otherwise. 2282 * @since 1.6 2283 */ 2284 public static boolean intersects(PathIterator pi, Rectangle2D r) { 2285 return intersects(pi, r.getX(), r.getY(), r.getWidth(), r.getHeight()); 2286 } 2287 2288 /** 2289 * {@inheritDoc} 2290 * <p> 2291 * This method object may conservatively return true in 2292 * cases where the specified rectangular area intersects a 2293 * segment of the path, but that segment does not represent a 2294 * boundary between the interior and exterior of the path. 2295 * Such a case may occur if some set of segments of the 2296 * path are retraced in the reverse direction such that the 2297 * two sets of segments cancel each other out without any 2298 * interior area between them. 2299 * To determine whether segments represent true boundaries of 2300 * the interior of the path would require extensive calculations 2301 * involving all of the segments of the path and the winding 2302 * rule and are thus beyond the scope of this implementation. 2303 * 2304 * @since 1.6 2305 */ 2306 public final boolean intersects(double x, double y, double w, double h) { 2307 if (java.lang.Double.isNaN(x+w) || java.lang.Double.isNaN(y+h)) { 2308 /* [xy]+[wh] is NaN if any of those values are NaN, 2309 * or if adding the two together would produce NaN 2310 * by virtue of adding opposing Infinte values. 2311 * Since we need to add them below, their sum must 2312 * not be NaN. 2313 * We return false because NaN always produces a 2314 * negative response to tests 2315 */ 2316 return false; 2317 } 2318 if (w <= 0 || h <= 0) { 2319 return false; 2320 } 2321 int mask = (windingRule == WIND_NON_ZERO ? -1 : 2); 2322 int crossings = rectCrossings(x, y, x+w, y+h); 2323 return (crossings == Curve.RECT_INTERSECTS || 2324 (crossings & mask) != 0); 2325 } 2326 2327 /** 2328 * {@inheritDoc} 2329 * <p> 2330 * This method object may conservatively return true in 2331 * cases where the specified rectangular area intersects a 2332 * segment of the path, but that segment does not represent a 2333 * boundary between the interior and exterior of the path. 2334 * Such a case may occur if some set of segments of the 2335 * path are retraced in the reverse direction such that the 2336 * two sets of segments cancel each other out without any 2337 * interior area between them. 2338 * To determine whether segments represent true boundaries of 2339 * the interior of the path would require extensive calculations 2340 * involving all of the segments of the path and the winding 2341 * rule and are thus beyond the scope of this implementation. 2342 * 2343 * @since 1.6 2344 */ 2345 public final boolean intersects(Rectangle2D r) { 2346 return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); 2347 } 2348 2349 /** 2350 * {@inheritDoc} 2351 * <p> 2352 * The iterator for this class is not multi-threaded safe, 2353 * which means that this {@code Path2D} class does not 2354 * guarantee that modifications to the geometry of this 2355 * {@code Path2D} object do not affect any iterations of 2356 * that geometry that are already in process. 2357 * 2358 * @since 1.6 2359 */ 2360 public final PathIterator getPathIterator(AffineTransform at, 2361 double flatness) 2362 { 2363 return new FlatteningPathIterator(getPathIterator(at), flatness); 2364 } 2365 2366 /** 2367 * Creates a new object of the same class as this object. 2368 * 2369 * @return a clone of this instance. 2370 * @exception OutOfMemoryError if there is not enough memory. 2371 * @see java.lang.Cloneable 2372 * @since 1.6 2373 */ 2374 public abstract Object clone(); 2375 // Note: It would be nice to have this return Path2D 2376 // but one of our subclasses (GeneralPath) needs to 2377 // offer "public Object clone()" for backwards 2378 // compatibility so we cannot restrict it further. 2379 // REMIND: Can we do both somehow? 2380 2381 /* 2382 * Support fields and methods for serializing the subclasses. 2383 */ 2384 private static final byte SERIAL_STORAGE_FLT_ARRAY = 0x30; 2385 private static final byte SERIAL_STORAGE_DBL_ARRAY = 0x31; 2386 2387 private static final byte SERIAL_SEG_FLT_MOVETO = 0x40; 2388 private static final byte SERIAL_SEG_FLT_LINETO = 0x41; 2389 private static final byte SERIAL_SEG_FLT_QUADTO = 0x42; 2390 private static final byte SERIAL_SEG_FLT_CUBICTO = 0x43; 2391 2392 private static final byte SERIAL_SEG_DBL_MOVETO = 0x50; 2393 private static final byte SERIAL_SEG_DBL_LINETO = 0x51; 2394 private static final byte SERIAL_SEG_DBL_QUADTO = 0x52; 2395 private static final byte SERIAL_SEG_DBL_CUBICTO = 0x53; 2396 2397 private static final byte SERIAL_SEG_CLOSE = 0x60; 2398 private static final byte SERIAL_PATH_END = 0x61; 2399 2400 final void writeObject(java.io.ObjectOutputStream s, boolean isdbl) 2401 throws java.io.IOException 2402 { 2403 s.defaultWriteObject(); 2404 2405 float fCoords[]; 2406 double dCoords[]; 2407 2408 if (isdbl) { 2409 dCoords = ((Path2D.Double) this).doubleCoords; 2410 fCoords = null; 2411 } else { 2412 fCoords = ((Path2D.Float) this).floatCoords; 2413 dCoords = null; 2414 } 2415 2416 int numTypes = this.numTypes; 2417 2418 s.writeByte(isdbl 2419 ? SERIAL_STORAGE_DBL_ARRAY 2420 : SERIAL_STORAGE_FLT_ARRAY); 2421 s.writeInt(numTypes); 2422 s.writeInt(numCoords); 2423 s.writeByte((byte) windingRule); 2424 2425 int cindex = 0; 2426 for (int i = 0; i < numTypes; i++) { 2427 int npoints; 2428 byte serialtype; 2429 switch (pointTypes[i]) { 2430 case SEG_MOVETO: 2431 npoints = 1; 2432 serialtype = (isdbl 2433 ? SERIAL_SEG_DBL_MOVETO 2434 : SERIAL_SEG_FLT_MOVETO); 2435 break; 2436 case SEG_LINETO: 2437 npoints = 1; 2438 serialtype = (isdbl 2439 ? SERIAL_SEG_DBL_LINETO 2440 : SERIAL_SEG_FLT_LINETO); 2441 break; 2442 case SEG_QUADTO: 2443 npoints = 2; 2444 serialtype = (isdbl 2445 ? SERIAL_SEG_DBL_QUADTO 2446 : SERIAL_SEG_FLT_QUADTO); 2447 break; 2448 case SEG_CUBICTO: 2449 npoints = 3; 2450 serialtype = (isdbl 2451 ? SERIAL_SEG_DBL_CUBICTO 2452 : SERIAL_SEG_FLT_CUBICTO); 2453 break; 2454 case SEG_CLOSE: 2455 npoints = 0; 2456 serialtype = SERIAL_SEG_CLOSE; 2457 break; 2458 2459 default: 2460 // Should never happen 2461 throw new InternalError("unrecognized path type"); 2462 } 2463 s.writeByte(serialtype); 2464 while (--npoints >= 0) { 2465 if (isdbl) { 2466 s.writeDouble(dCoords[cindex++]); 2467 s.writeDouble(dCoords[cindex++]); 2468 } else { 2469 s.writeFloat(fCoords[cindex++]); 2470 s.writeFloat(fCoords[cindex++]); 2471 } 2472 } 2473 } 2474 s.writeByte(SERIAL_PATH_END); 2475 } 2476 2477 final void readObject(java.io.ObjectInputStream s, boolean storedbl) 2478 throws java.lang.ClassNotFoundException, java.io.IOException 2479 { 2480 s.defaultReadObject(); 2481 2482 // The subclass calls this method with the storage type that 2483 // they want us to use (storedbl) so we ignore the storage 2484 // method hint from the stream. 2485 s.readByte(); 2486 int nT = s.readInt(); 2487 int nC = s.readInt(); 2488 try { 2489 setWindingRule(s.readByte()); 2490 } catch (IllegalArgumentException iae) { 2491 throw new java.io.InvalidObjectException(iae.getMessage()); 2492 } 2493 2494 pointTypes = new byte[(nT < 0) ? INIT_SIZE : nT]; 2495 if (nC < 0) { 2496 nC = INIT_SIZE * 2; 2497 } 2498 if (storedbl) { 2499 ((Path2D.Double) this).doubleCoords = new double[nC]; 2500 } else { 2501 ((Path2D.Float) this).floatCoords = new float[nC]; 2502 } 2503 2504 PATHDONE: 2505 for (int i = 0; nT < 0 || i < nT; i++) { 2506 boolean isdbl; 2507 int npoints; 2508 byte segtype; 2509 2510 byte serialtype = s.readByte(); 2511 switch (serialtype) { 2512 case SERIAL_SEG_FLT_MOVETO: 2513 isdbl = false; 2514 npoints = 1; 2515 segtype = SEG_MOVETO; 2516 break; 2517 case SERIAL_SEG_FLT_LINETO: 2518 isdbl = false; 2519 npoints = 1; 2520 segtype = SEG_LINETO; 2521 break; 2522 case SERIAL_SEG_FLT_QUADTO: 2523 isdbl = false; 2524 npoints = 2; 2525 segtype = SEG_QUADTO; 2526 break; 2527 case SERIAL_SEG_FLT_CUBICTO: 2528 isdbl = false; 2529 npoints = 3; 2530 segtype = SEG_CUBICTO; 2531 break; 2532 2533 case SERIAL_SEG_DBL_MOVETO: 2534 isdbl = true; 2535 npoints = 1; 2536 segtype = SEG_MOVETO; 2537 break; 2538 case SERIAL_SEG_DBL_LINETO: 2539 isdbl = true; 2540 npoints = 1; 2541 segtype = SEG_LINETO; 2542 break; 2543 case SERIAL_SEG_DBL_QUADTO: 2544 isdbl = true; 2545 npoints = 2; 2546 segtype = SEG_QUADTO; 2547 break; 2548 case SERIAL_SEG_DBL_CUBICTO: 2549 isdbl = true; 2550 npoints = 3; 2551 segtype = SEG_CUBICTO; 2552 break; 2553 2554 case SERIAL_SEG_CLOSE: 2555 isdbl = false; 2556 npoints = 0; 2557 segtype = SEG_CLOSE; 2558 break; 2559 2560 case SERIAL_PATH_END: 2561 if (nT < 0) { 2562 break PATHDONE; 2563 } 2564 throw new StreamCorruptedException("unexpected PATH_END"); 2565 2566 default: 2567 throw new StreamCorruptedException("unrecognized path type"); 2568 } 2569 needRoom(segtype != SEG_MOVETO, npoints * 2); 2570 if (isdbl) { 2571 while (--npoints >= 0) { 2572 append(s.readDouble(), s.readDouble()); 2573 } 2574 } else { 2575 while (--npoints >= 0) { 2576 append(s.readFloat(), s.readFloat()); 2577 } 2578 } 2579 pointTypes[numTypes++] = segtype; 2580 } 2581 if (nT >= 0 && s.readByte() != SERIAL_PATH_END) { 2582 throw new StreamCorruptedException("missing PATH_END"); 2583 } 2584 } 2585 2586 static abstract class Iterator implements PathIterator { 2587 int typeIdx; 2588 int pointIdx; 2589 Path2D path; 2590 2591 static final int curvecoords[] = {2, 2, 4, 6, 0}; 2592 2593 Iterator(Path2D path) { 2594 this.path = path; 2595 } 2596 2597 public int getWindingRule() { 2598 return path.getWindingRule(); 2599 } 2600 2601 public boolean isDone() { 2602 return (typeIdx >= path.numTypes); 2603 } 2604 2605 public void next() { 2606 int type = path.pointTypes[typeIdx++]; 2607 pointIdx += curvecoords[type]; 2608 } 2609 } 2610 }