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