/* * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /** * @test * @bug 4418285 * @summary Tests that transforms modified with degenerate operations * continue to return their more optimal type from getType(). * This test also confirms that isIdentity() returns the * optimal value under all histories of modification. * @run main GetTypeOptimization */ import java.awt.geom.AffineTransform; import java.util.Random; public class GetTypeOptimization { static int TYPE_IDENTITY = AffineTransform.TYPE_IDENTITY; static int TYPE_TRANSLATION = AffineTransform.TYPE_TRANSLATION; static int TYPE_UNIFORM_SCALE = AffineTransform.TYPE_UNIFORM_SCALE; static int TYPE_GENERAL_SCALE = AffineTransform.TYPE_GENERAL_SCALE; static int TYPE_FLIP = AffineTransform.TYPE_FLIP; static int TYPE_QUADRANT_ROTATION = AffineTransform.TYPE_QUADRANT_ROTATION; static int TYPE_GENERAL_ROTATION = AffineTransform.TYPE_GENERAL_ROTATION; static int TYPE_GENERAL_TRANSFORM = AffineTransform.TYPE_GENERAL_TRANSFORM; public static Random rand = new Random(); public static boolean verbose; public static int numerrors; public static void main(String argv[]) { verbose = (argv.length != 0); checkBug4418285(); checkAtType(new AffineTransform()); checkAtType(AffineTransform.getTranslateInstance(0, 0)); checkAtType(AffineTransform.getScaleInstance(1, 1)); checkAtType(AffineTransform.getShearInstance(0, 0)); checkAtType(AffineTransform.getRotateInstance(0)); checkAtType(AffineTransform.getRotateInstance(0, 0, 0)); for (int i = 90; i <= 360; i += 90) { double angle = Math.toRadians(i); checkAtType(AffineTransform.getRotateInstance(angle)); checkAtType(AffineTransform.getRotateInstance(angle, 0, 0)); } AffineTransform at = new AffineTransform(); checkAtType(at); at.setToIdentity(); checkAtType(at); at.setToTranslation(0.0, 0.0); checkAtType(at); at.setToScale(1.0, 1.0); checkAtType(at); at.setToShear(0.0, 0.0); checkAtType(at); at.setToRotation(0); checkAtType(at); at.setToRotation(0, 0, 0); checkAtType(at); for (int i = 90; i <= 360; i += 90) { double angle = Math.toRadians(i); at.setToRotation(angle); checkAtType(at); at.setToRotation(angle, 0, 0); checkAtType(at); } at.setToIdentity(); at.scale(1, 1); checkAtType(at); at.setToIdentity(); at.translate(0, 0); checkAtType(at); at.setToIdentity(); at.shear(0, 0); checkAtType(at); at.setToIdentity(); at.rotate(0); checkAtType(at); for (int i = 90; i <= 360; i += 90) { double angle = Math.toRadians(i); at.setToIdentity(); at.rotate(angle); checkAtType(at); at.setToIdentity(); at.rotate(angle, 0, 0); checkAtType(at); } at.setToIdentity(); for (int i = 0; i < 4; i++) { at.rotate(Math.toRadians(90)); checkAtType(at); } at.setToIdentity(); at.scale(2, 2); checkAtType(at); at.scale(.5, .5); checkAtType(at); for (int n = 1; n <= 3; n++) { for (int i = 0; i < 500; i++) { checkAtType(makeRandomTransform(n)); } } if (numerrors != 0) { if (!verbose) { System.err.println("Rerun test with an argument for details"); } throw new RuntimeException(numerrors+" tests failed!"); } } public static void checkBug4418285() { AffineTransform id = new AffineTransform (); AffineTransform translate0 = AffineTransform.getTranslateInstance (0, 0); if (id.isIdentity() != translate0.isIdentity() || id.getType() != translate0.getType()) { numerrors++; if (verbose) { System.err.println("id= " + id + ", isIdentity()=" + id.isIdentity()); System.err.println("translate0=" + translate0 + ", isIdentity()=" + translate0.isIdentity()); System.err.println("equals=" + id.equals (translate0)); System.err.println(); } } } public static AffineTransform makeRandomTransform(int numops) { AffineTransform at = new AffineTransform(); while (--numops >= 0) { switch (rand.nextInt(4)) { case 0: at.scale(rand.nextDouble() * 5 - 2.5, rand.nextDouble() * 5 - 2.5); break; case 1: at.shear(rand.nextDouble() * 5 - 2.5, rand.nextDouble() * 5 - 2.5); break; case 2: at.rotate(rand.nextDouble() * Math.PI * 2); break; case 3: at.translate(rand.nextDouble() * 50 - 25, rand.nextDouble() * 50 - 25); break; default: throw new InternalError("bad case!"); } } return at; } public static void checkAtType(AffineTransform at) { int reftype = getRefType(at); boolean isident = isIdentity(at); for (int i = 0; i < 5; i++) { boolean atisident = at.isIdentity(); int attype = at.getType(); if (isident != atisident || reftype != attype) { numerrors++; if (verbose) { System.err.println(at+".isIdentity() == "+atisident); System.err.println(at+".getType() == "+attype); System.err.println("should be "+isident+", "+reftype); new Throwable().printStackTrace(); System.err.println(); } break; } } } public static boolean isIdentity(AffineTransform at) { return (at.getScaleX() == 1 && at.getScaleY() == 1 && at.getShearX() == 0 && at.getShearY() == 0 && at.getTranslateX() == 0 && at.getTranslateY() == 0); } public static int getRefType(AffineTransform at) { double m00 = at.getScaleX(); double m11 = at.getScaleY(); double m01 = at.getShearX(); double m10 = at.getShearY(); if (m00 * m01 + m10 * m11 != 0) { // Transformed unit vectors are not perpendicular... return TYPE_GENERAL_TRANSFORM; } int type = ((at.getTranslateX() != 0 || at.getTranslateY() != 0) ? TYPE_TRANSLATION : TYPE_IDENTITY); boolean sgn0, sgn1; if (m01 == 0 && m10 == 0) { sgn0 = (m00 >= 0.0); sgn1 = (m11 >= 0.0); if (sgn0 == sgn1) { if (sgn0) { // Both scaling factors non-negative - simple scale if (m00 != m11) { type |= TYPE_GENERAL_SCALE; } else if (m00 != 1.0) { type |= TYPE_UNIFORM_SCALE; } } else { // Both scaling factors negative - 180 degree rotation type |= TYPE_QUADRANT_ROTATION; if (m00 != m11) { type |= TYPE_GENERAL_SCALE; } else if (m00 != -1.0) { type |= TYPE_UNIFORM_SCALE; } } } else { // Scaling factor signs different - flip about some axis type |= TYPE_FLIP; if (m00 != -m11) { type |= TYPE_GENERAL_SCALE; } else if (m00 != 1.0 && m00 != -1.0) { type |= TYPE_UNIFORM_SCALE; } } } else if (m00 == 0 && m11 == 0) { sgn0 = (m01 >= 0.0); sgn1 = (m10 >= 0.0); if (sgn0 != sgn1) { // Different signs - simple 90 degree rotation if (m01 != -m10) { type |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); } else if (m01 != 1.0 && m01 != -1.0) { type |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE); } else { type |= TYPE_QUADRANT_ROTATION; } } else { // Same signs - 90 degree rotation plus an axis flip too if (m01 == m10) { if (m01 == 0) { // All four m[01][01] elements are 0 type |= TYPE_UNIFORM_SCALE; } else { // Note - shouldn't (1,1) be no scale at all? type |= (TYPE_QUADRANT_ROTATION | TYPE_FLIP | TYPE_UNIFORM_SCALE); } } else { type |= (TYPE_QUADRANT_ROTATION | TYPE_FLIP | TYPE_GENERAL_SCALE); } } } else { if (m00 * m11 >= 0.0) { // sgn(m00) == sgn(m11) therefore sgn(m01) == -sgn(m10) // This is the "unflipped" (right-handed) state if (m00 != m11 || m01 != -m10) { type |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE); } else if (m00 == 0) { // then m11 == 0 also if (m01 == -m10) { type |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE); } else { type |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); } } else if (m00 * m11 - m01 * m10 != 1.0) { type |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE); } else { type |= TYPE_GENERAL_ROTATION; } } else { // sgn(m00) == -sgn(m11) therefore sgn(m01) == sgn(m10) // This is the "flipped" (left-handed) state if (m00 != -m11 || m01 != m10) { type |= (TYPE_GENERAL_ROTATION | TYPE_FLIP | TYPE_GENERAL_SCALE); } else if (m01 == 0) { if (m00 == 1.0 || m00 == -1.0) { type |= TYPE_FLIP; } else { type |= (TYPE_FLIP | TYPE_UNIFORM_SCALE); } } else if (m00 * m11 - m01 * m10 != 1.0) { type |= (TYPE_GENERAL_ROTATION | TYPE_FLIP | TYPE_UNIFORM_SCALE); } else { type |= (TYPE_GENERAL_ROTATION | TYPE_FLIP); } } } return type; } }