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