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