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