1 /* 2 * Copyright (c) 2006, 2018, 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 * @test 26 * @bug 4172661 27 * @summary Tests all public methods of Path2D classes on all 3 variants 28 * Path2D.Float, Path2D.Double, and GeneralPath. 29 * REMIND: Note that the hit testing tests will fail 30 * occasionally due to precision bugs in the various hit 31 * testing methods in the geometry classes. 32 * (Failure rates vary from 1 per 100 runs to 1 per thousands). 33 * See bug 6396047 to track progress on these failures. 34 */ 35 36 import java.awt.Rectangle; 37 import java.awt.Shape; 38 import java.awt.geom.AffineTransform; 39 import java.awt.geom.Arc2D; 40 import java.awt.geom.Area; 41 import java.awt.geom.CubicCurve2D; 42 import java.awt.geom.Ellipse2D; 43 import java.awt.geom.FlatteningPathIterator; 44 import java.awt.geom.GeneralPath; 45 import java.awt.geom.Line2D; 46 import java.awt.geom.Path2D; 47 import java.awt.geom.PathIterator; 48 import java.awt.geom.Point2D; 49 import java.awt.geom.QuadCurve2D; 50 import java.awt.geom.Rectangle2D; 51 import java.awt.geom.RoundRectangle2D; 52 import java.util.NoSuchElementException; 53 54 public class UnitTest { 55 public static boolean verbose; 56 57 public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO; 58 public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD; 59 60 public static int CoordsForType[] = { 2, 2, 4, 6, 0 }; 61 62 public static AffineTransform TxIdentity = new AffineTransform(); 63 public static AffineTransform TxComplex = makeAT(); 64 65 public static Shape TestShapes[]; 66 public static SampleShape ShortSampleNonZero; 67 public static SampleShape ShortSampleEvenOdd; 68 public static SampleShape LongSampleNonZero; 69 public static SampleShape LongSampleEvenOdd; 70 71 public static Shape EmptyShapeNonZero = 72 new EmptyShape(WIND_NON_ZERO); 73 public static Shape EmptyShapeEvenOdd = 74 new EmptyShape(WIND_EVEN_ODD); 75 76 // Note: We pick a shape that is not anywhere near any of 77 // our test shapes so that the Path2D does not try to collapse 78 // out the connecting segment - an optimization that is too 79 // difficult to account for in the AppendedShape code. 80 public static Shape AppendShape = new Arc2D.Double(1000, 1000, 40, 40, 81 Math.PI/4, Math.PI, 82 Arc2D.CHORD); 83 makeAT()84 public static AffineTransform makeAT() { 85 AffineTransform at = new AffineTransform(); 86 at.scale(0.66, 0.23); 87 at.rotate(Math.toRadians(35.0)); 88 at.shear(0.78, 1.32); 89 return at; 90 } 91 init()92 public static void init() { 93 TestShapes = new Shape[] { 94 EmptyShapeNonZero, 95 EmptyShapeEvenOdd, 96 new Line2D.Double(), 97 new Line2D.Double(rpc(), rpc(), rpc(), rpc()), 98 new Line2D.Double(rnc(), rnc(), rnc(), rnc()), 99 new Rectangle2D.Double(), 100 new Rectangle2D.Double(rpc(), rpc(), -1, -1), 101 new Rectangle2D.Double(rpc(), rpc(), rd(), rd()), 102 new Rectangle2D.Double(rnc(), rnc(), rd(), rd()), 103 new Ellipse2D.Double(), 104 new Ellipse2D.Double(rpc(), rpc(), -1, -1), 105 new Ellipse2D.Double(rpc(), rpc(), rd(), rd()), 106 new Ellipse2D.Double(rnc(), rnc(), rd(), rd()), 107 new Arc2D.Double(Arc2D.OPEN), 108 new Arc2D.Double(Arc2D.CHORD), 109 new Arc2D.Double(Arc2D.PIE), 110 new Arc2D.Double(rpc(), rpc(), -1, -1, rt(), rt(), Arc2D.OPEN), 111 new Arc2D.Double(rpc(), rpc(), -1, -1, rt(), rt(), Arc2D.CHORD), 112 new Arc2D.Double(rpc(), rpc(), -1, -1, rt(), rt(), Arc2D.PIE), 113 new Arc2D.Double(rpc(), rpc(), rd(), rd(), rt(), rt(), Arc2D.OPEN), 114 new Arc2D.Double(rpc(), rpc(), rd(), rd(), rt(), rt(), Arc2D.CHORD), 115 new Arc2D.Double(rpc(), rpc(), rd(), rd(), rt(), rt(), Arc2D.PIE), 116 new Arc2D.Double(rnc(), rnc(), rd(), rd(), rt(), rt(), Arc2D.OPEN), 117 new Arc2D.Double(rnc(), rnc(), rd(), rd(), rt(), rt(), Arc2D.CHORD), 118 new Arc2D.Double(rnc(), rnc(), rd(), rd(), rt(), rt(), Arc2D.PIE), 119 new RoundRectangle2D.Double(), 120 new RoundRectangle2D.Double(rpc(), rpc(), -1, -1, ra(), ra()), 121 new RoundRectangle2D.Double(rpc(), rpc(), rd(), rd(), ra(), ra()), 122 new RoundRectangle2D.Double(rnc(), rnc(), rd(), rd(), ra(), ra()), 123 new QuadCurve2D.Double(), 124 new QuadCurve2D.Double(rpc(), rpc(), rpc(), rpc(), rpc(), rpc()), 125 new QuadCurve2D.Double(rnc(), rnc(), rnc(), rnc(), rnc(), rnc()), 126 new CubicCurve2D.Double(), 127 new CubicCurve2D.Double(rpc(), rpc(), rpc(), rpc(), 128 rpc(), rpc(), rpc(), rpc()), 129 new CubicCurve2D.Double(rnc(), rnc(), rnc(), rnc(), 130 rnc(), rnc(), rnc(), rnc()), 131 makeGeneralPath(WIND_NON_ZERO, 1.0), 132 makeGeneralPath(WIND_EVEN_ODD, 1.0), 133 makeGeneralPath(WIND_NON_ZERO, -1.0), 134 makeGeneralPath(WIND_EVEN_ODD, -1.0), 135 }; 136 137 int types[] = new int[100]; 138 int i = 0; 139 types[i++] = PathIterator.SEG_MOVETO; 140 types[i++] = PathIterator.SEG_LINETO; 141 types[i++] = PathIterator.SEG_QUADTO; 142 types[i++] = PathIterator.SEG_CUBICTO; 143 types[i++] = PathIterator.SEG_CLOSE; 144 int shortlen = i; 145 int prevt = types[i-1]; 146 while (i < types.length) { 147 int t; 148 do { 149 t = (int) (Math.random() * 5); 150 } while (t == prevt && 151 (t == PathIterator.SEG_MOVETO || 152 t == PathIterator.SEG_CLOSE)); 153 types[i++] = t; 154 prevt = t; 155 } 156 157 int numcoords = 0; 158 int numshortcoords = 0; 159 for (i = 0; i < types.length; i++) { 160 if (i == shortlen) { 161 numshortcoords = numcoords; 162 } 163 numcoords += CoordsForType[types[i]]; 164 } 165 double coords[] = new double[numcoords]; 166 for (i = 0; i < coords.length; i++) { 167 coords[i] = rpc(); 168 } 169 ShortSampleNonZero = new SampleShape(WIND_NON_ZERO, 170 types, coords, 171 shortlen, numshortcoords); 172 ShortSampleEvenOdd = new SampleShape(WIND_EVEN_ODD, 173 types, coords, 174 shortlen, numshortcoords); 175 LongSampleNonZero = new SampleShape(WIND_NON_ZERO, 176 types, coords, 177 types.length, numcoords); 178 LongSampleEvenOdd = new SampleShape(WIND_EVEN_ODD, 179 types, coords, 180 types.length, numcoords); 181 } 182 makeGeneralPath(int windingrule, double sign)183 public static GeneralPath makeGeneralPath(int windingrule, double sign) { 184 GeneralPath gp = new GeneralPath(windingrule); 185 gp.moveTo((float) (sign * rpc()), (float) (sign * rpc())); 186 gp.lineTo((float) (sign * rpc()), (float) (sign * rpc())); 187 gp.quadTo((float) (sign * rpc()), (float) (sign * rpc()), 188 (float) (sign * rpc()), (float) (sign * rpc())); 189 gp.curveTo((float) (sign * rpc()), (float) (sign * rpc()), 190 (float) (sign * rpc()), (float) (sign * rpc()), 191 (float) (sign * rpc()), (float) (sign * rpc())); 192 gp.closePath(); 193 return gp; 194 } 195 196 // Due to odd issues with the sizes of errors when the values 197 // being manipulated are near zero, we try to avoid values 198 // near zero by ensuring that both the rpc (positive coords) 199 // stay away from zero and also by ensuring that the rpc+rd 200 // (positive coords + dimensions) stay away from zero. We 201 // also ensure that rnc+rd (negative coords + dimension) stay 202 // suitably negative without approaching zero. 203 204 // Random positive coordinate (10 -> 110) 205 // rpc + rd gives a total range of (30 -> 170) rpc()206 public static double rpc() { 207 return (Math.random() * 100.0) + 10.0; 208 } 209 210 // Random negative coordinate (-200 -> -100) 211 // rnc + rd gives a total range of (-180 -> -40) rnc()212 public static double rnc() { 213 return (Math.random() * 100.0) - 200.0; 214 } 215 216 // Random dimension (20 -> 60) rd()217 public static double rd() { 218 return (Math.random() * 40.0) + 20.0; 219 } 220 221 // Random arc width/height (0.1 -> 5.1) ra()222 public static double ra() { 223 return (Math.random() * 5.0) + 0.1; 224 } 225 226 // Random arc angle (theta) (PI/4 => 5PI/4) rt()227 public static double rt() { 228 return (Math.random() * Math.PI) + Math.PI/4; 229 } 230 fltulpdiff(double v1, double v2)231 public static int fltulpdiff(double v1, double v2) { 232 if (v1 == v2) { 233 return 0; 234 } 235 float vf1 = (float) v1; 236 float vf2 = (float) v2; 237 if (vf1 == vf2) { 238 return 0; 239 } 240 float diff = Math.abs(vf1-vf2); 241 //float ulp = Math.ulp((float) ((vf1 + vf2)/2f)); 242 float ulp = Math.max(Math.ulp(vf1), Math.ulp(vf2)); 243 if (verbose && diff > ulp) { 244 System.out.println("v1 = "+vf1+", ulp = "+Math.ulp(vf1)); 245 System.out.println("v2 = "+vf2+", ulp = "+Math.ulp(vf2)); 246 System.out.println((diff/ulp)+" ulps"); 247 } 248 return (int) (diff/ulp); 249 } 250 fltulpless(double v1, double v2)251 public static int fltulpless(double v1, double v2) { 252 if (v1 >= v2) { 253 return 0; 254 } 255 float vf1 = (float) v1; 256 float vf2 = (float) v2; 257 if (vf1 >= vf2) { 258 return 0; 259 } 260 float diff = Math.abs(vf1-vf2); 261 //float ulp = Math.ulp((float) ((vf1 + vf2)/2f)); 262 float ulp = Math.max(Math.ulp(vf1), Math.ulp(vf2)); 263 if (verbose && diff > ulp) { 264 System.out.println("v1 = "+vf1+", ulp = "+Math.ulp(vf1)); 265 System.out.println("v2 = "+vf2+", ulp = "+Math.ulp(vf2)); 266 System.out.println((diff/ulp)+" ulps"); 267 } 268 return (int) (diff/ulp); 269 } 270 dblulpdiff(double v1, double v2)271 public static int dblulpdiff(double v1, double v2) { 272 if (v1 == v2) { 273 return 0; 274 } 275 double diff = Math.abs(v1-v2); 276 //double ulp = Math.ulp((v1 + v2)/2.0); 277 double ulp = Math.max(Math.ulp(v1), Math.ulp(v2)); 278 if (verbose && diff > ulp) { 279 System.out.println("v1 = "+v1+", ulp = "+Math.ulp(v1)); 280 System.out.println("v2 = "+v2+", ulp = "+Math.ulp(v2)); 281 System.out.println((diff/ulp)+" ulps"); 282 } 283 return (int) (diff/ulp); 284 } 285 286 public static abstract class Creator { makePath()287 public abstract Path2D makePath(); makePath(int windingrule)288 public abstract Path2D makePath(int windingrule); makePath(int windingrule, int capacity)289 public abstract Path2D makePath(int windingrule, int capacity); makePath(Shape s)290 public abstract Path2D makePath(Shape s); makePath(Shape s, AffineTransform at)291 public abstract Path2D makePath(Shape s, AffineTransform at); 292 supportsFloatCompose()293 public abstract boolean supportsFloatCompose(); getRecommendedTxMaxUlp()294 public abstract int getRecommendedTxMaxUlp(); 295 compare(PathIterator testpi, PathIterator refpi, AffineTransform at, int maxulp)296 public abstract void compare(PathIterator testpi, 297 PathIterator refpi, 298 AffineTransform at, 299 int maxulp); 300 } 301 302 public static class FltCreator extends Creator { makePath()303 public Path2D makePath() { 304 return new Path2D.Float(); 305 } makePath(int windingrule)306 public Path2D makePath(int windingrule) { 307 return new Path2D.Float(windingrule); 308 } makePath(int windingrule, int capacity)309 public Path2D makePath(int windingrule, int capacity) { 310 return new Path2D.Float(windingrule, capacity); 311 } makePath(Shape s)312 public Path2D makePath(Shape s) { 313 return new Path2D.Float(s); 314 } makePath(Shape s, AffineTransform at)315 public Path2D makePath(Shape s, AffineTransform at) { 316 return new Path2D.Float(s, at); 317 } 318 supportsFloatCompose()319 public boolean supportsFloatCompose() { 320 return true; 321 } getRecommendedTxMaxUlp()322 public int getRecommendedTxMaxUlp() { 323 return 5; 324 } 325 compare(PathIterator testpi, PathIterator refpi, AffineTransform at, int maxulp)326 public void compare(PathIterator testpi, 327 PathIterator refpi, 328 AffineTransform at, 329 int maxulp) 330 { 331 if (testpi.getWindingRule() != refpi.getWindingRule()) { 332 throw new RuntimeException("wrong winding rule"); 333 } 334 float testcoords[] = new float[6]; 335 float refcoords[] = new float[6]; 336 while (!testpi.isDone()) { 337 if (refpi.isDone()) { 338 throw new RuntimeException("too many segments"); 339 } 340 int testtype = testpi.currentSegment(testcoords); 341 int reftype = refpi.currentSegment(refcoords); 342 if (testtype != reftype) { 343 throw new RuntimeException("different segment types"); 344 } 345 if (at != null) { 346 at.transform(refcoords, 0, refcoords, 0, 347 CoordsForType[reftype]/2); 348 } 349 for (int i = 0; i < CoordsForType[testtype]; i++) { 350 int ulps = fltulpdiff(testcoords[i], refcoords[i]); 351 if (ulps > maxulp) { 352 throw new RuntimeException("coords are different: "+ 353 testcoords[i]+" != "+ 354 refcoords[i]+ 355 " ("+ulps+" ulps)"); 356 } 357 } 358 testpi.next(); 359 refpi.next(); 360 } 361 if (!refpi.isDone()) { 362 throw new RuntimeException("not enough segments"); 363 } 364 } 365 } 366 367 public static class DblCreator extends Creator { makePath()368 public Path2D makePath() { 369 return new Path2D.Double(); 370 } makePath(int windingrule)371 public Path2D makePath(int windingrule) { 372 return new Path2D.Double(windingrule); 373 } makePath(int windingrule, int capacity)374 public Path2D makePath(int windingrule, int capacity) { 375 return new Path2D.Double(windingrule, capacity); 376 } makePath(Shape s)377 public Path2D makePath(Shape s) { 378 return new Path2D.Double(s); 379 } makePath(Shape s, AffineTransform at)380 public Path2D makePath(Shape s, AffineTransform at) { 381 return new Path2D.Double(s, at); 382 } 383 supportsFloatCompose()384 public boolean supportsFloatCompose() { 385 return false; 386 } getRecommendedTxMaxUlp()387 public int getRecommendedTxMaxUlp() { 388 return 3; 389 } 390 compare(PathIterator testpi, PathIterator refpi, AffineTransform at, int maxulp)391 public void compare(PathIterator testpi, 392 PathIterator refpi, 393 AffineTransform at, 394 int maxulp) 395 { 396 if (testpi.getWindingRule() != refpi.getWindingRule()) { 397 throw new RuntimeException("wrong winding rule"); 398 } 399 double testcoords[] = new double[6]; 400 double refcoords[] = new double[6]; 401 while (!testpi.isDone()) { 402 if (refpi.isDone()) { 403 throw new RuntimeException("too many segments"); 404 } 405 int testtype = testpi.currentSegment(testcoords); 406 int reftype = refpi.currentSegment(refcoords); 407 if (testtype != reftype) { 408 throw new RuntimeException("different segment types"); 409 } 410 if (at != null) { 411 at.transform(refcoords, 0, refcoords, 0, 412 CoordsForType[reftype]/2); 413 } 414 for (int i = 0; i < CoordsForType[testtype]; i++) { 415 int ulps = dblulpdiff(testcoords[i], refcoords[i]); 416 if (ulps > maxulp) { 417 throw new RuntimeException("coords are different: "+ 418 testcoords[i]+" != "+ 419 refcoords[i]+ 420 " ("+ulps+" ulps)"); 421 } 422 } 423 testpi.next(); 424 refpi.next(); 425 } 426 if (!refpi.isDone()) { 427 throw new RuntimeException("not enough segments"); 428 } 429 } 430 431 } 432 433 public static class GPCreator extends FltCreator { makePath()434 public Path2D makePath() { 435 return new GeneralPath(); 436 } makePath(int windingrule)437 public Path2D makePath(int windingrule) { 438 return new GeneralPath(windingrule); 439 } makePath(int windingrule, int capacity)440 public Path2D makePath(int windingrule, int capacity) { 441 return new GeneralPath(windingrule, capacity); 442 } makePath(Shape s)443 public Path2D makePath(Shape s) { 444 return new GeneralPath(s); 445 } makePath(Shape s, AffineTransform at)446 public Path2D makePath(Shape s, AffineTransform at) { 447 GeneralPath gp = new GeneralPath(); 448 PathIterator pi = s.getPathIterator(at); 449 gp.setWindingRule(pi.getWindingRule()); 450 gp.append(pi, false); 451 return gp; 452 } 453 supportsFloatCompose()454 public boolean supportsFloatCompose() { 455 return true; 456 } 457 } 458 459 public static class EmptyShape implements Shape { 460 private int windingrule; 461 EmptyShape(int windingrule)462 public EmptyShape(int windingrule) { 463 this.windingrule = windingrule; 464 } 465 getBounds()466 public Rectangle getBounds() { 467 return new Rectangle(); 468 } getBounds2D()469 public Rectangle2D getBounds2D() { 470 return new Rectangle(); 471 } contains(double x, double y)472 public boolean contains(double x, double y) { 473 return false; 474 } contains(Point2D p)475 public boolean contains(Point2D p) { 476 return false; 477 } intersects(double x, double y, double w, double h)478 public boolean intersects(double x, double y, double w, double h) { 479 return false; 480 } intersects(Rectangle2D r)481 public boolean intersects(Rectangle2D r) { 482 return false; 483 } contains(double x, double y, double w, double h)484 public boolean contains(double x, double y, double w, double h) { 485 return false; 486 } contains(Rectangle2D r)487 public boolean contains(Rectangle2D r) { 488 return false; 489 } getPathIterator(AffineTransform at)490 public PathIterator getPathIterator(AffineTransform at) { 491 return new PathIterator() { 492 public int getWindingRule() { 493 return windingrule; 494 } 495 public boolean isDone() { 496 return true; 497 } 498 public void next() {} 499 public int currentSegment(float[] coords) { 500 throw new NoSuchElementException(); 501 } 502 public int currentSegment(double[] coords) { 503 throw new NoSuchElementException(); 504 } 505 }; 506 } getPathIterator(AffineTransform at, double flatness)507 public PathIterator getPathIterator(AffineTransform at, 508 double flatness) 509 { 510 return getPathIterator(at); 511 } 512 } 513 514 public static class SampleShape implements Shape { 515 int windingrule; 516 int theTypes[]; 517 double theCoords[]; 518 int numTypes; 519 int numCoords; 520 521 public SampleShape(int windingrule, 522 int types[], double coords[], 523 int numtypes, int numcoords) 524 { 525 this.windingrule = windingrule; 526 this.theTypes = types; 527 this.theCoords = coords; 528 this.numTypes = numtypes; 529 this.numCoords = numcoords; 530 } 531 532 private Shape testshape; 533 534 public Shape getTestShape() { 535 if (testshape == null) { 536 testshape = new Area(this); 537 } 538 return testshape; 539 } 540 541 private Rectangle2D cachedBounds; 542 public Rectangle2D getCachedBounds2D() { 543 if (cachedBounds == null) { 544 double xmin, ymin, xmax, ymax; 545 int ci = 0; 546 xmin = xmax = theCoords[ci++]; 547 ymin = ymax = theCoords[ci++]; 548 while (ci < numCoords) { 549 double c = theCoords[ci++]; 550 if (xmin > c) xmin = c; 551 if (xmax < c) xmax = c; 552 c = theCoords[ci++]; 553 if (ymin > c) ymin = c; 554 if (ymax < c) ymax = c; 555 } 556 cachedBounds = new Rectangle2D.Double(xmin, ymin, 557 xmax - xmin, 558 ymax - ymin); 559 } 560 return cachedBounds; 561 } 562 563 public Rectangle getBounds() { 564 return getCachedBounds2D().getBounds(); 565 } 566 public Rectangle2D getBounds2D() { 567 return getCachedBounds2D().getBounds2D(); 568 } 569 public boolean contains(double x, double y) { 570 return getTestShape().contains(x, y); 571 } 572 public boolean contains(Point2D p) { 573 return getTestShape().contains(p); 574 } 575 public boolean intersects(double x, double y, double w, double h) { 576 return getTestShape().intersects(x, y, w, h); 577 } 578 public boolean intersects(Rectangle2D r) { 579 return getTestShape().intersects(r); 580 } 581 public boolean contains(double x, double y, double w, double h) { 582 return getTestShape().contains(x, y, w, h); 583 } 584 public boolean contains(Rectangle2D r) { 585 return getTestShape().contains(r); 586 } 587 public PathIterator getPathIterator(final AffineTransform at) { 588 return new PathIterator() { 589 int tindex; 590 int cindex; 591 public int getWindingRule() { 592 return windingrule; 593 } 594 public boolean isDone() { 595 return (tindex >= numTypes); 596 } 597 public void next() { 598 cindex += CoordsForType[theTypes[tindex]]; 599 tindex++; 600 } 601 public int currentSegment(float[] coords) { 602 int t = theTypes[tindex]; 603 int n = CoordsForType[t]; 604 if (n > 0) { 605 // Cast to float first, then transform 606 // to match accuracy of float paths 607 for (int i = 0; i < n; i++) { 608 coords[i] = (float) theCoords[cindex+i]; 609 } 610 if (at != null) { 611 at.transform(coords, 0, coords, 0, n/2); 612 } 613 } 614 return t; 615 } 616 public int currentSegment(double[] coords) { 617 int t = theTypes[tindex]; 618 int n = CoordsForType[t]; 619 if (n > 0) { 620 if (at == null) { 621 System.arraycopy(theCoords, cindex, 622 coords, 0, n); 623 } else { 624 at.transform(theCoords, cindex, 625 coords, 0, n/2); 626 } 627 } 628 return t; 629 } 630 }; 631 } 632 public PathIterator getPathIterator(AffineTransform at, 633 double flatness) 634 { 635 return new FlatteningPathIterator(getPathIterator(at), flatness); 636 } 637 638 public String toString() { 639 Rectangle2D r2d = getBounds2D(); 640 double xmin = r2d.getMinX(); 641 double ymin = r2d.getMinY(); 642 double xmax = r2d.getMaxX(); 643 double ymax = r2d.getMaxY(); 644 return ("SampleShape["+ 645 (windingrule == WIND_NON_ZERO 646 ? "NonZero" 647 : "EvenOdd")+ 648 ", nsegments = "+numTypes+ 649 ", ncoords = "+numCoords+ 650 ", bounds["+(r2d.getMinX()+", "+r2d.getMinY()+", "+ 651 r2d.getMaxX()+", "+r2d.getMaxY())+"]"+ 652 "]"); 653 } 654 655 public Path2D makeFloatPath(Creator c) { 656 Path2D.Float p2df = (Path2D.Float) c.makePath(windingrule); 657 int ci = 0; 658 for (int i = 0; i < numTypes; i++) { 659 int t = theTypes[i]; 660 switch (t) { 661 case PathIterator.SEG_MOVETO: 662 p2df.moveTo((float) theCoords[ci++], 663 (float) theCoords[ci++]); 664 break; 665 case PathIterator.SEG_LINETO: 666 p2df.lineTo((float) theCoords[ci++], 667 (float) theCoords[ci++]); 668 break; 669 case PathIterator.SEG_QUADTO: 670 p2df.quadTo((float) theCoords[ci++], 671 (float) theCoords[ci++], 672 (float) theCoords[ci++], 673 (float) theCoords[ci++]); 674 break; 675 case PathIterator.SEG_CUBICTO: 676 p2df.curveTo((float) theCoords[ci++], 677 (float) theCoords[ci++], 678 (float) theCoords[ci++], 679 (float) theCoords[ci++], 680 (float) theCoords[ci++], 681 (float) theCoords[ci++]); 682 break; 683 case PathIterator.SEG_CLOSE: 684 p2df.closePath(); 685 break; 686 default: 687 throw new InternalError("unrecognized path type: "+t); 688 } 689 if (t != PathIterator.SEG_CLOSE) { 690 Point2D curpnt = p2df.getCurrentPoint(); 691 if (((float) curpnt.getX()) != ((float) theCoords[ci-2]) || 692 ((float) curpnt.getY()) != ((float) theCoords[ci-1])) 693 { 694 throw new RuntimeException("currentpoint failed"); 695 } 696 } 697 } 698 if (ci != numCoords) { 699 throw new InternalError("numcoords did not match"); 700 } 701 return p2df; 702 } 703 704 public Path2D makeDoublePath(Creator c) { 705 Path2D p2d = c.makePath(windingrule); 706 int ci = 0; 707 for (int i = 0; i < numTypes; i++) { 708 int t = theTypes[i]; 709 switch (t) { 710 case PathIterator.SEG_MOVETO: 711 p2d.moveTo(theCoords[ci++], theCoords[ci++]); 712 break; 713 case PathIterator.SEG_LINETO: 714 p2d.lineTo(theCoords[ci++], theCoords[ci++]); 715 break; 716 case PathIterator.SEG_QUADTO: 717 p2d.quadTo(theCoords[ci++], theCoords[ci++], 718 theCoords[ci++], theCoords[ci++]); 719 break; 720 case PathIterator.SEG_CUBICTO: 721 p2d.curveTo(theCoords[ci++], theCoords[ci++], 722 theCoords[ci++], theCoords[ci++], 723 theCoords[ci++], theCoords[ci++]); 724 break; 725 case PathIterator.SEG_CLOSE: 726 p2d.closePath(); 727 break; 728 default: 729 throw new InternalError("unrecognized path type: "+t); 730 } 731 if (t != PathIterator.SEG_CLOSE) { 732 Point2D curpnt = p2d.getCurrentPoint(); 733 if (((float) curpnt.getX()) != ((float) theCoords[ci-2]) || 734 ((float) curpnt.getY()) != ((float) theCoords[ci-1])) 735 { 736 throw new RuntimeException("currentpoint failed"); 737 } 738 } 739 } 740 if (ci != numCoords) { 741 throw new InternalError("numcoords did not match"); 742 } 743 return p2d; 744 } 745 } 746 747 public static class AppendedShape implements Shape { 748 Shape s1; 749 Shape s2; 750 boolean connect; 751 752 public AppendedShape(Shape s1, Shape s2, boolean connect) { 753 this.s1 = s1; 754 this.s2 = s2; 755 this.connect = connect; 756 } 757 758 public Rectangle getBounds() { 759 return getBounds2D().getBounds(); 760 } 761 762 public Rectangle2D getBounds2D() { 763 return s1.getBounds2D().createUnion(s2.getBounds2D()); 764 } 765 766 private Shape testshape; 767 private Shape getTestShape() { 768 if (testshape == null) { 769 testshape = new GeneralPath(this); 770 } 771 return testshape; 772 } 773 774 public boolean contains(double x, double y) { 775 return getTestShape().contains(x, y); 776 } 777 778 public boolean contains(Point2D p) { 779 return getTestShape().contains(p); 780 } 781 782 public boolean intersects(double x, double y, double w, double h) { 783 return getTestShape().intersects(x, y, w, h); 784 } 785 786 public boolean intersects(Rectangle2D r) { 787 return getTestShape().intersects(r); 788 } 789 790 public boolean contains(double x, double y, double w, double h) { 791 return getTestShape().contains(x, y, w, h); 792 } 793 794 public boolean contains(Rectangle2D r) { 795 return getTestShape().contains(r); 796 } 797 798 public PathIterator getPathIterator(final AffineTransform at) { 799 return new AppendingPathIterator(s1, s2, connect, at); 800 } 801 802 public PathIterator getPathIterator(AffineTransform at, 803 double flatness) 804 { 805 return new FlatteningPathIterator(getPathIterator(at), flatness); 806 } 807 808 public static class AppendingPathIterator implements PathIterator { 809 AffineTransform at; 810 PathIterator pi; 811 Shape swaiting; 812 int windingrule; 813 boolean connectrequested; 814 boolean canconnect; 815 boolean converttoline; 816 817 public AppendingPathIterator(Shape s1, Shape s2, 818 boolean connect, 819 AffineTransform at) 820 { 821 this.at = at; 822 this.pi = s1.getPathIterator(at); 823 this.swaiting = s2; 824 this.windingrule = pi.getWindingRule(); 825 this.connectrequested = connect; 826 827 if (pi.isDone()) { 828 chain(); 829 } 830 } 831 832 public void chain() { 833 if (swaiting != null) { 834 pi = swaiting.getPathIterator(at); 835 swaiting = null; 836 converttoline = (connectrequested && canconnect); 837 } 838 } 839 840 public int getWindingRule() { 841 return windingrule; 842 } 843 844 public boolean isDone() { 845 return (pi.isDone()); 846 } 847 848 public void next() { 849 converttoline = false; 850 pi.next(); 851 if (pi.isDone()) { 852 chain(); 853 } 854 canconnect = true; 855 } 856 857 public int currentSegment(float[] coords) { 858 int type = pi.currentSegment(coords); 859 if (converttoline) { 860 type = SEG_LINETO; 861 } 862 return type; 863 } 864 865 public int currentSegment(double[] coords) { 866 int type = pi.currentSegment(coords); 867 if (converttoline) { 868 type = SEG_LINETO; 869 } 870 return type; 871 } 872 } 873 } 874 875 public static void checkEmpty(Path2D p2d, int windingrule) { 876 checkEmpty2(p2d, windingrule); 877 p2d.setWindingRule(PathIterator.WIND_NON_ZERO); 878 checkEmpty2(p2d, PathIterator.WIND_NON_ZERO); 879 p2d.setWindingRule(PathIterator.WIND_EVEN_ODD); 880 checkEmpty2(p2d, PathIterator.WIND_EVEN_ODD); 881 } 882 883 public static void checkEmpty2(Path2D p2d, int windingrule) { 884 if (p2d.getWindingRule() != windingrule) { 885 throw new RuntimeException("wrong winding rule in Path2D"); 886 } 887 PathIterator pi = p2d.getPathIterator(null); 888 if (pi.getWindingRule() != windingrule) { 889 throw new RuntimeException("wrong winding rule in iterator"); 890 } 891 if (!pi.isDone()) { 892 throw new RuntimeException("path not empty"); 893 } 894 } 895 896 public static void compare(Creator c, Path2D p2d, Shape ref, int maxulp) { 897 compare(c, p2d, (Shape) p2d.clone(), null, 0); 898 compare(c, p2d, ref, null, 0); 899 compare(c, p2d, ref, TxIdentity, 0); 900 p2d.transform(TxIdentity); 901 compare(c, p2d, ref, null, 0); 902 compare(c, p2d, ref, TxIdentity, 0); 903 Shape s2 = p2d.createTransformedShape(TxIdentity); 904 compare(c, s2, ref, null, 0); 905 compare(c, s2, ref, TxIdentity, 0); 906 s2 = p2d.createTransformedShape(TxComplex); 907 compare(c, s2, ref, TxComplex, maxulp); 908 p2d.transform(TxComplex); 909 compare(c, p2d, (Shape) p2d.clone(), null, 0); 910 compare(c, p2d, ref, TxComplex, maxulp); 911 } 912 913 public static void compare(Creator c, 914 Shape p2d, Shape s, 915 AffineTransform at, int maxulp) 916 { 917 c.compare(p2d.getPathIterator(null), s.getPathIterator(at), 918 null, maxulp); 919 c.compare(p2d.getPathIterator(null), s.getPathIterator(null), 920 at, maxulp); 921 } 922 923 public static void checkBounds(Shape stest, Shape sref) { 924 checkBounds(stest.getBounds2D(), sref.getBounds2D(), 925 "2D bounds too small"); 926 /* 927 checkBounds(stest.getBounds(), sref.getBounds(), 928 "int bounds too small"); 929 */ 930 checkBounds(stest.getBounds(), stest.getBounds2D(), 931 "int bounds too small for 2D bounds"); 932 } 933 934 public static void checkBounds(Rectangle2D tBounds, 935 Rectangle2D rBounds, 936 String faildesc) 937 { 938 if (rBounds.isEmpty()) { 939 if (!tBounds.isEmpty()) { 940 throw new RuntimeException("bounds not empty"); 941 } 942 return; 943 } else if (tBounds.isEmpty()) { 944 throw new RuntimeException("bounds empty"); 945 } 946 double rxmin = rBounds.getMinX(); 947 double rymin = rBounds.getMinY(); 948 double rxmax = rBounds.getMaxX(); 949 double rymax = rBounds.getMaxY(); 950 double txmin = tBounds.getMinX(); 951 double tymin = tBounds.getMinY(); 952 double txmax = tBounds.getMaxX(); 953 double tymax = tBounds.getMaxY(); 954 if (txmin > rxmin || tymin > rymin || 955 txmax < rxmax || tymax < rymax) 956 { 957 if (verbose) System.out.println("test bounds = "+tBounds); 958 if (verbose) System.out.println("ref bounds = "+rBounds); 959 // Allow fudge room of a couple of single precision ulps 960 double ltxmin = txmin - 5 * Math.max(Math.ulp((float) rxmin), 961 Math.ulp((float) txmin)); 962 double ltymin = tymin - 5 * Math.max(Math.ulp((float) rymin), 963 Math.ulp((float) tymin)); 964 double ltxmax = txmax + 5 * Math.max(Math.ulp((float) rxmax), 965 Math.ulp((float) txmax)); 966 double ltymax = tymax + 5 * Math.max(Math.ulp((float) rymax), 967 Math.ulp((float) tymax)); 968 if (ltxmin > rxmin || ltymin > rymin || 969 ltxmax < rxmax || ltymax < rymax) 970 { 971 if (!verbose) System.out.println("test bounds = "+tBounds); 972 if (!verbose) System.out.println("ref bounds = "+rBounds); 973 System.out.println("xmin: "+ 974 txmin+" + "+fltulpless(txmin, rxmin)+" = "+ 975 rxmin+" + "+fltulpless(rxmin, txmin)); 976 System.out.println("ymin: "+ 977 tymin+" + "+fltulpless(tymin, rymin)+" = "+ 978 rymin+" + "+fltulpless(rymin, tymin)); 979 System.out.println("xmax: "+ 980 txmax+" + "+fltulpless(txmax, rxmax)+" = "+ 981 rxmax+" + "+fltulpless(rxmax, txmax)); 982 System.out.println("ymax: "+ 983 tymax+" + "+fltulpless(tymax, rymax)+" = "+ 984 rymax+" + "+fltulpless(rymax, tymax)); 985 System.out.println("flt tbounds = ["+ 986 ((float) txmin)+", "+((float) tymin)+", "+ 987 ((float) txmax)+", "+((float) tymax)+"]"); 988 System.out.println("flt rbounds = ["+ 989 ((float) rxmin)+", "+((float) rymin)+", "+ 990 ((float) rxmax)+", "+((float) rymax)+"]"); 991 System.out.println("xmin ulp = "+fltulpless(rxmin, txmin)); 992 System.out.println("ymin ulp = "+fltulpless(rymin, tymin)); 993 System.out.println("xmax ulp = "+fltulpless(txmax, rxmax)); 994 System.out.println("ymax ulp = "+fltulpless(tymax, rymax)); 995 throw new RuntimeException(faildesc); 996 } 997 } 998 } 999 1000 public static void checkHits(Shape stest, Shape sref) { 1001 for (int i = 0; i < 10; i++) { 1002 double px = Math.random() * 500 - 250; 1003 double py = Math.random() * 500 - 250; 1004 Point2D pnt = new Point2D.Double(px, py); 1005 1006 double rw = Math.random()*10+0.4; 1007 double rh = Math.random()*10+0.4; 1008 double rx = px - rw/2; 1009 double ry = py - rh/2; 1010 Rectangle2D rect = new Rectangle2D.Double(rx, ry, rw, rh); 1011 Rectangle2D empty = new Rectangle2D.Double(rx, ry, 0, 0); 1012 1013 if (!rect.contains(pnt)) { 1014 throw new InternalError("test point not inside test rect!"); 1015 } 1016 1017 if (stest.contains(rx, ry, 0, 0)) { 1018 throw new RuntimeException("contains 0x0 rect"); 1019 } 1020 if (stest.contains(empty)) { 1021 throw new RuntimeException("contains empty rect"); 1022 } 1023 if (stest.intersects(rx, ry, 0, 0)) { 1024 throw new RuntimeException("intersects 0x0 rect"); 1025 } 1026 if (stest.intersects(empty)) { 1027 throw new RuntimeException("intersects empty rect"); 1028 } 1029 1030 boolean tContainsXY = stest.contains(px, py); 1031 boolean tContainsPnt = stest.contains(pnt); 1032 boolean tContainsXYWH = stest.contains(rx, ry, rw, rh); 1033 boolean tContainsRect = stest.contains(rect); 1034 boolean tIntersectsXYWH = stest.intersects(rx, ry, rw, rh); 1035 boolean tIntersectsRect = stest.intersects(rect); 1036 1037 if (tContainsXY != tContainsPnt) { 1038 throw new RuntimeException("contains(x,y) != "+ 1039 "contains(pnt)"); 1040 } 1041 if (tContainsXYWH != tContainsRect) { 1042 throw new RuntimeException("contains(x,y,w,h) != "+ 1043 "contains(rect)"); 1044 } 1045 if (tIntersectsXYWH != tIntersectsRect) { 1046 throw new RuntimeException("intersects(x,y,w,h) != "+ 1047 "intersects(rect)"); 1048 } 1049 1050 boolean uContainsXY = 1051 Path2D.contains(stest.getPathIterator(null), px, py); 1052 boolean uContainsXYWH = 1053 Path2D.contains(stest.getPathIterator(null), rx, ry, rw, rh); 1054 boolean uIntersectsXYWH = 1055 Path2D.intersects(stest.getPathIterator(null), rx, ry, rw, rh); 1056 1057 if (tContainsXY != uContainsXY) { 1058 throw new RuntimeException("contains(x,y) "+ 1059 "does not match utility"); 1060 } 1061 if (tContainsXYWH != uContainsXYWH) { 1062 throw new RuntimeException("contains(x,y,w,h) "+ 1063 "does not match utility"); 1064 } 1065 if (tIntersectsXYWH != uIntersectsXYWH) { 1066 throw new RuntimeException("intersects(x,y,w,h) "+ 1067 "does not match utility"); 1068 } 1069 1070 // Make rect slightly smaller to be more conservative for rContains 1071 double srx = rx + 0.1; 1072 double sry = ry + 0.1; 1073 double srw = rw - 0.2; 1074 double srh = rh - 0.2; 1075 Rectangle2D srect = new Rectangle2D.Double(srx, sry, srw, srh); 1076 // Make rect slightly larger to be more liberal for rIntersects 1077 double lrx = rx - 0.1; 1078 double lry = ry - 0.1; 1079 double lrw = rw + 0.2; 1080 double lrh = rh + 0.2; 1081 Rectangle2D lrect = new Rectangle2D.Double(lrx, lry, lrw, lrh); 1082 1083 if (srect.isEmpty()) { 1084 throw new InternalError("smaller rect too small (empty)"); 1085 } 1086 if (!lrect.contains(rect)) { 1087 throw new InternalError("test rect not inside larger rect!"); 1088 } 1089 if (!rect.contains(srect)) { 1090 throw new InternalError("smaller rect not inside test rect!"); 1091 } 1092 1093 boolean rContainsSmaller; 1094 boolean rIntersectsLarger; 1095 boolean rContainsPnt; 1096 1097 if (sref instanceof SampleShape || 1098 sref instanceof QuadCurve2D || 1099 sref instanceof CubicCurve2D) 1100 { 1101 // REMIND 1102 // Some of the source shapes are not proving reliable 1103 // enough to do reference verification of the hit 1104 // testing results. 1105 // Quad/CubicCurve2D have spaghetti test methods that could 1106 // very likely contain some bugs. They return a conflicting 1107 // answer in maybe 1 out of 20,000 tests. 1108 // Area causes a conflicting answer maybe 1 out of 1109 // 100 to 1000 runs and it infinite loops maybe 1 1110 // out of 10,000 runs or so. 1111 // So, we use some conservative "safe" answers for 1112 // these shapes and avoid their hit testing methods. 1113 rContainsSmaller = tContainsRect; 1114 rIntersectsLarger = tIntersectsRect; 1115 rContainsPnt = tContainsPnt; 1116 } else { 1117 rContainsSmaller = sref.contains(srect); 1118 rIntersectsLarger = sref.intersects(lrect); 1119 rContainsPnt = sref.contains(px, py); 1120 } 1121 1122 if (tIntersectsRect) { 1123 if (tContainsRect) { 1124 if (!tContainsPnt) { 1125 System.out.println("reference shape = "+sref); 1126 System.out.println("pnt = "+pnt); 1127 System.out.println("rect = "+rect); 1128 System.out.println("tbounds = "+stest.getBounds2D()); 1129 throw new RuntimeException("test contains rect, "+ 1130 "but not center point"); 1131 } 1132 } 1133 // Note: (tContainsPnt || tContainsRect) is same as 1134 // tContainsPnt because of the test above... 1135 if (tContainsPnt) { 1136 if (!rIntersectsLarger) { 1137 System.out.println("reference shape = "+sref); 1138 System.out.println("pnt = "+pnt); 1139 System.out.println("rect = "+rect); 1140 System.out.println("lrect = "+lrect); 1141 System.out.println("tbounds = "+stest.getBounds2D()); 1142 System.out.println("rbounds = "+sref.getBounds2D()); 1143 throw new RuntimeException("test claims containment, "+ 1144 "but no ref intersection"); 1145 } 1146 } 1147 } else { 1148 if (tContainsRect) { 1149 throw new RuntimeException("test contains rect, "+ 1150 "with no intersection"); 1151 } 1152 if (tContainsPnt) { 1153 System.out.println("reference shape = "+sref); 1154 System.out.println("rect = "+rect); 1155 throw new RuntimeException("test contains point, "+ 1156 "with no intersection"); 1157 } 1158 if (rContainsPnt || rContainsSmaller) { 1159 System.out.println("pnt = "+pnt); 1160 System.out.println("rect = "+rect); 1161 System.out.println("srect = "+lrect); 1162 throw new RuntimeException("test did not intersect, "+ 1163 "but ref claims containment"); 1164 } 1165 } 1166 } 1167 } 1168 1169 public static void test(Creator c) { 1170 testConstructors(c); 1171 testPathConstruction(c); 1172 testAppend(c); 1173 testBounds(c); 1174 testHits(c); 1175 } 1176 1177 public static void testConstructors(Creator c) { 1178 checkEmpty(c.makePath(), WIND_NON_ZERO); 1179 checkEmpty(c.makePath(WIND_NON_ZERO), WIND_NON_ZERO); 1180 checkEmpty(c.makePath(WIND_EVEN_ODD), WIND_EVEN_ODD); 1181 checkEmpty(c.makePath(EmptyShapeNonZero), WIND_NON_ZERO); 1182 checkEmpty(c.makePath(EmptyShapeNonZero, null), WIND_NON_ZERO); 1183 checkEmpty(c.makePath(EmptyShapeNonZero, TxIdentity), WIND_NON_ZERO); 1184 checkEmpty(c.makePath(EmptyShapeEvenOdd), WIND_EVEN_ODD); 1185 checkEmpty(c.makePath(EmptyShapeEvenOdd, null), WIND_EVEN_ODD); 1186 checkEmpty(c.makePath(EmptyShapeEvenOdd, TxIdentity), WIND_EVEN_ODD); 1187 try { 1188 c.makePath(null); 1189 throw new RuntimeException(c+" allowed null Shape in constructor"); 1190 } catch (NullPointerException npe) { 1191 // passes 1192 } 1193 try { 1194 c.makePath(null, TxIdentity); 1195 throw new RuntimeException(c+" allowed null Shape in constructor"); 1196 } catch (NullPointerException npe) { 1197 // passes 1198 } 1199 1200 for (int i = 0; i < TestShapes.length; i++) { 1201 Shape sref = TestShapes[i]; 1202 if (verbose) System.out.println("construct testing "+sref); 1203 compare(c, c.makePath(sref), sref, null, 0); 1204 compare(c, c.makePath(sref), sref, TxIdentity, 0); 1205 compare(c, c.makePath(sref, null), sref, null, 0); 1206 compare(c, c.makePath(sref, null), sref, TxIdentity, 0); 1207 compare(c, c.makePath(sref, TxIdentity), sref, null, 0); 1208 compare(c, c.makePath(sref, TxIdentity), sref, TxIdentity, 0); 1209 compare(c, c.makePath(sref, TxComplex), sref, TxComplex, 1210 c.getRecommendedTxMaxUlp()); 1211 } 1212 } 1213 1214 public static void testPathConstruction(Creator c) { 1215 testPathConstruction(c, LongSampleNonZero); 1216 testPathConstruction(c, LongSampleEvenOdd); 1217 } 1218 1219 public static void testPathConstruction(Creator c, SampleShape ref) { 1220 if (c.supportsFloatCompose()) { 1221 compare(c, ref.makeFloatPath(c), ref, c.getRecommendedTxMaxUlp()); 1222 } 1223 compare(c, ref.makeDoublePath(c), ref, c.getRecommendedTxMaxUlp()); 1224 } 1225 1226 public static void testAppend(Creator c) { 1227 for (int i = 0; i < TestShapes.length; i++) { 1228 Shape sref = TestShapes[i]; 1229 if (verbose) System.out.println("append testing "+sref); 1230 PathIterator spi = sref.getPathIterator(null); 1231 Path2D stest = c.makePath(spi.getWindingRule()); 1232 stest.append(spi, false); 1233 compare(c, stest, sref, null, 0); 1234 stest.reset(); 1235 stest.append(sref, false); 1236 compare(c, stest, sref, null, 0); 1237 stest.reset(); 1238 stest.append(sref.getPathIterator(TxComplex), false); 1239 compare(c, stest, sref, TxComplex, 0); 1240 // multiple shape append testing... 1241 if (sref.getBounds2D().isEmpty()) { 1242 // If the first shape is empty, then we really 1243 // are not testing multiple appended shapes, 1244 // we are just testing appending the AppendShape 1245 // to a null path over and over. 1246 // Also note that some empty shapes will spit out 1247 // a single useless SEG_MOVETO that has no affect 1248 // on the outcome, but it makes duplicating the 1249 // behavior that Path2D has in that case difficult 1250 // when the AppenedShape utility class has to 1251 // iterate the exact same segments. So, we will 1252 // just ignore all empty shapes here. 1253 continue; 1254 } 1255 stest.reset(); 1256 stest.append(sref, false); 1257 stest.append(AppendShape, false); 1258 compare(c, stest, 1259 new AppendedShape(sref, AppendShape, false), 1260 null, 0); 1261 stest.reset(); 1262 stest.append(sref, false); 1263 stest.append(AppendShape, true); 1264 compare(c, stest, 1265 new AppendedShape(sref, AppendShape, true), 1266 null, 0); 1267 stest.reset(); 1268 stest.append(sref.getPathIterator(null), false); 1269 stest.append(AppendShape.getPathIterator(null), false); 1270 compare(c, stest, 1271 new AppendedShape(sref, AppendShape, false), 1272 null, 0); 1273 stest.reset(); 1274 stest.append(sref.getPathIterator(null), false); 1275 stest.append(AppendShape.getPathIterator(null), true); 1276 compare(c, stest, 1277 new AppendedShape(sref, AppendShape, true), 1278 null, 0); 1279 stest.reset(); 1280 stest.append(sref.getPathIterator(TxComplex), false); 1281 stest.append(AppendShape.getPathIterator(TxComplex), false); 1282 compare(c, stest, 1283 new AppendedShape(sref, AppendShape, false), 1284 TxComplex, 0); 1285 stest.reset(); 1286 stest.append(sref.getPathIterator(TxComplex), false); 1287 stest.append(AppendShape.getPathIterator(TxComplex), true); 1288 compare(c, stest, 1289 new AppendedShape(sref, AppendShape, true), 1290 TxComplex, 0); 1291 } 1292 } 1293 1294 public static void testBounds(Creator c) { 1295 for (int i = 0; i < TestShapes.length; i++) { 1296 Shape sref = TestShapes[i]; 1297 if (verbose) System.out.println("bounds testing "+sref); 1298 Shape stest = c.makePath(sref); 1299 checkBounds(c.makePath(sref), sref); 1300 } 1301 testBounds(c, ShortSampleNonZero); 1302 testBounds(c, ShortSampleEvenOdd); 1303 testBounds(c, LongSampleNonZero); 1304 testBounds(c, LongSampleEvenOdd); 1305 } 1306 1307 public static void testBounds(Creator c, SampleShape ref) { 1308 if (verbose) System.out.println("bounds testing "+ref); 1309 if (c.supportsFloatCompose()) { 1310 checkBounds(ref.makeFloatPath(c), ref); 1311 } 1312 checkBounds(ref.makeDoublePath(c), ref); 1313 } 1314 1315 public static void testHits(Creator c) { 1316 for (int i = 0; i < TestShapes.length; i++) { 1317 Shape sref = TestShapes[i]; 1318 if (verbose) System.out.println("hit testing "+sref); 1319 Shape stest = c.makePath(sref); 1320 checkHits(c.makePath(sref), sref); 1321 } 1322 testHits(c, ShortSampleNonZero); 1323 testHits(c, ShortSampleEvenOdd); 1324 // These take too long to construct the Area for reference testing 1325 //testHits(c, LongSampleNonZero); 1326 //testHits(c, LongSampleEvenOdd); 1327 } 1328 1329 public static void testHits(Creator c, SampleShape ref) { 1330 if (verbose) System.out.println("hit testing "+ref); 1331 if (c.supportsFloatCompose()) { 1332 checkHits(ref.makeFloatPath(c), ref); 1333 } 1334 checkHits(ref.makeDoublePath(c), ref); 1335 } 1336 1337 public static void main(String argv[]) { 1338 int limit = (argv.length > 0) ? 10000 : 1; 1339 verbose = (argv.length > 1); 1340 for (int i = 0; i < limit; i++) { 1341 if (limit > 1) { 1342 System.out.println("loop #"+(i+1)); 1343 } 1344 init(); 1345 test(new GPCreator()); 1346 test(new FltCreator()); 1347 test(new DblCreator()); 1348 } 1349 } 1350 } 1351