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