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 }