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 41 import java.awt.geom.AffineTransform; 42 import java.awt.geom.Point2D; 43 44 public 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 main(String argv[])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 test(Mode mode)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 verify(AffineTransform at1, AffineTransform at2, Mode mode, double vectorscale, Point2D txpt, String message, double num, String units)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 test(Mode mode, double vectorscale, Point2D txpt)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 makeRandomAT()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 makeAT(Mode mode, Point2D txpt, double radians)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 makeAT(Mode mode, Point2D txpt, double vx, double vy)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 makeQuadAT(Mode mode, Point2D txpt, int quads)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 compare(AffineTransform at1, AffineTransform at2)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 getModifiedType(AffineTransform at)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 compare(double val1, double val2, double maxulps)343 public static boolean compare(double val1, double val2, double maxulps) { 344 return (ulps(val1, val2) <= maxulps); 345 } 346 ulps(double val1, double val2)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