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 Path2D c; 183 Path2D.Float pf; 184 Path2D.Double pd; 185 GeneralPath gp; 186 187 pf = new Path2D.Float(p2d); 188 testEqual(pf, p2d); 189 testEqual(pf.trimToSize(), p2d); 190 pd = new Path2D.Double(p2d); 191 testEqual(pd, p2d); 192 testEqual(pf.trimToSize(), p2d); 193 c = (Path2D)p2d.clone(); 194 testEqual(c, p2d); 195 testEqual(c.trimToSize(), p2d); 196 gp = new GeneralPath(p2d); 197 testEqual(gp, p2d); 198 testEqual(gp.trimToSize(), p2d); 199 200 pf = new Path2D.Float(p2d); 201 testIterator(pf, p2d); 202 testIterator(pf.trimToSize(), p2d); 203 pd = new Path2D.Double(p2d); 204 testIterator(pd, p2d); 205 testIterator(pf.trimToSize(), p2d); 206 c = (Path2D)p2d.clone(); 207 testIterator(c, p2d); 208 testIterator(c.trimToSize(), p2d); 209 gp = new GeneralPath(p2d); 210 testIterator(gp, p2d); 211 testIterator(gp.trimToSize(), p2d); 212 213 pf = new Path2D.Float(p2d); 214 testFlattening(pf, p2d); 215 testFlattening(pf.trimToSize(), p2d); 216 pd = new Path2D.Double(p2d); 217 testFlattening(pd, p2d); 218 testFlattening(pf.trimToSize(), p2d); 219 c = (Path2D)p2d.clone(); 220 testFlattening(c, p2d); 221 testFlattening(c.trimToSize(), p2d); 222 gp = new GeneralPath(p2d); 223 testFlattening(gp, p2d); 224 testFlattening(gp.trimToSize(), p2d); 225 226 pf = new Path2D.Float(p2d); 227 testAddMove(pf); 228 testAddMove(pf.trimToSize()); 229 pd = new Path2D.Double(p2d); 230 testAddMove(pd); 231 testAddMove(pf.trimToSize()); 232 c = (Path2D)p2d.clone(); 233 testAddMove(c); 234 testAddMove(c.trimToSize()); 235 gp = new GeneralPath(p2d); 236 testAddMove(gp); 237 testAddMove(gp.trimToSize()); 238 239 // These should expect exception if empty 240 pf = new Path2D.Float(p2d); 241 testAddLine(pf, isEmpty); 242 testAddLine(pf.trimToSize(), isEmpty); 243 pd = new Path2D.Double(p2d); 244 testAddLine(pd, isEmpty); 245 testAddLine(pf.trimToSize(), isEmpty); 246 c = (Path2D)p2d.clone(); 247 testAddLine(c, isEmpty); 248 testAddLine(c.trimToSize(), isEmpty); 249 gp = new GeneralPath(p2d); 250 testAddLine(gp, isEmpty); 251 testAddLine(gp.trimToSize(), isEmpty); 252 253 pf = new Path2D.Float(p2d); 254 testAddQuad(pf, isEmpty); 255 testAddQuad(pf.trimToSize(), isEmpty); 256 pd = new Path2D.Double(p2d); 257 testAddQuad(pd, isEmpty); 258 testAddQuad(pf.trimToSize(), isEmpty); 259 c = (Path2D)p2d.clone(); 260 testAddQuad(c, isEmpty); 261 testAddQuad(c.trimToSize(), isEmpty); 262 gp = new GeneralPath(p2d); 263 testAddQuad(gp, isEmpty); 264 testAddQuad(gp.trimToSize(), isEmpty); 265 266 pf = new Path2D.Float(p2d); 267 testAddCubic(pf, isEmpty); 268 testAddCubic(pf.trimToSize(), isEmpty); 269 pd = new Path2D.Double(p2d); 270 testAddCubic(pd, isEmpty); 271 testAddCubic(pf.trimToSize(), isEmpty); 272 c = (Path2D)p2d.clone(); 273 testAddCubic(c, isEmpty); 274 testAddCubic(c.trimToSize(), isEmpty); 275 gp = new GeneralPath(p2d); 276 testAddCubic(gp, isEmpty); 277 testAddCubic(gp.trimToSize(), isEmpty); 278 279 pf = new Path2D.Float(p2d); 280 testAddClose(pf, isEmpty); 281 testAddClose(pf.trimToSize(), isEmpty); 282 pd = new Path2D.Double(p2d); 283 testAddClose(pd, isEmpty); 284 testAddClose(pf.trimToSize(), isEmpty); 285 c = (Path2D)p2d.clone(); 286 testAddClose(c, isEmpty); 287 testAddClose(c.trimToSize(), isEmpty); 288 gp = new GeneralPath(p2d); 289 testAddClose(gp, isEmpty); 290 testAddClose(gp.trimToSize(), isEmpty); 291 292 pf = new Path2D.Float(p2d); 293 testGetBounds(pf, p2d); 294 testGetBounds(pf.trimToSize(), p2d); 295 pd = new Path2D.Double(p2d); 296 testGetBounds(pd, p2d); 297 testGetBounds(pf.trimToSize(), p2d); 298 c = (Path2D)p2d.clone(); 299 testGetBounds(c, p2d); 300 testGetBounds(c.trimToSize(), p2d); 301 gp = new GeneralPath(p2d); 302 testGetBounds(gp, p2d); 303 testGetBounds(gp.trimToSize(), p2d); 304 305 pf = new Path2D.Float(p2d); 306 testTransform(pf); 307 testTransform(pf.trimToSize()); 308 pd = new Path2D.Double(p2d); 309 testTransform(pd); 310 testTransform(pf.trimToSize()); 311 c = (Path2D)p2d.clone(); 312 testTransform(c); 313 testTransform(c.trimToSize()); 314 gp = new GeneralPath(p2d); 315 testTransform(gp); 316 testTransform(gp.trimToSize()); 317 318 pf = new Path2D.Float(p2d); 319 testIntersect(pf, p2d); 320 testIntersect(pf.trimToSize(), p2d); 321 pd = new Path2D.Double(p2d); 322 testIntersect(pd, p2d); 323 testIntersect(pf.trimToSize(), p2d); 324 c = (Path2D)p2d.clone(); 325 testIntersect(c, p2d); 326 testIntersect(c.trimToSize(), p2d); 327 gp = new GeneralPath(p2d); 328 testIntersect(gp, p2d); 329 testIntersect(gp.trimToSize(), p2d); 330 331 pf = new Path2D.Float(p2d); 332 testContains(pf, p2d); 333 testContains(pf.trimToSize(), p2d); 334 pd = new Path2D.Double(p2d); 335 testContains(pd, p2d); 336 testContains(pf.trimToSize(), p2d); 337 c = (Path2D)p2d.clone(); 338 testContains(c, p2d); 339 testContains(c.trimToSize(), p2d); 340 gp = new GeneralPath(p2d); 341 testContains(gp, p2d); 342 testContains(gp.trimToSize(), p2d); 343 344 pf = new Path2D.Float(p2d); 345 testGetCurrentPoint(pf, p2d); 346 testGetCurrentPoint(pf.trimToSize(), p2d); 347 pd = new Path2D.Double(p2d); 348 testGetCurrentPoint(pd, p2d); 349 testGetCurrentPoint(pf.trimToSize(), p2d); 350 c = (Path2D)p2d.clone(); 351 testGetCurrentPoint(c, p2d); 352 testGetCurrentPoint(c.trimToSize(), p2d); 353 gp = new GeneralPath(p2d); 354 testGetCurrentPoint(gp, p2d); 355 testGetCurrentPoint(gp.trimToSize(), p2d); 356 } 357 358 static void testEqual(Path2D pathA, Path2D pathB) { 359 final PathIterator itA = pathA.getPathIterator(null); 360 final PathIterator itB = pathB.getPathIterator(null); 361 362 float[] coordsA = new float[6]; 363 float[] coordsB = new float[6]; 364 365 int n = 0; 366 for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) { 367 int typeA = itA.currentSegment(coordsA); 368 int typeB = itB.currentSegment(coordsB); 369 370 if (typeA != typeB) { 371 throw new IllegalStateException("Path-segment[" + n + "] " 372 + " type are not equals [" + typeA + "|" + typeB + "] !"); 373 } 374 if (!equalsArray(coordsA, coordsB, getLength(typeA))) { 375 throw new IllegalStateException("Path-segment[" + n + "] coords" 376 + " are not equals [" + Arrays.toString(coordsA) + "|" 377 + Arrays.toString(coordsB) + "] !"); 378 } 379 } 380 if (!itA.isDone() || !itB.isDone()) { 381 throw new IllegalStateException("Paths do not have same lengths !"); 382 } 383 log("testEqual: " + n + " segments."); 384 } 385 386 static void testIterator(Path2D pathA, Path2D pathB) { 387 final PathIterator itA = pathA.getPathIterator(at); 388 final PathIterator itB = pathB.getPathIterator(at); 389 390 float[] coordsA = new float[6]; 391 float[] coordsB = new float[6]; 392 393 int n = 0; 394 for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) { 395 int typeA = itA.currentSegment(coordsA); 396 int typeB = itB.currentSegment(coordsB); 397 398 if (typeA != typeB) { 399 throw new IllegalStateException("Path-segment[" + n + "] " 400 + "type are not equals [" + typeA + "|" + typeB + "] !"); 401 } 402 // Take care of floating-point precision: 403 if (!equalsArrayEps(coordsA, coordsB, getLength(typeA))) { 404 throw new IllegalStateException("Path-segment[" + n + "] coords" 405 + " are not equals [" + Arrays.toString(coordsA) + "|" 406 + Arrays.toString(coordsB) + "] !"); 407 } 408 } 409 if (!itA.isDone() || !itB.isDone()) { 410 throw new IllegalStateException("Paths do not have same lengths !"); 411 } 412 log("testIterator: " + n + " segments."); 413 } 414 415 static void testFlattening(Path2D pathA, Path2D pathB) { 416 final PathIterator itA = pathA.getPathIterator(at, FLATNESS); 417 final PathIterator itB = pathB.getPathIterator(at, FLATNESS); 418 419 float[] coordsA = new float[6]; 420 float[] coordsB = new float[6]; 421 422 int n = 0; 423 for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) { 424 int typeA = itA.currentSegment(coordsA); 425 int typeB = itB.currentSegment(coordsB); 426 427 if (typeA != typeB) { 428 throw new IllegalStateException("Path-segment[" + n + "] " 429 + "type are not equals [" + typeA + "|" + typeB + "] !"); 430 } 431 // Take care of floating-point precision: 432 if (!equalsArrayEps(coordsA, coordsB, getLength(typeA))) { 433 throw new IllegalStateException("Path-segment[" + n + "] coords" 434 + " are not equals [" + Arrays.toString(coordsA) + "|" 435 + Arrays.toString(coordsB) + "] !"); 436 } 437 } 438 if (!itA.isDone() || !itB.isDone()) { 439 throw new IllegalStateException("Paths do not have same lengths !"); 440 } 441 log("testFlattening: " + n + " segments."); 442 } 443 444 static void testAddMove(Path2D pathA) { 445 addMove(pathA); 446 log("testAddMove: passed."); 447 } 448 449 static void testAddLine(Path2D pathA, boolean isEmpty) { 450 try { 451 addLines(pathA); 452 } 453 catch (IllegalPathStateException ipse) { 454 if (isEmpty) { 455 log("testAddLine: passed " 456 + "(expected IllegalPathStateException catched)."); 457 return; 458 } else { 459 throw ipse; 460 } 461 } 462 if (isEmpty) { 463 throw new IllegalStateException("IllegalPathStateException not thrown !"); 464 } 465 log("testAddLine: passed."); 466 } 467 468 static void testAddQuad(Path2D pathA, boolean isEmpty) { 469 try { 470 addQuads(pathA); 471 } 472 catch (IllegalPathStateException ipse) { 473 if (isEmpty) { 474 log("testAddQuad: passed " 475 + "(expected IllegalPathStateException catched)."); 476 return; 477 } else { 478 throw ipse; 479 } 480 } 481 if (isEmpty) { 482 throw new IllegalStateException("IllegalPathStateException not thrown !"); 483 } 484 log("testAddQuad: passed."); 485 } 486 487 static void testAddCubic(Path2D pathA, boolean isEmpty) { 488 try { 489 addCubics(pathA); 490 } 491 catch (IllegalPathStateException ipse) { 492 if (isEmpty) { 493 log("testAddCubic: passed " 494 + "(expected IllegalPathStateException catched)."); 495 return; 496 } else { 497 throw ipse; 498 } 499 } 500 if (isEmpty) { 501 throw new IllegalStateException("IllegalPathStateException not thrown !"); 502 } 503 log("testAddCubic: passed."); 504 } 505 506 static void testAddClose(Path2D pathA, boolean isEmpty) { 507 try { 508 addClose(pathA); 509 } 510 catch (IllegalPathStateException ipse) { 511 if (isEmpty) { 512 log("testAddClose: passed " 513 + "(expected IllegalPathStateException catched)."); 514 return; 515 } else { 516 throw ipse; 517 } 518 } 519 if (isEmpty) { 520 throw new IllegalStateException("IllegalPathStateException not thrown !"); 521 } 522 log("testAddClose: passed."); 523 } 524 525 static void testGetBounds(Path2D pathA, Path2D pathB) { 526 final Rectangle rA = pathA.getBounds(); 527 final Rectangle rB = pathB.getBounds(); 528 529 if (!rA.equals(rB)) { 530 throw new IllegalStateException("Bounds are not equals [" + rA 531 + "|" + rB + "] !"); 532 } 533 final Rectangle2D r2dA = pathA.getBounds2D(); 534 final Rectangle2D r2dB = pathB.getBounds2D(); 535 536 if (!equalsRectangle2D(r2dA, r2dB)) { 537 throw new IllegalStateException("Bounds2D are not equals [" 538 + r2dA + "|" + r2dB + "] !"); 539 } 540 log("testGetBounds: passed."); 541 } 542 543 static void testTransform(Path2D pathA) { 544 pathA.transform(at); 545 log("testTransform: passed."); 546 } 547 548 static void testIntersect(Path2D pathA, Path2D pathB) { 549 boolean resA = pathA.intersects(rect2d); 550 boolean resB = pathB.intersects(rect2d); 551 if (resA != resB) { 552 throw new IllegalStateException("Intersects(rect2d) are not equals [" 553 + resA + "|" + resB + "] !"); 554 } 555 resA = pathA.intersects(1.0, 2.0, 13.0, 17.0); 556 resB = pathB.intersects(1.0, 2.0, 13.0, 17.0); 557 if (resA != resB) { 558 throw new IllegalStateException("Intersects(doubles) are not equals [" 559 + resA + "|" + resB + "] !"); 560 } 561 log("testIntersect: passed."); 562 } 563 564 static void testContains(Path2D pathA, Path2D pathB) { 565 boolean resA = pathA.contains(pt2d); 566 boolean resB = pathB.contains(pt2d); 567 if (resA != resB) { 568 throw new IllegalStateException("Contains(pt) are not equals [" 569 + resA + "|" + resB + "] !"); 570 } 571 resA = pathA.contains(pt2d.getX(), pt2d.getY()); 572 resB = pathB.contains(pt2d.getX(), pt2d.getY()); 573 if (resA != resB) { 574 throw new IllegalStateException("Contains(x,y) are not equals [" 575 + resA + "|" + resB + "] !"); 576 } 577 resA = pathA.contains(rect2d); 578 resB = pathB.contains(rect2d); 579 if (resA != resB) { 580 throw new IllegalStateException("Contains(rect2d) are not equals [" 581 + resA + "|" + resB + "] !"); 582 } 583 resA = pathA.contains(1.0, 2.0, 13.0, 17.0); 584 resB = pathB.contains(1.0, 2.0, 13.0, 17.0); 585 if (resA != resB) { 586 throw new IllegalStateException("Contains(doubles) are not equals [" 587 + resA + "|" + resB + "] !"); 588 } 589 log("testContains: passed."); 590 } 591 592 static void testGetCurrentPoint(Path2D pathA, Path2D pathB) { 593 final Point2D ptA = pathA.getCurrentPoint(); 594 final Point2D ptB = pathA.getCurrentPoint(); 595 if (((ptA == null) && (ptB != null)) 596 || ((ptA != null) && !ptA.equals(ptB))) 597 { 598 throw new IllegalStateException("getCurrentPoint() are not equals [" 599 + ptA + "|" + ptB + "] !"); 600 } 601 log("testGetCurrentPoint: passed."); 602 } 603 604 static int getLength(int type) { 605 switch(type) { 606 case PathIterator.SEG_CUBICTO: 607 return 6; 608 case PathIterator.SEG_QUADTO: 609 return 4; 610 case PathIterator.SEG_LINETO: 611 case PathIterator.SEG_MOVETO: 612 return 2; 613 case PathIterator.SEG_CLOSE: 614 return 0; 615 default: 616 throw new IllegalStateException("Invalid type: " + type); 617 } 618 } 619 620 621 // Custom equals methods --- 622 623 public static boolean equalsArray(float[] a, float[] a2, final int len) { 624 for (int i = 0; i < len; i++) { 625 if (Float.floatToIntBits(a[i]) != Float.floatToIntBits(a2[i])) { 626 return false; 627 } 628 } 629 return true; 630 } 631 632 static boolean equalsArrayEps(float[] a, float[] a2, final int len) { 633 for (int i = 0; i < len; i++) { 634 if (!equalsEps(a[i], a2[i])) { 635 return false; 636 } 637 } 638 639 return true; 640 } 641 642 static boolean equalsRectangle2D(Rectangle2D a, Rectangle2D b) { 643 if (a == b) { 644 return true; 645 } 646 return equalsEps(a.getX(), b.getX()) 647 && equalsEps(a.getY(), b.getY()) 648 && equalsEps(a.getWidth(), b.getWidth()) 649 && equalsEps(a.getHeight(), b.getHeight()); 650 } 651 652 static boolean equalsEps(float a, float b) { 653 return (Math.abs(a - b) <= EPSILON); 654 } 655 656 static boolean equalsEps(double a, double b) { 657 return (Math.abs(a - b) <= EPSILON); 658 } 659 }