1 /* 2 * Copyright (c) 2002, 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 4418285 27 * @summary Tests that transforms modified with degenerate operations 28 * continue to return their more optimal type from getType(). 29 * This test also confirms that isIdentity() returns the 30 * optimal value under all histories of modification. 31 * @run main GetTypeOptimization 32 * @key randomness 33 */ 34 35 import java.awt.geom.AffineTransform; 36 import java.util.Random; 37 38 public class GetTypeOptimization { 39 static int TYPE_IDENTITY = AffineTransform.TYPE_IDENTITY; 40 static int TYPE_TRANSLATION = AffineTransform.TYPE_TRANSLATION; 41 static int TYPE_UNIFORM_SCALE = AffineTransform.TYPE_UNIFORM_SCALE; 42 static int TYPE_GENERAL_SCALE = AffineTransform.TYPE_GENERAL_SCALE; 43 static int TYPE_FLIP = AffineTransform.TYPE_FLIP; 44 static int TYPE_QUADRANT_ROTATION = AffineTransform.TYPE_QUADRANT_ROTATION; 45 static int TYPE_GENERAL_ROTATION = AffineTransform.TYPE_GENERAL_ROTATION; 46 static int TYPE_GENERAL_TRANSFORM = AffineTransform.TYPE_GENERAL_TRANSFORM; 47 48 public static Random rand = new Random(); 49 50 public static boolean verbose; 51 public static int numerrors; 52 53 public static void main(String argv[]) { 54 verbose = (argv.length != 0); 55 56 checkBug4418285(); 57 58 checkAtType(new AffineTransform()); 59 checkAtType(AffineTransform.getTranslateInstance(0, 0)); 60 checkAtType(AffineTransform.getScaleInstance(1, 1)); 61 checkAtType(AffineTransform.getShearInstance(0, 0)); 62 checkAtType(AffineTransform.getRotateInstance(0)); 63 checkAtType(AffineTransform.getRotateInstance(0, 0, 0)); 64 for (int i = 90; i <= 360; i += 90) { 65 double angle = Math.toRadians(i); 66 checkAtType(AffineTransform.getRotateInstance(angle)); 67 checkAtType(AffineTransform.getRotateInstance(angle, 0, 0)); 68 } 69 70 AffineTransform at = new AffineTransform(); 71 checkAtType(at); 72 73 at.setToIdentity(); checkAtType(at); 74 at.setToTranslation(0.0, 0.0); checkAtType(at); 75 at.setToScale(1.0, 1.0); checkAtType(at); 76 at.setToShear(0.0, 0.0); checkAtType(at); 77 at.setToRotation(0); checkAtType(at); 78 at.setToRotation(0, 0, 0); checkAtType(at); 79 for (int i = 90; i <= 360; i += 90) { 80 double angle = Math.toRadians(i); 81 at.setToRotation(angle); checkAtType(at); 82 at.setToRotation(angle, 0, 0); checkAtType(at); 83 } 84 85 at.setToIdentity(); at.scale(1, 1); checkAtType(at); 86 at.setToIdentity(); at.translate(0, 0); checkAtType(at); 87 at.setToIdentity(); at.shear(0, 0); checkAtType(at); 88 at.setToIdentity(); at.rotate(0); checkAtType(at); 89 for (int i = 90; i <= 360; i += 90) { 90 double angle = Math.toRadians(i); 91 at.setToIdentity(); at.rotate(angle); checkAtType(at); 92 at.setToIdentity(); at.rotate(angle, 0, 0); checkAtType(at); 93 } 94 95 at.setToIdentity(); 96 for (int i = 0; i < 4; i++) { 97 at.rotate(Math.toRadians(90)); checkAtType(at); 98 } 99 100 at.setToIdentity(); 101 at.scale(2, 2); checkAtType(at); 102 at.scale(.5, .5); checkAtType(at); 103 104 for (int n = 1; n <= 3; n++) { 105 for (int i = 0; i < 500; i++) { 106 checkAtType(makeRandomTransform(n)); 107 } 108 } 109 if (numerrors != 0) { 110 if (!verbose) { 111 System.err.println("Rerun test with an argument for details"); 112 } 113 throw new RuntimeException(numerrors+" tests failed!"); 114 } 115 } 116 117 public static void checkBug4418285() { 118 AffineTransform id = 119 new AffineTransform (); 120 AffineTransform translate0 = 121 AffineTransform.getTranslateInstance (0, 0); 122 if (id.isIdentity() != translate0.isIdentity() || 123 id.getType() != translate0.getType()) 124 { 125 numerrors++; 126 if (verbose) { 127 System.err.println("id= " + id + 128 ", isIdentity()=" + 129 id.isIdentity()); 130 System.err.println("translate0=" + translate0 + 131 ", isIdentity()=" + 132 translate0.isIdentity()); 133 System.err.println("equals=" + id.equals (translate0)); 134 System.err.println(); 135 } 136 } 137 } 138 139 public static AffineTransform makeRandomTransform(int numops) { 140 AffineTransform at = new AffineTransform(); 141 while (--numops >= 0) { 142 switch (rand.nextInt(4)) { 143 case 0: 144 at.scale(rand.nextDouble() * 5 - 2.5, 145 rand.nextDouble() * 5 - 2.5); 146 break; 147 case 1: 148 at.shear(rand.nextDouble() * 5 - 2.5, 149 rand.nextDouble() * 5 - 2.5); 150 break; 151 case 2: 152 at.rotate(rand.nextDouble() * Math.PI * 2); 153 break; 154 case 3: 155 at.translate(rand.nextDouble() * 50 - 25, 156 rand.nextDouble() * 50 - 25); 157 break; 158 default: 159 throw new InternalError("bad case!"); 160 } 161 } 162 return at; 163 } 164 165 public static void checkAtType(AffineTransform at) { 166 int reftype = getRefType(at); 167 boolean isident = isIdentity(at); 168 for (int i = 0; i < 5; i++) { 169 boolean atisident = at.isIdentity(); 170 int attype = at.getType(); 171 if (isident != atisident || reftype != attype) { 172 numerrors++; 173 if (verbose) { 174 System.err.println(at+".isIdentity() == "+atisident); 175 System.err.println(at+".getType() == "+attype); 176 System.err.println("should be "+isident+", "+reftype); 177 new Throwable().printStackTrace(); 178 System.err.println(); 179 } 180 break; 181 } 182 } 183 } 184 185 public static boolean isIdentity(AffineTransform at) { 186 return (at.getScaleX() == 1 && 187 at.getScaleY() == 1 && 188 at.getShearX() == 0 && 189 at.getShearY() == 0 && 190 at.getTranslateX() == 0 && 191 at.getTranslateY() == 0); 192 193 } 194 195 public static int getRefType(AffineTransform at) { 196 double m00 = at.getScaleX(); 197 double m11 = at.getScaleY(); 198 double m01 = at.getShearX(); 199 double m10 = at.getShearY(); 200 if (m00 * m01 + m10 * m11 != 0) { 201 // Transformed unit vectors are not perpendicular... 202 return TYPE_GENERAL_TRANSFORM; 203 } 204 int type = ((at.getTranslateX() != 0 || at.getTranslateY() != 0) 205 ? TYPE_TRANSLATION : TYPE_IDENTITY); 206 boolean sgn0, sgn1; 207 if (m01 == 0 && m10 == 0) { 208 sgn0 = (m00 >= 0.0); 209 sgn1 = (m11 >= 0.0); 210 if (sgn0 == sgn1) { 211 if (sgn0) { 212 // Both scaling factors non-negative - simple scale 213 if (m00 != m11) { 214 type |= TYPE_GENERAL_SCALE; 215 } else if (m00 != 1.0) { 216 type |= TYPE_UNIFORM_SCALE; 217 } 218 } else { 219 // Both scaling factors negative - 180 degree rotation 220 type |= TYPE_QUADRANT_ROTATION; 221 if (m00 != m11) { 222 type |= TYPE_GENERAL_SCALE; 223 } else if (m00 != -1.0) { 224 type |= TYPE_UNIFORM_SCALE; 225 } 226 } 227 } else { 228 // Scaling factor signs different - flip about some axis 229 type |= TYPE_FLIP; 230 if (m00 != -m11) { 231 type |= TYPE_GENERAL_SCALE; 232 } else if (m00 != 1.0 && m00 != -1.0) { 233 type |= TYPE_UNIFORM_SCALE; 234 } 235 } 236 } else if (m00 == 0 && m11 == 0) { 237 sgn0 = (m01 >= 0.0); 238 sgn1 = (m10 >= 0.0); 239 if (sgn0 != sgn1) { 240 // Different signs - simple 90 degree rotation 241 if (m01 != -m10) { 242 type |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); 243 } else if (m01 != 1.0 && m01 != -1.0) { 244 type |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE); 245 } else { 246 type |= TYPE_QUADRANT_ROTATION; 247 } 248 } else { 249 // Same signs - 90 degree rotation plus an axis flip too 250 if (m01 == m10) { 251 if (m01 == 0) { 252 // All four m[01][01] elements are 0 253 type |= TYPE_UNIFORM_SCALE; 254 } else { 255 // Note - shouldn't (1,1) be no scale at all? 256 type |= (TYPE_QUADRANT_ROTATION | 257 TYPE_FLIP | 258 TYPE_UNIFORM_SCALE); 259 } 260 } else { 261 type |= (TYPE_QUADRANT_ROTATION | 262 TYPE_FLIP | 263 TYPE_GENERAL_SCALE); 264 } 265 } 266 } else { 267 if (m00 * m11 >= 0.0) { 268 // sgn(m00) == sgn(m11) therefore sgn(m01) == -sgn(m10) 269 // This is the "unflipped" (right-handed) state 270 if (m00 != m11 || m01 != -m10) { 271 type |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE); 272 } else if (m00 == 0) { 273 // then m11 == 0 also 274 if (m01 == -m10) { 275 type |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE); 276 } else { 277 type |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); 278 } 279 } else if (m00 * m11 - m01 * m10 != 1.0) { 280 type |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE); 281 } else { 282 type |= TYPE_GENERAL_ROTATION; 283 } 284 } else { 285 // sgn(m00) == -sgn(m11) therefore sgn(m01) == sgn(m10) 286 // This is the "flipped" (left-handed) state 287 if (m00 != -m11 || m01 != m10) { 288 type |= (TYPE_GENERAL_ROTATION | 289 TYPE_FLIP | 290 TYPE_GENERAL_SCALE); 291 } else if (m01 == 0) { 292 if (m00 == 1.0 || m00 == -1.0) { 293 type |= TYPE_FLIP; 294 } else { 295 type |= (TYPE_FLIP | TYPE_UNIFORM_SCALE); 296 } 297 } else if (m00 * m11 - m01 * m10 != 1.0) { 298 type |= (TYPE_GENERAL_ROTATION | 299 TYPE_FLIP | 300 TYPE_UNIFORM_SCALE); 301 } else { 302 type |= (TYPE_GENERAL_ROTATION | TYPE_FLIP); 303 } 304 } 305 } 306 return type; 307 } 308 }