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