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