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