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