1 /*
   2  * Copyright (c) 2011, 2013, 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 test.com.sun.javafx.geom;
  27 
  28 import com.sun.javafx.geom.Arc2D;
  29 import com.sun.javafx.geom.BoxBounds;
  30 import com.sun.javafx.geom.IllegalPathStateException;
  31 import com.sun.javafx.geom.Path2D;
  32 import com.sun.javafx.geom.PathIterator;
  33 import com.sun.javafx.geom.RectBounds;
  34 import com.sun.javafx.geom.Shape;
  35 import com.sun.javafx.geom.transform.BaseTransform;
  36 import static junit.framework.Assert.assertTrue;
  37 import static junit.framework.Assert.assertFalse;
  38 import static junit.framework.Assert.assertEquals;
  39 
  40 import org.junit.Test;
  41 
  42 public class Path2DTest {
  43     void checkLine(PathIterator pi, float x1, float y1, float x2, float y2) {
  44         float coords[] = new float[2];
  45         assertFalse(pi.isDone());
  46         assertEquals(PathIterator.SEG_MOVETO, pi.currentSegment(coords));
  47         assertEquals(x1, coords[0], .001);
  48         assertEquals(y1, coords[1], .001);
  49         assertFalse(pi.isDone());
  50         pi.next();
  51         assertFalse(pi.isDone());
  52         assertEquals(PathIterator.SEG_LINETO, pi.currentSegment(coords));
  53         assertEquals(x2, coords[0], .001);
  54         assertEquals(y2, coords[1], .001);
  55         assertFalse(pi.isDone());
  56         pi.next();
  57         assertTrue(pi.isDone());
  58     }
  59 
  60     void checkAndResetPaths(Path2D pref, Path2D ptest, float curx, float cury) {
  61         checkAndResetPaths(pref, ptest, curx, cury, false);
  62     }
  63 
  64     void checkAndResetPaths(Path2D pref, Path2D ptest,
  65                             float curx, float cury,
  66                             boolean verbose)
  67     {
  68         assertEquals(curx, pref.getCurrentX(), .001);
  69         assertEquals(cury, pref.getCurrentY(), .001);
  70         checkShapes(pref, ptest, verbose);
  71         pref.reset();
  72         ptest.reset();
  73     }
  74 
  75     void checkShapes(Shape sref, Shape stest) {
  76         checkShapes(sref, stest, false);
  77     }
  78 
  79     void checkShapes(Shape sref, Shape stest, boolean verbose) {
  80         checkPaths(sref.getPathIterator(BaseTransform.IDENTITY_TRANSFORM),
  81                    stest.getPathIterator(BaseTransform.IDENTITY_TRANSFORM),
  82                    verbose);
  83     }
  84 
  85     void checkPaths(PathIterator piref, PathIterator pitest) {
  86         checkPaths(piref, pitest, false);
  87     }
  88 
  89     static int numcoords[];
  90     static {
  91         numcoords = new int[5];
  92         numcoords[PathIterator.SEG_MOVETO] = 2;
  93         numcoords[PathIterator.SEG_LINETO] = 2;
  94         numcoords[PathIterator.SEG_QUADTO] = 4;
  95         numcoords[PathIterator.SEG_CUBICTO] = 6;
  96         numcoords[PathIterator.SEG_CLOSE] = 0;
  97     }
  98 
  99     void checkPaths(PathIterator piref, PathIterator pitest, boolean verbose) {
 100         float coordsref[] = new float[6];
 101         float coordstest[] = new float[6];
 102         while (!piref.isDone()) {
 103             assertFalse(pitest.isDone());
 104             int typref = piref.currentSegment(coordsref);
 105             int typtest = pitest.currentSegment(coordstest);
 106             assertEquals(typref, typtest);
 107             if (verbose) System.out.println("type = "+typref);
 108             for (int i = 0; i < numcoords[typref]; i++) {
 109                 assertEquals(coordsref[i], coordstest[i], .001);
 110                 if (verbose) System.out.println("coord["+i+"] = "+coordsref[i]);
 111             }
 112             assertFalse(pitest.isDone());
 113             piref.next();
 114             pitest.next();
 115         }
 116         assertTrue(pitest.isDone());
 117     }
 118 
 119     double angle(double ux, double uy, double vx, double vy) {
 120         double sgn = (ux * vy - uy * vx) > 0 ? 1f : -1f;
 121         double dot = ux * vx + uy * vy;
 122         double ulen = Math.hypot(ux, uy);
 123         double vlen = Math.hypot(vx, vy);
 124         double cos = dot / (ulen * vlen);
 125         if (cos < -1f) cos = -1f;
 126         else if (cos > 1f) cos = 1f;
 127         return sgn * Math.acos(cos);
 128     }
 129 
 130     void checkArcTo(float x1, float y1,
 131                     float rw, float rh, float arcrad,
 132                     boolean largeArcs, boolean sweepFlag,
 133                     float x2, float y2)
 134     {
 135 //        System.out.println("rw="+rw+", rh="+rh+", phi="+arcrad+", fA="+largeArcs+", fS="+sweepFlag+", x="+x2+", y="+y2);
 136         // Comparing to math specified at:
 137         // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
 138         Path2D path = new Path2D();
 139         path.moveTo(x1, y1);
 140         path.arcTo(rw/2f, rh/2f, arcrad, largeArcs, sweepFlag, x2, y2);
 141         double rx = rw/2.0;
 142         double ry = rh/2.0;
 143         if (rx == 0 || ry == 0) {
 144             checkLine(path.getPathIterator(BaseTransform.IDENTITY_TRANSFORM),
 145                       x1, y1, x2, y2);
 146             return;
 147         }
 148         double cosphi = Math.cos(arcrad);
 149         double sinphi = Math.sin(arcrad);
 150         double x1p =  cosphi * ((x1 - x2) / 2f) + sinphi * ((y1 - y2) / 2f);
 151         double y1p = -sinphi * ((x1 - x2) / 2f) + cosphi * ((y1 - y2) / 2f);
 152         double x1psq = x1p * x1p;
 153         double y1psq = y1p * y1p;
 154         double rxsq = rx * rx;
 155         double rysq = ry * ry;
 156         double delta = (x1psq / rxsq) + (y1psq / rysq);
 157         double num = rxsq * rysq - rxsq * y1psq - rysq * x1psq;
 158         if (delta > 1f) {
 159             rx *= Math.sqrt(delta);
 160             ry *= Math.sqrt(delta);
 161             rxsq = rx * rx;
 162             rysq = ry * ry;
 163             // Note that repeating the num calculation can sometimes yield
 164             // a negative answer...
 165             num = 0;
 166         }
 167         double den = rxsq * y1psq + rysq * x1psq;
 168         double sgn = (largeArcs == sweepFlag) ? -1f : 1f;
 169         double cxp = sgn * Math.sqrt(num / den) * (rx * y1p) / ry;
 170         double cyp = sgn * Math.sqrt(num / den) * -(ry * x1p) / rx;
 171         double cx = cosphi * cxp - sinphi * cyp + (x1 + x2) / 2f;
 172         double cy = sinphi * cxp + cosphi * cyp + (y1 + y2) / 2f;
 173         double theta = angle(1, 0,
 174                              (x1p - cxp) / rx, (y1p - cyp) / ry);
 175         double dtheta = angle((x1p - cxp) / rx, (y1p - cyp) / ry,
 176                               (-x1p - cxp) / rx, (-y1p - cyp) / ry);
 177         theta = Math.toDegrees(theta);
 178         dtheta = Math.toDegrees(dtheta);
 179         if (sweepFlag && dtheta < 0) dtheta += 360;
 180         if (!sweepFlag && dtheta > 0) dtheta -= 360;
 181         Arc2D arc = new Arc2D((float) (cx-rx), (float) (cy-ry),
 182                               (float) (rx*2.0), (float) (ry*2.0),
 183                               (float) -theta, (float) -dtheta, Arc2D.OPEN);
 184         BaseTransform arctx =
 185             BaseTransform.getRotateInstance(arcrad, cx, cy);
 186         checkPaths(arc.getPathIterator(arctx),
 187                    path.getPathIterator(BaseTransform.IDENTITY_TRANSFORM));
 188     }
 189 
 190     public @Test
 191     void testArcTo() {
 192         Path2D path = new Path2D();
 193         for (int pathdeg = 0; pathdeg <= 360; pathdeg += 15) {
 194             double pathrad = Math.toRadians(pathdeg);
 195             float px = (float) Math.cos(pathrad) * 50;
 196             float py = (float) Math.sin(pathrad) * 50;
 197             for (int arcdeg = 0; arcdeg <= 360; arcdeg += 15) {
 198                 float arcrad = (float) Math.toRadians(arcdeg);
 199                 for (int rw = 0; rw < 100; rw += 10) {
 200                     for (int rh = 0; rh < 100; rh += 10) {
 201                         checkArcTo(-px, -py, rw, rh, arcrad, false, false, px, py);
 202                         checkArcTo(-px, -py, rw, rh, arcrad, false,  true, px, py);
 203                         checkArcTo(-px, -py, rw, rh, arcrad,  true, false, px, py);
 204                         checkArcTo(-px, -py, rw, rh, arcrad,  true,  true, px, py);
 205                     }
 206                 }
 207             }
 208         }
 209         RectBounds rectBounds = new RectBounds(10, 20, 20, 30);
 210         assertFalse(rectBounds.isEmpty());
 211         rectBounds.makeEmpty();
 212         assertTrue(rectBounds.isEmpty());
 213         assertEquals(new RectBounds(), rectBounds);
 214 
 215         BoxBounds boxBounds = new BoxBounds(10, 20, 10, 40, 50, 20);
 216         assertFalse(boxBounds.isEmpty());
 217         boxBounds.makeEmpty();
 218         assertTrue(boxBounds.isEmpty());
 219         assertEquals(new BoxBounds(), boxBounds);
 220     }
 221 
 222     public @Test
 223     void testEmptyPathException() {
 224         int bad = 0;
 225         Path2D p = new Path2D();
 226         try { p.lineTo(0, 0); bad++; } catch (IllegalPathStateException e) {}
 227         try { p.quadTo(0, 0, 0, 0); bad++; } catch (IllegalPathStateException e) {}
 228         try { p.curveTo(0, 0, 0, 0, 0, 0); bad++; } catch (IllegalPathStateException e) {}
 229         try { p.arcTo(1, 1, 0, true, true, 1, 1); bad++; } catch (IllegalPathStateException e) {}
 230         try { p.moveToRel(0, 0); bad++; } catch (IllegalPathStateException e) {}
 231         try { p.lineToRel(0, 0); bad++; } catch (IllegalPathStateException e) {}
 232         try { p.quadToRel(0, 0, 0, 0); bad++; } catch (IllegalPathStateException e) {}
 233         try { p.curveToRel(0, 0, 0, 0, 0, 0); bad++; } catch (IllegalPathStateException e) {}
 234         try { p.arcToRel(1, 1, 0, true, true, 1, 1); bad++; } catch (IllegalPathStateException e) {}
 235         try { p.quadToSmooth(0, 0); bad++; } catch (IllegalPathStateException e) {}
 236         try { p.curveToSmooth(0, 0, 0, 0); bad++; } catch (IllegalPathStateException e) {}
 237         try { p.quadToSmoothRel(0, 0); bad++; } catch (IllegalPathStateException e) {}
 238         try { p.curveToSmoothRel(0, 0, 0, 0); bad++; } catch (IllegalPathStateException e) {}
 239         assertEquals(0, bad);
 240     }
 241 
 242     public @Test
 243     void testRelative() {
 244         Path2D pabs = new Path2D();
 245         Path2D prel = new Path2D();
 246         for (int x0 = -100; x0 < 100; x0 += 50) {
 247             for (int y0 = -100; y0 < 100; y0 += 50) {
 248                 for (int x1 = -100; x1 < 100; x1 += 50) {
 249                     for (int y1 = -100; y1 < 100; y1 += 50) {
 250                         testRelative(pabs, prel, x0, y0, x1, y1);
 251                     }
 252                 }
 253             }
 254         }
 255     }
 256 
 257     private void testRelative(Path2D pabs, Path2D prel,
 258                               int x0, int y0, int x1, int y1)
 259     {
 260         // Test relative moveTo following moveTo
 261         pabs.moveTo(x0, y0);
 262         pabs.moveTo(x1, y1);
 263         prel.moveTo(x0, y0);
 264         prel.moveToRel(x1-x0, y1-y0);
 265         checkAndResetPaths(pabs, prel, x1, y1);
 266 
 267         // Test relative lineTo
 268         pabs.moveTo(x0, y0);
 269         pabs.lineTo(x1, y1);
 270         prel.moveTo(x0, y0);
 271         prel.lineToRel(x1-x0, y1-y0);
 272         checkAndResetPaths(pabs, prel, x1, y1);
 273 
 274         // test relative arcTo
 275         pabs.moveTo(x0, y0);
 276         pabs.arcTo(1, 1, 0, true, true, x1, y1);
 277         prel.moveTo(x0, y0);
 278         prel.arcToRel(1, 1, 0, true, true, x1-x0, y1-y0);
 279         checkAndResetPaths(pabs, prel, x1, y1);
 280 
 281         // test relative paths with longer coordinate lists
 282         for (int x2 = -100; x2 < 100; x2 += 50) {
 283             for (int y2 = -100; y2 < 100; y2 += 50) {
 284                 testRelative(pabs, prel, x0, y0, x1, y1, x2, y2);
 285             }
 286         }
 287     }
 288 
 289     private void testRelative(Path2D pabs, Path2D prel,
 290                               int x0, int y0, int x1, int y1, int x2, int y2)
 291     {
 292         // test relative quadTo
 293         pabs.moveTo(x0, y0);
 294         pabs.quadTo(x1, y1, x2, y2);
 295         prel.moveTo(x0, y0);
 296         prel.quadToRel(x1-x0, y1-y0, x2-x0, y2-y0);
 297         checkAndResetPaths(pabs, prel, x2, y2);
 298 
 299         for (int x3 = -100; x3 < 100; x3 += 50) {
 300             for (int y3 = -100; y3 < 100; y3 += 50) {
 301                 // test relative cubic curveTo
 302                 pabs.moveTo(x0, y0);
 303                 pabs.curveTo(x1, y1, x2, y2, x3, y3);
 304                 prel.moveTo(x0, y0);
 305                 prel.curveToRel(x1-x0, y1-y0, x2-x0, y2-y0, x3-x0, y3-y0);
 306                 checkAndResetPaths(pabs, prel, x3, y3);
 307             }
 308         }
 309     }
 310 
 311     public @Test
 312     void testSmoothCurves() {
 313         Path2D pabs = new Path2D();
 314         Path2D psmooth = new Path2D();
 315         for (int x0 = -100; x0 < 100; x0 += 50) {
 316             for (int y0 = -100; y0 < 100; y0 += 50) {
 317                 for (int x1 = -100; x1 < 100; x1 += 50) {
 318                     for (int y1 = -100; y1 < 100; y1 += 50) {
 319                         testSmoothCurves(pabs, psmooth, x0, y0, x1, y1);
 320                     }
 321                 }
 322             }
 323         }
 324     }
 325     
 326     private void testSmoothCurves(Path2D pabs, Path2D psmooth,
 327                                   int x0, int y0, int x1, int y1)
 328     {
 329         for (int xc0 = -100; xc0 < 100; xc0 += 100) {
 330             for (int yc0 = -100; yc0 < 100; yc0 += 100) {
 331                 // test smooth quadto after lineTo
 332                 pabs.moveTo(x0, y0);
 333                 pabs.lineTo(xc0, yc0);
 334                 pabs.quadTo(xc0, yc0, x1, y1);
 335                 psmooth.moveTo(x0, y0);
 336                 psmooth.lineTo(xc0, yc0);
 337                 psmooth.quadToSmooth(x1, y1);
 338                 checkAndResetPaths(pabs, psmooth, x1, y1);
 339 
 340                 // test smooth relative quadTo after lineTo
 341                 pabs.moveTo(x0, y0);
 342                 pabs.lineTo(xc0, yc0);
 343                 pabs.quadTo(xc0, yc0, x1, y1);
 344                 psmooth.moveTo(x0, y0);
 345                 psmooth.lineTo(xc0, yc0);
 346                 psmooth.quadToSmoothRel(x1-xc0, y1-yc0);
 347                 checkAndResetPaths(pabs, psmooth, x1, y1);
 348 
 349                 for (int xc1 = -100; xc1 < 100; xc1 += 100) {
 350                     for (int yc1 = -100; yc1 < 100; yc1 += 100) {
 351                         float xc01 = (xc0 + xc1) / 2f;
 352                         float yc01 = (yc0 + yc1) / 2f;
 353 
 354                         // test smooth quadTo after quadTo
 355                         pabs.moveTo(x0, y0);
 356                         pabs.quadTo(xc0, yc0, xc01, yc01);
 357                         pabs.quadTo(xc1, yc1, x1, y1);
 358                         psmooth.moveTo(x0, y0);
 359                         psmooth.quadTo(xc0, yc0, xc01, yc01);
 360                         psmooth.quadToSmooth(x1, y1);
 361                         checkAndResetPaths(pabs, psmooth, x1, y1);
 362 
 363                         // test smooth relative quadTo after quadTo
 364                         pabs.moveTo(x0, y0);
 365                         pabs.quadTo(xc0, yc0, xc01, yc01);
 366                         pabs.quadTo(xc1, yc1, x1, y1);
 367                         psmooth.moveTo(x0, y0);
 368                         psmooth.quadTo(xc0, yc0, xc01, yc01);
 369                         psmooth.quadToSmoothRel(x1-xc01, y1-yc01);
 370                         checkAndResetPaths(pabs, psmooth, x1, y1);
 371 
 372                         // test smooth curveTo after lineTo
 373                         pabs.moveTo(x0, y0);
 374                         pabs.lineTo(xc0, yc0);
 375                         pabs.curveTo(xc0, yc0, xc1, yc1, x1, y1);
 376                         psmooth.moveTo(x0, y0);
 377                         psmooth.lineTo(xc0, yc0);
 378                         psmooth.curveToSmooth(xc1, yc1, x1, y1);
 379                         checkAndResetPaths(pabs, psmooth, x1, y1);
 380 
 381                         // test smooth relative curveTo after lineTo
 382                         pabs.moveTo(x0, y0);
 383                         pabs.lineTo(xc0, yc0);
 384                         pabs.curveTo(xc0, yc0, xc1, yc1, x1, y1);
 385                         psmooth.moveTo(x0, y0);
 386                         psmooth.lineTo(xc0, yc0);
 387                         psmooth.curveToSmoothRel(xc1-xc0, yc1-yc0, x1-xc0, y1-yc0);
 388                         checkAndResetPaths(pabs, psmooth, x1, y1);
 389 
 390                         testSmoothCurves(pabs, psmooth,
 391                                          x0, y0, x1, y1,
 392                                          xc0, yc0, xc01, yc01, xc1, yc1);
 393                     }
 394                 }
 395             }
 396         }
 397     }
 398 
 399     private void testSmoothCurves(Path2D pabs, Path2D psmooth,
 400                                   int x0, int y0, int x1, int y1,
 401                                   int xc0, int yc0,
 402                                   float xc01, float yc01,
 403                                   int xc1, int yc1)
 404     {
 405         for (int xc2 = -100; xc2 < 100; xc2 += 100) {
 406             for (int yc2 = -100; yc2 < 100; yc2 += 100) {
 407                 // test smooth curveTo after quadTo
 408                 pabs.moveTo(x0, y0);
 409                 pabs.quadTo(xc0, yc0, xc01, yc01);
 410                 pabs.curveTo(xc1, yc1, xc2, yc2, x1, y1);
 411                 psmooth.moveTo(x0, y0);
 412                 psmooth.quadTo(xc0, yc0, xc01, yc01);
 413                 psmooth.curveToSmooth(xc2, yc2, x1, y1);
 414                 checkAndResetPaths(pabs, psmooth, x1, y1);
 415 
 416                 // test smooth relative curveTo after quadTo
 417                 pabs.moveTo(x0, y0);
 418                 pabs.quadTo(xc0, yc0, xc01, yc01);
 419                 pabs.curveTo(xc1, yc1, xc2, yc2, x1, y1);
 420                 psmooth.moveTo(x0, y0);
 421                 psmooth.quadTo(xc0, yc0, xc01, yc01);
 422                 psmooth.curveToSmoothRel(xc2-xc01, yc2-yc01, x1-xc01, y1-yc01);
 423                 checkAndResetPaths(pabs, psmooth, x1, y1);
 424 
 425                 float xc12 = (xc1 + xc2) / 2f;
 426                 float yc12 = (yc1 + yc2) / 2f;
 427 
 428                 // test smooth quadTo after curveTo
 429                 pabs.moveTo(x0, y0);
 430                 pabs.curveTo(xc0, yc0, xc1, yc1, xc12, yc12);
 431                 pabs.quadTo(xc2, yc2, x1, y1);
 432                 psmooth.moveTo(x0, y0);
 433                 psmooth.curveTo(xc0, yc0, xc1, yc1, xc12, yc12);
 434                 psmooth.quadToSmooth(x1, y1);
 435                 checkAndResetPaths(pabs, psmooth, x1, y1);
 436 
 437                 // test smooth relative quadTo after curveTo
 438                 pabs.moveTo(x0, y0);
 439                 pabs.curveTo(xc0, yc0, xc1, yc1, xc12, yc12);
 440                 pabs.quadTo(xc2, yc2, x1, y1);
 441                 psmooth.moveTo(x0, y0);
 442                 psmooth.curveTo(xc0, yc0, xc1, yc1, xc12, yc12);
 443                 psmooth.quadToSmoothRel(x1-xc12, y1-yc12);
 444                 checkAndResetPaths(pabs, psmooth, x1, y1);
 445 
 446                 for (int xc3 = -100; xc3 < 100; xc3 += 100) {
 447                     for (int yc3 = -100; yc3 < 100; yc3 += 100) {
 448                         // test smooth curveTo after curveTo
 449                         pabs.moveTo(x0, y0);
 450                         pabs.curveTo(xc0, yc0, xc1, yc1, xc12, yc12);
 451                         pabs.curveTo(xc2, yc2, xc3, yc3, x1, y1);
 452                         psmooth.moveTo(x0, y0);
 453                         psmooth.curveTo(xc0, yc0, xc1, yc1, xc12, yc12);
 454                         psmooth.curveToSmooth(xc3, yc3, x1, y1);
 455                         checkAndResetPaths(pabs, psmooth, x1, y1);
 456 
 457                         // test smooth relative curveTo after curveTo
 458                         pabs.moveTo(x0, y0);
 459                         pabs.curveTo(xc0, yc0, xc1, yc1, xc12, yc12);
 460                         pabs.curveTo(xc2, yc2, xc3, yc3, x1, y1);
 461                         psmooth.moveTo(x0, y0);
 462                         psmooth.curveTo(xc0, yc0, xc1, yc1, xc12, yc12);
 463                         psmooth.curveToSmoothRel(xc3-xc12, yc3-yc12, x1-xc12, y1-yc12);
 464                         checkAndResetPaths(pabs, psmooth, x1, y1);
 465                     }
 466                 }
 467             }
 468         }
 469     }
 470 
 471     public @Test
 472     void testSVGPath() {
 473         String svgpath =
 474             "M 10 20 "+
 475             "L 20 30 "+
 476             "H 10 "+
 477             "V 20 "+
 478             "Q 20 30 10 20 "+
 479             "C 20 30 20 20 10 40 "+
 480             "T 10 50 "+
 481             "S 20 25 10 35 "+
 482             "A 40 60 10 0 0 15 20 "+
 483             "A 40 60 10 0 1 25 30 "+
 484             "A 40 60 10 1 0 15 10 "+
 485             "A 40 60 10 1 1 25 20 "+
 486             "Z "+
 487             "m 10 20 "+
 488             "l 20 20 "+
 489             "h 10 "+
 490             "v 20 "+
 491             "q 10 30 10 20 "+
 492             "c 10 30 10 20 10 40 "+
 493             "t 10 50 "+
 494             "s 10 25 10 35 "+
 495             "a 40 60 10 0 0 10 20 "+
 496             "a 40 60 10 0 1 10 20 "+
 497             "a 40 60 10 1 0 10 20 "+
 498             "a 40 60 10 1 1 10 20 "+
 499             "z";
 500         Path2D p2dtest = new Path2D();
 501         p2dtest.appendSVGPath(svgpath);
 502         Path2D p2dref = new Path2D();
 503         p2dref.moveTo(10, 20);
 504         p2dref.lineTo(20, 30);
 505         p2dref.lineTo(10, p2dref.getCurrentY());
 506         p2dref.lineTo(p2dref.getCurrentX(), 20);
 507         p2dref.quadTo(20, 30, 10, 20);
 508         p2dref.curveTo(20, 30, 20, 20, 10, 40);
 509         p2dref.quadToSmooth(10, 50);
 510         p2dref.curveToSmooth(20, 25, 10, 35);
 511         p2dref.arcTo(40, 60, (float) Math.toRadians(10), false, false, 15, 20);
 512         p2dref.arcTo(40, 60, (float) Math.toRadians(10), false, true,  25, 30);
 513         p2dref.arcTo(40, 60, (float) Math.toRadians(10), true,  false, 15, 10);
 514         p2dref.arcTo(40, 60, (float) Math.toRadians(10), true,  true,  25, 20);
 515         p2dref.closePath();
 516         p2dref.moveToRel(10, 20);
 517         p2dref.lineToRel(20, 20);
 518         p2dref.lineToRel(10, 0);
 519         p2dref.lineToRel(0, 20);
 520         p2dref.quadToRel(10, 30, 10, 20);
 521         p2dref.curveToRel(10, 30, 10, 20, 10, 40);
 522         p2dref.quadToSmoothRel(10, 50);
 523         p2dref.curveToSmoothRel(10, 25, 10, 35);
 524         p2dref.arcToRel(40, 60, (float) Math.toRadians(10), false, false, 10, 20);
 525         p2dref.arcToRel(40, 60, (float) Math.toRadians(10), false, true,  10, 20);
 526         p2dref.arcToRel(40, 60, (float) Math.toRadians(10), true,  false, 10, 20);
 527         p2dref.arcToRel(40, 60, (float) Math.toRadians(10), true,  true,  10, 20);
 528         p2dref.closePath();
 529         checkShapes(p2dref, p2dtest);
 530     }
 531 
 532     public @Test
 533     void testSVGPathWS() {
 534         String svgpathlotsofWS =
 535             "M 10, 20 "+
 536             "L 20, 30 "+
 537             "H 10 "+
 538             "V 20 "+
 539             "Q 20, 30 10, 20 "+
 540             "C 20, 30 20, 20 10, 40 "+
 541             "T 10, 50 "+
 542             "S 20, 25 10, 35 "+
 543             "A 40, 60 10 0 0 15, 20 "+
 544             "A 40, 60 10 0 1 25, 30 "+
 545             "A 40, 60 10 1 0 15, 10 "+
 546             "A 40, 60 10 1 1 25, 20 "+
 547             "Z "+
 548             "m 10, 20 "+
 549             "l 20, 20 "+
 550             "h 10 "+
 551             "v 20 "+
 552             "q 10, 30 10, 20 "+
 553             "c 10, 30 10, 20 10, 40 "+
 554             "t 10, 50 "+
 555             "s 10, 25 10 35 "+
 556             "a 40, 60 10 0 0 10, 20 "+
 557             "a 40, 60 10 0 1 10, 20 "+
 558             "a 40, 60 10 1 0 10, 20 "+
 559             "a 40, 60 10 1 1 10, 20 "+
 560             "z";
 561         String svgpathminWS =
 562             "M10,20"+
 563             "L20,30"+
 564             "H10"+
 565             "V20"+
 566             "Q20,30,10,20"+
 567             "C20,30,20,20,10,40"+
 568             "T10,50"+
 569             "S20,25,10,35"+
 570             "A40,60,10,0,0,15,20"+
 571             "A40,60,10,0,1,25,30"+
 572             "A40,60,10,1,0,15,10"+
 573             "A40,60,10,1,1,25,20"+
 574             "Z"+
 575             "m10,20"+
 576             "l20,20"+
 577             "h10"+
 578             "v20"+
 579             "q10,30,10,20"+
 580             "c10,30,10,20,10,40"+
 581             "t10,50"+
 582             "s10,25,10,35"+
 583             "a40,60,10,0,0,10,20"+
 584             "a40,60,10,0,1,10,20"+
 585             "a40,60,10,1,0,10,20"+
 586             "a40,60,10,1,1,10,20"+
 587             "z";
 588         Path2D p2dref = new Path2D();
 589         p2dref.appendSVGPath(svgpathlotsofWS);
 590         Path2D p2dtest = new Path2D();
 591         p2dtest.appendSVGPath(svgpathminWS);
 592         checkShapes(p2dref, p2dtest);
 593     }
 594 }