1 /*
   2  * Copyright (c) 2006, 2016, 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 com.sun.javafx.geom;
  27 
  28 import com.sun.javafx.geom.transform.BaseTransform;
  29 import java.util.Arrays;
  30 
  31 /**
  32  * The {@code Path2D} class provides a simple, yet flexible
  33  * shape which represents an arbitrary geometric path.
  34  * It can fully represent any path which can be iterated by the
  35  * {@link PathIterator} interface including all of its segment
  36  * types and winding rules and it implements all of the
  37  * basic hit testing methods of the {@link Shape} interface.
  38  * <p>
  39  * Use {@link Path2D} when dealing with data that can be represented
  40  * and used with floating point precision.
  41  * <p>
  42  * {@code Path2D} provides exactly those facilities required for
  43  * basic construction and management of a geometric path and
  44  * implementation of the above interfaces with little added
  45  * interpretation.
  46  * If it is useful to manipulate the interiors of closed
  47  * geometric shapes beyond simple hit testing then the
  48  * {@link Area} class provides additional capabilities
  49  * specifically targeted at closed figures.
  50  * While both classes nominally implement the {@code Shape}
  51  * interface, they differ in purpose and together they provide
  52  * two useful views of a geometric shape where {@code Path2D}
  53  * deals primarily with a trajectory formed by path segments
  54  * and {@code Area} deals more with interpretation and manipulation
  55  * of enclosed regions of 2D geometric space.
  56  * <p>
  57  * The {@link PathIterator} interface has more detailed descriptions
  58  * of the types of segments that make up a path and the winding rules
  59  * that control how to determine which regions are inside or outside
  60  * the path.
  61  *
  62  * @version 1.10, 05/05/07
  63  */
  64  public class Path2D extends Shape implements PathConsumer2D {
  65 
  66      static final int curvecoords[] = {2, 2, 4, 6, 0};
  67 
  68      public enum CornerPrefix {
  69          CORNER_ONLY,
  70          MOVE_THEN_CORNER,
  71          LINE_THEN_CORNER
  72      }
  73 
  74      /**
  75      * An even-odd winding rule for determining the interior of
  76      * a path.
  77      *
  78      * @see PathIterator#WIND_EVEN_ODD
  79      */
  80     public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
  81 
  82     /**
  83      * A non-zero winding rule for determining the interior of a
  84      * path.
  85      *
  86      * @see PathIterator#WIND_NON_ZERO
  87      */
  88     public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
  89 
  90     // For code simplicity, copy these constants to our namespace
  91     // and cast them to byte constants for easy storage.
  92     private static final byte SEG_MOVETO  = (byte) PathIterator.SEG_MOVETO;
  93     private static final byte SEG_LINETO  = (byte) PathIterator.SEG_LINETO;
  94     private static final byte SEG_QUADTO  = (byte) PathIterator.SEG_QUADTO;
  95     private static final byte SEG_CUBICTO = (byte) PathIterator.SEG_CUBICTO;
  96     private static final byte SEG_CLOSE   = (byte) PathIterator.SEG_CLOSE;
  97 
  98     byte[] pointTypes;
  99     int numTypes;
 100     int numCoords;
 101     int windingRule;
 102 
 103     static final int INIT_SIZE = 20;
 104     static final int EXPAND_MAX = 500;
 105     static final int EXPAND_MAX_COORDS = EXPAND_MAX * 2;
 106 
 107     float floatCoords[];
 108     float moveX, moveY;
 109     float prevX, prevY;
 110     float currX, currY;
 111 
 112     /**
 113      * Constructs a new empty single precision {@code Path2D} object
 114      * with a default winding rule of {@link #WIND_NON_ZERO}.
 115      */
 116     public Path2D() {
 117         this(WIND_NON_ZERO, INIT_SIZE);
 118     }
 119 
 120     /**
 121      * Constructs a new empty single precision {@code Path2D} object
 122      * with the specified winding rule to control operations that
 123      * require the interior of the path to be defined.
 124      *
 125      * @param rule the winding rule
 126      * @see #WIND_EVEN_ODD
 127      * @see #WIND_NON_ZERO
 128      */
 129     public Path2D(int rule) {
 130         this(rule, INIT_SIZE);
 131     }
 132 
 133     /**
 134      * Constructs a new empty single precision {@code Path2D} object
 135      * with the specified winding rule and the specified initial
 136      * capacity to store path segments.
 137      * This number is an initial guess as to how many path segments
 138      * will be added to the path, but the storage is expanded as
 139      * needed to store whatever path segments are added.
 140      *
 141      * @param rule the winding rule
 142      * @param initialCapacity the estimate for the number of path segments
 143      *                        in the path
 144      * @see #WIND_EVEN_ODD
 145      * @see #WIND_NON_ZERO
 146      */
 147     public Path2D(int rule, int initialCapacity) {
 148         setWindingRule(rule);
 149         this.pointTypes = new byte[initialCapacity];
 150         floatCoords = new float[initialCapacity * 2];
 151     }
 152 
 153     /**
 154      * Constructs a new single precision {@code Path2D} object
 155      * from an arbitrary {@link Shape} object.
 156      * All of the initial geometry and the winding rule for this path are
 157      * taken from the specified {@code Shape} object.
 158      *
 159      * @param s the specified {@code Shape} object
 160      */
 161     public Path2D(Shape s) {
 162         this(s, null);
 163     }
 164 
 165     /**
 166      * Constructs a new single precision {@code Path2D} object
 167      * from an arbitrary {@link Shape} object, transformed by an
 168      * {@link BaseTransform} object.
 169      * All of the initial geometry and the winding rule for this path are
 170      * taken from the specified {@code Shape} object and transformed
 171      * by the specified {@code BaseTransform} object.
 172      *
 173      * @param s the specified {@code Shape} object
 174      * @param tx the specified {@code BaseTransform} object
 175      */
 176     public Path2D(Shape s, BaseTransform tx) {
 177         if (s instanceof Path2D) {
 178             Path2D p2d = (Path2D) s;
 179             setWindingRule(p2d.windingRule);
 180             this.numTypes = p2d.numTypes;
 181             this.pointTypes = Arrays.copyOf(p2d.pointTypes, numTypes);
 182             this.numCoords = p2d.numCoords;
 183             if (tx == null || tx.isIdentity()) {
 184                 this.floatCoords = Arrays.copyOf(p2d.floatCoords, numCoords);
 185                 this.moveX = p2d.moveX;
 186                 this.moveY = p2d.moveY;
 187                 this.prevX = p2d.prevX;
 188                 this.prevY = p2d.prevY;
 189                 this.currX = p2d.currX;
 190                 this.currY = p2d.currY;
 191             } else {
 192                 this.floatCoords = new float[numCoords + 6];
 193                 tx.transform(p2d.floatCoords, 0, this.floatCoords, 0, numCoords / 2);
 194                 floatCoords[numCoords + 0] = moveX;
 195                 floatCoords[numCoords + 1] = moveY;
 196                 floatCoords[numCoords + 2] = prevX;
 197                 floatCoords[numCoords + 3] = prevY;
 198                 floatCoords[numCoords + 4] = currX;
 199                 floatCoords[numCoords + 5] = currY;
 200                 tx.transform(this.floatCoords, numCoords, this.floatCoords, numCoords, 3);
 201                 moveX = floatCoords[numCoords + 0];
 202                 moveY = floatCoords[numCoords + 1];
 203                 prevX = floatCoords[numCoords + 2];
 204                 prevY = floatCoords[numCoords + 3];
 205                 currX = floatCoords[numCoords + 4];
 206                 currY = floatCoords[numCoords + 5];
 207             }
 208         } else {
 209             PathIterator pi = s.getPathIterator(tx);
 210             setWindingRule(pi.getWindingRule());
 211             this.pointTypes = new byte[INIT_SIZE];
 212             this.floatCoords = new float[INIT_SIZE * 2];
 213             append(pi, false);
 214         }
 215     }
 216 
 217 
 218      /**
 219       * Construct a Path2D from pre-composed data.
 220       * Used by internal font code which has obtained the path data
 221       * for a glyph outline, and which promises not to
 222       * mess with the arrays, dropping all other references,
 223       so there's no need to clone them here.
 224       */
 225     public Path2D(int windingRule,
 226                   byte[] pointTypes,
 227                   int numTypes,
 228                   float[] pointCoords,
 229                   int numCoords)
 230     {
 231         this.windingRule = windingRule;
 232         this.pointTypes = pointTypes;
 233         this.numTypes = numTypes;
 234         this.floatCoords = pointCoords;
 235         this.numCoords = numCoords;
 236     }
 237 
 238     Point2D getPoint(int coordindex) {
 239         return new Point2D(floatCoords[coordindex],
 240                            floatCoords[coordindex+1]);
 241     }
 242 
 243     private boolean close(int ix, float fx, float tolerance) {
 244         return (Math.abs(ix - fx) <= tolerance);
 245     }
 246 
 247     /**
 248      * Check and return if the fillable interior of the path is a simple
 249      * rectangle on nearly integer bounds and initialize the indicated
 250      * {@link Rectangle} with the integer representation of the rectangle
 251      * if it is.
 252      * The method will return false if the path is not rectangular, or if
 253      * the horizontal and linear segments are not within the indicated
 254      * tolerance of an integer coordinate, or if the resulting rectangle
 255      * cannot be safely represented by the integer attributes of the
 256      * {@code Rectangle} object.
 257      *
 258      * @param retrect the {@code Rectangle} to return the rectangular area,
 259      *                or null
 260      * @param tolerance the maximum difference from an integer allowed
 261      *                  for any edge of the rectangle
 262      * @return true iff the path is a simple rectangle
 263      */
 264     public boolean checkAndGetIntRect(Rectangle retrect, float tolerance) {
 265         // Valid rectangular paths are:
 266         //     4 segs: MOVE, LINE, LINE, LINE (implicit CLOSE)
 267         //     5 segs: MOVE, LINE, LINE, LINE, LINE
 268         //     5 segs: MOVE, LINE, LINE, LINE, CLOSE
 269         //     6 segs: MOVE, LINE, LINE, LINE, LINE, CLOSE
 270         if (numTypes == 5) {
 271             // points[4] can be LINETO or CLOSE
 272             if (pointTypes[4] != SEG_LINETO && pointTypes[4] != SEG_CLOSE) {
 273                 return false;
 274             }
 275         } else if (numTypes == 6) {
 276             // points[4] must be LINETO and
 277             // points[5] must be CLOSE
 278             if (pointTypes[4] != SEG_LINETO) return false;
 279             if (pointTypes[5] != SEG_CLOSE) return false;
 280         } else if (numTypes != 4) {
 281             return false;
 282         }
 283         if (pointTypes[0] != SEG_MOVETO) return false;
 284         if (pointTypes[1] != SEG_LINETO) return false;
 285         if (pointTypes[2] != SEG_LINETO) return false;
 286         if (pointTypes[3] != SEG_LINETO) return false;
 287 
 288         int x0 = (int) (floatCoords[0] + 0.5f);
 289         int y0 = (int) (floatCoords[1] + 0.5f);
 290         if (!close(x0, floatCoords[0], tolerance)) return false;
 291         if (!close(y0, floatCoords[1], tolerance)) return false;
 292 
 293         int x1 = (int) (floatCoords[2] + 0.5f);
 294         int y1 = (int) (floatCoords[3] + 0.5f);
 295         if (!close(x1, floatCoords[2], tolerance)) return false;
 296         if (!close(y1, floatCoords[3], tolerance)) return false;
 297 
 298         int x2 = (int) (floatCoords[4] + 0.5f);
 299         int y2 = (int) (floatCoords[5] + 0.5f);
 300         if (!close(x2, floatCoords[4], tolerance)) return false;
 301         if (!close(y2, floatCoords[5], tolerance)) return false;
 302 
 303         int x3 = (int) (floatCoords[6] + 0.5f);
 304         int y3 = (int) (floatCoords[7] + 0.5f);
 305         if (!close(x3, floatCoords[6], tolerance)) return false;
 306         if (!close(y3, floatCoords[7], tolerance)) return false;
 307 
 308         if (numTypes > 4 && pointTypes[4] == SEG_LINETO) {
 309             if (!close(x0, floatCoords[8], tolerance)) return false;
 310             if (!close(y0, floatCoords[9], tolerance)) return false;
 311         }
 312 
 313         if ((x0 == x1 && x2 == x3 && y0 == y3 && y1 == y2) ||
 314             (y0 == y1 && y2 == y3 && x0 == x3 && x1 == x2))
 315         {
 316             // We can use either diagonal to calculate the rectangle:
 317             //     (x0, y0) -> (x2, y2)
 318             //     (x1, y1) -> (x3, y3)
 319             // We also need to deal with upside down and/or backwards rectangles
 320             int x, y, w, h;
 321             if (x2 < x0) { x = x2; w = x0 - x2; }
 322             else         { x = x0; w = x2 - x0; }
 323             if (y2 < y0) { y = y2; h = y0 - y2; }
 324             else         { y = y0; h = y2 - y0; }
 325             // Overflow protection...
 326             if (w < 0) return false;
 327             if (h < 0) return false;
 328 
 329             if (retrect != null) {
 330                 retrect.setBounds(x, y, w, h);
 331             }
 332             return true;
 333         }
 334         return false;
 335     }
 336 
 337     void needRoom(boolean needMove, int newCoords) {
 338         if (needMove && (numTypes == 0)) {
 339             throw new IllegalPathStateException("missing initial moveto "+
 340                                                 "in path definition");
 341         }
 342         int size = pointTypes.length;
 343         if (size == 0) {
 344             pointTypes = new byte[2];
 345         } else if (numTypes >= size) {
 346             pointTypes = expandPointTypes(pointTypes, 1);
 347         }
 348         size = floatCoords.length;
 349         if (numCoords > (floatCoords.length - newCoords)) {
 350             floatCoords = expandCoords(floatCoords, newCoords);
 351         }
 352     }
 353 
 354     static byte[] expandPointTypes(byte[] oldPointTypes, int needed) {
 355         final int oldSize = oldPointTypes.length;
 356         final int newSizeMin = oldSize + needed;
 357         if (newSizeMin < oldSize) {
 358             // hard overflow failure - we can't even accommodate
 359             // new items without overflowing
 360             throw new ArrayIndexOutOfBoundsException(
 361                           "pointTypes exceeds maximum capacity !");
 362         }
 363         // growth algorithm computation
 364         int grow = oldSize;
 365         if (grow > EXPAND_MAX) {
 366             grow = Math.max(EXPAND_MAX, oldSize >> 3); // 1/8th min
 367         } else if (grow < INIT_SIZE) {
 368             grow = INIT_SIZE; // ensure > 6 (cubics)
 369         }
 370         assert grow > 0;
 371 
 372         int newSize = oldSize + grow;
 373         if (newSize < newSizeMin) {
 374             // overflow in growth algorithm computation
 375             newSize = Integer.MAX_VALUE;
 376         }
 377 
 378         while (true) {
 379             try {
 380                 // try allocating the larger array
 381                 return Arrays.copyOf(oldPointTypes, newSize);
 382             } catch (OutOfMemoryError oome) {
 383                 if (newSize == newSizeMin) {
 384                     throw oome;
 385                 }
 386             }
 387             newSize = newSizeMin + (newSize - newSizeMin) / 2;
 388         }
 389     }
 390 
 391     static float[] expandCoords(float[] oldCoords, int needed) {
 392         final int oldSize = oldCoords.length;
 393         final int newSizeMin = oldSize + needed;
 394         if (newSizeMin < oldSize) {
 395             // hard overflow failure - we can't even accommodate
 396             // new items without overflowing
 397             throw new ArrayIndexOutOfBoundsException(
 398                           "coords exceeds maximum capacity !");
 399         }
 400         // growth algorithm computation
 401         int grow = oldSize;
 402         if (grow > EXPAND_MAX_COORDS) {
 403             grow = Math.max(EXPAND_MAX_COORDS, oldSize >> 3); // 1/8th min
 404         } else if (grow < INIT_SIZE) {
 405             grow = INIT_SIZE; // ensure > 6 (cubics)
 406         }
 407         assert grow > needed;
 408 
 409         int newSize = oldSize + grow;
 410         if (newSize < newSizeMin) {
 411             // overflow in growth algorithm computation
 412             newSize = Integer.MAX_VALUE;
 413         }
 414         while (true) {
 415             try {
 416                 // try allocating the larger array
 417                 return Arrays.copyOf(oldCoords, newSize);
 418             } catch (OutOfMemoryError oome) {
 419                 if (newSize == newSizeMin) {
 420                     throw oome;
 421                 }
 422             }
 423             newSize = newSizeMin + (newSize - newSizeMin) / 2;
 424         }
 425     }
 426 
 427     /**
 428      * Adds a point to the path by moving to the specified
 429      * coordinates specified in float precision.
 430      *
 431      * @param x the specified X coordinate
 432      * @param y the specified Y coordinate
 433      */
 434     public final void moveTo(float x, float y) {
 435         if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
 436             floatCoords[numCoords-2] = moveX = prevX = currX = x;
 437             floatCoords[numCoords-1] = moveY = prevY = currY = y;
 438         } else {
 439             needRoom(false, 2);
 440             pointTypes[numTypes++] = SEG_MOVETO;
 441             floatCoords[numCoords++] = moveX = prevX = currX = x;
 442             floatCoords[numCoords++] = moveY = prevY = currY = y;
 443         }
 444     }
 445 
 446     /**
 447      * Adds a point to the path by moving to the specified coordinates
 448      * relative to the current point, specified in float precision.
 449      *
 450      * @param relx the specified relative X coordinate
 451      * @param rely the specified relative Y coordinate
 452      * @see Path2D#moveTo
 453      */
 454     public final void moveToRel(float relx, float rely) {
 455         if (numTypes > 0 && pointTypes[numTypes - 1] == SEG_MOVETO) {
 456             floatCoords[numCoords-2] = moveX = prevX = (currX += relx);
 457             floatCoords[numCoords-1] = moveY = prevY = (currY += rely);
 458         } else {
 459             needRoom(true, 2);
 460             pointTypes[numTypes++] = SEG_MOVETO;
 461             floatCoords[numCoords++] = moveX = prevX = (currX += relx);
 462             floatCoords[numCoords++] = moveY = prevY = (currY += rely);
 463         }
 464     }
 465 
 466     /**
 467      * Adds a point to the path by drawing a straight line from the
 468      * current coordinates to the new coordinates.
 469      *
 470      * @param x the specified X coordinate
 471      * @param y the specified Y coordinate
 472      */
 473     public final void lineTo(float x, float y) {
 474         needRoom(true, 2);
 475         pointTypes[numTypes++] = SEG_LINETO;
 476         floatCoords[numCoords++] = prevX = currX = x;
 477         floatCoords[numCoords++] = prevY = currY = y;
 478     }
 479 
 480     /**
 481      * Adds a point to the path by drawing a straight line from the
 482      * current coordinates to the new coordinates relative to the
 483      * current point.
 484      *
 485      * @param relx the specified relative X coordinate
 486      * @param rely the specified relative Y coordinate
 487      * @see Path2D#lineTo
 488      */
 489     public final void lineToRel(float relx, float rely) {
 490         needRoom(true, 2);
 491         pointTypes[numTypes++] = SEG_LINETO;
 492         floatCoords[numCoords++] = prevX = (currX += relx);
 493         floatCoords[numCoords++] = prevY = (currY += rely);
 494     }
 495 
 496     /**
 497      * Adds a curved segment to the path, defined by two new points, by
 498      * drawing a Quadratic curve that intersects both the current
 499      * coordinates and the specified coordinates {@code (x2,y2)},
 500      * using the specified point {@code (x1,y1)} as a quadratic
 501      * parametric control point.
 502      *
 503      * @param x1 the X coordinate of the quadratic control point
 504      * @param y1 the Y coordinate of the quadratic control point
 505      * @param x2 the X coordinate of the final end point
 506      * @param y2 the Y coordinate of the final end point
 507      */
 508     public final void quadTo(float x1, float y1,
 509                              float x2, float y2)
 510     {
 511         needRoom(true, 4);
 512         pointTypes[numTypes++] = SEG_QUADTO;
 513         floatCoords[numCoords++] = prevX = x1;
 514         floatCoords[numCoords++] = prevY = y1;
 515         floatCoords[numCoords++] = currX = x2;
 516         floatCoords[numCoords++] = currY = y2;
 517     }
 518 
 519     /**
 520      * Adds a curved segment to the path, defined by two new points
 521      * relative to the current point, by
 522      * drawing a Quadratic curve that intersects both the current
 523      * coordinates and the specified relative coordinates {@code (rx2,ry2)},
 524      * using the specified relative point {@code (rx1,ry1)} as a quadratic
 525      * parametric control point.
 526      * This is equivalent to:
 527      * <pre>
 528      *     quadTo(getCurrentX() + rx1, getCurrentY() + ry1,
 529      *            getCurrentX() + rx2, getCurrentY() + ry2);
 530      * </pre>
 531      *
 532      * @param relx1 the relative X coordinate of the quadratic control point
 533      * @param rely1 the relative Y coordinate of the quadratic control point
 534      * @param relx2 the relative X coordinate of the final end point
 535      * @param rely2 the relative Y coordinate of the final end point
 536      * @see Path2D#quadTo
 537      */
 538     public final void quadToRel(float relx1, float rely1,
 539                                 float relx2, float rely2)
 540     {
 541         needRoom(true, 4);
 542         pointTypes[numTypes++] = SEG_QUADTO;
 543         floatCoords[numCoords++] = prevX = currX + relx1;
 544         floatCoords[numCoords++] = prevY = currY + rely1;
 545         floatCoords[numCoords++] = (currX += relx2);
 546         floatCoords[numCoords++] = (currY += rely2);
 547     }
 548 
 549     /**
 550      * Adds a curved segment to the path, defined by a new point, by
 551      * drawing a Quadratic curve that intersects both the current
 552      * coordinates and the specified coordinates {@code (x,y)},
 553      * using a quadratic parametric control point that is positioned
 554      * symmetrically across the current point from the previous curve
 555      * control point.
 556      * If the previous path segment is not a curve, then the control
 557      * point will be positioned at the current point.  This is
 558      * equivalent to:
 559      * <pre>
 560      *     quadTo(getCurrentX() * 2 - <previousControlX>,
 561      *            getCurrentY() * 2 - <previousControlY>,
 562      *            x, y);
 563      * </pre>
 564      *
 565      * @param x2 the X coordinate of the final end point
 566      * @param y2 the Y coordinate of the final end point
 567      * @see Path2D#quadTo
 568      */
 569     public final void quadToSmooth(float x2, float y2) {
 570         needRoom(true, 4);
 571         pointTypes[numTypes++] = SEG_QUADTO;
 572         floatCoords[numCoords++] = prevX = (currX * 2.0f - prevX);
 573         floatCoords[numCoords++] = prevY = (currY * 2.0f - prevY);
 574         floatCoords[numCoords++] = currX = x2;
 575         floatCoords[numCoords++] = currY = y2;
 576     }
 577 
 578     /**
 579      * Adds a curved segment to the path, defined by a new point
 580      * relative to the current point, by
 581      * drawing a Quadratic curve that intersects both the current
 582      * coordinates and the specified relative coordinates {@code (x,y)},
 583      * using a quadratic parametric control point that is positioned
 584      * symmetrically across the current point from the previous curve
 585      * control point.
 586      * If the previous path segment is not a curve, then the control
 587      * point will be positioned at the current point.  This is
 588      * equivalent to:
 589      * <pre>
 590      *     quadTo(getCurrentX() * 2 - <previousControlX>,
 591      *            getCurrentY() * 2 - <previousControlY>,
 592      *            getCurrentX() + x, getCurrentY() + y);
 593      * </pre>
 594      *
 595      * @param relx2 the relative X coordinate of the final end point
 596      * @param rely2 the relative Y coordinate of the final end point
 597      * @see Path2D#quadTo
 598      */
 599     public final void quadToSmoothRel(float relx2, float rely2) {
 600         needRoom(true, 4);
 601         pointTypes[numTypes++] = SEG_QUADTO;
 602         floatCoords[numCoords++] = prevX = (currX * 2.0f - prevX);
 603         floatCoords[numCoords++] = prevY = (currY * 2.0f - prevY);
 604         floatCoords[numCoords++] = (currX += relx2);
 605         floatCoords[numCoords++] = (currY += rely2);
 606     }
 607 
 608     /**
 609      * Adds a curved segment to the path, defined by three new points, by
 610      * drawing a B&eacute;zier curve that intersects both the current
 611      * coordinates and the specified coordinates {@code (x3,y3)},
 612      * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
 613      * B&eacute;zier control points.
 614      *
 615      * @param x1 the X coordinate of the first B&eacute;zier control point
 616      * @param y1 the Y coordinate of the first B&eacute;zier control point
 617      * @param x2 the X coordinate of the second B&eacute;zier control point
 618      * @param y2 the Y coordinate of the second B&eacute;zier control point
 619      * @param x3 the X coordinate of the final end point
 620      * @param y3 the Y coordinate of the final end point
 621      * @see Path2D#curveTo
 622      */
 623     public final void curveTo(float x1, float y1,
 624                               float x2, float y2,
 625                               float x3, float y3)
 626     {
 627         needRoom(true, 6);
 628         pointTypes[numTypes++] = SEG_CUBICTO;
 629         floatCoords[numCoords++] = x1;
 630         floatCoords[numCoords++] = y1;
 631         floatCoords[numCoords++] = prevX = x2;
 632         floatCoords[numCoords++] = prevY = y2;
 633         floatCoords[numCoords++] = currX = x3;
 634         floatCoords[numCoords++] = currY = y3;
 635     }
 636 
 637     /**
 638      * Adds a curved segment to the path, defined by three new points
 639      * relative to the current point, by
 640      * drawing a B&eacute;zier curve that intersects both the current
 641      * coordinates and the specified coordinates {@code (x3,y3)},
 642      * using the specified points {@code (x1,y1)} and {@code (x2,y2)} as
 643      * B&eacute;zier control points.
 644      * This is equivalent to:
 645      * <pre>
 646      *     curveTo(getCurrentX() + rx1, getCurrentY() + ry1,
 647      *             getCurrentX() + rx2, getCurrentY() + ry2,
 648      *             getCurrentX() + rx3, getCurrentY() + ry3)
 649      * </pre>
 650      *
 651      * @param relx1 the relative X coordinate of the first B&eacute;zier control point
 652      * @param rely1 the relative Y coordinate of the first B&eacute;zier control point
 653      * @param relx2 the relative X coordinate of the second B&eacute;zier control point
 654      * @param rely2 the relative Y coordinate of the second B&eacute;zier control point
 655      * @param relx3 the relative X coordinate of the final end point
 656      * @param rely3 the relative Y coordinate of the final end point
 657      * @see Path2D#curveTo
 658      */
 659     public final void curveToRel(float relx1, float rely1,
 660                                  float relx2, float rely2,
 661                                  float relx3, float rely3)
 662     {
 663         needRoom(true, 6);
 664         pointTypes[numTypes++] = SEG_CUBICTO;
 665         floatCoords[numCoords++] = currX + relx1;
 666         floatCoords[numCoords++] = currY + rely1;
 667         floatCoords[numCoords++] = prevX = currX + relx2;
 668         floatCoords[numCoords++] = prevY = currY + rely2;
 669         floatCoords[numCoords++] = (currX += relx3);
 670         floatCoords[numCoords++] = (currY += rely3);
 671     }
 672 
 673     /**
 674      * Adds a curved segment to the path, defined by two new points and
 675      * a third point inferred from the previous curve, by
 676      * drawing a B&eacute;zier curve that intersects both the current
 677      * coordinates and the specified coordinates {@code (x3,y3)},
 678      * using the specified point {@code (x2,y2)} as the second
 679      * B&eacute;zier control point and a first B&eacute;zier control
 680      * point that is positioned
 681      * symmetrically across the current point from the previous curve
 682      * control point.
 683      * This is equivalent to:
 684      * <pre>
 685      *     curveTo(getCurrentX() * 2.0f - <previousControlX>,
 686      *             getCurrentY() * 2.0f - <previousControlY>,
 687      *             x2, y2, x3, y3);
 688      * </pre>
 689      *
 690      * @param x2 the X coordinate of the second B&eacute;zier control point
 691      * @param y2 the Y coordinate of the second B&eacute;zier control point
 692      * @param x3 the X coordinate of the final end point
 693      * @param y3 the Y coordinate of the final end point
 694      * @see Path2D#curveTo
 695      */
 696     public final void curveToSmooth(float x2, float y2,
 697                                     float x3, float y3)
 698     {
 699         needRoom(true, 6);
 700         pointTypes[numTypes++] = SEG_CUBICTO;
 701         floatCoords[numCoords++] = currX * 2.0f - prevX;
 702         floatCoords[numCoords++] = currY * 2.0f - prevY;
 703         floatCoords[numCoords++] = prevX = x2;
 704         floatCoords[numCoords++] = prevY = y2;
 705         floatCoords[numCoords++] = currX = x3;
 706         floatCoords[numCoords++] = currY = y3;
 707     }
 708 
 709     /**
 710      * Adds a curved segment to the path, defined by two new points relative
 711      * to the current point and
 712      * a third point inferred from the previous curve, by
 713      * drawing a B&eacute;zier curve that intersects both the current
 714      * coordinates and the specified relative coordinates {@code (rx3,ry3)},
 715      * using the specified relative point {@code (rx2,ry2)} as the second
 716      * B&eacute;zier control point and a first B&eacute;zier control
 717      * point that is positioned
 718      * symmetrically across the current point from the previous curve
 719      * control point.
 720      * This is equivalent to:
 721      * <pre>
 722      *     curveTo(getCurrentX() * 2.0f - <previousControlX>,
 723      *             getCurrentY() * 2.0f - <previousControlY>,
 724      *             getCurrentX() + x2, getCurrentY() + y2,
 725      *             getCurrentX() + x3, getCurrentY() + y3);
 726      * </pre>
 727      *
 728      * @param relx2 the relative X coordinate of the second B&eacute;zier control point
 729      * @param rely2 the relative Y coordinate of the second B&eacute;zier control point
 730      * @param relx3 the relative X coordinate of the final end point
 731      * @param rely3 the relative Y coordinate of the final end point
 732      * @see Path2D#curveTo
 733      */
 734     public final void curveToSmoothRel(float relx2, float rely2,
 735                                        float relx3, float rely3)
 736     {
 737         needRoom(true, 6);
 738         pointTypes[numTypes++] = SEG_CUBICTO;
 739         floatCoords[numCoords++] = currX * 2.0f - prevX;
 740         floatCoords[numCoords++] = currY * 2.0f - prevY;
 741         floatCoords[numCoords++] = prevX = currX + relx2;
 742         floatCoords[numCoords++] = prevY = currY + rely2;
 743         floatCoords[numCoords++] = (currX += relx3);
 744         floatCoords[numCoords++] = (currY += rely3);
 745     }
 746 
 747     /**
 748      * Append a section of a quadrant of an oval to the current path,
 749      * relative to the current point.
 750      * See {@link appendOvalQuadrant} for a precise definition of the
 751      * path segments to be added, considering that this method uses the
 752      * current point of the path as the first pair of coordinates and
 753      * a hard-coded prefix of {@link CornerPrefix.CORNER_ONLY CORNER_ONLY}.
 754      * This method is equivalent to (and only slightly faster than):
 755      * <pre>
 756      *     appendOvalQuadrant(getCurrentX(), getCurrentY(),
 757      *                        cx, cy, ex, ey, tfrom, tto,
 758      *                        CornerPrefix.CORNER_ONLY);
 759      * </pre>
 760      * Note that you could define a circle inscribed in the rectangular
 761      * bounding box from {@code (x0, y0)} to {@code (x1, y1)} with the
 762      * following 4 calls to this method:
 763      * <pre>
 764      *     Path2D path = new Path2D();
 765      *     float cx = (x0 + x1) * 0.5f; // center X coordinate of top and bottom
 766      *     float cy = (y0 + y1) * 0.5f; // center Y coordinate of left and right
 767      *     path.moveTo(cx, y0);
 768      *     path.ovalQuadrantTo(x1, y0, x1, cy, 0f, 1f);
 769      *     path.ovalQuadrantTo(x1, y1, cx, y1, 0f, 1f);
 770      *     path.ovalQuadrantTo(x0, y1, x0, cy, 0f, 1f);
 771      *     path.ovalQuadrantTo(x0, y0, cx, y0, 0f, 1f);
 772      *     path.closePath();
 773      * </pre>
 774      * You could also define a rounded rectangle inscribed in the rectangular
 775      * bounding box from {@code (x0, y0)} to {@code (x1, y1)} with a corner
 776      * arc radius {@code r} less than half the width and the height with the
 777      * following 4 calls to this method:
 778      * <pre>
 779      *     Path2D path = new Path2D();
 780      *     float lx = x0 + r;
 781      *     float rx = x1 - r;
 782      *     float ty = y0 + r;
 783      *     float by = y1 - r;
 784      *     path.moveTo(rx, y0);
 785      *     path.ovalQuadrantTo(x1, y0, x1, ty, 0f, 1f);
 786      *     path.lineTo(x1, by);
 787      *     path.ovalQuadrantTo(x1, y1, rx, y1, 0f, 1f);
 788      *     path.lineTo(lx, y1);
 789      *     path.ovalQuadrantTo(x0, y1, x0, by, 0f, 1f);
 790      *     path.lineTo(x0, by);
 791      *     path.ovalQuadrantTo(x0, y0, lx, y0, 0f, 1f);
 792      *     path.closePath();
 793      * </pre>
 794      *
 795      * @param cx the X coordinate of the corner
 796      * @param cy the Y coordinate of the corner
 797      * @param ex the X coordinate of the midpoint of the trailing edge
 798      *           interpolated by the oval
 799      * @param ey the Y coordinate of the midpoint of the trailing edge
 800      *           interpolated by the oval
 801      * @param tfrom the fraction of the oval section where the curve should start
 802      * @param tto the fraction of the oval section where the curve should end
 803      * @throws IllegalPathStateException
 804      *     if there is no current point in the path
 805      * @throws IllegalArgumentException
 806      *     if the {@code tfrom} and {@code tto} values do not satisfy the
 807      *     required relationship {@code (0 <= tfrom <= tto <= 1).
 808      */
 809     public final void ovalQuadrantTo(float cx, float cy,
 810                                      float ex, float ey,
 811                                      float tfrom, float tto)
 812     {
 813         if (numTypes < 1) {
 814             throw new IllegalPathStateException("missing initial moveto "+
 815                                                 "in path definition");
 816         }
 817         appendOvalQuadrant(currX, currY,
 818                            cx, cy, ex, ey, tfrom, tto, CornerPrefix.CORNER_ONLY);
 819     }
 820 
 821     /**
 822      * Append a section of a quadrant of an oval to the current path.
 823      * The oval from which a quadrant is taken is the oval that would be
 824      * inscribed in a parallelogram defined by 3 points,
 825      * {@code (sx, sy)} which is considered to be the midpoint of the edge
 826      * leading into the corner of the oval where the oval grazes it,
 827      * {@code (cx, cy)} which is considered to be the location of the
 828      * corner of the parallelogram in which the oval is inscribed,
 829      * and {@code (ex, ey)} which is considered to be the midpoint of the
 830      * edge leading away from the corner of the oval where the oval grazes it.
 831      * A typical case involves the two segments being equal in length and
 832      * at right angles to each other in which case the oval is a quarter of
 833      * a circle.
 834      * <p>
 835      * Only the portion of the oval from {@code tfrom} to {@code tto}
 836      * will be included where {@code 0f} represents the point where the
 837      * oval grazes the leading edge, {@code 1f} represents the point where
 838      * the oval grazes the trailing edge, and {@code 0.5f} represents the
 839      * point on the oval closest to the corner (i.e. the "45 degree" point).
 840      * The two values must satisfy the relation
 841      * {@code (0 <= tfrom <= tto <= 1)}.
 842      * If {@code tfrom} is not {@code 0f} then the caller would most likely
 843      * want to use one of the {@code prefix} values that inserts a segment
 844      * leading to the initial point (see below).
 845      * <p>
 846      * An initial {@link moveTo} or {@link lineTo} can be added to direct
 847      * the path to the starting point of the oval section if
 848      * {@link CornerPrefix.MOVE_THEN_CORNER MOVE_THEN_CORNER} or
 849      * {@link CornerPrefix.LINE_THEN_CORNER LINE_THEN_CORNER} are
 850      * specified by the prefix argument.
 851      * The {@code lineTo} path segment will only be added if the current point
 852      * is not already at the indicated location to avoid spurious empty line
 853      * segments.
 854      * The prefix can be specified as
 855      * {@link CornerPrefix.CORNER_ONLY CORNER_ONLY} if the current point
 856      * on the path is known to be at the starting point of the oval section,
 857      * but could otherwise produce odd results if the current point is not
 858      * appropriate.
 859      * <p>
 860      * Note that you could define a circle inscribed in the rectangular
 861      * bounding box from {@code (x0, y0)} to {@code (x1, y1)} with the
 862      * following 4 calls to this method:
 863      * <pre>
 864      *     Path2D path = new Path2D();
 865      *     float cx = (x0 + x1) * 0.5f; // center X coordinate of top and bottom
 866      *     float cy = (y0 + y1) * 0.5f; // center Y coordinate of left and right
 867      *     path.appendOvalQuadrant(cx, y0, x1, y0, x1, cy, 0f, 1f, MOVE_THEN_CORNER);
 868      *     path.appendOvalQuadrant(x1, cy, x1, y1, cx, y1, 0f, 1f, CORNER_ONLY);
 869      *     path.appendOvalQuadrant(cx, y1, x0, y1, x0, cy, 0f, 1f, CORNER_ONLY);
 870      *     path.appendOvalQuadrant(x0, cy, x0, y0, cx, y0, 0f, 1f, CORNER_ONLY);
 871      *     path.closePath();
 872      * </pre>
 873      * You could also define a rounded rectangle inscribed in the rectangular
 874      * bounding box from {@code (x0, y0)} to {@code (x1, y1)} with a corner
 875      * arc radius {@code r} less than half the width and the height with the
 876      * following 4 calls to this method:
 877      * <pre>
 878      *     Path2D path = new Path2D();
 879      *     float lx = x0 + r;
 880      *     float rx = x1 - r;
 881      *     float ty = y0 + r;
 882      *     float by = y1 - r;
 883      *     path.appendOvalQuadrant(rx, y0, x1, y0, x1, ty, 0f, 1f, MOVE_THEN_CORNER);
 884      *     path.appendOvalQuadrant(x1, by, x1, y1, rx, y1, 0f, 1f, LINE_THEN_CORNER);
 885      *     path.appendOvalQuadrant(lx, y1, x0, y1, x0, by, 0f, 1f, LINE_THEN_CORNER);
 886      *     path.appendOvalQuadrant(x0, by, x0, y0, lx, y0, 0f, 1f, LINE_THEN_CORNER);
 887      *     path.closePath();
 888      * </pre>
 889      *
 890      * @param sx the X coordinate of the midpoint of the leading edge
 891      *           interpolated by the oval
 892      * @param sy the Y coordinate of the midpoint of the leading edge
 893      *           interpolated by the oval
 894      * @param cx the X coordinate of the corner
 895      * @param cy the Y coordinate of the corner
 896      * @param ex the X coordinate of the midpoint of the trailing edge
 897      *           interpolated by the oval
 898      * @param ey the Y coordinate of the midpoint of the trailing edge
 899      *           interpolated by the oval
 900      * @param tfrom the fraction of the oval section where the curve should start
 901      * @param tto the fraction of the oval section where the curve should end
 902      * @param prefix the specification of what additional path segments should
 903      *               be appended to lead the current path to the starting point
 904      * @throws IllegalPathStateException
 905      *     if there is no current point in the path and the prefix is
 906      *     not {@code CornerPrevix.MOVE_THEN_CORNER MOVE_THEN_CORNER}.
 907      * @throws IllegalArgumentException
 908      *     if the {@code tfrom} and {@code tto} values do not satisfy the
 909      *     required relationship {@code (0 <= tfrom <= tto <= 1).
 910      */
 911     public final void appendOvalQuadrant(float sx, float sy,
 912                                          float cx, float cy,
 913                                          float ex, float ey,
 914                                          float tfrom, float tto,
 915                                          CornerPrefix prefix)
 916     {
 917         if (!(tfrom >= 0f && tfrom <= tto && tto <= 1f)) {
 918             throw new IllegalArgumentException("0 <= tfrom <= tto <= 1 required");
 919         }
 920         float cx0 = (float) (sx + (cx - sx) * EllipseIterator.CtrlVal);
 921         float cy0 = (float) (sy + (cy - sy) * EllipseIterator.CtrlVal);
 922         float cx1 = (float) (ex + (cx - ex) * EllipseIterator.CtrlVal);
 923         float cy1 = (float) (ey + (cy - ey) * EllipseIterator.CtrlVal);
 924         if (tto < 1f) {
 925             float t = 1f - tto;
 926             ex += (cx1 - ex) * t;
 927             ey += (cy1 - ey) * t;
 928             cx1 += (cx0 - cx1) * t;
 929             cy1 += (cy0 - cy1) * t;
 930             cx0 += (sx - cx0) * t;
 931             cy0 += (sy - cy0) * t;
 932             ex += (cx1 - ex) * t;
 933             ey += (cy1 - ey) * t;
 934             cx1 += (cx0 - cx1) * t;
 935             cy1 += (cy0 - cy1) * t;
 936             ex += (cx1 - ex) * t;
 937             ey += (cy1 - ey) * t;
 938         }
 939         if (tfrom > 0f) {
 940             if (tto < 1f) {
 941                 tfrom = tfrom / tto;
 942             }
 943             sx += (cx0 - sx) * tfrom;
 944             sy += (cy0 - sy) * tfrom;
 945             cx0 += (cx1 - cx0) * tfrom;
 946             cy0 += (cy1 - cy0) * tfrom;
 947             cx1 += (ex - cx1) * tfrom;
 948             cy1 += (ey - cy1) * tfrom;
 949             sx += (cx0 - sx) * tfrom;
 950             sy += (cy0 - sy) * tfrom;
 951             cx0 += (cx1 - cx0) * tfrom;
 952             cy0 += (cy1 - cy0) * tfrom;
 953             sx += (cx0 - sx) * tfrom;
 954             sy += (cy0 - sy) * tfrom;
 955         }
 956         if (prefix == CornerPrefix.MOVE_THEN_CORNER) {
 957             // Always execute moveTo so we break the path...
 958             moveTo(sx, sy);
 959         } else if (prefix == CornerPrefix.LINE_THEN_CORNER) {
 960             if (numTypes == 1 ||
 961                 sx != currX ||
 962                 sy != currY)
 963             {
 964                 lineTo(sx, sy);
 965             }
 966         }
 967         if (tfrom == tto ||
 968             (sx == cx0 && cx0 == cx1 && cx1 == ex &&
 969              sy == cy0 && cy0 == cy1 && cy1 == ey))
 970         {
 971             if (prefix != CornerPrefix.LINE_THEN_CORNER) {
 972                 lineTo(ex, ey);
 973             }
 974         } else {
 975             curveTo(cx0, cy0, cx1, cy1, ex, ey);
 976         }
 977     }
 978 
 979     /**
 980      * Append a portion of an ellipse to the path.
 981      * The ellipse from which the portions are extracted follows the rules:
 982      * <ul>
 983      * <li>The ellipse will have its X axis tilted from horizontal by the
 984      * angle {@code xAxisRotation} specified in radians.
 985      * <li>The ellipse will have the X and Y radii (viewed from its tilted
 986      * coordinate system) specified by {@code radiusx} and {@code radiusy}
 987      * unless that ellipse is too small to bridge the gap from the current
 988      * point to the specified destination point in which case a larger
 989      * ellipse with the same ratio of dimensions will be substituted instead.
 990      * <li>The ellipse may slide perpendicular to the direction from the
 991      * current point to the specified destination point so that it just
 992      * touches the two points.
 993      * The direction it slides (to the "left" or to the "right") will be
 994      * chosen to meet the criteria specified by the two boolean flags as
 995      * described below.
 996      * Only one direction will allow the method to meet both criteria.
 997      * <li>If the {@code largeArcFlag} is true, then the ellipse will sweep
 998      * the longer way around the ellipse that meets these criteria.
 999      * <li>If the {@code sweepFlag} is true, then the ellipse will sweep
1000      * clockwise around the ellipse that meets these criteria.
1001      * </ul>
1002      * The method will do nothing if the destination point is the same as
1003      * the current point.
1004      * The method will draw a simple line segment to the destination point
1005      * if either of the two radii are zero.
1006      * <p>
1007      * Note: This method adheres to the definition of an elliptical arc path
1008      * segment from the SVG spec:
1009      * <pre>
1010      * http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
1011      * </pre>
1012      *
1013      * @param radiusx the X radius of the tilted ellipse
1014      * @param radiusy the Y radius of the tilted ellipse
1015      * @param xAxisRotation the angle of tilt of the ellipse
1016      * @param largeArcFlag true iff the path will sweep the long way around
1017      *                     the ellipse
1018      * @param sweepFlag true iff the path will sweep clockwise around
1019      *                  the ellipse
1020      * @param x the destination X coordinate
1021      * @param y the destination Y coordinate
1022      * @throws IllegalPathStateException
1023      *     if there is no current point in the path
1024      */
1025     public void arcTo(float radiusx, float radiusy, float xAxisRotation,
1026                       boolean largeArcFlag, boolean sweepFlag,
1027                       float x, float y)
1028     {
1029         // First ensure preceding moveto
1030         if (numTypes < 1) {
1031             throw new IllegalPathStateException("missing initial moveto "+
1032                                                 "in path definition");
1033         }
1034         // Reference equations are provided for implementation assistance:
1035         // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
1036         // We use the following modifications:
1037         // They use a secondary coordinate system which is based on
1038         // - translating to the midpoint between the endpoints
1039         // - rotating so that the xAxis is "horizontal"
1040         // You can see that most of their math then has their secondary
1041         // coordinates being divided by rx and ry everywhere so we scale
1042         // by 1/rx and 1/ry so that we are working on a unit circle:
1043         // [ x' ]   [ +cos/rx  +sin/rx ]   [ x - mx ]
1044         // [    ] = [                  ] * [        ]
1045         // [ y' ]   [ -sin/ry  +cos/ry ]   [ y - my ]
1046         // and reversing back to user space coordinates:
1047         // [ x ]   [ +cos  -sin ]   [ x' * rx ]   [ mx ]
1048         // [   ] = [            ] * [         ] + [    ]
1049         // [ y ]   [ +sin  +cos ]   [ y' * ry ]   [ my ]
1050         double rx = Math.abs(radiusx);
1051         double ry = Math.abs(radiusy);
1052         if (rx == 0 || ry == 0) {
1053             lineTo(x, y);
1054             return;
1055         }
1056         double x1 = currX;
1057         double y1 = currY;
1058         double x2 = x;
1059         double y2 = y;
1060         if (x1 == x2 && y1 == y2) {
1061             return;
1062         }
1063         double cosphi, sinphi;
1064         if (xAxisRotation == 0.0) {
1065             cosphi = 1.0;
1066             sinphi = 0.0;
1067         } else {
1068             cosphi = Math.cos(xAxisRotation);
1069             sinphi = Math.sin(xAxisRotation);
1070         }
1071         double mx = (x1 + x2) / 2.0;
1072         double my = (y1 + y2) / 2.0;
1073         double relx1 = x1 - mx;
1074         double rely1 = y1 - my;
1075         double x1p = (cosphi * relx1 + sinphi * rely1) / rx;
1076         double y1p = (cosphi * rely1 - sinphi * relx1) / ry;
1077         // The documentation for the SVG arc operator recommends computing
1078         // a "scale" value and then scaling the radii appropriately if the
1079         // scale is greater than 1.  Technically, they are computing the
1080         // ratio of the distance to the endpoints compared to the distance
1081         // across the indicated section of the ellipse centered at the midpoint.
1082         // If the ratio is greater than 1 then the endpoints are outside the
1083         // ellipse and so the ellipse is not large enough to bridge the gap
1084         // without growing.  If they are inside, then we slide the ellipse
1085         // in the appropriate direction as specified by the 2 flags so that
1086         // the transformed relative points are on the edge.  If they
1087         // are outside, then we note that we simply have a (distorted) half
1088         // circle to render in that case since the endpoints will be on
1089         // opposite sides of a stretched version of the unmoved ellipse.
1090         double lenpsq = x1p * x1p + y1p * y1p;
1091         if (lenpsq >= 1.0) {
1092             // Unlike the reference equations, we do not need to scale the
1093             // radii here since we will work directly from the transformed
1094             // relative vectors which already have the proper distance from
1095             // the midpoint.  (They are already on the "stretched" ellipse.)
1096 
1097             // Produce 2 quadrant circles from:
1098             // x1p,y1p => xqp,yqp => x2p,y2p
1099             // where x2p,y2p = -x1p,-y1p
1100             // and xqp,yqp = either y1p,-x1p or -y1p,x1p depending on sweepFlag
1101             // the corners of the quadrants are at:
1102             // x1p+xqp,y1p+yqp and x2p+xqp,y2p+yqp
1103             // or consequently at:
1104             // x1+(xq-mx),y1+(yq-my) and x2+(xq-mx),y2+(yq-my)
1105             double xqpr = y1p * rx;
1106             double yqpr = x1p * ry;
1107             if (sweepFlag) { xqpr = -xqpr; } else { yqpr = -yqpr; }
1108             double relxq = cosphi * xqpr - sinphi * yqpr;
1109             double relyq = cosphi * yqpr + sinphi * xqpr;
1110             double xq = mx + relxq;
1111             double yq = my + relyq;
1112             double xc = x1 + relxq;
1113             double yc = y1 + relyq;
1114             appendOvalQuadrant((float) x1, (float) y1,
1115                                (float) xc, (float) yc,
1116                                (float) xq, (float) yq,
1117                                0f, 1f, CornerPrefix.CORNER_ONLY);
1118             xc = x2 + relxq;
1119             yc = y2 + relyq;
1120             appendOvalQuadrant((float) xq, (float) yq,
1121                                (float) xc, (float) yc,
1122                                (float) x2, (float) y2,
1123                                0f, 1f, CornerPrefix.CORNER_ONLY);
1124             return;
1125         }
1126         // We now need to displace the circle perpendicularly to the line
1127         // between the end points so that the new center is at a unit distance
1128         // to either end point.  One component of the new distance will be
1129         // the distance from the midpoint to either end point (the square
1130         // of which is already computed in "den" above).  The other component
1131         // of the new distances will be how far we displace the center:
1132         // lenpsq + displen^2 = 1.0
1133         // displen^2 = 1.0 - lenpsq
1134         // displen = sqrt(1 - lenpsq)
1135         // The vector we displace along is the perpendicular of the x1p,y1p
1136         // vector whose length is sqrt(lenpsq) so we need to divide that vector
1137         // by that length to turn it into a unit vector:
1138         // cxp = +/-y1p / sqrt(lenpsq) * displen
1139         // cyp = +/-x1p / sqrt(lenpsq) * displen
1140         // To simplify, we combine the "/sqrt(lenpsq)" factor into displen to
1141         // share the one sqrt() calculation:
1142         // scalef = displen / sqrt(lenpsq) = sqrt((1-lenpsq)/lenpsq)
1143         double scalef = Math.sqrt((1.0 - lenpsq) / lenpsq);
1144         // cxp,cyp is displaced perpendicularly to the relative vector x1p,y1p
1145         // by the scalef value.  The perpendicular is either -y1p,x1p or
1146         // y1p,-x1p depending on the values of the flags.
1147         double cxp = scalef * y1p;
1148         double cyp = scalef * x1p;
1149         // The direction of the perpendicular (which component is negated)
1150         // depends on both flags.
1151         if (largeArcFlag == sweepFlag) { cxp = -cxp; } else { cyp = -cyp; }
1152         mx += (cosphi * cxp * rx - sinphi * cyp * ry);
1153         my += (cosphi * cyp * ry + sinphi * cxp * rx);
1154         // Now we sweep by quadrants in the direction specified until we
1155         // reach the angle to the destination point and possibly perform
1156         // one last partial-quadrant arc segment.
1157         // First we need to reexpress our vectors relative to the new center.
1158         double ux = x1p - cxp;
1159         double uy = y1p - cyp;
1160         // x2p = -x1p; x2p-cxp = -x1p-cxp = -(x1p+cxp)
1161         // y2p = -y1p; y2p-cyp = -y1p-cyp = -(y1p+cyp)
1162         double vx = -(x1p + cxp);
1163         double vy = -(y1p + cyp);
1164         // px and py are the factors that produce the perpendicular for ux,uy
1165         // in the direction specified by sweepFlag.
1166         boolean done = false;  // set to true when we detect "last quadrant"
1167         float quadlen = 1.0f;  // 1.0 yields a full 90 degree arc at a time
1168         boolean wasclose = false;  // overshoot prevention
1169         do {
1170             // Compute the next circle quadrant endpoint, cw or ccw
1171             double xqp = uy;
1172             double yqp = ux;
1173             if (sweepFlag) { xqp = -xqp; } else { yqp = -yqp; }
1174             // qp.v > 0 tells us if sweep towards v is < 180
1175             if (xqp * vx + yqp * vy > 0) {
1176                 // u.v >= 0 now tells us if sweep towards v is <= 90
1177                 // (It is also true for >270, but we already checked for <180)
1178                 double dot = ux * vx + uy * vy;
1179                 if (dot >= 0) {
1180                     // u.v is the cosine of the angle we have left since both
1181                     // u and v are unit vectors.  We now need to express how
1182                     // much we want to shorten this last arc segment in terms
1183                     // of 0.0=>1.0 meaning 0=>90 degrees.
1184                     quadlen = (float) (Math.acos(dot) / (Math.PI / 2.0));
1185                     done = true;
1186                 }
1187                 // Remember that we were once within 180 degrees so we
1188                 // do not accidentally overshoot due to fp rounding error.
1189                 wasclose = true;
1190             } else if (wasclose) {
1191                 // At some point we were in the <180 case above, but now we
1192                 // are back at the >180 case having never gone into the <90
1193                 // case where done would have been set to true.  This should
1194                 // not happen, but since we are computing the perpendiculars
1195                 // and then expecting that they will have predictable results
1196                 // in the dot product equations, there is a theoretical chance
1197                 // of a tiny round-off error that would cause us to overshoot
1198                 // from just barely >90 left to suddenly past the 0 point.
1199                 // If that ever happens, we will end up in here and we can just
1200                 // break out of the loop since that last quadrant we rendered
1201                 // should have landed us right on top of the vx,vy location.
1202                 break;
1203             }
1204             double relxq = (cosphi * xqp * rx - sinphi * yqp * ry);
1205             double relyq = (cosphi * yqp * ry + sinphi * xqp * rx);
1206             double xq = mx + relxq;
1207             double yq = my + relyq;
1208             double xc = x1 + relxq;
1209             double yc = y1 + relyq;
1210             appendOvalQuadrant((float) x1, (float) y1,
1211                                (float) xc, (float) yc,
1212                                (float) xq, (float) yq,
1213                                0f, quadlen, CornerPrefix.CORNER_ONLY);
1214             x1 = xq;
1215             y1 = yq;
1216             ux = xqp;
1217             uy = yqp;
1218         } while (!done);
1219     }
1220 
1221     /**
1222      * Append a portion of an ellipse to the path using relative coordinates.
1223      * This method is identical to calling:
1224      * <pre>
1225      *     arcTo(radiusX, radiusY, xAxisRotation,
1226      *           largeArcFlag, sweepFlag,
1227      *           getCurrentX() + rx, getCurrentY() + ry);
1228      * </pre>
1229      *
1230      * @param radiusx the X radius of the tilted ellipse
1231      * @param radiusy the Y radius of the tilted ellipse
1232      * @param xAxisRotation the angle of tilt of the ellipse
1233      * @param largeArcFlag true iff the path will sweep the long way around
1234      *                     the ellipse
1235      * @param sweepFlag true iff the path will sweep clockwise around
1236      *                  the ellipse
1237      * @param relx the relative destination relative X coordinate
1238      * @param rely the relative destination relative Y coordinate
1239      * @throws IllegalPathStateException
1240      *     if there is no current point in the path
1241      * @see Path2D#arcTo
1242      */
1243     public void arcToRel(float radiusx, float radiusy, float xAxisRotation,
1244                          boolean largeArcFlag, boolean sweepFlag,
1245                          float relx, float rely)
1246     {
1247         arcTo(radiusx, radiusy, xAxisRotation,
1248               largeArcFlag, sweepFlag,
1249               currX + relx, currY + rely);
1250     }
1251 
1252     int pointCrossings(float px, float py) {
1253         float movx, movy, curx, cury, endx, endy;
1254         float coords[] = floatCoords;
1255         curx = movx = coords[0];
1256         cury = movy = coords[1];
1257         int crossings = 0;
1258         int ci = 2;
1259         for (int i = 1; i < numTypes; i++) {
1260             switch (pointTypes[i]) {
1261             case PathIterator.SEG_MOVETO:
1262                 if (cury != movy) {
1263                     crossings +=
1264                         Shape.pointCrossingsForLine(px, py,
1265                                                     curx, cury,
1266                                                     movx, movy);
1267                 }
1268                 movx = curx = coords[ci++];
1269                 movy = cury = coords[ci++];
1270                 break;
1271             case PathIterator.SEG_LINETO:
1272                 crossings +=
1273                     Shape.pointCrossingsForLine(px, py,
1274                                                 curx, cury,
1275                                                 endx = coords[ci++],
1276                                                 endy = coords[ci++]);
1277                 curx = endx;
1278                 cury = endy;
1279                 break;
1280             case PathIterator.SEG_QUADTO:
1281                 crossings +=
1282                     Shape.pointCrossingsForQuad(px, py,
1283                                                 curx, cury,
1284                                                 coords[ci++],
1285                                                 coords[ci++],
1286                                                 endx = coords[ci++],
1287                                                 endy = coords[ci++],
1288                                                 0);
1289                 curx = endx;
1290                 cury = endy;
1291                 break;
1292         case PathIterator.SEG_CUBICTO:
1293                 crossings +=
1294                     Shape.pointCrossingsForCubic(px, py,
1295                                                  curx, cury,
1296                                                  coords[ci++],
1297                                                  coords[ci++],
1298                                                  coords[ci++],
1299                                                  coords[ci++],
1300                                                  endx = coords[ci++],
1301                                                  endy = coords[ci++],
1302                                                  0);
1303                 curx = endx;
1304                 cury = endy;
1305                 break;
1306             case PathIterator.SEG_CLOSE:
1307                 if (cury != movy) {
1308                     crossings +=
1309                         Shape.pointCrossingsForLine(px, py,
1310                                                     curx, cury,
1311                                                     movx, movy);
1312                 }
1313                 curx = movx;
1314                 cury = movy;
1315                 break;
1316             }
1317         }
1318         if (cury != movy) {
1319             crossings +=
1320                 Shape.pointCrossingsForLine(px, py,
1321                                             curx, cury,
1322                                             movx, movy);
1323         }
1324         return crossings;
1325     }
1326 
1327     int rectCrossings(float rxmin, float rymin,
1328                       float rxmax, float rymax)
1329     {
1330         float coords[] = floatCoords;
1331         float curx, cury, movx, movy, endx, endy;
1332         curx = movx = coords[0];
1333         cury = movy = coords[1];
1334         int crossings = 0;
1335         int ci = 2;
1336         for (int i = 1;
1337              crossings != Shape.RECT_INTERSECTS && i < numTypes;
1338              i++)
1339         {
1340             switch (pointTypes[i]) {
1341             case PathIterator.SEG_MOVETO:
1342                 if (curx != movx || cury != movy) {
1343                     crossings =
1344                         Shape.rectCrossingsForLine(crossings,
1345                                                    rxmin, rymin,
1346                                                    rxmax, rymax,
1347                                                    curx, cury,
1348                                                    movx, movy);
1349                 }
1350                 // Count should always be a multiple of 2 here.
1351                 // assert((crossings & 1) != 0);
1352                 movx = curx = coords[ci++];
1353                 movy = cury = coords[ci++];
1354                 break;
1355             case PathIterator.SEG_LINETO:
1356                 crossings =
1357                     Shape.rectCrossingsForLine(crossings,
1358                                                rxmin, rymin,
1359                                                rxmax, rymax,
1360                                                curx, cury,
1361                                                endx = coords[ci++],
1362                                                endy = coords[ci++]);
1363                 curx = endx;
1364                 cury = endy;
1365                 break;
1366             case PathIterator.SEG_QUADTO:
1367                 crossings =
1368                     Shape.rectCrossingsForQuad(crossings,
1369                                                rxmin, rymin,
1370                                                rxmax, rymax,
1371                                                curx, cury,
1372                                                coords[ci++],
1373                                                coords[ci++],
1374                                                endx = coords[ci++],
1375                                                endy = coords[ci++],
1376                                                0);
1377                 curx = endx;
1378                 cury = endy;
1379                 break;
1380             case PathIterator.SEG_CUBICTO:
1381                 crossings =
1382                     Shape.rectCrossingsForCubic(crossings,
1383                                                 rxmin, rymin,
1384                                                 rxmax, rymax,
1385                                                 curx, cury,
1386                                                 coords[ci++],
1387                                                 coords[ci++],
1388                                                 coords[ci++],
1389                                                 coords[ci++],
1390                                                 endx = coords[ci++],
1391                                                 endy = coords[ci++],
1392                                                 0);
1393                 curx = endx;
1394                 cury = endy;
1395                 break;
1396             case PathIterator.SEG_CLOSE:
1397                 if (curx != movx || cury != movy) {
1398                     crossings =
1399                         Shape.rectCrossingsForLine(crossings,
1400                                                    rxmin, rymin,
1401                                                    rxmax, rymax,
1402                                                    curx, cury,
1403                                                    movx, movy);
1404                 }
1405                 curx = movx;
1406                 cury = movy;
1407                 // Count should always be a multiple of 2 here.
1408                 // assert((crossings & 1) != 0);
1409                 break;
1410             }
1411         }
1412         if (crossings != Shape.RECT_INTERSECTS &&
1413             (curx != movx || cury != movy))
1414         {
1415             crossings =
1416                 Shape.rectCrossingsForLine(crossings,
1417                                            rxmin, rymin,
1418                                            rxmax, rymax,
1419                                            curx, cury,
1420                                            movx, movy);
1421         }
1422         // Count should always be a multiple of 2 here.
1423         // assert((crossings & 1) != 0);
1424         return crossings;
1425     }
1426 
1427     /**
1428      * {@inheritDoc}
1429      */
1430     public final void append(PathIterator pi, boolean connect) {
1431         float coords[] = new float[6];
1432         while (!pi.isDone()) {
1433             switch (pi.currentSegment(coords)) {
1434             case SEG_MOVETO:
1435                 if (!connect || numTypes < 1 || numCoords < 1) {
1436                     moveTo(coords[0], coords[1]);
1437                     break;
1438                 }
1439                 if (pointTypes[numTypes - 1] != SEG_CLOSE &&
1440                     floatCoords[numCoords-2] == coords[0] &&
1441                     floatCoords[numCoords-1] == coords[1])
1442                 {
1443                     // Collapse out initial moveto/lineto
1444                     break;
1445                 }
1446                 // NO BREAK;
1447             case SEG_LINETO:
1448                 lineTo(coords[0], coords[1]);
1449                 break;
1450             case SEG_QUADTO:
1451                 quadTo(coords[0], coords[1],
1452                        coords[2], coords[3]);
1453                 break;
1454             case SEG_CUBICTO:
1455                 curveTo(coords[0], coords[1],
1456                         coords[2], coords[3],
1457                         coords[4], coords[5]);
1458                 break;
1459             case SEG_CLOSE:
1460                 closePath();
1461                 break;
1462             }
1463             pi.next();
1464             connect = false;
1465         }
1466     }
1467 
1468     /**
1469      * {@inheritDoc}
1470      */
1471     public final void transform(BaseTransform tx) {
1472         if (numCoords == 0) return;
1473         needRoom(false, 6);
1474         floatCoords[numCoords + 0] = moveX;
1475         floatCoords[numCoords + 1] = moveY;
1476         floatCoords[numCoords + 2] = prevX;
1477         floatCoords[numCoords + 3] = prevY;
1478         floatCoords[numCoords + 4] = currX;
1479         floatCoords[numCoords + 5] = currY;
1480         tx.transform(floatCoords, 0, floatCoords, 0, numCoords / 2 + 3);
1481         moveX = floatCoords[numCoords + 0];
1482         moveY = floatCoords[numCoords + 1];
1483         prevX = floatCoords[numCoords + 2];
1484         prevY = floatCoords[numCoords + 3];
1485         currX = floatCoords[numCoords + 4];
1486         currY = floatCoords[numCoords + 5];
1487     }
1488 
1489     /**
1490      * {@inheritDoc}
1491      */
1492     public final RectBounds getBounds() {
1493         float x1, y1, x2, y2;
1494         int i = numCoords;
1495         if (i > 0) {
1496             y1 = y2 = floatCoords[--i];
1497             x1 = x2 = floatCoords[--i];
1498             while (i > 0) {
1499                 float y = floatCoords[--i];
1500                 float x = floatCoords[--i];
1501                 if (x < x1) x1 = x;
1502                 if (y < y1) y1 = y;
1503                 if (x > x2) x2 = x;
1504                 if (y > y2) y2 = y;
1505             }
1506         } else {
1507             x1 = y1 = x2 = y2 = 0.0f;
1508         }
1509         return new RectBounds(x1, y1, x2, y2);
1510     }
1511 
1512     // The following three methods are used only by Prism to access
1513     // internal structures; not intended for general use!
1514     public final int getNumCommands() {
1515         return numTypes;
1516     }
1517     public final byte[] getCommandsNoClone() {
1518         return pointTypes;
1519     }
1520     public final float[] getFloatCoordsNoClone() {
1521         return floatCoords;
1522     }
1523 
1524     /**
1525      * {@inheritDoc}
1526      * <p>
1527      * The iterator for this class is not multi-threaded safe,
1528      * which means that the {@code Path2D} class does not
1529      * guarantee that modifications to the geometry of this
1530      * {@code Path2D} object do not affect any iterations of
1531      * that geometry that are already in process.
1532      */
1533     public PathIterator getPathIterator(BaseTransform tx) {
1534         if (tx == null) {
1535             return new CopyIterator(this);
1536         } else {
1537             return new TxIterator(this, tx);
1538         }
1539     }
1540 
1541     static class CopyIterator extends Path2D.Iterator {
1542         float floatCoords[];
1543 
1544         CopyIterator(Path2D p2df) {
1545             super(p2df);
1546             this.floatCoords = p2df.floatCoords;
1547         }
1548 
1549         public int currentSegment(float[] coords) {
1550             int type = path.pointTypes[typeIdx];
1551             int numCoords = curvecoords[type];
1552             if (numCoords > 0) {
1553                 System.arraycopy(floatCoords, pointIdx,
1554                                  coords, 0, numCoords);
1555             }
1556             return type;
1557         }
1558 
1559         public int currentSegment(double[] coords) {
1560             int type = path.pointTypes[typeIdx];
1561             int numCoords = curvecoords[type];
1562             if (numCoords > 0) {
1563                 for (int i = 0; i < numCoords; i++) {
1564                     coords[i] = floatCoords[pointIdx + i];
1565                 }
1566             }
1567             return type;
1568         }
1569     }
1570 
1571     static class TxIterator extends Path2D.Iterator {
1572         float floatCoords[];
1573         BaseTransform transform;
1574 
1575         TxIterator(Path2D p2df, BaseTransform tx) {
1576             super(p2df);
1577             this.floatCoords = p2df.floatCoords;
1578             this.transform = tx;
1579         }
1580 
1581         public int currentSegment(float[] coords) {
1582             int type = path.pointTypes[typeIdx];
1583             int numCoords = curvecoords[type];
1584             if (numCoords > 0) {
1585                 transform.transform(floatCoords, pointIdx,
1586                                  coords, 0, numCoords / 2);
1587             }
1588             return type;
1589         }
1590 
1591         public int currentSegment(double[] coords) {
1592             int type = path.pointTypes[typeIdx];
1593             int numCoords = curvecoords[type];
1594             if (numCoords > 0) {
1595                 transform.transform(floatCoords, pointIdx,
1596                                  coords, 0, numCoords / 2);
1597             }
1598             return type;
1599         }
1600     }
1601 
1602     /**
1603      * Closes the current subpath by drawing a straight line back to
1604      * the coordinates of the last {@code moveTo}.  If the path is already
1605      * closed then this method has no effect.
1606      */
1607     public final void closePath() {
1608         if (numTypes == 0 || pointTypes[numTypes - 1] != SEG_CLOSE) {
1609             needRoom(true, 0);
1610             pointTypes[numTypes++] = SEG_CLOSE;
1611             prevX = currX = moveX;
1612             prevY = currY = moveY;
1613         }
1614     }
1615 
1616     public void pathDone() {
1617     }
1618 
1619     /**
1620      * Appends the geometry of the specified {@code Shape} object to the
1621      * path, possibly connecting the new geometry to the existing path
1622      * segments with a line segment.
1623      * If the {@code connect} parameter is {@code true} and the
1624      * path is not empty then any initial {@code moveTo} in the
1625      * geometry of the appended {@code Shape}
1626      * is turned into a {@code lineTo} segment.
1627      * If the destination coordinates of such a connecting {@code lineTo}
1628      * segment match the ending coordinates of a currently open
1629      * subpath then the segment is omitted as superfluous.
1630      * The winding rule of the specified {@code Shape} is ignored
1631      * and the appended geometry is governed by the winding
1632      * rule specified for this path.
1633      *
1634      * @param s the {@code Shape} whose geometry is appended
1635      *          to this path
1636      * @param connect a boolean to control whether or not to turn an initial
1637      *                {@code moveTo} segment into a {@code lineTo} segment
1638      *                to connect the new geometry to the existing path
1639      */
1640     public final void append(Shape s, boolean connect) {
1641         append(s.getPathIterator(null), connect);
1642     }
1643 
1644     static class SVGParser {
1645         final String svgpath;
1646         final int len;
1647         int pos;
1648         boolean allowcomma;
1649 
1650         public SVGParser(String svgpath) {
1651             this.svgpath = svgpath;
1652             this.len = svgpath.length();
1653         }
1654 
1655         public boolean isDone() {
1656             return (toNextNonWsp() >= len);
1657         }
1658 
1659         public char getChar() {
1660             return svgpath.charAt(pos++);
1661         }
1662 
1663         public boolean nextIsNumber() {
1664             if (toNextNonWsp() < len) {
1665                 switch (svgpath.charAt(pos)) {
1666                     case '-':
1667                     case '+':
1668                     case '0': case '1': case '2': case '3': case '4':
1669                     case '5': case '6': case '7': case '8': case '9':
1670                     case '.':
1671                         return true;
1672                 }
1673             }
1674             return false;
1675         }
1676 
1677         public float f() {
1678             return getFloat();
1679         }
1680 
1681         public float a() {
1682             return (float) Math.toRadians(getFloat());
1683         }
1684 
1685         public float getFloat() {
1686             int start = toNextNonWsp();
1687             this.allowcomma = true;
1688             int end = toNumberEnd();
1689             if (start < end) {
1690                 String flstr = svgpath.substring(start, end);
1691                 try {
1692                     return Float.parseFloat(flstr);
1693                 } catch (NumberFormatException e) {
1694                 }
1695                 throw new IllegalArgumentException("invalid float ("+flstr+
1696                                                    ") in path at pos="+start);
1697             }
1698             throw new IllegalArgumentException("end of path looking for float");
1699         }
1700 
1701         public boolean b() {
1702             toNextNonWsp();
1703             this.allowcomma = true;
1704             if (pos < len) {
1705                 char flag = svgpath.charAt(pos);
1706                 switch (flag) {
1707                     case '0': pos++; return false;
1708                     case '1': pos++; return true;
1709                 }
1710                 throw new IllegalArgumentException("invalid boolean flag ("+flag+
1711                                                    ") in path at pos="+pos);
1712             }
1713             throw new IllegalArgumentException("end of path looking for boolean");
1714         }
1715 
1716         private int toNextNonWsp() {
1717             boolean canbecomma = this.allowcomma;
1718             while (pos < len) {
1719                 switch (svgpath.charAt(pos)) {
1720                     case ',':
1721                         if (!canbecomma) {
1722                             return pos;
1723                         }
1724                         canbecomma = false;
1725                         break;
1726                     case ' ':
1727                     case '\t':
1728                     case '\r':
1729                     case '\n':
1730                         break;
1731                     default:
1732                         return pos;
1733                 }
1734                 pos++;
1735             }
1736             return pos;
1737         }
1738 
1739         private int toNumberEnd() {
1740             boolean allowsign = true;
1741             boolean hasexp = false;
1742             boolean hasdecimal = false;
1743             while (pos < len) {
1744                 switch (svgpath.charAt(pos)) {
1745                     case '-':
1746                     case '+':
1747                         if (!allowsign) return pos;
1748                         allowsign = false;
1749                         break;
1750                     case '0': case '1': case '2': case '3': case '4':
1751                     case '5': case '6': case '7': case '8': case '9':
1752                         allowsign = false;
1753                         break;
1754                     case 'E': case 'e':
1755                         if (hasexp) return pos;
1756                         hasexp = allowsign = true;
1757                         break;
1758                     case '.':
1759                         if (hasexp || hasdecimal) return pos;
1760                         hasdecimal = true;
1761                         allowsign = false;
1762                         break;
1763                     default:
1764                         return pos;
1765                 }
1766                 pos++;
1767             }
1768             return pos;
1769         }
1770     }
1771 
1772     /**
1773      * Appends the geometry of the path in the specified {@code String}
1774      * argument in the format of an SVG path.
1775      * The specification of the grammar of the language for an SVG path
1776      * is specified on the W3C web page:
1777      * <pre>
1778      * http://www.w3.org/TR/SVG/paths.html#PathDataBNF
1779      * </pre>
1780      * and the interpretation of the various elements in the format is
1781      * specified on the W3C web page:
1782      * <pre>
1783      * http://www.w3.org/TR/SVG/paths.html#PathData
1784      * </pre>
1785      *
1786      * @param svgpath the {@code String} object containing the SVG style
1787      *                definition of the geometry to be apppended
1788      * @throws IllegalArgumentException
1789      *     if {@code svgpath} does not match the indicated SVG path grammar
1790      * @throws IllegalPathStateException
1791      *     if there is no current point in the path
1792      */
1793     public final void appendSVGPath(String svgpath) {
1794         SVGParser p = new SVGParser(svgpath);
1795         p.allowcomma = false;
1796         while (!p.isDone()) {
1797             p.allowcomma = false;
1798             char cmd = p.getChar();
1799             switch (cmd) {
1800                 case 'M':
1801                     moveTo(p.f(), p.f());
1802                     while (p.nextIsNumber()) {
1803                         lineTo(p.f(), p.f());
1804                     }
1805                     break;
1806                 case 'm':
1807                     if (numTypes > 0) {
1808                         moveToRel(p.f(), p.f());
1809                     } else {
1810                         moveTo(p.f(), p.f());
1811                     }
1812                     while (p.nextIsNumber()) {
1813                         lineToRel(p.f(), p.f());
1814                     }
1815                     break;
1816                 case 'L':
1817                     do {
1818                         lineTo(p.f(), p.f());
1819                     } while (p.nextIsNumber());
1820                     break;
1821                 case 'l':
1822                     do {
1823                         lineToRel(p.f(), p.f());
1824                     } while (p.nextIsNumber());
1825                     break;
1826                 case 'H':
1827                     do {
1828                         lineTo(p.f(), currY);
1829                     } while (p.nextIsNumber());
1830                     break;
1831                 case 'h':
1832                     do {
1833                         lineToRel(p.f(), 0);
1834                     } while (p.nextIsNumber());
1835                     break;
1836                 case 'V':
1837                     do {
1838                         lineTo(currX, p.f());
1839                     } while (p.nextIsNumber());
1840                     break;
1841                 case 'v':
1842                     do {
1843                         lineToRel(0, p.f());
1844                     } while (p.nextIsNumber());
1845                     break;
1846                 case 'Q':
1847                     do {
1848                         quadTo(p.f(), p.f(), p.f(), p.f());
1849                     } while (p.nextIsNumber());
1850                     break;
1851                 case 'q':
1852                     do {
1853                         quadToRel(p.f(), p.f(), p.f(), p.f());
1854                     } while (p.nextIsNumber());
1855                     break;
1856                 case 'T':
1857                     do {
1858                         quadToSmooth(p.f(), p.f());
1859                     } while (p.nextIsNumber());
1860                     break;
1861                 case 't':
1862                     do {
1863                         quadToSmoothRel(p.f(), p.f());
1864                     } while (p.nextIsNumber());
1865                     break;
1866                 case 'C':
1867                     do {
1868                         curveTo(p.f(), p.f(), p.f(), p.f(), p.f(), p.f());
1869                     } while (p.nextIsNumber());
1870                     break;
1871                 case 'c':
1872                     do {
1873                         curveToRel(p.f(), p.f(), p.f(), p.f(), p.f(), p.f());
1874                     } while (p.nextIsNumber());
1875                     break;
1876                 case 'S':
1877                     do {
1878                         curveToSmooth(p.f(), p.f(), p.f(), p.f());
1879                     } while (p.nextIsNumber());
1880                     break;
1881                 case 's':
1882                     do {
1883                         curveToSmoothRel(p.f(), p.f(), p.f(), p.f());
1884                     } while (p.nextIsNumber());
1885                     break;
1886                 case 'A':
1887                     do {
1888                         arcTo(p.f(), p.f(), p.a(), p.b(), p.b(), p.f(), p.f());
1889                     } while (p.nextIsNumber());
1890                     break;
1891                 case 'a':
1892                     do {
1893                         arcToRel(p.f(), p.f(), p.a(), p.b(), p.b(), p.f(), p.f());
1894                     } while (p.nextIsNumber());
1895                     break;
1896                 case 'Z': case 'z': closePath(); break;
1897                 default:
1898                     throw new IllegalArgumentException("invalid command ("+cmd+
1899                                                        ") in SVG path at pos="+p.pos);
1900             }
1901             p.allowcomma = false;
1902         }
1903     }
1904 
1905     /**
1906      * Returns the fill style winding rule.
1907      *
1908      * @return an integer representing the current winding rule.
1909      * @see #WIND_EVEN_ODD
1910      * @see #WIND_NON_ZERO
1911      * @see #setWindingRule
1912      */
1913     public final int getWindingRule() {
1914         return windingRule;
1915     }
1916 
1917     /**
1918      * Sets the winding rule for this path to the specified value.
1919      *
1920      * @param rule an integer representing the specified
1921      *             winding rule
1922      * @exception IllegalArgumentException if
1923      *      {@code rule} is not either
1924      *      {@link #WIND_EVEN_ODD} or
1925      *      {@link #WIND_NON_ZERO}
1926      * @see #getWindingRule
1927      */
1928     public final void setWindingRule(int rule) {
1929         if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
1930             throw new IllegalArgumentException("winding rule must be "+
1931                                "WIND_EVEN_ODD or "+
1932                                "WIND_NON_ZERO");
1933         }
1934         windingRule = rule;
1935     }
1936 
1937     /**
1938      * Returns the coordinates most recently added to the end of the path
1939      * as a {@link Point2D} object.
1940      *
1941      * @return a {@code Point2D} object containing the ending coordinates of
1942      *         the path or {@code null} if there are no points in the path.
1943      */
1944     public final Point2D getCurrentPoint() {
1945         if (numTypes < 1) {
1946             return null;
1947         }
1948         return new Point2D(currX, currY);
1949     }
1950 
1951     public final float getCurrentX() {
1952         if (numTypes < 1) {
1953             throw new IllegalPathStateException("no current point in empty path");
1954         }
1955         return currX;
1956     }
1957 
1958     public final float getCurrentY() {
1959         if (numTypes < 1) {
1960             throw new IllegalPathStateException("no current point in empty path");
1961         }
1962         return currY;
1963     }
1964 
1965     /**
1966      * Resets the path to empty.  The append position is set back to the
1967      * beginning of the path and all coordinates and point types are
1968      * forgotten.
1969      */
1970     public final void reset() {
1971         numTypes = numCoords = 0;
1972         moveX = moveY = prevX = prevY = currX = currY = 0;
1973     }
1974 
1975     /**
1976      * Returns a new {@code Shape} representing a transformed version
1977      * of this {@code Path2D}.
1978      * Note that the exact type and coordinate precision of the return
1979      * value is not specified for this method.
1980      * The method will return a Shape that contains no less precision
1981      * for the transformed geometry than this {@code Path2D} currently
1982      * maintains, but it may contain no more precision either.
1983      * If the tradeoff of precision vs. storage size in the result is
1984      * important then the convenience constructors in the
1985      * {@link Path2D(Shape, BaseTransform) Path2D}
1986      *
1987      * @param tx the {@code BaseTransform} used to transform a
1988      *           new {@code Shape}.
1989      * @return a new {@code Shape}, transformed with the specified
1990      *         {@code BaseTransform}.
1991      */
1992     public final Shape createTransformedShape(BaseTransform tx) {
1993         return new Path2D(this, tx);
1994     }
1995 
1996     @Override
1997     public Path2D copy() {
1998         return new Path2D(this);
1999     }
2000 
2001     /**
2002      * {@inheritDoc}
2003      *
2004      * Note that this method may return false when the geometry of the
2005      * given {@code Path2D} is identical to the geometry of this object
2006      * but is expressed in a different way.  This method will only return
2007      * true when the internal representation of this object is exactly the
2008      * same as that of the given object.
2009      */
2010     @Override
2011     public boolean equals(Object obj) {
2012         if (obj == this) {
2013             return true;
2014         }
2015         if (obj instanceof Path2D) {
2016             Path2D p = (Path2D)obj;
2017             if (p.numTypes == this.numTypes &&
2018                 p.numCoords == this.numCoords &&
2019                 p.windingRule == this.windingRule)
2020             {
2021                 for (int i = 0; i < numTypes; i++) {
2022                     if (p.pointTypes[i] != this.pointTypes[i]) {
2023                         return false;
2024                     }
2025                 }
2026                 for (int i = 0; i < numCoords; i++) {
2027                     if (p.floatCoords[i] != this.floatCoords[i]) {
2028                         return false;
2029                     }
2030                 }
2031                 return true;
2032             }
2033         }
2034         return false;
2035     }
2036 
2037     @Override
2038     public int hashCode() {
2039         int hash = 7;
2040         hash = 11 * hash + numTypes;
2041         hash = 11 * hash + numCoords;
2042         hash = 11 * hash + windingRule;
2043         for (int i = 0; i < numTypes; i++) {
2044             hash = 11 * hash + pointTypes[i];
2045         }
2046         for (int i = 0; i < numCoords; i++) {
2047             hash = 11 * hash + Float.floatToIntBits(floatCoords[i]);
2048         }
2049         return hash;
2050     }
2051 
2052     /**
2053      * Tests if the specified coordinates are inside the closed
2054      * boundary of the specified {@link PathIterator}.
2055      * <p>
2056      * This method provides a basic facility for implementors of
2057      * the {@link Shape} interface to implement support for the
2058      * {@link Shape#contains(double, double)} method.
2059      *
2060      * @param pi the specified {@code PathIterator}
2061      * @param x the specified X coordinate
2062      * @param y the specified Y coordinate
2063      * @return {@code true} if the specified coordinates are inside the
2064      *         specified {@code PathIterator}; {@code false} otherwise
2065      */
2066     public static boolean contains(PathIterator pi, float x, float y) {
2067         if (x * 0f + y * 0f == 0f) {
2068             /* N * 0.0 is 0.0 only if N is finite.
2069              * Here we know that both x and y are finite.
2070              */
2071             int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 1);
2072             int cross = Shape.pointCrossingsForPath(pi, x, y);
2073             return ((cross & mask) != 0);
2074         } else {
2075             /* Either x or y was infinite or NaN.
2076              * A NaN always produces a negative response to any test
2077              * and Infinity values cannot be "inside" any path so
2078              * they should return false as well.
2079              */
2080             return false;
2081         }
2082     }
2083 
2084     /**
2085      * Tests if the specified {@link Point2D} is inside the closed
2086      * boundary of the specified {@link PathIterator}.
2087      * <p>
2088      * This method provides a basic facility for implementors of
2089      * the {@link Shape} interface to implement support for the
2090      * {@link Shape#contains(Point2D)} method.
2091      *
2092      * @param pi the specified {@code PathIterator}
2093      * @param p the specified {@code Point2D}
2094      * @return {@code true} if the specified coordinates are inside the
2095      *         specified {@code PathIterator}; {@code false} otherwise
2096      */
2097     public static boolean contains(PathIterator pi, Point2D p) {
2098         return contains(pi, p.x, p.y);
2099     }
2100 
2101     /**
2102      * {@inheritDoc}
2103      */
2104     public final boolean contains(float x, float y) {
2105         if (x * 0f + y * 0f == 0f) {
2106             /* N * 0.0 is 0.0 only if N is finite.
2107              * Here we know that both x and y are finite.
2108              */
2109             if (numTypes < 2) {
2110                 return false;
2111             }
2112             int mask = (windingRule == WIND_NON_ZERO ? -1 : 1);
2113             return ((pointCrossings(x, y) & mask) != 0);
2114         } else {
2115             /* Either x or y was infinite or NaN.
2116              * A NaN always produces a negative response to any test
2117              * and Infinity values cannot be "inside" any path so
2118              * they should return false as well.
2119              */
2120             return false;
2121         }
2122     }
2123 
2124     /**
2125      * {@inheritDoc}
2126      */
2127     @Override
2128     public final boolean contains(Point2D p) {
2129         return contains(p.x, p.y);
2130     }
2131 
2132     /**
2133      * Tests if the specified rectangular area is entirely inside the
2134      * closed boundary of the specified {@link PathIterator}.
2135      * <p>
2136      * This method provides a basic facility for implementors of
2137      * the {@link Shape} interface to implement support for the
2138      * {@link Shape#contains(double, double, double, double)} method.
2139      * <p>
2140      * This method object may conservatively return false in
2141      * cases where the specified rectangular area intersects a
2142      * segment of the path, but that segment does not represent a
2143      * boundary between the interior and exterior of the path.
2144      * Such segments could lie entirely within the interior of the
2145      * path if they are part of a path with a {@link #WIND_NON_ZERO}
2146      * winding rule or if the segments are retraced in the reverse
2147      * direction such that the two sets of segments cancel each
2148      * other out without any exterior area falling between them.
2149      * To determine whether segments represent true boundaries of
2150      * the interior of the path would require extensive calculations
2151      * involving all of the segments of the path and the winding
2152      * rule and are thus beyond the scope of this implementation.
2153      *
2154      * @param pi the specified {@code PathIterator}
2155      * @param x the specified X coordinate
2156      * @param y the specified Y coordinate
2157      * @param w the width of the specified rectangular area
2158      * @param h the height of the specified rectangular area
2159      * @return {@code true} if the specified {@code PathIterator} contains
2160      *         the specified rectangluar area; {@code false} otherwise.
2161      */
2162     public static boolean contains(PathIterator pi,
2163                                    float x, float y, float w, float h)
2164     {
2165         if (java.lang.Float.isNaN(x+w) || java.lang.Float.isNaN(y+h)) {
2166             /* [xy]+[wh] is NaN if any of those values are NaN,
2167              * or if adding the two together would produce NaN
2168              * by virtue of adding opposing Infinte values.
2169              * Since we need to add them below, their sum must
2170              * not be NaN.
2171              * We return false because NaN always produces a
2172              * negative response to tests
2173              */
2174             return false;
2175         }
2176         if (w <= 0 || h <= 0) {
2177             return false;
2178         }
2179         int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
2180         int crossings = Shape.rectCrossingsForPath(pi, x, y, x+w, y+h);
2181         return (crossings != Shape.RECT_INTERSECTS &&
2182                     (crossings & mask) != 0);
2183     }
2184 
2185     /**
2186      * {@inheritDoc}
2187      * <p>
2188      * This method object may conservatively return false in
2189      * cases where the specified rectangular area intersects a
2190      * segment of the path, but that segment does not represent a
2191      * boundary between the interior and exterior of the path.
2192      * Such segments could lie entirely within the interior of the
2193      * path if they are part of a path with a {@link #WIND_NON_ZERO}
2194      * winding rule or if the segments are retraced in the reverse
2195      * direction such that the two sets of segments cancel each
2196      * other out without any exterior area falling between them.
2197      * To determine whether segments represent true boundaries of
2198      * the interior of the path would require extensive calculations
2199      * involving all of the segments of the path and the winding
2200      * rule and are thus beyond the scope of this implementation.
2201      */
2202     public final boolean contains(float x, float y, float w, float h) {
2203         if (java.lang.Float.isNaN(x+w) || java.lang.Float.isNaN(y+h)) {
2204             /* [xy]+[wh] is NaN if any of those values are NaN,
2205              * or if adding the two together would produce NaN
2206              * by virtue of adding opposing Infinte values.
2207              * Since we need to add them below, their sum must
2208              * not be NaN.
2209              * We return false because NaN always produces a
2210              * negative response to tests
2211              */
2212             return false;
2213         }
2214         if (w <= 0 || h <= 0) {
2215             return false;
2216         }
2217         int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
2218         int crossings = rectCrossings(x, y, x+w, y+h);
2219         return (crossings != Shape.RECT_INTERSECTS &&
2220                     (crossings & mask) != 0);
2221     }
2222 
2223     /**
2224      * Tests if the interior of the specified {@link PathIterator}
2225      * intersects the interior of a specified set of rectangular
2226      * coordinates.
2227      * <p>
2228      * This method provides a basic facility for implementors of
2229      * the {@link Shape} interface to implement support for the
2230      * {@link Shape#intersects(double, double, double, double)} method.
2231      * <p>
2232      * This method object may conservatively return true in
2233      * cases where the specified rectangular area intersects a
2234      * segment of the path, but that segment does not represent a
2235      * boundary between the interior and exterior of the path.
2236      * Such a case may occur if some set of segments of the
2237      * path are retraced in the reverse direction such that the
2238      * two sets of segments cancel each other out without any
2239      * interior area between them.
2240      * To determine whether segments represent true boundaries of
2241      * the interior of the path would require extensive calculations
2242      * involving all of the segments of the path and the winding
2243      * rule and are thus beyond the scope of this implementation.
2244      *
2245      * @param pi the specified {@code PathIterator}
2246      * @param x the specified X coordinate
2247      * @param y the specified Y coordinate
2248      * @param w the width of the specified rectangular coordinates
2249      * @param h the height of the specified rectangular coordinates
2250      * @return {@code true} if the specified {@code PathIterator} and
2251      *         the interior of the specified set of rectangular
2252      *         coordinates intersect each other; {@code false} otherwise.
2253      */
2254     public static boolean intersects(PathIterator pi,
2255                                      float x, float y, float w, float h)
2256     {
2257         if (java.lang.Float.isNaN(x+w) || java.lang.Float.isNaN(y+h)) {
2258             /* [xy]+[wh] is NaN if any of those values are NaN,
2259              * or if adding the two together would produce NaN
2260              * by virtue of adding opposing Infinte values.
2261              * Since we need to add them below, their sum must
2262              * not be NaN.
2263              * We return false because NaN always produces a
2264              * negative response to tests
2265              */
2266             return false;
2267         }
2268         if (w <= 0 || h <= 0) {
2269             return false;
2270         }
2271         int mask = (pi.getWindingRule() == WIND_NON_ZERO ? -1 : 2);
2272         int crossings = Shape.rectCrossingsForPath(pi, x, y, x+w, y+h);
2273         return (crossings == Shape.RECT_INTERSECTS ||
2274                     (crossings & mask) != 0);
2275     }
2276 
2277     /**
2278      * {@inheritDoc}
2279      * <p>
2280      * This method object may conservatively return true in
2281      * cases where the specified rectangular area intersects a
2282      * segment of the path, but that segment does not represent a
2283      * boundary between the interior and exterior of the path.
2284      * Such a case may occur if some set of segments of the
2285      * path are retraced in the reverse direction such that the
2286      * two sets of segments cancel each other out without any
2287      * interior area between them.
2288      * To determine whether segments represent true boundaries of
2289      * the interior of the path would require extensive calculations
2290      * involving all of the segments of the path and the winding
2291      * rule and are thus beyond the scope of this implementation.
2292      */
2293     public final boolean intersects(float x, float y, float w, float h) {
2294         if (java.lang.Float.isNaN(x+w) || java.lang.Float.isNaN(y+h)) {
2295             /* [xy]+[wh] is NaN if any of those values are NaN,
2296              * or if adding the two together would produce NaN
2297              * by virtue of adding opposing Infinte values.
2298              * Since we need to add them below, their sum must
2299              * not be NaN.
2300              * We return false because NaN always produces a
2301              * negative response to tests
2302              */
2303             return false;
2304         }
2305         if (w <= 0 || h <= 0) {
2306             return false;
2307         }
2308         int mask = (windingRule == WIND_NON_ZERO ? -1 : 2);
2309         int crossings = rectCrossings(x, y, x+w, y+h);
2310         return (crossings == Shape.RECT_INTERSECTS ||
2311                     (crossings & mask) != 0);
2312     }
2313 
2314     /**
2315      * {@inheritDoc}
2316      * <p>
2317      * The iterator for this class is not multi-threaded safe,
2318      * which means that this {@code Path2D} class does not
2319      * guarantee that modifications to the geometry of this
2320      * {@code Path2D} object do not affect any iterations of
2321      * that geometry that are already in process.
2322      */
2323     public PathIterator getPathIterator(BaseTransform tx,
2324                                         float flatness)
2325     {
2326         return new FlatteningPathIterator(getPathIterator(tx), flatness);
2327     }
2328 
2329     static abstract class Iterator implements PathIterator {
2330         int typeIdx;
2331         int pointIdx;
2332         Path2D path;
2333 
2334         Iterator(Path2D path) {
2335             this.path = path;
2336         }
2337 
2338         public int getWindingRule() {
2339             return path.getWindingRule();
2340         }
2341 
2342         public boolean isDone() {
2343             return (typeIdx >= path.numTypes);
2344         }
2345 
2346         public void next() {
2347             int type = path.pointTypes[typeIdx++];
2348             pointIdx += curvecoords[type];
2349         }
2350     }
2351 
2352     public void setTo(Path2D otherPath) {
2353         numTypes = otherPath.numTypes;
2354         numCoords = otherPath.numCoords;
2355         if (numTypes > pointTypes.length) {
2356             pointTypes = new byte[numTypes];
2357         }
2358         System.arraycopy(otherPath.pointTypes, 0, pointTypes, 0, numTypes);
2359         if (numCoords > floatCoords.length) {
2360             floatCoords = new float[numCoords];
2361         }
2362         System.arraycopy(otherPath.floatCoords, 0, floatCoords, 0, numCoords);
2363         windingRule = otherPath.windingRule;
2364         moveX = otherPath.moveX;
2365         moveY = otherPath.moveY;
2366         prevX = otherPath.prevX;
2367         prevY = otherPath.prevY;
2368         currX = otherPath.currX;
2369         currY = otherPath.currY;
2370     }
2371 }