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  * @test
  37  * @bug 8076419
  38  * @summary Check Path2D copy constructor (trims arrays)
  39  *          and constructor with zero capacity
  40  * @run main Path2DCopyConstructor
  41  */
  42 public class Path2DCopyConstructor {
  43 
  44     private final static float EPSILON = 5e-6f;
  45     private final static float FLATNESS = 1e-2f;
  46 
  47     private final static AffineTransform at
  48         = AffineTransform.getScaleInstance(1.3, 2.4);
  49 
  50     private final static Rectangle2D.Double rect2d
  51         = new Rectangle2D.Double(3.2, 4.1, 5.0, 10.0);
  52 
  53     private final static Point2D.Double pt2d
  54         = new Point2D.Double(2.0, 2.5);
  55 
  56     public static boolean verbose;
  57 
  58     static void log(String msg) {
  59         if (verbose) {
  60             System.out.println(msg);
  61         }
  62     }
  63 
  64     public static void main(String argv[]) {
  65         verbose = (argv.length != 0);
  66 
  67         testEmptyDoublePaths();
  68         testDoublePaths();
  69 
  70         testEmptyFloatPaths();
  71         testFloatPaths();
  72 
  73         testEmptyGeneralPath();
  74         testGeneralPath();
  75     }
  76 
  77     static void testEmptyDoublePaths() {
  78         log("\n - Test(Path2D.Double[0]) ---");
  79         test(() -> new Path2D.Double(Path2D.WIND_NON_ZERO, 0));
  80     }
  81 
  82     static void testDoublePaths() {
  83         log("\n - Test(Path2D.Double) ---");
  84         test(() -> new Path2D.Double());
  85     }
  86 
  87     static void testEmptyFloatPaths() {
  88         log("\n - Test(Path2D.Float[0]) ---");
  89         test(() -> new Path2D.Float(Path2D.WIND_NON_ZERO, 0));
  90     }
  91 
  92     static void testFloatPaths() {
  93         log("\n - Test(Path2D.Float) ---");
  94         test(() -> new Path2D.Float());
  95     }
  96 
  97     static void testEmptyGeneralPath() {
  98         log("\n - Test(GeneralPath[0]) ---");
  99         test(() -> new GeneralPath(Path2D.WIND_NON_ZERO, 0));
 100     }
 101 
 102     static void testGeneralPath() {
 103         log("\n - Test(GeneralPath) ---");
 104         test(() -> new GeneralPath());
 105     }
 106 
 107     interface PathFactory {
 108         Path2D makePath();
 109     }
 110 
 111     static void test(PathFactory pf) {
 112         log("\n --- test: path(empty) ---");
 113         test(pf.makePath(), true);
 114         log("\n\n --- test: path(addMove) ---");
 115         test(addMove(pf.makePath()), false);
 116         log("\n\n --- test: path(addMoveAndLines) ---");
 117         test(addMoveAndLines(pf.makePath()), false);
 118         log("\n\n --- test: path(addMoveAndQuads) ---");
 119         test(addMoveAndQuads(pf.makePath()), false);
 120         log("\n\n --- test: path(addMoveAndCubics) ---");
 121         test(addMoveAndCubics(pf.makePath()), false);
 122         log("\n\n --- test: path(addMoveAndClose) ---");
 123         test(addMoveAndClose(pf.makePath()), false);
 124     }
 125 
 126     static Path2D addMove(Path2D p2d) {
 127         p2d.moveTo(1.0, 0.5);
 128         return p2d;
 129     }
 130 
 131     static Path2D addMoveAndLines(Path2D p2d) {
 132         addMove(p2d);
 133         addLines(p2d);
 134         return p2d;
 135     }
 136 
 137     static Path2D addLines(Path2D p2d) {
 138         for (int i = 0; i < 10; i++) {
 139             p2d.lineTo(1.1 * i, 2.3 * i);
 140         }
 141         return p2d;
 142     }
 143 
 144     static Path2D addMoveAndCubics(Path2D p2d) {
 145         addMove(p2d);
 146         addCubics(p2d);
 147         return p2d;
 148     }
 149 
 150     static Path2D addCubics(Path2D p2d) {
 151         for (int i = 0; i < 10; i++) {
 152             p2d.curveTo(1.1 * i, 1.2 * i, 1.3 * i, 1.4 * i, 1.5 * i, 1.6 * i);
 153         }
 154         return p2d;
 155     }
 156 
 157     static Path2D addMoveAndQuads(Path2D p2d) {
 158         addMove(p2d);
 159         addQuads(p2d);
 160         return p2d;
 161     }
 162 
 163     static Path2D addQuads(Path2D p2d) {
 164         for (int i = 0; i < 10; i++) {
 165             p2d.quadTo(1.1 * i, 1.2 * i, 1.3 * i, 1.4 * i);
 166         }
 167         return p2d;
 168     }
 169 
 170     static Path2D addMoveAndClose(Path2D p2d) {
 171         addMove(p2d);
 172         addClose(p2d);
 173         return p2d;
 174     }
 175 
 176     static Path2D addClose(Path2D p2d) {
 177         p2d.closePath();
 178         return p2d;
 179     }
 180 
 181     static void test(Path2D p2d, boolean isEmpty) {
 182         testEqual(new Path2D.Float(p2d), p2d);
 183         testEqual(new Path2D.Double(p2d), p2d);
 184         testEqual(new GeneralPath(p2d), p2d);
 185 
 186         testIterator(new Path2D.Float(p2d), p2d);
 187         testIterator(new Path2D.Double(p2d), p2d);
 188         testIterator((Path2D) p2d.clone(), p2d);
 189 
 190         testFlattening(new Path2D.Float(p2d), p2d);
 191         testFlattening(new Path2D.Double(p2d), p2d);
 192         testFlattening((Path2D) p2d.clone(), p2d);
 193 
 194         testAddMove(new Path2D.Float(p2d));
 195         testAddMove(new Path2D.Double(p2d));
 196         testAddMove((Path2D) p2d.clone());
 197 
 198         // These should expect exception if empty
 199         testAddLine(new Path2D.Float(p2d), isEmpty);
 200         testAddLine(new Path2D.Double(p2d), isEmpty);
 201         testAddLine((Path2D) p2d.clone(), isEmpty);
 202 
 203         testAddQuad(new Path2D.Float(p2d), isEmpty);
 204         testAddQuad(new Path2D.Double(p2d), isEmpty);
 205         testAddQuad((Path2D) p2d.clone(), isEmpty);
 206 
 207         testAddCubic(new Path2D.Float(p2d), isEmpty);
 208         testAddCubic(new Path2D.Double(p2d), isEmpty);
 209         testAddCubic((Path2D) p2d.clone(), isEmpty);
 210 
 211         testAddClose(new Path2D.Float(p2d), isEmpty);
 212         testAddClose(new Path2D.Double(p2d), isEmpty);
 213         testAddClose((Path2D) p2d.clone(), isEmpty);
 214 
 215         testGetBounds(new Path2D.Float(p2d), p2d);
 216         testGetBounds(new Path2D.Double(p2d), p2d);
 217         testGetBounds((Path2D) p2d.clone(), p2d);
 218 
 219         testTransform(new Path2D.Float(p2d));
 220         testTransform(new Path2D.Double(p2d));
 221         testTransform((Path2D) p2d.clone());
 222 
 223         testIntersect(new Path2D.Float(p2d), p2d);
 224         testIntersect(new Path2D.Double(p2d), p2d);
 225         testIntersect((Path2D) p2d.clone(), p2d);
 226 
 227         testContains(new Path2D.Float(p2d), p2d);
 228         testContains(new Path2D.Double(p2d), p2d);
 229         testContains((Path2D) p2d.clone(), p2d);
 230 
 231         testGetCurrentPoint(new Path2D.Float(p2d), p2d);
 232         testGetCurrentPoint(new Path2D.Double(p2d), p2d);
 233         testGetCurrentPoint((Path2D) p2d.clone(), p2d);
 234     }
 235 
 236     static void testEqual(Path2D pathA, Path2D pathB) {
 237         final PathIterator itA = pathA.getPathIterator(null);
 238         final PathIterator itB = pathB.getPathIterator(null);
 239 
 240         float[] coordsA = new float[6];
 241         float[] coordsB = new float[6];
 242 
 243         int n = 0;
 244         for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) {
 245             int typeA = itA.currentSegment(coordsA);
 246             int typeB = itB.currentSegment(coordsB);
 247 
 248             if (typeA != typeB) {
 249                 throw new IllegalStateException("Path-segment[" + n + "] "
 250                     + " type are not equals [" + typeA + "|" + typeB + "] !");
 251             }
 252             if (!equalsArray(coordsA, coordsB, getLength(typeA))) {
 253                 throw new IllegalStateException("Path-segment[" + n + "] coords"
 254                     + " are not equals [" + Arrays.toString(coordsA) + "|"
 255                     + Arrays.toString(coordsB) + "] !");
 256             }
 257         }
 258         if (!itA.isDone() || !itB.isDone()) {
 259             throw new IllegalStateException("Paths do not have same lengths !");
 260         }
 261         log("testEqual: " + n + " segments.");
 262     }
 263 
 264     static void testIterator(Path2D pathA, Path2D pathB) {
 265         final PathIterator itA = pathA.getPathIterator(at);
 266         final PathIterator itB = pathB.getPathIterator(at);
 267 
 268         float[] coordsA = new float[6];
 269         float[] coordsB = new float[6];
 270 
 271         int n = 0;
 272         for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) {
 273             int typeA = itA.currentSegment(coordsA);
 274             int typeB = itB.currentSegment(coordsB);
 275 
 276             if (typeA != typeB) {
 277                 throw new IllegalStateException("Path-segment[" + n + "] "
 278                     + "type are not equals [" + typeA + "|" + typeB + "] !");
 279             }
 280             // Take care of floating-point precision:
 281             if (!equalsArrayEps(coordsA, coordsB, getLength(typeA))) {
 282                 throw new IllegalStateException("Path-segment[" + n + "] coords"
 283                     + " are not equals [" + Arrays.toString(coordsA) + "|"
 284                     + Arrays.toString(coordsB) + "] !");
 285             }
 286         }
 287         if (!itA.isDone() || !itB.isDone()) {
 288             throw new IllegalStateException("Paths do not have same lengths !");
 289         }
 290         log("testIterator: " + n + " segments.");
 291     }
 292 
 293     static void testFlattening(Path2D pathA, Path2D pathB) {
 294         final PathIterator itA = pathA.getPathIterator(at, FLATNESS);
 295         final PathIterator itB = pathB.getPathIterator(at, FLATNESS);
 296 
 297         float[] coordsA = new float[6];
 298         float[] coordsB = new float[6];
 299 
 300         int n = 0;
 301         for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) {
 302             int typeA = itA.currentSegment(coordsA);
 303             int typeB = itB.currentSegment(coordsB);
 304 
 305             if (typeA != typeB) {
 306                 throw new IllegalStateException("Path-segment[" + n + "] "
 307                     + "type are not equals [" + typeA + "|" + typeB + "] !");
 308             }
 309             // Take care of floating-point precision:
 310             if (!equalsArrayEps(coordsA, coordsB, getLength(typeA))) {
 311                 throw new IllegalStateException("Path-segment[" + n + "] coords"
 312                     + " are not equals [" + Arrays.toString(coordsA) + "|"
 313                     + Arrays.toString(coordsB) + "] !");
 314             }
 315         }
 316         if (!itA.isDone() || !itB.isDone()) {
 317             throw new IllegalStateException("Paths do not have same lengths !");
 318         }
 319         log("testFlattening: " + n + " segments.");
 320     }
 321 
 322     static void testAddMove(Path2D pathA) {
 323         addMove(pathA);
 324         log("testAddMove: passed.");
 325     }
 326 
 327     static void testAddLine(Path2D pathA, boolean isEmpty) {
 328         try {
 329             addLines(pathA);
 330         }
 331         catch (IllegalPathStateException ipse) {
 332             if (isEmpty) {
 333                 log("testAddLine: passed "
 334                     + "(expected IllegalPathStateException catched).");
 335                 return;
 336             } else {
 337                 throw ipse;
 338             }
 339         }
 340         if (isEmpty) {
 341             throw new IllegalStateException("IllegalPathStateException not thrown !");
 342         }
 343         log("testAddLine: passed.");
 344     }
 345 
 346     static void testAddQuad(Path2D pathA, boolean isEmpty) {
 347         try {
 348             addQuads(pathA);
 349         }
 350         catch (IllegalPathStateException ipse) {
 351             if (isEmpty) {
 352                 log("testAddQuad: passed "
 353                     + "(expected IllegalPathStateException catched).");
 354                 return;
 355             } else {
 356                 throw ipse;
 357             }
 358         }
 359         if (isEmpty) {
 360             throw new IllegalStateException("IllegalPathStateException not thrown !");
 361         }
 362         log("testAddQuad: passed.");
 363     }
 364 
 365     static void testAddCubic(Path2D pathA, boolean isEmpty) {
 366         try {
 367             addCubics(pathA);
 368         }
 369         catch (IllegalPathStateException ipse) {
 370             if (isEmpty) {
 371                 log("testAddCubic: passed "
 372                     + "(expected IllegalPathStateException catched).");
 373                 return;
 374             } else {
 375                 throw ipse;
 376             }
 377         }
 378         if (isEmpty) {
 379             throw new IllegalStateException("IllegalPathStateException not thrown !");
 380         }
 381         log("testAddCubic: passed.");
 382     }
 383 
 384     static void testAddClose(Path2D pathA, boolean isEmpty) {
 385         try {
 386             addClose(pathA);
 387         }
 388         catch (IllegalPathStateException ipse) {
 389             if (isEmpty) {
 390                 log("testAddClose: passed "
 391                     + "(expected IllegalPathStateException catched).");
 392                 return;
 393             } else {
 394                 throw ipse;
 395             }
 396         }
 397         if (isEmpty) {
 398             throw new IllegalStateException("IllegalPathStateException not thrown !");
 399         }
 400         log("testAddClose: passed.");
 401     }
 402 
 403     static void testGetBounds(Path2D pathA, Path2D pathB) {
 404         final Rectangle rA = pathA.getBounds();
 405         final Rectangle rB = pathB.getBounds();
 406 
 407         if (!rA.equals(rB)) {
 408             throw new IllegalStateException("Bounds are not equals [" + rA
 409                 + "|" + rB + "] !");
 410         }
 411         final Rectangle2D r2dA = pathA.getBounds2D();
 412         final Rectangle2D r2dB = pathB.getBounds2D();
 413 
 414         if (!equalsRectangle2D(r2dA, r2dB)) {
 415             throw new IllegalStateException("Bounds2D are not equals ["
 416                 + r2dA + "|" + r2dB + "] !");
 417         }
 418         log("testGetBounds: passed.");
 419     }
 420 
 421     static void testTransform(Path2D pathA) {
 422         pathA.transform(at);
 423         log("testTransform: passed.");
 424     }
 425 
 426     static void testIntersect(Path2D pathA, Path2D pathB) {
 427         boolean resA = pathA.intersects(rect2d);
 428         boolean resB = pathB.intersects(rect2d);
 429         if (resA != resB) {
 430             throw new IllegalStateException("Intersects(rect2d) are not equals ["
 431                 + resA + "|" + resB + "] !");
 432         }
 433         resA = pathA.intersects(1.0, 2.0, 13.0, 17.0);
 434         resB = pathB.intersects(1.0, 2.0, 13.0, 17.0);
 435         if (resA != resB) {
 436             throw new IllegalStateException("Intersects(doubles) are not equals ["
 437                 + resA + "|" + resB + "] !");
 438         }
 439         log("testIntersect: passed.");
 440     }
 441 
 442     static void testContains(Path2D pathA, Path2D pathB) {
 443         boolean resA = pathA.contains(pt2d);
 444         boolean resB = pathB.contains(pt2d);
 445         if (resA != resB) {
 446             throw new IllegalStateException("Contains(pt) are not equals ["
 447                 + resA + "|" + resB + "] !");
 448         }
 449         resA = pathA.contains(pt2d.getX(), pt2d.getY());
 450         resB = pathB.contains(pt2d.getX(), pt2d.getY());
 451         if (resA != resB) {
 452             throw new IllegalStateException("Contains(x,y) are not equals ["
 453                 + resA + "|" + resB + "] !");
 454         }
 455         resA = pathA.contains(rect2d);
 456         resB = pathB.contains(rect2d);
 457         if (resA != resB) {
 458             throw new IllegalStateException("Contains(rect2d) are not equals ["
 459                 + resA + "|" + resB + "] !");
 460         }
 461         resA = pathA.contains(1.0, 2.0, 13.0, 17.0);
 462         resB = pathB.contains(1.0, 2.0, 13.0, 17.0);
 463         if (resA != resB) {
 464             throw new IllegalStateException("Contains(doubles) are not equals ["
 465                 + resA + "|" + resB + "] !");
 466         }
 467         log("testContains: passed.");
 468     }
 469 
 470     static void testGetCurrentPoint(Path2D pathA, Path2D pathB) {
 471         final Point2D ptA = pathA.getCurrentPoint();
 472         final Point2D ptB = pathA.getCurrentPoint();
 473         if (((ptA == null) && (ptB != null))
 474             || ((ptA != null) && !ptA.equals(ptB)))
 475         {
 476             throw new IllegalStateException("getCurrentPoint() are not equals ["
 477                 + ptA + "|" + ptB + "] !");
 478         }
 479         log("testGetCurrentPoint: passed.");
 480     }
 481 
 482     static int getLength(int type) {
 483         switch(type) {
 484             case PathIterator.SEG_CUBICTO:
 485                 return 6;
 486             case PathIterator.SEG_QUADTO:
 487                 return 4;
 488             case PathIterator.SEG_LINETO:
 489             case PathIterator.SEG_MOVETO:
 490                 return 2;
 491             case PathIterator.SEG_CLOSE:
 492                 return 0;
 493             default:
 494                 throw new IllegalStateException("Invalid type: " + type);
 495         }
 496     }
 497 
 498 
 499     // Custom equals methods ---
 500 
 501     public static boolean equalsArray(float[] a, float[] a2, final int len) {
 502         for (int i = 0; i < len; i++) {
 503             if (Float.floatToIntBits(a[i]) != Float.floatToIntBits(a2[i])) {
 504                 return false;
 505             }
 506         }
 507         return true;
 508     }
 509 
 510     static boolean equalsArrayEps(float[] a, float[] a2, final int len) {
 511         for (int i = 0; i < len; i++) {
 512             if (!equalsEps(a[i], a2[i])) {
 513                 return false;
 514             }
 515         }
 516 
 517         return true;
 518     }
 519 
 520     static boolean equalsRectangle2D(Rectangle2D a, Rectangle2D b) {
 521         if (a == b) {
 522             return true;
 523         }
 524         return equalsEps(a.getX(), b.getX())
 525             && equalsEps(a.getY(), b.getY())
 526             && equalsEps(a.getWidth(), b.getWidth())
 527             && equalsEps(a.getHeight(), b.getHeight());
 528     }
 529 
 530     static boolean equalsEps(float a, float b) {
 531         return (Math.abs(a - b) <= EPSILON);
 532     }
 533 
 534     static boolean equalsEps(double a, double b) {
 535         return (Math.abs(a - b) <= EPSILON);
 536     }
 537 }