1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 import java.awt.Rectangle;
  26 import java.awt.geom.AffineTransform;
  27 import java.awt.geom.GeneralPath;
  28 import java.awt.geom.IllegalPathStateException;
  29 import java.awt.geom.Path2D;
  30 import java.awt.geom.PathIterator;
  31 import java.awt.geom.Point2D;
  32 import java.awt.geom.Rectangle2D;
  33 import java.util.Arrays;
  34 
  35 /**
  36  * @summary Check Path2D constructor with zero capacity
  37  * @run main Path2DTrimCopy
  38  */
  39 public class Path2DEmpty {
  40 
  41     private final static float EPSILON = 5e-6f;
  42     private final static float FLATNESS = 1e-2f;
  43 
  44     private final static AffineTransform at 
  45         = AffineTransform.getScaleInstance(1.3, 2.4);
  46 
  47     private final static Rectangle2D.Double rect2d 
  48         = new Rectangle2D.Double(3.2, 4.1, 5.0, 10.0);
  49 
  50     private final static Point2D.Double pt2d 
  51         = new Point2D.Double(2.0, 2.5);
  52 
  53     public static void main(String[] args) {
  54         testDoublePaths();
  55 
  56         testFloatPaths();
  57         testGeneralPath();
  58     }
  59 
  60     static void testDoublePaths() {
  61         System.out.println("\n - Test(Path2D.Double) ---");
  62         test(() -> new Path2D.Double(Path2D.WIND_NON_ZERO, 0));
  63     }
  64 
  65     static void testFloatPaths() {
  66         System.out.println("\n - Test(Path2D.Float) ---");
  67         test(() -> new Path2D.Float(Path2D.WIND_NON_ZERO, 0));
  68     }
  69 
  70     static void testGeneralPath() {
  71         System.out.println("\n - Test(GeneralPath) ---");
  72         test(() -> new GeneralPath(Path2D.WIND_NON_ZERO, 0));
  73     }
  74 
  75     interface PathFactory {
  76 
  77         Path2D makePath();
  78     }
  79 
  80     /**
  81      - Perform each of the above tests on a (set of) clone(s) of the following paths:
  82      - each of the following executed on both a Float and a Double instance...
  83      - an empty path
  84      - a path with just a moveto
  85      - a path with a moveto+some lines
  86      - a path with a moveto+some curves
  87      */
  88     static void test(PathFactory pf) {
  89         System.out.println("\n --- test: path(empty) ---");
  90         test(pf.makePath(), true);
  91     }
  92 
  93     static Path2D addMove(Path2D p2d) {
  94         p2d.moveTo(1.0, 0.5);
  95         return p2d;
  96     }
  97 
  98     static Path2D addLinesOnly(Path2D p2d) {
  99         for (int i = 0; i < 10; i++) {
 100             p2d.lineTo(1.1 * i, 2.3 * i);
 101         }
 102         return p2d;
 103     }
 104 
 105     static Path2D addCurvesOnly(Path2D p2d) {
 106         for (int i = 0; i < 10; i++) {
 107             p2d.quadTo(1.1 * i, 1.2 * i, 1.3 * i, 1.4 * i);
 108             p2d.curveTo(1.1 * i, 1.2 * i, 1.3 * i, 1.4 * i, 1.5 * i, 1.6 * i);
 109         }
 110         return p2d;
 111     }
 112 
 113     /**
 114      - Write tests that create paths in various forms and run them through the following sub-tests:
 115      - each of the following should be tested on a fresh clone...
 116      - get a PathIterator and iterate it through until it is done
 117      (optional - compare to the expected segments that were in the original)
 118      - get a flattened PathIterator using "getPathIterator(flatness,)" and make sure it works
 119      (optional - compare to original path if the original was already flat)
 120      (but, also run it on a path with curves to make sure flattening engine didn't break)
 121      - add various kinds of segments to the cloned path
 122      - get the bounds of the cloned path
 123      - use the transform() method on the cloned path
 124      - call intersects(point), intersects(rect) and contains(rect) on a cloned path
 125      - call getCurrentPoint() on the cloned path
 126      */
 127     static void test(Path2D p2d, boolean isEmpty) {
 128         testEqual(new Path2D.Float(p2d), p2d);
 129         testEqual(new Path2D.Double(p2d), p2d);
 130         testEqual(new GeneralPath(p2d), p2d);
 131 
 132         // 3 clone variants:
 133         testIterator(new Path2D.Float(p2d), p2d);
 134         testIterator(new Path2D.Double(p2d), p2d);
 135         testIterator((Path2D) p2d.clone(), p2d);
 136 
 137         // 3 clone variants:
 138         testFlattening(new Path2D.Float(p2d), p2d);
 139         testFlattening(new Path2D.Double(p2d), p2d);
 140         testFlattening((Path2D) p2d.clone(), p2d);
 141 
 142         // 3 clone variants:
 143         testAddMove(new Path2D.Float(p2d));
 144         testAddMove(new Path2D.Double(p2d));
 145         testAddMove((Path2D) p2d.clone());
 146 
 147         // These should expect exception if empty
 148         // 3 clone variants:
 149         testAddLine(new Path2D.Float(p2d), isEmpty);
 150         testAddLine(new Path2D.Double(p2d), isEmpty);
 151         testAddLine((Path2D) p2d.clone(), isEmpty);
 152 
 153         // 3 clone variants:
 154         testAddQuad(new Path2D.Float(p2d), isEmpty);
 155         testAddQuad(new Path2D.Double(p2d), isEmpty);
 156         testAddQuad((Path2D) p2d.clone(), isEmpty);
 157 
 158         // 3 clone variants:
 159         testGetBounds(new Path2D.Float(p2d), p2d);
 160         testGetBounds(new Path2D.Double(p2d), p2d);
 161         testGetBounds((Path2D) p2d.clone(), p2d);
 162 
 163         // 3 clone variants:
 164         testTransform(new Path2D.Float(p2d));
 165         testTransform(new Path2D.Double(p2d));
 166         testTransform((Path2D) p2d.clone());
 167 
 168         final boolean doTestThatFail = true;
 169         
 170         // FAILS JDK8
 171         if (doTestThatFail) {
 172             // 3 clone variants:
 173             testIntersect(new Path2D.Float(p2d), p2d);
 174             testIntersect(new Path2D.Double(p2d), p2d);
 175             testIntersect((Path2D) p2d.clone(), p2d);
 176         }
 177 
 178         // FAILS JDK8
 179         if (doTestThatFail) {
 180             // 3 clone variants:
 181             testContains(new Path2D.Float(p2d), p2d);
 182             testContains(new Path2D.Double(p2d), p2d);
 183             testContains((Path2D) p2d.clone(), p2d);
 184         }
 185 
 186         // 3 clone variants:
 187         testGetCurrentPoint(new Path2D.Float(p2d), p2d);
 188         testGetCurrentPoint(new Path2D.Double(p2d), p2d);
 189         testGetCurrentPoint((Path2D) p2d.clone(), p2d);
 190         
 191         // TODO: add all other public methods ?
 192         /*
 193         public abstract void append(PathIterator pi, boolean connect)
 194         public final synchronized void closePath()
 195         */
 196     }
 197 
 198     static void testEqual(Path2D pathA, Path2D pathB) {
 199         // Grab 2 path iterators and test for equality with float coords[]
 200         final PathIterator itA = pathA.getPathIterator(null);
 201         final PathIterator itB = pathB.getPathIterator(null);
 202 
 203         float[] coordsA = new float[6];
 204         float[] coordsB = new float[6];
 205 
 206         int n = 0;
 207         for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) {
 208             int typeA = itA.currentSegment(coordsA);
 209             int typeB = itB.currentSegment(coordsB);
 210 
 211             if (typeA != typeB) {
 212                 throw new IllegalStateException("Path-segment[" + n + "] "
 213                     + " type are not equals [" + typeA + "|" + typeB + "] !");
 214             }
 215             if (!Arrays.equals(coordsA, coordsB)) {
 216                 throw new IllegalStateException("Path-segment[" + n + "] coords"
 217                     + " are not equals [" + Arrays.toString(coordsA) + "|" 
 218                     + Arrays.toString(coordsB) + "] !");
 219             }
 220         }
 221         if (!itA.isDone() || !itB.isDone()) {
 222             throw new IllegalStateException("Paths do not have same lengths !");
 223         }
 224         System.out.println("testEqual: " + n + " segments.");
 225     }
 226 
 227     static void testIterator(Path2D pathA, Path2D pathB) {
 228         // - get a PathIterator and iterate it through until it is done
 229         // (optional - compare to the expected segments that were in the original)
 230         final PathIterator itA = pathA.getPathIterator(at);
 231         final PathIterator itB = pathB.getPathIterator(at);
 232 
 233         float[] coordsA = new float[6];
 234         float[] coordsB = new float[6];
 235 
 236         int n = 0;
 237         for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) {
 238             int typeA = itA.currentSegment(coordsA);
 239             int typeB = itB.currentSegment(coordsB);
 240 
 241             if (typeA != typeB) {
 242                 throw new IllegalStateException("Path-segment[" + n + "] "
 243                     + "type are not equals [" + typeA + "|" + typeB + "] !");
 244             }
 245             // Take care of floating-point precision:
 246             if (!equalsArray(coordsA, coordsB)) {
 247                 throw new IllegalStateException("Path-segment[" + n + "] coords"
 248                     + " are not equals [" + Arrays.toString(coordsA) + "|" 
 249                     + Arrays.toString(coordsB) + "] !");
 250             }
 251         }
 252         if (!itA.isDone() || !itB.isDone()) {
 253             throw new IllegalStateException("Paths do not have same lengths !");
 254         }
 255         System.out.println("testIterator: " + n + " segments.");
 256     }
 257 
 258     static void testFlattening(Path2D pathA, Path2D pathB) {
 259         // - get a flattened PathIterator using "getPathIterator(flatness,)" and make sure it works
 260         // (optional - compare to original path if the original was already flat)
 261         // (but, also run it on a path with curves to make sure flattening engine didn't break)
 262         final PathIterator itA = pathA.getPathIterator(at, FLATNESS);
 263         final PathIterator itB = pathB.getPathIterator(at, FLATNESS);
 264 
 265         float[] coordsA = new float[6];
 266         float[] coordsB = new float[6];
 267 
 268         int n = 0;
 269         for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) {
 270             int typeA = itA.currentSegment(coordsA);
 271             int typeB = itB.currentSegment(coordsB);
 272 
 273             if (typeA != typeB) {
 274                 throw new IllegalStateException("Path-segment[" + n + "] "
 275                     + "type are not equals [" + typeA + "|" + typeB + "] !");
 276             }
 277             // Take care of floating-point precision:
 278             if (!equalsArray(coordsA, coordsB)) {
 279                 throw new IllegalStateException("Path-segment[" + n + "] coords"
 280                     + " are not equals [" + Arrays.toString(coordsA) + "|" 
 281                     + Arrays.toString(coordsB) + "] !");
 282             }
 283         }
 284         if (!itA.isDone() || !itB.isDone()) {
 285             throw new IllegalStateException("Paths do not have same lengths !");
 286         }
 287         System.out.println("testFlattening: " + n + " segments.");
 288     }
 289 
 290     static void testAddMove(Path2D pathA) {
 291         addMove(pathA);
 292         System.out.println("testAddMove: passed.");
 293     }
 294 
 295     static void testAddLine(Path2D pathA, boolean isEmpty) {
 296         try {
 297             addLinesOnly(pathA);
 298         }
 299         catch (IllegalPathStateException ipse) {
 300             if (isEmpty) {
 301                 System.out.println("testAddLine: passed "
 302                     + "(expected IllegalPathStateException catched).");
 303                 return;
 304             } else {
 305                 throw ipse;
 306             }
 307         }
 308         if (isEmpty) {
 309             throw new IllegalStateException("IllegalPathStateException not thrown !");
 310         }
 311         System.out.println("testAddLine: passed.");
 312     }
 313 
 314     static void testAddQuad(Path2D pathA, boolean isEmpty) {
 315         try {
 316             addCurvesOnly(pathA);
 317         }
 318         catch (IllegalPathStateException ipse) {
 319             if (isEmpty) {
 320                 System.out.println("testAddQuad: passed "
 321                     + "(expected IllegalPathStateException catched).");
 322                 return;
 323             } else {
 324                 throw ipse;
 325             }
 326         }
 327         if (isEmpty) {
 328             throw new IllegalStateException("IllegalPathStateException not thrown !");
 329         }
 330         System.out.println("testAddQuad: passed.");
 331     }
 332 
 333     static void testGetBounds(Path2D pathA, Path2D pathB) {
 334         // - get the bounds of the cloned path
 335         final Rectangle rA = pathA.getBounds();
 336         final Rectangle rB = pathB.getBounds();
 337 
 338         if (!rA.equals(rB)) {
 339             throw new IllegalStateException("Bounds are not equals [" + rA 
 340                 + "|" + rB + "] !");
 341         }
 342         final Rectangle2D r2dA = pathA.getBounds2D();
 343         final Rectangle2D r2dB = pathB.getBounds2D();
 344 
 345         if (!equalsRectangle2D(r2dA, r2dB)) {
 346             throw new IllegalStateException("Bounds2D are not equals [" 
 347                 + r2dA + "|" + r2dB + "] !");
 348         }
 349         System.out.println("testGetBounds: passed.");
 350     }
 351 
 352     static void testTransform(Path2D pathA) {
 353         // - use the transform() method on the cloned path
 354         pathA.transform(at);
 355         System.out.println("testTransform: passed.");
 356     }
 357 
 358     static void testIntersect(Path2D pathA, Path2D pathB) {
 359         // - call intersects(points), intersects(rect) on a cloned path
 360         boolean resA = pathA.intersects(rect2d);
 361         boolean resB = pathB.intersects(rect2d);
 362         if (resA != resB) {
 363             throw new IllegalStateException("Intersects(rect2d) are not equals ["
 364                 + resA + "|" + resB + "] !");
 365         }
 366         resA = pathA.intersects(1.0, 2.0, 13.0, 17.0);
 367         resB = pathB.intersects(1.0, 2.0, 13.0, 17.0);
 368         if (resA != resB) {
 369             throw new IllegalStateException("Intersects(doubles) are not equals ["
 370                 + resA + "|" + resB + "] !");
 371         }
 372         System.out.println("testIntersect: passed.");
 373     }
 374 
 375     static void testContains(Path2D pathA, Path2D pathB) {
 376         // - call contains(pt), contains(rect) on a cloned path
 377         boolean resA = pathA.contains(pt2d);
 378         boolean resB = pathB.contains(pt2d);
 379         if (resA != resB) {
 380             throw new IllegalStateException("Contains(pt) are not equals [" 
 381                 + resA + "|" + resB + "] !");
 382         }
 383         resA = pathA.contains(pt2d.getX(), pt2d.getY());
 384         resB = pathB.contains(pt2d.getX(), pt2d.getY());
 385         if (resA != resB) {
 386             throw new IllegalStateException("Contains(x,y) are not equals [" 
 387                 + resA + "|" + resB + "] !");
 388         }
 389         resA = pathA.contains(rect2d);
 390         resB = pathB.contains(rect2d);
 391         if (resA != resB) {
 392             throw new IllegalStateException("Contains(rect2d) are not equals [" 
 393                 + resA + "|" + resB + "] !");
 394         }
 395         resA = pathA.contains(1.0, 2.0, 13.0, 17.0);
 396         resB = pathB.contains(1.0, 2.0, 13.0, 17.0);
 397         if (resA != resB) {
 398             throw new IllegalStateException("Contains(doubles) are not equals [" 
 399                 + resA + "|" + resB + "] !");
 400         }
 401         System.out.println("testContains: passed.");
 402     }
 403 
 404     static void testGetCurrentPoint(Path2D pathA, Path2D pathB) {
 405         // - call getCurrentPoint() on the cloned path
 406         final Point2D ptA = pathA.getCurrentPoint();
 407         final Point2D ptB = pathA.getCurrentPoint();
 408         if (((ptA == null) && (ptB != null))
 409             || ((ptA != null) && !ptA.equals(ptB)))
 410         {
 411             throw new IllegalStateException("getCurrentPoint() are not equals [" 
 412                 + ptA + "|" + ptB + "] !");
 413         }
 414         System.out.println("testGetCurrentPoint: passed.");
 415     }
 416 
 417     // Custom equals methods ---
 418     static boolean equalsArray(float[] a, float[] a2) {
 419         if (a == a2) {
 420             return true;
 421         }
 422         if (a == null || a2 == null) {
 423             return false;
 424         }
 425 
 426         int length = a.length;
 427         if (a2.length != length) {
 428             return false;
 429         }
 430 
 431         for (int i = 0; i < length; i++) {
 432             if (!equals(a[i], a2[i])) {
 433                 return false;
 434             }
 435         }
 436 
 437         return true;
 438     }
 439 
 440     static boolean equalsRectangle2D(Rectangle2D a, Rectangle2D b) {
 441         if (a == b) {
 442             return true;
 443         }
 444         return equals(a.getX(), b.getX())
 445             && equals(a.getY(), b.getY())
 446             && equals(a.getWidth(), b.getWidth())
 447             && equals(a.getHeight(), b.getHeight());
 448     }
 449 
 450     static boolean equals(float a, float b) {
 451         return (Math.abs(a - b) <= EPSILON);
 452     }
 453 
 454     static boolean equals(double a, double b) {
 455         return (Math.abs(a - b) <= EPSILON);
 456     }
 457 
 458 }