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