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