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