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