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