jdk/test/java/awt/geom/AffineTransform/TestRotateMethods.java
changeset 27507 6e3bc02ae872
equal deleted inserted replaced
27506:41ccac32e143 27507:6e3bc02ae872
       
     1 /*
       
     2  * Copyright (c) 2004, 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 4980035
       
    27  * @summary Unit test for new methods:
       
    28  *
       
    29  *          AffineTransform.getRotateInstance(double x, double y);
       
    30  *          AffineTransform.setToRotation(double x, double y);
       
    31  *          AffineTransform.rotate(double x, double y);
       
    32  *
       
    33  *          AffineTransform.getQuadrantRotateInstance(int numquads);
       
    34  *          AffineTransform.setToQuadrantRotation(int numquads);
       
    35  *          AffineTransform.quadrantRotate(int numquads);
       
    36  *
       
    37  * @author flar
       
    38  * @run main TestRotateMethods
       
    39  */
       
    40 
       
    41 import java.awt.geom.AffineTransform;
       
    42 import java.awt.geom.Point2D;
       
    43 
       
    44 public class TestRotateMethods {
       
    45     /* The maximum errors allowed, measured in double precision "ulps"
       
    46      * Note that for most fields, the tests are extremely accurate - to
       
    47      * within 3 ulps of the smaller value in the comparison
       
    48      * For the translation components, the tests are still very accurate,
       
    49      * but the absolute number of ulps can be noticeably higher when we
       
    50      * use one of the rotate methods that takes an anchor point.
       
    51      * Since a double precision value has 56 bits of precision, even
       
    52      * 1024 ulps is extremely small as a ratio of the value.
       
    53      */
       
    54     public static final double MAX_ULPS = 3.0;
       
    55     public static final double MAX_ANCHOR_TX_ULPS = 1024.0;
       
    56     public static double MAX_TX_ULPS = MAX_ULPS;
       
    57 
       
    58     // Vectors for quadrant rotations
       
    59     public static final double quadxvec[] = {  1.0,  0.0, -1.0,  0.0 };
       
    60     public static final double quadyvec[] = {  0.0,  1.0,  0.0, -1.0 };
       
    61 
       
    62     // Run tests once for each type of method:
       
    63     //     tx = AffineTransform.get<Rotate>Instance()
       
    64     //     tx.set<Rotate>()
       
    65     //     tx.<rotate>()
       
    66     public static enum Mode { GET, SET, MOD };
       
    67 
       
    68     // Used to accumulate and report largest differences encountered by tests
       
    69     public static double maxulps = 0.0;
       
    70     public static double maxtxulps = 0.0;
       
    71 
       
    72     // Sample anchor points for testing.
       
    73     public static Point2D zeropt = new Point2D.Double(0, 0);
       
    74     public static Point2D testtxpts[] = {
       
    75         new Point2D.Double(       5,      5),
       
    76         new Point2D.Double(      20,    -10),
       
    77         new Point2D.Double(-Math.PI, Math.E),
       
    78     };
       
    79 
       
    80     public static void main(String argv[]) {
       
    81         test(Mode.GET);
       
    82         test(Mode.SET);
       
    83         test(Mode.MOD);
       
    84 
       
    85         System.out.println("Max scale and shear difference: "+maxulps+" ulps");
       
    86         System.out.println("Max translate difference: "+maxtxulps+" ulps");
       
    87     }
       
    88 
       
    89     public static void test(Mode mode) {
       
    90         MAX_TX_ULPS = MAX_ULPS; // Stricter tx testing with no anchor point
       
    91         test(mode, 0.5, null);
       
    92         test(mode, 1.0, null);
       
    93         test(mode, 3.0, null);
       
    94 
       
    95         // Anchor points make the tx values less reliable
       
    96         MAX_TX_ULPS = MAX_ANCHOR_TX_ULPS;
       
    97         for (int i = 0; i < testtxpts.length; i++) {
       
    98             test(mode, 1.0, testtxpts[i]);
       
    99         }
       
   100         MAX_TX_ULPS = MAX_ULPS; // Restore to default
       
   101     }
       
   102 
       
   103     public static void verify(AffineTransform at1, AffineTransform at2,
       
   104                               Mode mode, double vectorscale, Point2D txpt,
       
   105                               String message, double num, String units)
       
   106     {
       
   107         if (!compare(at1, at2)) {
       
   108             System.out.println("mode == "+mode);
       
   109             System.out.println("vectorscale == "+vectorscale);
       
   110             System.out.println("txpt == "+txpt);
       
   111             System.out.println(at1+", type = "+at1.getType());
       
   112             System.out.println(at2+", type = "+at2.getType());
       
   113             System.out.println("ScaleX values differ by "+
       
   114                                ulps(at1.getScaleX(), at2.getScaleX())+" ulps");
       
   115             System.out.println("ScaleY values differ by "+
       
   116                                ulps(at1.getScaleY(), at2.getScaleY())+" ulps");
       
   117             System.out.println("ShearX values differ by "+
       
   118                                ulps(at1.getShearX(), at2.getShearX())+" ulps");
       
   119             System.out.println("ShearY values differ by "+
       
   120                                ulps(at1.getShearY(), at2.getShearY())+" ulps");
       
   121             System.out.println("TranslateX values differ by "+
       
   122                                ulps(at1.getTranslateX(),
       
   123                                     at2.getTranslateX())+" ulps");
       
   124             System.out.println("TranslateY values differ by "+
       
   125                                ulps(at1.getTranslateY(),
       
   126                                     at2.getTranslateY())+" ulps");
       
   127             throw new RuntimeException(message + num + units);
       
   128         }
       
   129     }
       
   130 
       
   131     public static void test(Mode mode, double vectorscale, Point2D txpt) {
       
   132         AffineTransform at1, at2, at3;
       
   133 
       
   134         for (int deg = -720; deg <= 720; deg++) {
       
   135             if ((deg % 90) == 0) continue;
       
   136             double radians = Math.toRadians(deg);
       
   137             double vecy = Math.sin(radians) * vectorscale;
       
   138             double vecx = Math.cos(radians) * vectorscale;
       
   139 
       
   140             at1 = makeAT(mode, txpt, radians);
       
   141             at2 = makeAT(mode, txpt, vecx, vecy);
       
   142             verify(at1, at2, mode, vectorscale, txpt,
       
   143                    "vector and radians do not match for ", deg, " degrees");
       
   144 
       
   145             if (txpt == null) {
       
   146                 // Make sure output was same as a with a 0,0 anchor point
       
   147                 if (vectorscale == 1.0) {
       
   148                     // Only need to test radians method for one scale factor
       
   149                     at3 = makeAT(mode, zeropt, radians);
       
   150                     verify(at1, at3, mode, vectorscale, zeropt,
       
   151                            "radians not invariant with 0,0 translate at ",
       
   152                            deg, " degrees");
       
   153                 }
       
   154                 // But test vector methods with all scale factors
       
   155                 at3 = makeAT(mode, zeropt, vecx, vecy);
       
   156                 verify(at2, at3, mode, vectorscale, zeropt,
       
   157                        "vector not invariant with 0,0 translate at ",
       
   158                        deg, " degrees");
       
   159             }
       
   160         }
       
   161 
       
   162         for (int quad = -8; quad <= 8; quad++) {
       
   163             double degrees = quad * 90.0;
       
   164             double radians = Math.toRadians(degrees);
       
   165             double vecx = quadxvec[quad & 3] * vectorscale;
       
   166             double vecy = quadyvec[quad & 3] * vectorscale;
       
   167 
       
   168             at1 = makeAT(mode, txpt, radians);
       
   169             at2 = makeAT(mode, txpt, vecx, vecy);
       
   170             verify(at1, at2, mode, vectorscale, txpt,
       
   171                    "quadrant vector and radians do not match for ",
       
   172                    degrees, " degrees");
       
   173             at2 = makeQuadAT(mode, txpt, quad);
       
   174             verify(at1, at2, mode, vectorscale, txpt,
       
   175                    "quadrant and radians do not match for ",
       
   176                    quad, " quadrants");
       
   177             if (txpt == null) {
       
   178                 at3 = makeQuadAT(mode, zeropt, quad);
       
   179                 verify(at2, at3, mode, vectorscale, zeropt,
       
   180                        "quadrant not invariant with 0,0 translate at ",
       
   181                        quad, " quadrants");
       
   182             }
       
   183         }
       
   184     }
       
   185 
       
   186     public static AffineTransform makeRandomAT() {
       
   187         AffineTransform at = new AffineTransform();
       
   188         at.scale(Math.random() * -10.0, Math.random() * 100.0);
       
   189         at.rotate(Math.random() * Math.PI);
       
   190         at.shear(Math.random(), Math.random());
       
   191         at.translate(Math.random() * 300.0, Math.random() * -20.0);
       
   192         return at;
       
   193     }
       
   194 
       
   195     public static AffineTransform makeAT(Mode mode, Point2D txpt,
       
   196                                          double radians)
       
   197     {
       
   198         AffineTransform at;
       
   199         double tx = (txpt == null) ? 0.0 : txpt.getX();
       
   200         double ty = (txpt == null) ? 0.0 : txpt.getY();
       
   201         switch (mode) {
       
   202         case GET:
       
   203             if (txpt != null) {
       
   204                 at = AffineTransform.getRotateInstance(radians, tx, ty);
       
   205             } else {
       
   206                 at = AffineTransform.getRotateInstance(radians);
       
   207             }
       
   208             break;
       
   209         case SET:
       
   210             at = makeRandomAT();
       
   211             if (txpt != null) {
       
   212                 at.setToRotation(radians, tx, ty);
       
   213             } else {
       
   214                 at.setToRotation(radians);
       
   215             }
       
   216             break;
       
   217         case MOD:
       
   218             at = makeRandomAT();
       
   219             at.setToIdentity();
       
   220             if (txpt != null) {
       
   221                 at.rotate(radians, tx, ty);
       
   222             } else {
       
   223                 at.rotate(radians);
       
   224             }
       
   225             break;
       
   226         default:
       
   227             throw new InternalError("unrecognized mode: "+mode);
       
   228         }
       
   229 
       
   230         return at;
       
   231     }
       
   232 
       
   233     public static AffineTransform makeAT(Mode mode, Point2D txpt,
       
   234                                          double vx, double vy)
       
   235     {
       
   236         AffineTransform at;
       
   237         double tx = (txpt == null) ? 0.0 : txpt.getX();
       
   238         double ty = (txpt == null) ? 0.0 : txpt.getY();
       
   239         switch (mode) {
       
   240         case GET:
       
   241             if (txpt != null) {
       
   242                 at = AffineTransform.getRotateInstance(vx, vy, tx, ty);
       
   243             } else {
       
   244                 at = AffineTransform.getRotateInstance(vx, vy);
       
   245             }
       
   246             break;
       
   247         case SET:
       
   248             at = makeRandomAT();
       
   249             if (txpt != null) {
       
   250                 at.setToRotation(vx, vy, tx, ty);
       
   251             } else {
       
   252                 at.setToRotation(vx, vy);
       
   253             }
       
   254             break;
       
   255         case MOD:
       
   256             at = makeRandomAT();
       
   257             at.setToIdentity();
       
   258             if (txpt != null) {
       
   259                 at.rotate(vx, vy, tx, ty);
       
   260             } else {
       
   261                 at.rotate(vx, vy);
       
   262             }
       
   263             break;
       
   264         default:
       
   265             throw new InternalError("unrecognized mode: "+mode);
       
   266         }
       
   267 
       
   268         return at;
       
   269     }
       
   270 
       
   271     public static AffineTransform makeQuadAT(Mode mode, Point2D txpt,
       
   272                                              int quads)
       
   273     {
       
   274         AffineTransform at;
       
   275         double tx = (txpt == null) ? 0.0 : txpt.getX();
       
   276         double ty = (txpt == null) ? 0.0 : txpt.getY();
       
   277         switch (mode) {
       
   278         case GET:
       
   279             if (txpt != null) {
       
   280                 at = AffineTransform.getQuadrantRotateInstance(quads, tx, ty);
       
   281             } else {
       
   282                 at = AffineTransform.getQuadrantRotateInstance(quads);
       
   283             }
       
   284             break;
       
   285         case SET:
       
   286             at = makeRandomAT();
       
   287             if (txpt != null) {
       
   288                 at.setToQuadrantRotation(quads, tx, ty);
       
   289             } else {
       
   290                 at.setToQuadrantRotation(quads);
       
   291             }
       
   292             break;
       
   293         case MOD:
       
   294             at = makeRandomAT();
       
   295             at.setToIdentity();
       
   296             if (txpt != null) {
       
   297                 at.quadrantRotate(quads, tx, ty);
       
   298             } else {
       
   299                 at.quadrantRotate(quads);
       
   300             }
       
   301             break;
       
   302         default:
       
   303             throw new InternalError("unrecognized mode: "+mode);
       
   304         }
       
   305 
       
   306         return at;
       
   307     }
       
   308 
       
   309     public static boolean compare(AffineTransform at1, AffineTransform at2) {
       
   310         maxulps = Math.max(maxulps, ulps(at1.getScaleX(), at2.getScaleX()));
       
   311         maxulps = Math.max(maxulps, ulps(at1.getScaleY(), at2.getScaleY()));
       
   312         maxulps = Math.max(maxulps, ulps(at1.getShearX(), at2.getShearX()));
       
   313         maxulps = Math.max(maxulps, ulps(at1.getShearY(), at2.getShearY()));
       
   314         maxtxulps = Math.max(maxtxulps,
       
   315                              ulps(at1.getTranslateX(), at2.getTranslateX()));
       
   316         maxtxulps = Math.max(maxtxulps,
       
   317                              ulps(at1.getTranslateY(), at2.getTranslateY()));
       
   318         return (getModifiedType(at1) == getModifiedType(at2) &&
       
   319                 (compare(at1.getScaleX(), at2.getScaleX(), MAX_ULPS)) &&
       
   320                 (compare(at1.getScaleY(), at2.getScaleY(), MAX_ULPS)) &&
       
   321                 (compare(at1.getShearX(), at2.getShearX(), MAX_ULPS)) &&
       
   322                 (compare(at1.getShearY(), at2.getShearY(), MAX_ULPS)) &&
       
   323                 (compare(at1.getTranslateX(),
       
   324                          at2.getTranslateX(), MAX_TX_ULPS)) &&
       
   325                 (compare(at1.getTranslateY(),
       
   326                          at2.getTranslateY(), MAX_TX_ULPS)));
       
   327     }
       
   328 
       
   329     public static int getModifiedType(AffineTransform at) {
       
   330         int type = at.getType();
       
   331         // Some of the vector methods can introduce a tiny uniform scale
       
   332         // at some angles...
       
   333         if ((type & AffineTransform.TYPE_UNIFORM_SCALE) != 0) {
       
   334             maxulps = Math.max(maxulps, ulps(at.getDeterminant(), 1.0));
       
   335             if (ulps(at.getDeterminant(), 1.0) <= MAX_ULPS) {
       
   336                 // Really tiny - we will ignore it
       
   337                 type &= (~AffineTransform.TYPE_UNIFORM_SCALE);
       
   338             }
       
   339         }
       
   340         return type;
       
   341     }
       
   342 
       
   343     public static boolean compare(double val1, double val2, double maxulps) {
       
   344         return (ulps(val1, val2) <= maxulps);
       
   345     }
       
   346 
       
   347     public static double ulps(double val1, double val2) {
       
   348         double diff = Math.abs(val1 - val2);
       
   349         double ulpmax = Math.min(Math.ulp(val1), Math.ulp(val2));
       
   350         return (diff / ulpmax);
       
   351     }
       
   352 }