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