1 /* 2 * Copyright (c) 2004, 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 4980035 27 * @summary Unit test for new methods: 28 * 29 * AffineTransform.getRotateInstance(double x, double y); 30 * AffineTransform.setToRotation(double x, double y); 31 * AffineTransform.rotate(double x, double y); 32 * 33 * AffineTransform.getQuadrantRotateInstance(int numquads); 34 * AffineTransform.setToQuadrantRotation(int numquads); 35 * AffineTransform.quadrantRotate(int numquads); 36 * 37 * @author flar 38 * @run main TestRotateMethods 39 * @key randomness 40 */ 41 42 import java.awt.geom.AffineTransform; 43 import java.awt.geom.Point2D; 44 45 public class TestRotateMethods { 46 /* The maximum errors allowed, measured in double precision "ulps" 47 * Note that for most fields, the tests are extremely accurate - to 48 * within 3 ulps of the smaller value in the comparison 49 * For the translation components, the tests are still very accurate, 50 * but the absolute number of ulps can be noticeably higher when we 51 * use one of the rotate methods that takes an anchor point. 52 * Since a double precision value has 56 bits of precision, even 53 * 1024 ulps is extremely small as a ratio of the value. 54 */ 55 public static final double MAX_ULPS = 3.0; 56 public static final double MAX_ANCHOR_TX_ULPS = 1024.0; 57 public static double MAX_TX_ULPS = MAX_ULPS; 58 59 // Vectors for quadrant rotations 60 public static final double quadxvec[] = { 1.0, 0.0, -1.0, 0.0 }; 61 public static final double quadyvec[] = { 0.0, 1.0, 0.0, -1.0 }; 62 63 // Run tests once for each type of method: 64 // tx = AffineTransform.get<Rotate>Instance() 65 // tx.set<Rotate>() 66 // tx.<rotate>() 67 public static enum Mode { GET, SET, MOD }; 68 69 // Used to accumulate and report largest differences encountered by tests 70 public static double maxulps = 0.0; 71 public static double maxtxulps = 0.0; 72 73 // Sample anchor points for testing. 74 public static Point2D zeropt = new Point2D.Double(0, 0); 75 public static Point2D testtxpts[] = { 76 new Point2D.Double( 5, 5), 77 new Point2D.Double( 20, -10), 78 new Point2D.Double(-Math.PI, Math.E), 79 }; 80 81 public static void main(String argv[]) { 82 test(Mode.GET); 83 test(Mode.SET); 84 test(Mode.MOD); 85 86 System.out.println("Max scale and shear difference: "+maxulps+" ulps"); 87 System.out.println("Max translate difference: "+maxtxulps+" ulps"); 88 } 89 90 public static void test(Mode mode) { 91 MAX_TX_ULPS = MAX_ULPS; // Stricter tx testing with no anchor point 92 test(mode, 0.5, null); 93 test(mode, 1.0, null); 94 test(mode, 3.0, null); 95 96 // Anchor points make the tx values less reliable 97 MAX_TX_ULPS = MAX_ANCHOR_TX_ULPS; 98 for (int i = 0; i < testtxpts.length; i++) { 99 test(mode, 1.0, testtxpts[i]); 100 } 101 MAX_TX_ULPS = MAX_ULPS; // Restore to default 102 } 103 104 public static void verify(AffineTransform at1, AffineTransform at2, 105 Mode mode, double vectorscale, Point2D txpt, 106 String message, double num, String units) 107 { 108 if (!compare(at1, at2)) { 109 System.out.println("mode == "+mode); 110 System.out.println("vectorscale == "+vectorscale); 111 System.out.println("txpt == "+txpt); 112 System.out.println(at1+", type = "+at1.getType()); 113 System.out.println(at2+", type = "+at2.getType()); 114 System.out.println("ScaleX values differ by "+ 115 ulps(at1.getScaleX(), at2.getScaleX())+" ulps"); 116 System.out.println("ScaleY values differ by "+ 117 ulps(at1.getScaleY(), at2.getScaleY())+" ulps"); 118 System.out.println("ShearX values differ by "+ 119 ulps(at1.getShearX(), at2.getShearX())+" ulps"); 120 System.out.println("ShearY values differ by "+ 121 ulps(at1.getShearY(), at2.getShearY())+" ulps"); 122 System.out.println("TranslateX values differ by "+ 123 ulps(at1.getTranslateX(), 124 at2.getTranslateX())+" ulps"); 125 System.out.println("TranslateY values differ by "+ 126 ulps(at1.getTranslateY(), 127 at2.getTranslateY())+" ulps"); 128 throw new RuntimeException(message + num + units); 129 } 130 } 131 132 public static void test(Mode mode, double vectorscale, Point2D txpt) { 133 AffineTransform at1, at2, at3; 134 135 for (int deg = -720; deg <= 720; deg++) { 136 if ((deg % 90) == 0) continue; 137 double radians = Math.toRadians(deg); 138 double vecy = Math.sin(radians) * vectorscale; 139 double vecx = Math.cos(radians) * vectorscale; 140 141 at1 = makeAT(mode, txpt, radians); 142 at2 = makeAT(mode, txpt, vecx, vecy); 143 verify(at1, at2, mode, vectorscale, txpt, 144 "vector and radians do not match for ", deg, " degrees"); 145 146 if (txpt == null) { 147 // Make sure output was same as a with a 0,0 anchor point 148 if (vectorscale == 1.0) { 149 // Only need to test radians method for one scale factor 150 at3 = makeAT(mode, zeropt, radians); 151 verify(at1, at3, mode, vectorscale, zeropt, 152 "radians not invariant with 0,0 translate at ", 153 deg, " degrees"); 154 } 155 // But test vector methods with all scale factors 156 at3 = makeAT(mode, zeropt, vecx, vecy); 157 verify(at2, at3, mode, vectorscale, zeropt, 158 "vector not invariant with 0,0 translate at ", 159 deg, " degrees"); 160 } 161 } 162 163 for (int quad = -8; quad <= 8; quad++) { 164 double degrees = quad * 90.0; 165 double radians = Math.toRadians(degrees); 166 double vecx = quadxvec[quad & 3] * vectorscale; 167 double vecy = quadyvec[quad & 3] * vectorscale; 168 169 at1 = makeAT(mode, txpt, radians); 170 at2 = makeAT(mode, txpt, vecx, vecy); 171 verify(at1, at2, mode, vectorscale, txpt, 172 "quadrant vector and radians do not match for ", 173 degrees, " degrees"); 174 at2 = makeQuadAT(mode, txpt, quad); 175 verify(at1, at2, mode, vectorscale, txpt, 176 "quadrant and radians do not match for ", 177 quad, " quadrants"); 178 if (txpt == null) { 179 at3 = makeQuadAT(mode, zeropt, quad); 180 verify(at2, at3, mode, vectorscale, zeropt, 181 "quadrant not invariant with 0,0 translate at ", 182 quad, " quadrants"); 183 } 184 } 185 } 186 187 public static AffineTransform makeRandomAT() { 188 AffineTransform at = new AffineTransform(); 189 at.scale(Math.random() * -10.0, Math.random() * 100.0); 190 at.rotate(Math.random() * Math.PI); 191 at.shear(Math.random(), Math.random()); 192 at.translate(Math.random() * 300.0, Math.random() * -20.0); 193 return at; 194 } 195 196 public static AffineTransform makeAT(Mode mode, Point2D txpt, 197 double radians) 198 { 199 AffineTransform at; 200 double tx = (txpt == null) ? 0.0 : txpt.getX(); 201 double ty = (txpt == null) ? 0.0 : txpt.getY(); 202 switch (mode) { 203 case GET: 204 if (txpt != null) { 205 at = AffineTransform.getRotateInstance(radians, tx, ty); 206 } else { 207 at = AffineTransform.getRotateInstance(radians); 208 } 209 break; 210 case SET: 211 at = makeRandomAT(); 212 if (txpt != null) { 213 at.setToRotation(radians, tx, ty); 214 } else { 215 at.setToRotation(radians); 216 } 217 break; 218 case MOD: 219 at = makeRandomAT(); 220 at.setToIdentity(); 221 if (txpt != null) { 222 at.rotate(radians, tx, ty); 223 } else { 224 at.rotate(radians); 225 } 226 break; 227 default: 228 throw new InternalError("unrecognized mode: "+mode); 229 } 230 231 return at; 232 } 233 234 public static AffineTransform makeAT(Mode mode, Point2D txpt, 235 double vx, double vy) 236 { 237 AffineTransform at; 238 double tx = (txpt == null) ? 0.0 : txpt.getX(); 239 double ty = (txpt == null) ? 0.0 : txpt.getY(); 240 switch (mode) { 241 case GET: 242 if (txpt != null) { 243 at = AffineTransform.getRotateInstance(vx, vy, tx, ty); 244 } else { 245 at = AffineTransform.getRotateInstance(vx, vy); 246 } 247 break; 248 case SET: 249 at = makeRandomAT(); 250 if (txpt != null) { 251 at.setToRotation(vx, vy, tx, ty); 252 } else { 253 at.setToRotation(vx, vy); 254 } 255 break; 256 case MOD: 257 at = makeRandomAT(); 258 at.setToIdentity(); 259 if (txpt != null) { 260 at.rotate(vx, vy, tx, ty); 261 } else { 262 at.rotate(vx, vy); 263 } 264 break; 265 default: 266 throw new InternalError("unrecognized mode: "+mode); 267 } 268 269 return at; 270 } 271 272 public static AffineTransform makeQuadAT(Mode mode, Point2D txpt, 273 int quads) 274 { 275 AffineTransform at; 276 double tx = (txpt == null) ? 0.0 : txpt.getX(); 277 double ty = (txpt == null) ? 0.0 : txpt.getY(); 278 switch (mode) { 279 case GET: 280 if (txpt != null) { 281 at = AffineTransform.getQuadrantRotateInstance(quads, tx, ty); 282 } else { 283 at = AffineTransform.getQuadrantRotateInstance(quads); 284 } 285 break; 286 case SET: 287 at = makeRandomAT(); 288 if (txpt != null) { 289 at.setToQuadrantRotation(quads, tx, ty); 290 } else { 291 at.setToQuadrantRotation(quads); 292 } 293 break; 294 case MOD: 295 at = makeRandomAT(); 296 at.setToIdentity(); 297 if (txpt != null) { 298 at.quadrantRotate(quads, tx, ty); 299 } else { 300 at.quadrantRotate(quads); 301 } 302 break; 303 default: 304 throw new InternalError("unrecognized mode: "+mode); 305 } 306 307 return at; 308 } 309 310 public static boolean compare(AffineTransform at1, AffineTransform at2) { 311 maxulps = Math.max(maxulps, ulps(at1.getScaleX(), at2.getScaleX())); 312 maxulps = Math.max(maxulps, ulps(at1.getScaleY(), at2.getScaleY())); 313 maxulps = Math.max(maxulps, ulps(at1.getShearX(), at2.getShearX())); 314 maxulps = Math.max(maxulps, ulps(at1.getShearY(), at2.getShearY())); 315 maxtxulps = Math.max(maxtxulps, 316 ulps(at1.getTranslateX(), at2.getTranslateX())); 317 maxtxulps = Math.max(maxtxulps, 318 ulps(at1.getTranslateY(), at2.getTranslateY())); 319 return (getModifiedType(at1) == getModifiedType(at2) && 320 (compare(at1.getScaleX(), at2.getScaleX(), MAX_ULPS)) && 321 (compare(at1.getScaleY(), at2.getScaleY(), MAX_ULPS)) && 322 (compare(at1.getShearX(), at2.getShearX(), MAX_ULPS)) && 323 (compare(at1.getShearY(), at2.getShearY(), MAX_ULPS)) && 324 (compare(at1.getTranslateX(), 325 at2.getTranslateX(), MAX_TX_ULPS)) && 326 (compare(at1.getTranslateY(), 327 at2.getTranslateY(), MAX_TX_ULPS))); 328 } 329 330 public static int getModifiedType(AffineTransform at) { 331 int type = at.getType(); 332 // Some of the vector methods can introduce a tiny uniform scale 333 // at some angles... 334 if ((type & AffineTransform.TYPE_UNIFORM_SCALE) != 0) { 335 maxulps = Math.max(maxulps, ulps(at.getDeterminant(), 1.0)); 336 if (ulps(at.getDeterminant(), 1.0) <= MAX_ULPS) { 337 // Really tiny - we will ignore it 338 type &= (~AffineTransform.TYPE_UNIFORM_SCALE); 339 } 340 } 341 return type; 342 } 343 344 public static boolean compare(double val1, double val2, double maxulps) { 345 return (ulps(val1, val2) <= maxulps); 346 } 347 348 public static double ulps(double val1, double val2) { 349 double diff = Math.abs(val1 - val2); 350 double ulpmax = Math.min(Math.ulp(val1), Math.ulp(val2)); 351 return (diff / ulpmax); 352 } 353 }