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 * @summary Check Path2D constructor with zero capacity 37 * @run main Path2DTrimCopy 38 */ 39 public class Path2DEmpty { 40 41 private final static float EPSILON = 5e-6f; 42 private final static float FLATNESS = 1e-2f; 43 44 private final static AffineTransform at 45 = AffineTransform.getScaleInstance(1.3, 2.4); 46 47 private final static Rectangle2D.Double rect2d 48 = new Rectangle2D.Double(3.2, 4.1, 5.0, 10.0); 49 50 private final static Point2D.Double pt2d 51 = new Point2D.Double(2.0, 2.5); 52 53 public static void main(String[] args) { 54 testDoublePaths(); 55 56 testFloatPaths(); 57 testGeneralPath(); 58 } 59 60 static void testDoublePaths() { 61 System.out.println("\n - Test(Path2D.Double) ---"); 62 test(() -> new Path2D.Double(Path2D.WIND_NON_ZERO, 0)); 63 } 64 65 static void testFloatPaths() { 66 System.out.println("\n - Test(Path2D.Float) ---"); 67 test(() -> new Path2D.Float(Path2D.WIND_NON_ZERO, 0)); 68 } 69 70 static void testGeneralPath() { 71 System.out.println("\n - Test(GeneralPath) ---"); 72 test(() -> new GeneralPath(Path2D.WIND_NON_ZERO, 0)); 73 } 74 75 interface PathFactory { 76 77 Path2D makePath(); 78 } 79 80 /** 81 - Perform each of the above tests on a (set of) clone(s) of the following paths: 82 - each of the following executed on both a Float and a Double instance... 83 - an empty path 84 - a path with just a moveto 85 - a path with a moveto+some lines 86 - a path with a moveto+some curves 87 */ 88 static void test(PathFactory pf) { 89 System.out.println("\n --- test: path(empty) ---"); 90 test(pf.makePath(), true); 91 } 92 93 static Path2D addMove(Path2D p2d) { 94 p2d.moveTo(1.0, 0.5); 95 return p2d; 96 } 97 98 static Path2D addLinesOnly(Path2D p2d) { 99 for (int i = 0; i < 10; i++) { 100 p2d.lineTo(1.1 * i, 2.3 * i); 101 } 102 return p2d; 103 } 104 105 static Path2D addCurvesOnly(Path2D p2d) { 106 for (int i = 0; i < 10; i++) { 107 p2d.quadTo(1.1 * i, 1.2 * i, 1.3 * i, 1.4 * i); 108 p2d.curveTo(1.1 * i, 1.2 * i, 1.3 * i, 1.4 * i, 1.5 * i, 1.6 * i); 109 } 110 return p2d; 111 } 112 113 /** 114 - Write tests that create paths in various forms and run them through the following sub-tests: 115 - each of the following should be tested on a fresh clone... 116 - get a PathIterator and iterate it through until it is done 117 (optional - compare to the expected segments that were in the original) 118 - get a flattened PathIterator using "getPathIterator(flatness,)" and make sure it works 119 (optional - compare to original path if the original was already flat) 120 (but, also run it on a path with curves to make sure flattening engine didn't break) 121 - add various kinds of segments to the cloned path 122 - get the bounds of the cloned path 123 - use the transform() method on the cloned path 124 - call intersects(point), intersects(rect) and contains(rect) on a cloned path 125 - call getCurrentPoint() on the cloned path 126 */ 127 static void test(Path2D p2d, boolean isEmpty) { 128 testEqual(new Path2D.Float(p2d), p2d); 129 testEqual(new Path2D.Double(p2d), p2d); 130 testEqual(new GeneralPath(p2d), p2d); 131 132 // 3 clone variants: 133 testIterator(new Path2D.Float(p2d), p2d); 134 testIterator(new Path2D.Double(p2d), p2d); 135 testIterator((Path2D) p2d.clone(), p2d); 136 137 // 3 clone variants: 138 testFlattening(new Path2D.Float(p2d), p2d); 139 testFlattening(new Path2D.Double(p2d), p2d); 140 testFlattening((Path2D) p2d.clone(), p2d); 141 142 // 3 clone variants: 143 testAddMove(new Path2D.Float(p2d)); 144 testAddMove(new Path2D.Double(p2d)); 145 testAddMove((Path2D) p2d.clone()); 146 147 // These should expect exception if empty 148 // 3 clone variants: 149 testAddLine(new Path2D.Float(p2d), isEmpty); 150 testAddLine(new Path2D.Double(p2d), isEmpty); 151 testAddLine((Path2D) p2d.clone(), isEmpty); 152 153 // 3 clone variants: 154 testAddQuad(new Path2D.Float(p2d), isEmpty); 155 testAddQuad(new Path2D.Double(p2d), isEmpty); 156 testAddQuad((Path2D) p2d.clone(), isEmpty); 157 158 // 3 clone variants: 159 testGetBounds(new Path2D.Float(p2d), p2d); 160 testGetBounds(new Path2D.Double(p2d), p2d); 161 testGetBounds((Path2D) p2d.clone(), p2d); 162 163 // 3 clone variants: 164 testTransform(new Path2D.Float(p2d)); 165 testTransform(new Path2D.Double(p2d)); 166 testTransform((Path2D) p2d.clone()); 167 168 final boolean doTestThatFail = true; 169 170 // FAILS JDK8 171 if (doTestThatFail) { 172 // 3 clone variants: 173 testIntersect(new Path2D.Float(p2d), p2d); 174 testIntersect(new Path2D.Double(p2d), p2d); 175 testIntersect((Path2D) p2d.clone(), p2d); 176 } 177 178 // FAILS JDK8 179 if (doTestThatFail) { 180 // 3 clone variants: 181 testContains(new Path2D.Float(p2d), p2d); 182 testContains(new Path2D.Double(p2d), p2d); 183 testContains((Path2D) p2d.clone(), p2d); 184 } 185 186 // 3 clone variants: 187 testGetCurrentPoint(new Path2D.Float(p2d), p2d); 188 testGetCurrentPoint(new Path2D.Double(p2d), p2d); 189 testGetCurrentPoint((Path2D) p2d.clone(), p2d); 190 191 // TODO: add all other public methods ? 192 /* 193 public abstract void append(PathIterator pi, boolean connect) 194 public final synchronized void closePath() 195 */ 196 } 197 198 static void testEqual(Path2D pathA, Path2D pathB) { 199 // Grab 2 path iterators and test for equality with float coords[] 200 final PathIterator itA = pathA.getPathIterator(null); 201 final PathIterator itB = pathB.getPathIterator(null); 202 203 float[] coordsA = new float[6]; 204 float[] coordsB = new float[6]; 205 206 int n = 0; 207 for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) { 208 int typeA = itA.currentSegment(coordsA); 209 int typeB = itB.currentSegment(coordsB); 210 211 if (typeA != typeB) { 212 throw new IllegalStateException("Path-segment[" + n + "] " 213 + " type are not equals [" + typeA + "|" + typeB + "] !"); 214 } 215 if (!Arrays.equals(coordsA, coordsB)) { 216 throw new IllegalStateException("Path-segment[" + n + "] coords" 217 + " are not equals [" + Arrays.toString(coordsA) + "|" 218 + Arrays.toString(coordsB) + "] !"); 219 } 220 } 221 if (!itA.isDone() || !itB.isDone()) { 222 throw new IllegalStateException("Paths do not have same lengths !"); 223 } 224 System.out.println("testEqual: " + n + " segments."); 225 } 226 227 static void testIterator(Path2D pathA, Path2D pathB) { 228 // - get a PathIterator and iterate it through until it is done 229 // (optional - compare to the expected segments that were in the original) 230 final PathIterator itA = pathA.getPathIterator(at); 231 final PathIterator itB = pathB.getPathIterator(at); 232 233 float[] coordsA = new float[6]; 234 float[] coordsB = new float[6]; 235 236 int n = 0; 237 for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) { 238 int typeA = itA.currentSegment(coordsA); 239 int typeB = itB.currentSegment(coordsB); 240 241 if (typeA != typeB) { 242 throw new IllegalStateException("Path-segment[" + n + "] " 243 + "type are not equals [" + typeA + "|" + typeB + "] !"); 244 } 245 // Take care of floating-point precision: 246 if (!equalsArray(coordsA, coordsB)) { 247 throw new IllegalStateException("Path-segment[" + n + "] coords" 248 + " are not equals [" + Arrays.toString(coordsA) + "|" 249 + Arrays.toString(coordsB) + "] !"); 250 } 251 } 252 if (!itA.isDone() || !itB.isDone()) { 253 throw new IllegalStateException("Paths do not have same lengths !"); 254 } 255 System.out.println("testIterator: " + n + " segments."); 256 } 257 258 static void testFlattening(Path2D pathA, Path2D pathB) { 259 // - get a flattened PathIterator using "getPathIterator(flatness,)" and make sure it works 260 // (optional - compare to original path if the original was already flat) 261 // (but, also run it on a path with curves to make sure flattening engine didn't break) 262 final PathIterator itA = pathA.getPathIterator(at, FLATNESS); 263 final PathIterator itB = pathB.getPathIterator(at, FLATNESS); 264 265 float[] coordsA = new float[6]; 266 float[] coordsB = new float[6]; 267 268 int n = 0; 269 for (; !itA.isDone() && !itB.isDone(); itA.next(), itB.next(), n++) { 270 int typeA = itA.currentSegment(coordsA); 271 int typeB = itB.currentSegment(coordsB); 272 273 if (typeA != typeB) { 274 throw new IllegalStateException("Path-segment[" + n + "] " 275 + "type are not equals [" + typeA + "|" + typeB + "] !"); 276 } 277 // Take care of floating-point precision: 278 if (!equalsArray(coordsA, coordsB)) { 279 throw new IllegalStateException("Path-segment[" + n + "] coords" 280 + " are not equals [" + Arrays.toString(coordsA) + "|" 281 + Arrays.toString(coordsB) + "] !"); 282 } 283 } 284 if (!itA.isDone() || !itB.isDone()) { 285 throw new IllegalStateException("Paths do not have same lengths !"); 286 } 287 System.out.println("testFlattening: " + n + " segments."); 288 } 289 290 static void testAddMove(Path2D pathA) { 291 addMove(pathA); 292 System.out.println("testAddMove: passed."); 293 } 294 295 static void testAddLine(Path2D pathA, boolean isEmpty) { 296 try { 297 addLinesOnly(pathA); 298 } 299 catch (IllegalPathStateException ipse) { 300 if (isEmpty) { 301 System.out.println("testAddLine: passed " 302 + "(expected IllegalPathStateException catched)."); 303 return; 304 } else { 305 throw ipse; 306 } 307 } 308 if (isEmpty) { 309 throw new IllegalStateException("IllegalPathStateException not thrown !"); 310 } 311 System.out.println("testAddLine: passed."); 312 } 313 314 static void testAddQuad(Path2D pathA, boolean isEmpty) { 315 try { 316 addCurvesOnly(pathA); 317 } 318 catch (IllegalPathStateException ipse) { 319 if (isEmpty) { 320 System.out.println("testAddQuad: passed " 321 + "(expected IllegalPathStateException catched)."); 322 return; 323 } else { 324 throw ipse; 325 } 326 } 327 if (isEmpty) { 328 throw new IllegalStateException("IllegalPathStateException not thrown !"); 329 } 330 System.out.println("testAddQuad: passed."); 331 } 332 333 static void testGetBounds(Path2D pathA, Path2D pathB) { 334 // - get the bounds of the cloned path 335 final Rectangle rA = pathA.getBounds(); 336 final Rectangle rB = pathB.getBounds(); 337 338 if (!rA.equals(rB)) { 339 throw new IllegalStateException("Bounds are not equals [" + rA 340 + "|" + rB + "] !"); 341 } 342 final Rectangle2D r2dA = pathA.getBounds2D(); 343 final Rectangle2D r2dB = pathB.getBounds2D(); 344 345 if (!equalsRectangle2D(r2dA, r2dB)) { 346 throw new IllegalStateException("Bounds2D are not equals [" 347 + r2dA + "|" + r2dB + "] !"); 348 } 349 System.out.println("testGetBounds: passed."); 350 } 351 352 static void testTransform(Path2D pathA) { 353 // - use the transform() method on the cloned path 354 pathA.transform(at); 355 System.out.println("testTransform: passed."); 356 } 357 358 static void testIntersect(Path2D pathA, Path2D pathB) { 359 // - call intersects(points), intersects(rect) on a cloned path 360 boolean resA = pathA.intersects(rect2d); 361 boolean resB = pathB.intersects(rect2d); 362 if (resA != resB) { 363 throw new IllegalStateException("Intersects(rect2d) are not equals [" 364 + resA + "|" + resB + "] !"); 365 } 366 resA = pathA.intersects(1.0, 2.0, 13.0, 17.0); 367 resB = pathB.intersects(1.0, 2.0, 13.0, 17.0); 368 if (resA != resB) { 369 throw new IllegalStateException("Intersects(doubles) are not equals [" 370 + resA + "|" + resB + "] !"); 371 } 372 System.out.println("testIntersect: passed."); 373 } 374 375 static void testContains(Path2D pathA, Path2D pathB) { 376 // - call contains(pt), contains(rect) on a cloned path 377 boolean resA = pathA.contains(pt2d); 378 boolean resB = pathB.contains(pt2d); 379 if (resA != resB) { 380 throw new IllegalStateException("Contains(pt) are not equals [" 381 + resA + "|" + resB + "] !"); 382 } 383 resA = pathA.contains(pt2d.getX(), pt2d.getY()); 384 resB = pathB.contains(pt2d.getX(), pt2d.getY()); 385 if (resA != resB) { 386 throw new IllegalStateException("Contains(x,y) are not equals [" 387 + resA + "|" + resB + "] !"); 388 } 389 resA = pathA.contains(rect2d); 390 resB = pathB.contains(rect2d); 391 if (resA != resB) { 392 throw new IllegalStateException("Contains(rect2d) are not equals [" 393 + resA + "|" + resB + "] !"); 394 } 395 resA = pathA.contains(1.0, 2.0, 13.0, 17.0); 396 resB = pathB.contains(1.0, 2.0, 13.0, 17.0); 397 if (resA != resB) { 398 throw new IllegalStateException("Contains(doubles) are not equals [" 399 + resA + "|" + resB + "] !"); 400 } 401 System.out.println("testContains: passed."); 402 } 403 404 static void testGetCurrentPoint(Path2D pathA, Path2D pathB) { 405 // - call getCurrentPoint() on the cloned path 406 final Point2D ptA = pathA.getCurrentPoint(); 407 final Point2D ptB = pathA.getCurrentPoint(); 408 if (((ptA == null) && (ptB != null)) 409 || ((ptA != null) && !ptA.equals(ptB))) 410 { 411 throw new IllegalStateException("getCurrentPoint() are not equals [" 412 + ptA + "|" + ptB + "] !"); 413 } 414 System.out.println("testGetCurrentPoint: passed."); 415 } 416 417 // Custom equals methods --- 418 static boolean equalsArray(float[] a, float[] a2) { 419 if (a == a2) { 420 return true; 421 } 422 if (a == null || a2 == null) { 423 return false; 424 } 425 426 int length = a.length; 427 if (a2.length != length) { 428 return false; 429 } 430 431 for (int i = 0; i < length; i++) { 432 if (!equals(a[i], a2[i])) { 433 return false; 434 } 435 } 436 437 return true; 438 } 439 440 static boolean equalsRectangle2D(Rectangle2D a, Rectangle2D b) { 441 if (a == b) { 442 return true; 443 } 444 return equals(a.getX(), b.getX()) 445 && equals(a.getY(), b.getY()) 446 && equals(a.getWidth(), b.getWidth()) 447 && equals(a.getHeight(), b.getHeight()); 448 } 449 450 static boolean equals(float a, float b) { 451 return (Math.abs(a - b) <= EPSILON); 452 } 453 454 static boolean equals(double a, double b) { 455 return (Math.abs(a - b) <= EPSILON); 456 } 457 458 }