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