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