Java API By Example, From Geeks To Geeks.

# Java > Open Source Codes > java > awt > geom > AffineTransform

 `1 /*2  * @(#)AffineTransform.java 1.71 03/12/193  *4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.6  */7 8 package java.awt.geom;9 10 import java.awt.Shape ;11 12 /**13  * The AffineTransform class represents a 2D affine transform14  * that performs a linear mapping from 2D coordinates to other 2D15  * coordinates that preserves the "straightness" and16  * "parallelness" of lines. Affine transformations can be constructed17  * using sequences of translations, scales, flips, rotations, and shears.18  *

19  * Such a coordinate transformation can be represented by a 3 row by20  * 3 column matrix with an implied last row of [ 0 0 1 ]. This matrix 21  * transforms source coordinates (x, y) into22  * destination coordinates (x', y') by considering23  * them to be a column vector and multiplying the coordinate vector24  * by the matrix according to the following process:25  *

26   *  [ x']   [  m00  m01  m02  ] [ x ]   [ m00x + m01y + m02 ]27   *  [ y'] = [  m10  m11  m12  ] [ y ] = [ m10x + m11y + m12 ]28   *  [ 1 ]   [   0    0    1   ] [ 1 ]   [         1         ]29   *
495      *      [   1    0    tx  ]496      *      [   0    1    ty  ]497      *      [   0    0    1   ]498      *
499      * @param tx the distance by which coordinates are translated in the500      * X axis direction501      * @param ty the distance by which coordinates are translated in the502      * Y axis direction503      * @return an AffineTransform object that represents a504      * translation transformation, created with the specified vector.505      */506     public static AffineTransform getTranslateInstance(double tx, double ty) {507     AffineTransform Tx = new AffineTransform ();508     Tx.setToTranslation(tx, ty);509     return Tx;510     }511 512     /**513      * Returns a transform representing a rotation transformation.514      * The matrix representing the returned transform is:515      *
516      *      [   cos(theta)    -sin(theta)    0   ]517      *      [   sin(theta)     cos(theta)    0   ]518      *      [       0              0         1   ]519      *
520      * Rotating with a positive angle theta rotates points on the positive521      * x axis toward the positive y axis.522      * @param theta the angle of rotation in radians523      * @return an AffineTransform object that is a rotation524      * transformation, created with the specified angle of rotation.525      */526     public static AffineTransform getRotateInstance(double theta) {527     AffineTransform Tx = new AffineTransform ();528     Tx.setToRotation(theta);529     return Tx;530     }531 532     /**533      * Returns a transform that rotates coordinates around an anchor point.534      * This operation is equivalent to translating the coordinates so535      * that the anchor point is at the origin (S1), then rotating them536      * about the new origin (S2), and finally translating so that the537      * intermediate origin is restored to the coordinates of the original538      * anchor point (S3).539      *

540      * This operation is equivalent to the following sequence of calls:541      *

542      *      AffineTransform Tx = new AffineTransform();543      *      Tx.setToTranslation(x, y);  // S3: final translation544      *      Tx.rotate(theta);       // S2: rotate around anchor545      *      Tx.translate(-x, -y);   // S1: translate anchor to origin546      *
547      * The matrix representing the returned transform is:548      *
549      *      [   cos(theta)    -sin(theta)    x-x*cos+y*sin  ]550      *      [   sin(theta)     cos(theta)    y-x*sin-y*cos  ]551      *      [       0              0               1        ]552      *
553      * Rotating with a positive angle theta rotates points on the positive554      * x axis toward the positive y axis.555      * @param theta the angle of rotation in radians556      * @param x, y the coordinates of the anchor point of the557      * rotation558      * @return an AffineTransform object that rotates 559      * coordinates around the specified point by the specified angle of560      * rotation.561      */562     public static AffineTransform getRotateInstance(double theta,563                             double x, double y) {564     AffineTransform Tx = new AffineTransform ();565     Tx.setToRotation(theta, x, y);566     return Tx;567     }568 569     /**570      * Returns a transform representing a scaling transformation.571      * The matrix representing the returned transform is:572      *
573      *      [   sx   0    0   ]574      *      [   0    sy   0   ]575      *      [   0    0    1   ]576      *
577      * @param sx the factor by which coordinates are scaled along the578      * X axis direction579      * @param sy the factor by which coordinates are scaled along the580      * Y axis direction581      * @return an AffineTransform object that scales 582      * coordinates by the specified factors.583      */584     public static AffineTransform getScaleInstance(double sx, double sy) {585     AffineTransform Tx = new AffineTransform ();586     Tx.setToScale(sx, sy);587     return Tx;588     }589 590     /**591      * Returns a transform representing a shearing transformation.592      * The matrix representing the returned transform is:593      *
594      *      [   1   shx   0   ]595      *      [  shy   1    0   ]596      *      [   0    0    1   ]597      *

773      * If the determinant is non-zero, then this transform is774      * invertible and the various methods that depend on the inverse775      * transform do not need to throw a776      * {@link NoninvertibleTransformException}.777      * If the determinant is zero then this transform can not be778      * inverted since the transform maps all input coordinates onto779      * a line or a point.780      * If the determinant is near enough to zero then inverse transform781      * operations might not carry enough precision to produce meaningful782      * results.783      *

784      * If this transform represents a uniform scale, as indicated by785      * the getType method then the determinant also786      * represents the square of the uniform scale factor by which all of787      * the points are expanded from or contracted towards the origin.788      * If this transform represents a non-uniform scale or more general789      * transform then the determinant is not likely to represent a790      * value useful for any purpose other than determining if inverse791      * transforms are possible.792      *

793      * Mathematically, the determinant is calculated using the formula:794      *

795      *      |  m00  m01  m02  |796      *      |  m10  m11  m12  |  =  m00 * m11 - m01 * m10797      *      |   0    0    1   |798      *
799      *800      * @return the determinant of the matrix used to transform the801      * coordinates.802      * @see #getType803      * @see #createInverse804      * @see #inverseTransform805      * @see #TYPE_UNIFORM_SCALE806      */807     public double getDeterminant() {808     switch (state) {809     default:810         stateError();811         /* NOTREACHED */812     case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):813     case (APPLY_SHEAR | APPLY_SCALE):814         return m00 * m11 - m01 * m10;815     case (APPLY_SHEAR | APPLY_TRANSLATE):816     case (APPLY_SHEAR):817         return -(m01 * m10);818     case (APPLY_SCALE | APPLY_TRANSLATE):819     case (APPLY_SCALE):820         return m00 * m11;821     case (APPLY_TRANSLATE):822     case (APPLY_IDENTITY):823         return 1.0;824     }825     }826 827     /**828      * Manually recalculates the state of the transform when the matrix829      * changes too much to predict the effects on the state.830      * The following table specifies what the various settings of the831      * state field say about the values of the corresponding matrix832      * element fields.833      * Note that the rules governing the SCALE fields are slightly834      * different depending on whether the SHEAR flag is also set.835      *
836      *                     SCALE            SHEAR          TRANSLATE837      *                    m00/m11          m01/m10          m02/m12838      *839      * IDENTITY             1.0              0.0              0.0840      * TRANSLATE (TR)       1.0              0.0          not both 0.0841      * SCALE (SC)       not both 1.0         0.0              0.0842      * TR | SC          not both 1.0         0.0          not both 0.0843      * SHEAR (SH)           0.0          not both 0.0         0.0844      * TR | SH              0.0          not both 0.0     not both 0.0845      * SC | SH          not both 0.0     not both 0.0         0.0846      * TR | SC | SH     not both 0.0     not both 0.0     not both 0.0847      *
848      */849     void updateState() {850     if (m01 == 0.0 && m10 == 0.0) {851         if (m00 == 1.0 && m11 == 1.0) {852         if (m02 == 0.0 && m12 == 0.0) {853             state = APPLY_IDENTITY;854             type = TYPE_IDENTITY;855         } else {856             state = APPLY_TRANSLATE;857             type = TYPE_TRANSLATION;858         }859         } else {860         if (m02 == 0.0 && m12 == 0.0) {861             state = APPLY_SCALE;862             type = TYPE_UNKNOWN;863         } else {864             state = (APPLY_SCALE | APPLY_TRANSLATE);865             type = TYPE_UNKNOWN;866         }867         }868     } else {869         if (m00 == 0.0 && m11 == 0.0) {870         if (m02 == 0.0 && m12 == 0.0) {871             state = APPLY_SHEAR;872             type = TYPE_UNKNOWN;873         } else {874             state = (APPLY_SHEAR | APPLY_TRANSLATE);875             type = TYPE_UNKNOWN;876         }877         } else {878         if (m02 == 0.0 && m12 == 0.0) {879             state = (APPLY_SHEAR | APPLY_SCALE);880             type = TYPE_UNKNOWN;881         } else {882             state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE);883             type = TYPE_UNKNOWN;884         }885         }886     }887     }888 889     /*890      * Convenience method used internally to throw exceptions when891      * a case was forgotten in a switch statement.892      */893     private void stateError() {894     throw new InternalError ("missing case in transform state switch");895     }896     897     /**898      * Retrieves the 6 specifiable values in the 3x3 affine transformation899      * matrix and places them into an array of double precisions values.900      * The values are stored in the array as 901      * { m00 m10 m01 m11 m02 m12 }.902      * An array of 4 doubles can also be specified, in which case only the903      * first four elements representing the non-transform904      * parts of the array are retrieved and the values are stored into 905      * the array as { m00 m10 m01 m11 }906      * @param flatmatrix the double array used to store the returned907      * values.908      * @see #getScaleX909      * @see #getScaleY910      * @see #getShearX911      * @see #getShearY912      * @see #getTranslateX913      * @see #getTranslateY914      */915     public void getMatrix(double[] flatmatrix) {916     flatmatrix[0] = m00;917     flatmatrix[1] = m10;918     flatmatrix[2] = m01;919     flatmatrix[3] = m11;920     if (flatmatrix.length > 5) {921         flatmatrix[4] = m02;922         flatmatrix[5] = m12;923     }924     }925 926     /**927      * Returns the X coordinate scaling element (m00) of the 3x3928      * affine transformation matrix.929      * @return a double value that is the X coordinate of the scaling930      * element of the affine transformation matrix.931      * @see #getMatrix932      */933     public double getScaleX() {934     return m00;935     }936 937     /**938      * Returns the Y coordinate scaling element (m11) of the 3x3939      * affine transformation matrix.940      * @return a double value that is the Y coordinate of the scaling941      * element of the affine transformation matrix.942      * @see #getMatrix943      */944     public double getScaleY() {945     return m11;946     }947 948     /**949      * Returns the X coordinate shearing element (m01) of the 3x3950      * affine transformation matrix.951      * @return a double value that is the X coordinate of the shearing952      * element of the affine transformation matrix.953      * @see #getMatrix954      */955     public double getShearX() {956     return m01;957     }958 959     /**960      * Returns the Y coordinate shearing element (m10) of the 3x3961      * affine transformation matrix.962      * @return a double value that is the Y coordinate of the shearing963      * element of the affine transformation matrix.964      * @see #getMatrix965      */966     public double getShearY() {967     return m10;968     }969 970     /**971      * Returns the X coordinate of the translation element (m02) of the972      * 3x3 affine transformation matrix.973      * @return a double value that is the X coordinate of the translation974      * element of the affine transformation matrix.975      * @see #getMatrix976      */977     public double getTranslateX() {978     return m02;979     }980 981     /**982      * Returns the Y coordinate of the translation element (m12) of the983      * 3x3 affine transformation matrix.984      * @return a double value that is the Y coordinate of the translation985      * element of the affine transformation matrix. 986      * @see #getMatrix987      */988     public double getTranslateY() {989     return m12;990     }991 992     /**993      * Concatenates this transform with a translation transformation.994      * This is equivalent to calling concatenate(T), where T is an995      * AffineTransform represented by the following matrix:996      *
997      *      [   1    0    tx  ]998      *      [   0    1    ty  ]999      *      [   0    0    1   ]1000     *
1001     * @param tx the distance by which coordinates are translated in the1002     * X axis direction1003     * @param ty the distance by which coordinates are translated in the1004     * Y axis direction1005     */1006    public void translate(double tx, double ty) {1007    switch (state) {1008    default:1009        stateError();1010        /* NOTREACHED */1011    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1012        m02 = tx * m00 + ty * m01 + m02;1013        m12 = tx * m10 + ty * m11 + m12;1014        if (m02 == 0.0 && m12 == 0.0) {1015        state = APPLY_SHEAR | APPLY_SCALE;1016        if (type != TYPE_UNKNOWN) {1017            type -= TYPE_TRANSLATION;1018        }1019        }1020        return;1021    case (APPLY_SHEAR | APPLY_SCALE):1022        m02 = tx * m00 + ty * m01;1023        m12 = tx * m10 + ty * m11;1024        if (m02 != 0.0 || m12 != 0.0) {1025        state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE;1026        type |= TYPE_TRANSLATION;1027        }1028        return;1029    case (APPLY_SHEAR | APPLY_TRANSLATE):1030        m02 = ty * m01 + m02;1031        m12 = tx * m10 + m12;1032        if (m02 == 0.0 && m12 == 0.0) {1033        state = APPLY_SHEAR;1034        if (type != TYPE_UNKNOWN) {1035            type -= TYPE_TRANSLATION;1036        }1037        }1038        return;1039    case (APPLY_SHEAR):1040        m02 = ty * m01;1041        m12 = tx * m10;1042        if (m02 != 0.0 || m12 != 0.0) {1043        state = APPLY_SHEAR | APPLY_TRANSLATE;1044        type |= TYPE_TRANSLATION;1045        }1046        return;1047    case (APPLY_SCALE | APPLY_TRANSLATE):1048        m02 = tx * m00 + m02;1049        m12 = ty * m11 + m12;1050        if (m02 == 0.0 && m12 == 0.0) {1051        state = APPLY_SCALE;1052        if (type != TYPE_UNKNOWN) {1053            type -= TYPE_TRANSLATION;1054        }1055        }1056        return;1057    case (APPLY_SCALE):1058        m02 = tx * m00;1059        m12 = ty * m11;1060        if (m02 != 0.0 || m12 != 0.0) {1061        state = APPLY_SCALE | APPLY_TRANSLATE;1062        type |= TYPE_TRANSLATION;1063        }1064        return;1065    case (APPLY_TRANSLATE):1066        m02 = tx + m02;1067        m12 = ty + m12;1068        if (m02 == 0.0 && m12 == 0.0) {1069        state = APPLY_IDENTITY;1070        type = TYPE_IDENTITY;1071        }1072        return;1073    case (APPLY_IDENTITY):1074        m02 = tx;1075        m12 = ty;1076        if (tx != 0.0 || ty != 0.0) {1077        state = APPLY_TRANSLATE;1078        type = TYPE_TRANSLATION;1079        }1080        return;1081    }1082    }10831084    /**1085     * Concatenates this transform with a rotation transformation.1086     * This is equivalent to calling concatenate(R), where R is an1087     * AffineTransform represented by the following matrix:1088     *
1089     *      [   cos(theta)    -sin(theta)    0   ]1090     *      [   sin(theta)     cos(theta)    0   ]1091     *      [       0              0         1   ]1092     *
1093     * Rotating with a positive angle theta rotates points on the positive1094     * x axis toward the positive y axis.1095     * @param theta the angle of rotation in radians1096     */1097    public void rotate(double theta) {1098    double sin = Math.sin(theta);1099    double cos = Math.cos(theta);1100    if (Math.abs(sin) < 1E-15) {1101        if (cos < 0.0) {1102        m00 = -m00;1103        m11 = -m11;1104        int state = this.state;1105        if ((state & (APPLY_SHEAR)) != 0) {1106            // If there was a shear, then this rotation has no1107 // effect on the state.1108 m01 = -m01;1109            m10 = -m10;1110        } else {1111            // No shear means the SCALE state may toggle when1112 // m00 and m11 are negated.1113 if (m00 == 1.0 && m11 == 1.0) {1114            this.state = state & ~APPLY_SCALE;1115            } else {1116            this.state = state | APPLY_SCALE;1117            }1118        }1119        type = TYPE_UNKNOWN;1120        }1121        return;1122    }1123    if (Math.abs(cos) < 1E-15) {1124        if (sin < 0.0) {1125        double M0 = m00;1126        m00 = -m01;1127        m01 = M0;1128        M0 = m10;1129        m10 = -m11;1130        m11 = M0;1131        } else {1132        double M0 = m00;1133        m00 = m01;1134        m01 = -M0;1135        M0 = m10;1136        m10 = m11;1137        m11 = -M0;1138        }1139        int state = rot90conversion[this.state];1140        if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE &&1141        m00 == 1.0 && m11 == 1.0)1142        {1143        state -= APPLY_SCALE;1144        }1145        this.state = state;1146        type = TYPE_UNKNOWN;1147        return;1148    }1149    double M0, M1;1150    M0 = m00;1151    M1 = m01;1152    m00 = cos * M0 + sin * M1;1153    m01 = -sin * M0 + cos * M1;1154    M0 = m10;1155    M1 = m11;1156    m10 = cos * M0 + sin * M1;1157    m11 = -sin * M0 + cos * M1;1158    updateState();1159    }1160    private static int rot90conversion[] = {1161    APPLY_SHEAR, APPLY_SHEAR | APPLY_TRANSLATE,1162    APPLY_SHEAR, APPLY_SHEAR | APPLY_TRANSLATE,1163    APPLY_SCALE, APPLY_SCALE | APPLY_TRANSLATE,1164    APPLY_SHEAR | APPLY_SCALE,1165    APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE,1166    };11671168    /**1169     * Concatenates this transform with a transform that rotates1170     * coordinates around an anchor point.1171     * This operation is equivalent to translating the coordinates so1172     * that the anchor point is at the origin (S1), then rotating them1173     * about the new origin (S2), and finally translating so that the1174     * intermediate origin is restored to the coordinates of the original1175     * anchor point (S3).1176     *

1177     * This operation is equivalent to the following sequence of calls:1178     *

1179     *      translate(x, y);    // S3: final translation1180     *      rotate(theta);      // S2: rotate around anchor1181     *      translate(-x, -y);  // S1: translate anchor to origin1182     *
1183     * Rotating with a positive angle theta rotates points on the positive1184     * x axis toward the positive y axis.1185     * @param theta the angle of rotation in radians1186     * @param x, y the coordinates of the anchor point of the1187     * rotation1188     */1189    public void rotate(double theta, double x, double y) {1190    // REMIND: Simple for now - optimize later1191 translate(x, y);1192    rotate(theta);1193    translate(-x, -y);1194    }11951196    /**1197     * Concatenates this transform with a scaling transformation.1198     * This is equivalent to calling concatenate(S), where S is an1199     * AffineTransform represented by the following matrix:1200     *
1201     *      [   sx   0    0   ]1202     *      [   0    sy   0   ]1203     *      [   0    0    1   ]1204     *
1205     * @param sx the factor by which coordinates are scaled along the 1206     * X axis direction1207     * @param sy the factor by which coordinates are scaled along the1208     * Y axis direction 1209    */1210    public void scale(double sx, double sy) {1211    int state = this.state;1212    switch (state) {1213    default:1214        stateError();1215        /* NOTREACHED */1216    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1217    case (APPLY_SHEAR | APPLY_SCALE):1218        m00 *= sx;1219        m11 *= sy;1220        /* NOBREAK */1221    case (APPLY_SHEAR | APPLY_TRANSLATE):1222    case (APPLY_SHEAR):1223        m01 *= sy;1224        m10 *= sx;1225        if (m01 == 0 && m10 == 0) {1226        this.state = state - APPLY_SHEAR;1227        // REMIND: Is it possible for m00 and m11 to be both 1.0?1228 }1229        this.type = TYPE_UNKNOWN;1230        return;1231    case (APPLY_SCALE | APPLY_TRANSLATE):1232    case (APPLY_SCALE):1233        m00 *= sx;1234        m11 *= sy;1235        if (m00 == 1.0 && m11 == 1.0) {1236        this.state = (state &= APPLY_TRANSLATE);1237        this.type = (state == APPLY_IDENTITY1238                 ? TYPE_IDENTITY1239                 : TYPE_TRANSLATION);1240        } else {1241        this.type = TYPE_UNKNOWN;1242        }1243        return;1244    case (APPLY_TRANSLATE):1245    case (APPLY_IDENTITY):1246        m00 = sx;1247        m11 = sy;1248        if (sx != 1.0 || sy != 1.0) {1249        this.state = state | APPLY_SCALE;1250        this.type = TYPE_UNKNOWN;1251        }1252        return;1253    }1254    }12551256    /**1257     * Concatenates this transform with a shearing transformation.1258     * This is equivalent to calling concatenate(SH), where SH is an1259     * AffineTransform represented by the following matrix:1260     *
1261     *      [   1   shx   0   ]1262     *      [  shy   1    0   ]1263     *      [   0    0    1   ]1264     *
1265     * @param shx the multiplier by which coordinates are shifted in the1266     * direction of the positive X axis as a factor of their Y coordinate1267     * @param shy the multiplier by which coordinates are shifted in the1268     * direction of the positive Y axis as a factor of their X coordinate1269     */1270    public void shear(double shx, double shy) {1271    int state = this.state;1272    switch (state) {1273    default:1274        stateError();1275        /* NOTREACHED */1276    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1277    case (APPLY_SHEAR | APPLY_SCALE):1278        double M0, M1;1279        M0 = m00;1280        M1 = m01;1281        m00 = M0 + M1 * shy;1282        m01 = M0 * shx + M1;12831284        M0 = m10;1285        M1 = m11;1286        m10 = M0 + M1 * shy;1287        m11 = M0 * shx + M1;1288        updateState();1289        return;1290    case (APPLY_SHEAR | APPLY_TRANSLATE):1291    case (APPLY_SHEAR):1292        m00 = m01 * shy;1293        m11 = m10 * shx;1294        if (m00 != 0.0 || m11 != 0.0) {1295        this.state = state | APPLY_SCALE;1296        }1297        this.type = TYPE_UNKNOWN;1298        return;1299    case (APPLY_SCALE | APPLY_TRANSLATE):1300    case (APPLY_SCALE):1301        m01 = m00 * shx;1302        m10 = m11 * shy;1303        if (m01 != 0.0 || m10 != 0.0) {1304        this.state = state | APPLY_SHEAR;1305        }1306        this.type = TYPE_UNKNOWN;1307        return;1308    case (APPLY_TRANSLATE):1309    case (APPLY_IDENTITY):1310        m01 = shx;1311        m10 = shy;1312        if (m01 != 0.0 || m10 != 0.0) {1313        this.state = state | APPLY_SCALE | APPLY_SHEAR;1314        this.type = TYPE_UNKNOWN;1315        }1316        return;1317    }1318    }13191320    /**1321     * Resets this transform to the Identity transform.1322     */1323    public void setToIdentity() {1324    m00 = m11 = 1.0;1325    m10 = m01 = m02 = m12 = 0.0;1326    state = APPLY_IDENTITY;1327    type = TYPE_IDENTITY;1328    }13291330    /**1331     * Sets this transform to a translation transformation.1332     * The matrix representing this transform becomes:1333     *
1334     *      [   1    0    tx  ]1335     *      [   0    1    ty  ]1336     *      [   0    0    1   ]1337     *
1338     * @param tx the distance by which coordinates are translated in the1339     * X axis direction1340     * @param ty the distance by which coordinates are translated in the1341     * Y axis direction1342     */1343    public void setToTranslation(double tx, double ty) {1344    m00 = 1.0;1345    m10 = 0.0;1346    m01 = 0.0;1347    m11 = 1.0;1348    m02 = tx;1349    m12 = ty;1350    if (tx != 0.0 || ty != 0.0) {1351        state = APPLY_TRANSLATE;1352        type = TYPE_TRANSLATION;1353    } else {1354        state = APPLY_IDENTITY;1355        type = TYPE_IDENTITY;1356    }1357    }13581359    /**1360     * Sets this transform to a rotation transformation.1361     * The matrix representing this transform becomes:1362     *
1363     *      [   cos(theta)    -sin(theta)    0   ]1364     *      [   sin(theta)     cos(theta)    0   ]1365     *      [       0              0         1   ]1366     *
1367     * Rotating with a positive angle theta rotates points on the positive1368     * x axis toward the positive y axis.1369     * @param theta the angle of rotation in radians1370     */1371    public void setToRotation(double theta) {1372    m02 = 0.0;1373    m12 = 0.0;1374    double sin = Math.sin(theta);1375    double cos = Math.cos(theta);1376    if (Math.abs(sin) < 1E-15) {1377        m01 = m10 = 0.0;1378        if (cos < 0) {1379        m00 = m11 = -1.0;1380        state = APPLY_SCALE;1381        type = TYPE_QUADRANT_ROTATION;1382        } else {1383        m00 = m11 = 1.0;1384        state = APPLY_IDENTITY;1385        type = TYPE_IDENTITY;1386        }1387        return;1388    }1389    if (Math.abs(cos) < 1E-15) {1390        m00 = m11 = 0.0;1391        if (sin < 0.0) {1392        m01 = 1.0;1393        m10 = -1.0;1394        } else {1395        m01 = -1.0;1396        m10 = 1.0;1397        }1398        state = APPLY_SHEAR;1399        type = TYPE_QUADRANT_ROTATION;1400        return;1401    }1402    m00 = cos;1403    m01 = -sin;1404    m10 = sin;1405    m11 = cos;1406    state = APPLY_SHEAR | APPLY_SCALE;1407    type = TYPE_GENERAL_ROTATION;1408    return;1409    }14101411    /**1412     * Sets this transform to a translated rotation transformation.1413     * This operation is equivalent to translating the coordinates so1414     * that the anchor point is at the origin (S1), then rotating them1415     * about the new origin (S2), and finally translating so that the1416     * intermediate origin is restored to the coordinates of the original1417     * anchor point (S3).1418     *

1419     * This operation is equivalent to the following sequence of calls:1420     *

1421     *      setToTranslation(x, y); // S3: final translation1422     *      rotate(theta);      // S2: rotate around anchor1423     *      translate(-x, -y);      // S1: translate anchor to origin1424     *
1425     * The matrix representing this transform becomes:1426     *
1427     *      [   cos(theta)    -sin(theta)    x-x*cos+y*sin  ]1428     *      [   sin(theta)     cos(theta)    y-x*sin-y*cos  ]1429     *      [       0              0               1        ]1430     *
1431     * Rotating with a positive angle theta rotates points on the positive1432     * x axis toward the positive y axis.1433     * @param theta the angle of rotation in radians1434     * @param x, y the coordinates of the anchor point of the1435     * rotation1436     */1437    public void setToRotation(double theta, double x, double y) {1438    setToRotation(theta);1439    double sin = m10;1440    double oneMinusCos = 1.0 - m00;1441    m02 = x * oneMinusCos + y * sin;1442    m12 = y * oneMinusCos - x * sin;1443    if (m02 != 0.0 || m12 != 0.0) {1444        state |= APPLY_TRANSLATE;1445        type |= TYPE_TRANSLATION;1446    }1447    return;1448    }14491450    /**1451     * Sets this transform to a scaling transformation.1452     * The matrix representing this transform becomes:1453     *
1454     *      [   sx   0    0   ]1455     *      [   0    sy   0   ]1456     *      [   0    0    1   ]1457     *
1458     * @param sx the factor by which coordinates are scaled along the1459     * X axis direction1460     * @param sy the factor by which coordinates are scaled along the1461     * Y axis direction1462     */1463    public void setToScale(double sx, double sy) {1464    m00 = sx;1465    m10 = 0.0;1466    m01 = 0.0;1467    m11 = sy;1468    m02 = 0.0;1469    m12 = 0.0;1470    if (sx != 1.0 || sy != 1.0) {1471        state = APPLY_SCALE;1472        type = TYPE_UNKNOWN;1473    } else {1474        state = APPLY_IDENTITY;1475        type = TYPE_IDENTITY;1476    }1477    }14781479    /**1480     * Sets this transform to a shearing transformation.1481     * The matrix representing this transform becomes:1482     *
1483     *      [   1   shx   0   ]1484     *      [  shy   1    0   ]1485     *      [   0    0    1   ]1486     *
1487     * @param shx the multiplier by which coordinates are shifted in the1488     * direction of the positive X axis as a factor of their Y coordinate1489     * @param shy the multiplier by which coordinates are shifted in the1490     * direction of the positive Y axis as a factor of their X coordinate1491     */1492    public void setToShear(double shx, double shy) {1493    m00 = 1.0;1494    m01 = shx;1495    m10 = shy;1496    m11 = 1.0;1497    m02 = 0.0;1498    m12 = 0.0;1499    if (shx != 0.0 || shy != 0.0) {1500        state = (APPLY_SHEAR | APPLY_SCALE);1501        type = TYPE_UNKNOWN;1502    } else {1503        state = APPLY_IDENTITY;1504        type = TYPE_IDENTITY;1505    }1506    }15071508    /**1509     * Sets this transform to a copy of the transform in the specified1510     * AffineTransform object.1511     * @param Tx the AffineTransform object from which to1512     * copy the transform1513     */1514    public void setTransform(AffineTransform Tx) {1515    this.m00 = Tx.m00;1516    this.m10 = Tx.m10;1517    this.m01 = Tx.m01;1518    this.m11 = Tx.m11;1519    this.m02 = Tx.m02;1520    this.m12 = Tx.m12;1521    this.state = Tx.state;1522    this.type = Tx.type;1523    }15241525    /**1526     * Sets this transform to the matrix specified by the 61527     * double precision values.1528     * @param m00, m01, m02, m10, m11, m12 the1529     * 6 floating point values that compose the 3x3 transformation matrix1530     */1531    public void setTransform(double m00, double m10,1532                 double m01, double m11,1533                 double m02, double m12) {1534    this.m00 = m00;1535    this.m10 = m10;1536    this.m01 = m01;1537    this.m11 = m11;1538    this.m02 = m02;1539    this.m12 = m12;1540    updateState();1541    }15421543    /**1544     * Concatenates an AffineTransform Tx to1545     * this AffineTransform Cx in the most commonly useful1546     * way to provide a new user space1547     * that is mapped to the former user space by Tx.1548     * Cx is updated to perform the combined transformation.1549     * Transforming a point p by the updated transform Cx' is1550     * equivalent to first transforming p by Tx and then1551     * transforming the result by the original transform Cx like this:1552     * Cx'(p) = Cx(Tx(p)) 1553     * In matrix notation, if this transform Cx is1554     * represented by the matrix [this] and Tx is represented1555     * by the matrix [Tx] then this method does the following:1556     *
1557     *      [this] = [this] x [Tx]1558     *
1559     * @param Tx the AffineTransform object to be1560     * concatenated with this AffineTransform object.1561     * @see #preConcatenate1562     */1563    public void concatenate(AffineTransform Tx) {1564        double M0, M1;1565    double T00, T01, T10, T11;1566    double T02, T12;1567    int mystate = state;1568    int txstate = Tx.state;1569    switch ((txstate << HI_SHIFT) | mystate) {15701571        /* ---------- Tx == IDENTITY cases ---------- */1572    case (HI_IDENTITY | APPLY_IDENTITY):1573    case (HI_IDENTITY | APPLY_TRANSLATE):1574    case (HI_IDENTITY | APPLY_SCALE):1575    case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):1576    case (HI_IDENTITY | APPLY_SHEAR):1577    case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):1578    case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):1579    case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1580        return;15811582        /* ---------- this == IDENTITY cases ---------- */1583    case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):1584        m01 = Tx.m01;1585        m10 = Tx.m10;1586        /* NOBREAK */1587    case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):1588        m00 = Tx.m00;1589        m11 = Tx.m11;1590        /* NOBREAK */1591    case (HI_TRANSLATE | APPLY_IDENTITY):1592        m02 = Tx.m02;1593        m12 = Tx.m12;1594        state = txstate;1595        type = Tx.type;1596        return;1597    case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY):1598        m01 = Tx.m01;1599        m10 = Tx.m10;1600        /* NOBREAK */1601    case (HI_SCALE | APPLY_IDENTITY):1602        m00 = Tx.m00;1603        m11 = Tx.m11;1604        state = txstate;1605        type = Tx.type;1606        return;1607    case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY):1608        m02 = Tx.m02;1609        m12 = Tx.m12;1610        /* NOBREAK */1611    case (HI_SHEAR | APPLY_IDENTITY):1612        m01 = Tx.m01;1613        m10 = Tx.m10;1614            m00 = m11 = 0.0;1615        state = txstate;1616        type = Tx.type;1617        return;16181619        /* ---------- Tx == TRANSLATE cases ---------- */1620    case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1621    case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):1622    case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):1623    case (HI_TRANSLATE | APPLY_SHEAR):1624    case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):1625    case (HI_TRANSLATE | APPLY_SCALE):1626    case (HI_TRANSLATE | APPLY_TRANSLATE):1627        translate(Tx.m02, Tx.m12);1628        return;16291630        /* ---------- Tx == SCALE cases ---------- */1631    case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1632    case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):1633    case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):1634    case (HI_SCALE | APPLY_SHEAR):1635    case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):1636    case (HI_SCALE | APPLY_SCALE):1637    case (HI_SCALE | APPLY_TRANSLATE):1638        scale(Tx.m00, Tx.m11);1639        return;16401641        /* ---------- Tx == SHEAR cases ---------- */1642    case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1643    case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):1644        T01 = Tx.m01; T10 = Tx.m10;1645        M0 = m00;1646        m00 = m01 * T10;1647        m01 = M0 * T01;1648        M0 = m10;1649        m10 = m11 * T10;1650        m11 = M0 * T01;1651        type = TYPE_UNKNOWN;1652        return;1653    case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):1654    case (HI_SHEAR | APPLY_SHEAR):1655        m00 = m01 * Tx.m10;1656        m01 = 0.0;1657        m11 = m10 * Tx.m01;1658        m10 = 0.0;1659        state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);1660        type = TYPE_UNKNOWN;1661        return;1662    case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1663    case (HI_SHEAR | APPLY_SCALE):1664        m01 = m00 * Tx.m01;1665        m00 = 0.0;1666        m10 = m11 * Tx.m10;1667        m11 = 0.0;1668        state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);1669        type = TYPE_UNKNOWN;1670        return;1671    case (HI_SHEAR | APPLY_TRANSLATE):1672        m00 = 0.0;1673        m01 = Tx.m01;1674        m10 = Tx.m10;1675        m11 = 0.0;1676        state = APPLY_TRANSLATE | APPLY_SHEAR;1677        type = TYPE_UNKNOWN;1678        return;1679    }1680    // If Tx has more than one attribute, it is not worth optimizing1681 // all of those cases...1682 T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02;1683    T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12;1684    switch (mystate) {1685    default:1686        stateError();1687        /* NOTREACHED */1688    case (APPLY_SHEAR | APPLY_SCALE):1689        state = mystate | txstate;1690        /* NOBREAK */1691    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1692        M0 = m00;1693        M1 = m01;1694        m00 = T00 * M0 + T10 * M1;1695        m01 = T01 * M0 + T11 * M1;1696        m02 += T02 * M0 + T12 * M1;16971698        M0 = m10;1699        M1 = m11;1700        m10 = T00 * M0 + T10 * M1;1701        m11 = T01 * M0 + T11 * M1;1702        m12 += T02 * M0 + T12 * M1;1703        type = TYPE_UNKNOWN;1704        return;17051706    case (APPLY_SHEAR | APPLY_TRANSLATE):1707    case (APPLY_SHEAR):1708        M0 = m01;1709        m00 = T10 * M0;1710        m01 = T11 * M0;1711        m02 += T12 * M0;17121713        M0 = m10;1714        m10 = T00 * M0;1715        m11 = T01 * M0;1716        m12 += T02 * M0;1717        break;17181719    case (APPLY_SCALE | APPLY_TRANSLATE):1720    case (APPLY_SCALE):1721        M0 = m00;1722        m00 = T00 * M0;1723        m01 = T01 * M0;1724        m02 += T02 * M0;17251726        M0 = m11;1727        m10 = T10 * M0;1728        m11 = T11 * M0;1729        m12 += T12 * M0;1730        break;17311732    case (APPLY_TRANSLATE):1733        m00 = T00;1734        m01 = T01;1735        m02 += T02;17361737        m10 = T10;1738        m11 = T11;1739        m12 += T12;1740        state = txstate | APPLY_TRANSLATE;1741        type = TYPE_UNKNOWN;1742        return;1743    }1744    updateState();1745    }17461747    /**1748     * Concatenates an AffineTransform Tx to1749     * this AffineTransform Cx1750     * in a less commonly used way such that Tx modifies the1751     * coordinate transformation relative to the absolute pixel1752     * space rather than relative to the existing user space.1753     * Cx is updated to perform the combined transformation.1754     * Transforming a point p by the updated transform Cx' is1755     * equivalent to first transforming p by the original transform1756     * Cx and then transforming the result by 1757     * Tx like this: 1758     * Cx'(p) = Tx(Cx(p)) 1759     * In matrix notation, if this transform Cx1760     * is represented by the matrix [this] and Tx is1761     * represented by the matrix [Tx] then this method does the1762     * following:1763     *
1764     *      [this] = [Tx] x [this]1765     *
1766     * @param Tx the AffineTransform object to be1767     * concatenated with this AffineTransform object.1768     * @see #concatenate1769     */1770    public void preConcatenate(AffineTransform Tx) {1771    double M0, M1;1772    double T00, T01, T10, T11;1773    double T02, T12;1774    int mystate = state;1775    int txstate = Tx.state;1776    switch ((txstate << HI_SHIFT) | mystate) {1777    case (HI_IDENTITY | APPLY_IDENTITY):1778    case (HI_IDENTITY | APPLY_TRANSLATE):1779    case (HI_IDENTITY | APPLY_SCALE):1780    case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):1781    case (HI_IDENTITY | APPLY_SHEAR):1782    case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):1783    case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):1784    case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1785        // Tx is IDENTITY...1786 return;17871788    case (HI_TRANSLATE | APPLY_IDENTITY):1789    case (HI_TRANSLATE | APPLY_SCALE):1790    case (HI_TRANSLATE | APPLY_SHEAR):1791    case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):1792        // Tx is TRANSLATE, this has no TRANSLATE1793 m02 = Tx.m02;1794        m12 = Tx.m12;1795        state = mystate | APPLY_TRANSLATE;1796        type |= TYPE_TRANSLATION;1797        return;17981799    case (HI_TRANSLATE | APPLY_TRANSLATE):1800    case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):1801    case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):1802    case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1803        // Tx is TRANSLATE, this has one too1804 m02 = m02 + Tx.m02;1805        m12 = m12 + Tx.m12;1806        return;18071808    case (HI_SCALE | APPLY_TRANSLATE):1809    case (HI_SCALE | APPLY_IDENTITY):1810        // Only these two existing states need a new state1811 state = mystate | APPLY_SCALE;1812        /* NOBREAK */1813    case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1814    case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):1815    case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):1816    case (HI_SCALE | APPLY_SHEAR):1817    case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):1818    case (HI_SCALE | APPLY_SCALE):1819        // Tx is SCALE, this is anything1820 T00 = Tx.m00;1821        T11 = Tx.m11;1822        if ((mystate & APPLY_SHEAR) != 0) {1823        m01 = m01 * T00;1824        m10 = m10 * T11;1825        if ((mystate & APPLY_SCALE) != 0) {1826            m00 = m00 * T00;1827            m11 = m11 * T11;1828        }1829        } else {1830        m00 = m00 * T00;1831        m11 = m11 * T11;1832        }1833        if ((mystate & APPLY_TRANSLATE) != 0) {1834        m02 = m02 * T00;1835        m12 = m12 * T11;1836        }1837        type = TYPE_UNKNOWN;1838        return;1839    case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):1840    case (HI_SHEAR | APPLY_SHEAR):1841        mystate = mystate | APPLY_SCALE;1842        /* NOBREAK */1843    case (HI_SHEAR | APPLY_TRANSLATE):1844    case (HI_SHEAR | APPLY_IDENTITY):1845    case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1846    case (HI_SHEAR | APPLY_SCALE):1847        state = mystate ^ APPLY_SHEAR;1848        /* NOBREAK */1849    case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1850    case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):1851        // Tx is SHEAR, this is anything1852 T01 = Tx.m01;1853        T10 = Tx.m10;18541855        M0 = m00;1856        m00 = m10 * T01;1857        m10 = M0 * T10;18581859        M0 = m01;1860        m01 = m11 * T01;1861        m11 = M0 * T10;18621863        M0 = m02;1864        m02 = m12 * T01;1865        m12 = M0 * T10;1866        type = TYPE_UNKNOWN;1867        return;1868    }1869    // If Tx has more than one attribute, it is not worth optimizing1870 // all of those cases...1871 T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02;1872    T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12;1873    switch (mystate) {1874    default:1875        stateError();1876        /* NOTREACHED */1877    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1878        M0 = m02;1879        M1 = m12;1880        T02 += M0 * T00 + M1 * T01;1881        T12 += M0 * T10 + M1 * T11;18821883        /* NOBREAK */1884    case (APPLY_SHEAR | APPLY_SCALE):1885        m02 = T02;1886        m12 = T12;18871888        M0 = m00;1889        M1 = m10;1890        m00 = M0 * T00 + M1 * T01;1891        m10 = M0 * T10 + M1 * T11;18921893        M0 = m01;1894        M1 = m11;1895        m01 = M0 * T00 + M1 * T01;1896        m11 = M0 * T10 + M1 * T11;1897        break;18981899    case (APPLY_SHEAR | APPLY_TRANSLATE):1900        M0 = m02;1901        M1 = m12;1902        T02 += M0 * T00 + M1 * T01;1903        T12 += M0 * T10 + M1 * T11;19041905        /* NOBREAK */1906    case (APPLY_SHEAR):1907        m02 = T02;1908        m12 = T12;19091910        M0 = m10;1911        m00 = M0 * T01;1912        m10 = M0 * T11;19131914        M0 = m01;1915        m01 = M0 * T00;1916        m11 = M0 * T10;1917        break;19181919    case (APPLY_SCALE | APPLY_TRANSLATE):1920        M0 = m02;1921        M1 = m12;1922        T02 += M0 * T00 + M1 * T01;1923        T12 += M0 * T10 + M1 * T11;19241925        /* NOBREAK */1926    case (APPLY_SCALE):1927        m02 = T02;1928        m12 = T12;19291930        M0 = m00;1931        m00 = M0 * T00;1932        m10 = M0 * T10;19331934        M0 = m11;1935        m01 = M0 * T01;1936        m11 = M0 * T11;1937        break;19381939    case (APPLY_TRANSLATE):1940        M0 = m02;1941        M1 = m12;1942        T02 += M0 * T00 + M1 * T01;1943        T12 += M0 * T10 + M1 * T11;19441945        /* NOBREAK */1946    case (APPLY_IDENTITY):1947        m02 = T02;1948        m12 = T12;19491950        m00 = T00;1951        m10 = T10;19521953        m01 = T01;1954        m11 = T11;19551956        state = mystate | txstate;1957        type = TYPE_UNKNOWN;1958        return;1959    }1960    updateState();1961    }19621963    /**1964     * Returns an AffineTransform object representing the1965     * inverse transformation.1966     * The inverse transform Tx' of this transform Tx 1967     * maps coordinates transformed by Tx back1968     * to their original coordinates.1969     * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).1970     *

1971     * If this transform maps all coordinates onto a point or a line1972     * then it will not have an inverse, since coordinates that do1973     * not lie on the destination point or line will not have an inverse1974     * mapping.1975     * The getDeterminant method can be used to determine if this1976     * transform has no inverse, in which case an exception will be1977     * thrown if the createInverse method is called.1978     * @return a new AffineTransform object representing the1979     * inverse transformation.1980     * @see #getDeterminant1981     * @exception NoninvertibleTransformException1982     * if the matrix cannot be inverted.1983     */1984    public AffineTransform createInverse()1985    throws NoninvertibleTransformException 1986    {1987    double det;1988    switch (state) {1989    default:1990        stateError();1991        /* NOTREACHED */1992    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):1993        det = m00 * m11 - m01 * m10;1994        if (Math.abs(det) <= Double.MIN_VALUE) {1995        throw new NoninvertibleTransformException ("Determinant is "+1996                              det);1997        }1998        return new AffineTransform ( m11 / det, -m10 / det,1999                       -m01 / det, m00 / det,2000                       (m01 * m12 - m11 * m02) / det,2001                       (m10 * m02 - m00 * m12) / det,2002                       (APPLY_SHEAR |2003                    APPLY_SCALE |2004                    APPLY_TRANSLATE));2005    case (APPLY_SHEAR | APPLY_SCALE):2006        det = m00 * m11 - m01 * m10;2007        if (Math.abs(det) <= Double.MIN_VALUE) {2008        throw new NoninvertibleTransformException ("Determinant is "+2009                              det);2010        }2011        return new AffineTransform ( m11 / det, -m10 / det,2012                       -m01 / det, m00 / det,2013                        0.0, 0.0,2014                       (APPLY_SHEAR | APPLY_SCALE));2015    case (APPLY_SHEAR | APPLY_TRANSLATE):2016        if (m01 == 0.0 || m10 == 0.0) {2017        throw new NoninvertibleTransformException ("Determinant is 0");2018        }2019        return new AffineTransform ( 0.0, 1.0 / m01,2020                        1.0 / m10, 0.0,2021                       -m12 / m10, -m02 / m01,2022                       (APPLY_SHEAR | APPLY_TRANSLATE));2023    case (APPLY_SHEAR):2024        if (m01 == 0.0 || m10 == 0.0) {2025        throw new NoninvertibleTransformException ("Determinant is 0");2026        }2027        return new AffineTransform (0.0, 1.0 / m01,2028                       1.0 / m10, 0.0,2029                       0.0, 0.0,2030                       (APPLY_SHEAR));2031    case (APPLY_SCALE | APPLY_TRANSLATE):2032        if (m00 == 0.0 || m11 == 0.0) {2033        throw new NoninvertibleTransformException ("Determinant is 0");2034        }2035        return new AffineTransform ( 1.0 / m00, 0.0,2036                        0.0, 1.0 / m11,2037                       -m02 / m00, -m12 / m11,2038                       (APPLY_SCALE | APPLY_TRANSLATE));2039    case (APPLY_SCALE):2040        if (m00 == 0.0 || m11 == 0.0) {2041        throw new NoninvertibleTransformException ("Determinant is 0");2042        }2043        return new AffineTransform (1.0 / m00, 0.0,2044                       0.0, 1.0 / m11,2045                       0.0, 0.0,2046                       (APPLY_SCALE));2047    case (APPLY_TRANSLATE):2048        return new AffineTransform ( 1.0, 0.0,2049                        0.0, 1.0,2050                       -m02, -m12,2051                       (APPLY_TRANSLATE));2052    case (APPLY_IDENTITY):2053        return new AffineTransform ();2054    }20552056    /* NOTREACHED */2057    }20582059    /**2060     * Transforms the specified ptSrc and stores the result2061     * in ptDst.2062     * If ptDst is null, a new {@link Point2D}2063     * object is allocated and then the result of the transformation is2064     * stored in this object.2065     * In either case, ptDst, which contains the2066     * transformed point, is returned for convenience.2067     * If ptSrc and ptDst are the same2068     * object, the input point is correctly overwritten with2069     * the transformed point.2070     * @param ptSrc the specified Point2D to be transformed2071     * @param ptDst the specified Point2D that stores the2072     * result of transforming ptSrc2073     * @return the ptDst after transforming2074     * ptSrc and stroring the result in ptDst.2075     */2076    public Point2D transform(Point2D ptSrc, Point2D ptDst) {2077    if (ptDst == null) {2078        if (ptSrc instanceof Point2D.Double ) {2079        ptDst = new Point2D.Double ();2080        } else {2081        ptDst = new Point2D.Float ();2082        }2083    }2084    // Copy source coords into local variables in case src == dst2085 double x = ptSrc.getX();2086    double y = ptSrc.getY();2087    switch (state) {2088    default:2089        stateError();2090        /* NOTREACHED */2091    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):2092        ptDst.setLocation(x * m00 + y * m01 + m02,2093                  x * m10 + y * m11 + m12);2094        return ptDst;2095    case (APPLY_SHEAR | APPLY_SCALE):2096        ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11);2097        return ptDst;2098    case (APPLY_SHEAR | APPLY_TRANSLATE):2099        ptDst.setLocation(y * m01 + m02, x * m10 + m12);2100        return ptDst;2101    case (APPLY_SHEAR):2102        ptDst.setLocation(y * m01, x * m10);2103        return ptDst;2104    case (APPLY_SCALE | APPLY_TRANSLATE):2105        ptDst.setLocation(x * m00 + m02, y * m11 + m12);2106        return ptDst;2107    case (APPLY_SCALE):2108        ptDst.setLocation(x * m00, y * m11);2109        return ptDst;2110    case (APPLY_TRANSLATE):2111        ptDst.setLocation(x + m02, y + m12);2112        return ptDst;2113    case (APPLY_IDENTITY):2114        ptDst.setLocation(x, y);2115        return ptDst;2116    }21172118    /* NOTREACHED */2119    }21202121    /**2122     * Transforms an array of point objects by this transform.2123     * If any element of the ptDst array is2124     * null, a new Point2D object is allocated2125     * and stored into that element before storing the results of the2126     * transformation.2127     *

2128     * Note that this method does not take any precautions to2129     * avoid problems caused by storing results into Point2D2130     * objects that will be used as the source for calculations2131     * further down the source array.2132     * This method does guarantee that if a specified Point2D 2133     * object is both the source and destination for the same single point2134     * transform operation then the results will not be stored until2135     * the calculations are complete to avoid storing the results on2136     * top of the operands.2137     * If, however, the destination Point2D object for one2138     * operation is the same object as the source Point2D 2139     * object for another operation further down the source array then2140     * the original coordinates in that point are overwritten before2141     * they can be converted.2142     * @param ptSrc the array containing the source point objects2143     * @param ptDst the array into which the transform point objects are2144     * returned2145     * @param srcOff the offset to the first point object to be2146     * transformed in the source array2147     * @param dstOff the offset to the location of the first2148     * transformed point object that is stored in the destination array2149     * @param numPts the number of point objects to be transformed2150     */2151    public void transform(Point2D [] ptSrc, int srcOff,2152              Point2D [] ptDst, int dstOff,2153              int numPts) {2154    int state = this.state;2155        while (--numPts >= 0) {2156            // Copy source coords into local variables in case src == dst2157 Point2D src = ptSrc[srcOff++];2158            double x = src.getX();2159            double y = src.getY();2160        Point2D dst = ptDst[dstOff++];2161        if (dst == null) {2162        if (src instanceof Point2D.Double ) {2163            dst = new Point2D.Double ();2164        } else {2165            dst = new Point2D.Float ();2166        }2167        ptDst[dstOff - 1] = dst;2168        }2169        switch (state) {2170        default:2171        stateError();2172        /* NOTREACHED */2173        case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):2174        dst.setLocation(x * m00 + y * m01 + m02,2175                x * m10 + y * m11 + m12);2176        break;2177        case (APPLY_SHEAR | APPLY_SCALE):2178        dst.setLocation(x * m00 + y * m01, x * m10 + y * m11);2179        break;2180        case (APPLY_SHEAR | APPLY_TRANSLATE):2181        dst.setLocation(y * m01 + m02, x * m10 + m12);2182        break;2183        case (APPLY_SHEAR):2184        dst.setLocation(y * m01, x * m10);2185        break;2186        case (APPLY_SCALE | APPLY_TRANSLATE):2187        dst.setLocation(x * m00 + m02, y * m11 + m12);2188        break;2189        case (APPLY_SCALE):2190        dst.setLocation(x * m00, y * m11);2191        break;2192        case (APPLY_TRANSLATE):2193        dst.setLocation(x + m02, y + m12);2194        break;2195        case (APPLY_IDENTITY):2196        dst.setLocation(x, y);2197        break;2198        }2199    }22002201    /* NOTREACHED */2202    }22032204    /**2205     * Transforms an array of floating point coordinates by this transform.2206     * The two coordinate array sections can be exactly the same or2207     * can be overlapping sections of the same array without affecting the2208     * validity of the results.2209     * This method ensures that no source coordinates are overwritten by a2210     * previous operation before they can be transformed.2211     * The coordinates are stored in the arrays starting at the specified2212     * offset in the order [x0, y0, x1, y1, ..., xn, yn].2213     * @param srcPts the array containing the source point coordinates.2214     * Each point is stored as a pair of x, y coordinates.2215     * @param dstPts the array into which the transformed point coordinates2216     * are returned. Each point is stored as a pair of x, y2217     * coordinates.2218     * @param srcOff the offset to the first point to be transformed2219     * in the source array2220     * @param dstOff the offset to the location of the first2221     * transformed point that is stored in the destination array2222     * @param numPts the number of points to be transformed2223     */2224    public void transform(float[] srcPts, int srcOff,2225              float[] dstPts, int dstOff,2226              int numPts) {2227    double M00, M01, M02, M10, M11, M12; // For caching2228 if (dstPts == srcPts &&2229        dstOff > srcOff && dstOff < srcOff + numPts * 2)2230    {2231        // If the arrays overlap partially with the destination higher2232 // than the source and we transform the coordinates normally2233 // we would overwrite some of the later source coordinates2234 // with results of previous transformations.2235 // To get around this we use arraycopy to copy the points2236 // to their final destination with correct overwrite2237 // handling and then transform them in place in the new2238 // safer location.2239 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);2240        // srcPts = dstPts; // They are known to be equal.2241 srcOff = dstOff;2242    }2243    switch (state) {2244    default:2245        stateError();2246        /* NOTREACHED */2247    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):2248        M00 = m00; M01 = m01; M02 = m02;2249        M10 = m10; M11 = m11; M12 = m12;2250        while (--numPts >= 0) {2251        double x = srcPts[srcOff++];2252        double y = srcPts[srcOff++];2253        dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);2254        dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);2255        }2256        return;2257    case (APPLY_SHEAR | APPLY_SCALE):2258        M00 = m00; M01 = m01;2259        M10 = m10; M11 = m11;2260        while (--numPts >= 0) {2261        double x = srcPts[srcOff++];2262        double y = srcPts[srcOff++];2263        dstPts[dstOff++] = (float) (M00 * x + M01 * y);2264        dstPts[dstOff++] = (float) (M10 * x + M11 * y);2265        }2266        return;2267    case (APPLY_SHEAR | APPLY_TRANSLATE):2268        M01 = m01; M02 = m02;2269        M10 = m10; M12 = m12;2270        while (--numPts >= 0) {2271        double x = srcPts[srcOff++];2272        dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);2273        dstPts[dstOff++] = (float) (M10 * x + M12);2274        }2275        return;2276    case (APPLY_SHEAR):2277        M01 = m01; M10 = m10;2278        while (--numPts >= 0) {2279        double x = srcPts[srcOff++];2280        dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);2281        dstPts[dstOff++] = (float) (M10 * x);2282        }2283        return;2284    case (APPLY_SCALE | APPLY_TRANSLATE):2285        M00 = m00; M02 = m02;2286        M11 = m11; M12 = m12;2287        while (--numPts >= 0) {2288        dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);2289        dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);2290        }2291        return;2292    case (APPLY_SCALE):2293        M00 = m00; M11 = m11;2294        while (--numPts >= 0) {2295        dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);2296        dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);2297        }2298        return;2299    case (APPLY_TRANSLATE):2300        M02 = m02; M12 = m12;2301        while (--numPts >= 0) {2302        dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);2303        dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);2304        }2305        return;2306    case (APPLY_IDENTITY):2307        if (srcPts != dstPts || srcOff != dstOff) {2308        System.arraycopy(srcPts, srcOff, dstPts, dstOff,2309                 numPts * 2);2310        }2311        return;2312    }23132314    /* NOTREACHED */2315    }23162317    /**2318     * Transforms an array of double precision coordinates by this transform.2319     * The two coordinate array sections can be exactly the same or2320     * can be overlapping sections of the same array without affecting the2321     * validity of the results.2322     * This method ensures that no source coordinates are2323     * overwritten by a previous operation before they can be transformed.2324     * The coordinates are stored in the arrays starting at the indicated2325     * offset in the order [x0, y0, x1, y1, ..., xn, yn].2326     * @param srcPts the array containing the source point coordinates.2327     * Each point is stored as a pair of x, y coordinates.2328     * @param dstPts the array into which the transformed point2329     * coordinates are returned. Each point is stored as a pair of2330     * x, y coordinates.2331     * @param srcOff the offset to the first point to be transformed2332     * in the source array2333     * @param dstOff the offset to the location of the first2334     * transformed point that is stored in the destination array2335     * @param numPts the number of point objects to be transformed2336     */2337    public void transform(double[] srcPts, int srcOff,2338              double[] dstPts, int dstOff,2339              int numPts) {2340    double M00, M01, M02, M10, M11, M12; // For caching2341 if (dstPts == srcPts &&2342        dstOff > srcOff && dstOff < srcOff + numPts * 2)2343    {2344        // If the arrays overlap partially with the destination higher2345 // than the source and we transform the coordinates normally2346 // we would overwrite some of the later source coordinates2347 // with results of previous transformations.2348 // To get around this we use arraycopy to copy the points2349 // to their final destination with correct overwrite2350 // handling and then transform them in place in the new2351 // safer location.2352 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);2353        // srcPts = dstPts; // They are known to be equal.2354 srcOff = dstOff;2355    }2356    switch (state) {2357    default:2358        stateError();2359        /* NOTREACHED */2360    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):2361        M00 = m00; M01 = m01; M02 = m02;2362        M10 = m10; M11 = m11; M12 = m12;2363        while (--numPts >= 0) {2364        double x = srcPts[srcOff++];2365        double y = srcPts[srcOff++];2366        dstPts[dstOff++] = M00 * x + M01 * y + M02;2367        dstPts[dstOff++] = M10 * x + M11 * y + M12;2368        }2369        return;2370    case (APPLY_SHEAR | APPLY_SCALE):2371        M00 = m00; M01 = m01;2372        M10 = m10; M11 = m11;2373        while (--numPts >= 0) {2374        double x = srcPts[srcOff++];2375        double y = srcPts[srcOff++];2376        dstPts[dstOff++] = M00 * x + M01 * y;2377        dstPts[dstOff++] = M10 * x + M11 * y;2378        }2379        return;2380    case (APPLY_SHEAR | APPLY_TRANSLATE):2381        M01 = m01; M02 = m02;2382        M10 = m10; M12 = m12;2383        while (--numPts >= 0) {2384        double x = srcPts[srcOff++];2385        dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;2386        dstPts[dstOff++] = M10 * x + M12;2387        }2388        return;2389    case (APPLY_SHEAR):2390        M01 = m01; M10 = m10;2391        while (--numPts >= 0) {2392        double x = srcPts[srcOff++];2393        dstPts[dstOff++] = M01 * srcPts[srcOff++];2394        dstPts[dstOff++] = M10 * x;2395        }2396        return;2397    case (APPLY_SCALE | APPLY_TRANSLATE):2398        M00 = m00; M02 = m02;2399        M11 = m11; M12 = m12;2400        while (--numPts >= 0) {2401        dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;2402        dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;2403        }2404        return;2405    case (APPLY_SCALE):2406        M00 = m00; M11 = m11;2407        while (--numPts >= 0) {2408        dstPts[dstOff++] = M00 * srcPts[srcOff++];2409        dstPts[dstOff++] = M11 * srcPts[srcOff++];2410        }2411        return;2412    case (APPLY_TRANSLATE):2413        M02 = m02; M12 = m12;2414        while (--numPts >= 0) {2415        dstPts[dstOff++] = srcPts[srcOff++] + M02;2416        dstPts[dstOff++] = srcPts[srcOff++] + M12;2417        }2418        return;2419    case (APPLY_IDENTITY):2420        if (srcPts != dstPts || srcOff != dstOff) {2421        System.arraycopy(srcPts, srcOff, dstPts, dstOff,2422                 numPts * 2);2423        }2424        return;2425    }24262427    /* NOTREACHED */2428    }24292430    /**2431     * Transforms an array of floating point coordinates by this transform2432     * and stores the results into an array of doubles.2433     * The coordinates are stored in the arrays starting at the specified2434     * offset in the order [x0, y0, x1, y1, ..., xn, yn].2435     * @param srcPts the array containing the source point coordinates.2436     * Each point is stored as a pair of x, y coordinates.2437     * @param dstPts the array into which the transformed point coordinates2438     * are returned. Each point is stored as a pair of x, y2439     * coordinates.2440     * @param srcOff the offset to the first point to be transformed2441     * in the source array2442     * @param dstOff the offset to the location of the first2443     * transformed point that is stored in the destination array2444     * @param numPts the number of points to be transformed2445     */2446    public void transform(float[] srcPts, int srcOff,2447              double[] dstPts, int dstOff,2448              int numPts) {2449    double M00, M01, M02, M10, M11, M12; // For caching2450 switch (state) {2451    default:2452        stateError();2453        /* NOTREACHED */2454    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):2455        M00 = m00; M01 = m01; M02 = m02;2456        M10 = m10; M11 = m11; M12 = m12;2457        while (--numPts >= 0) {2458        double x = srcPts[srcOff++];2459        double y = srcPts[srcOff++];2460        dstPts[dstOff++] = M00 * x + M01 * y + M02;2461        dstPts[dstOff++] = M10 * x + M11 * y + M12;2462        }2463        return;2464    case (APPLY_SHEAR | APPLY_SCALE):2465        M00 = m00; M01 = m01;2466        M10 = m10; M11 = m11;2467        while (--numPts >= 0) {2468        double x = srcPts[srcOff++];2469        double y = srcPts[srcOff++];2470        dstPts[dstOff++] = M00 * x + M01 * y;2471        dstPts[dstOff++] = M10 * x + M11 * y;2472        }2473        return;2474    case (APPLY_SHEAR | APPLY_TRANSLATE):2475        M01 = m01; M02 = m02;2476        M10 = m10; M12 = m12;2477        while (--numPts >= 0) {2478        double x = srcPts[srcOff++];2479        dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;2480        dstPts[dstOff++] = M10 * x + M12;2481        }2482        return;2483    case (APPLY_SHEAR):2484        M01 = m01; M10 = m10;2485        while (--numPts >= 0) {2486        double x = srcPts[srcOff++];2487        dstPts[dstOff++] = M01 * srcPts[srcOff++];2488        dstPts[dstOff++] = M10 * x;2489        }2490        return;2491    case (APPLY_SCALE | APPLY_TRANSLATE):2492        M00 = m00; M02 = m02;2493        M11 = m11; M12 = m12;2494        while (--numPts >= 0) {2495        dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;2496        dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;2497        }2498        return;2499    case (APPLY_SCALE):2500        M00 = m00; M11 = m11;2501        while (--numPts >= 0) {2502        dstPts[dstOff++] = M00 * srcPts[srcOff++];2503        dstPts[dstOff++] = M11 * srcPts[srcOff++];2504        }2505        return;2506    case (APPLY_TRANSLATE):2507        M02 = m02; M12 = m12;2508        while (--numPts >= 0) {2509        dstPts[dstOff++] = srcPts[srcOff++] + M02;2510        dstPts[dstOff++] = srcPts[srcOff++] + M12;2511        }2512        return;2513    case (APPLY_IDENTITY):2514        while (--numPts >= 0) {2515        dstPts[dstOff++] = srcPts[srcOff++];2516        dstPts[dstOff++] = srcPts[srcOff++];2517        }2518        return;2519    }25202521    /* NOTREACHED */2522    }25232524    /**2525     * Transforms an array of double precision coordinates by this transform2526     * and stores the results into an array of floats.2527     * The coordinates are stored in the arrays starting at the specified2528     * offset in the order [x0, y0, x1, y1, ..., xn, yn].2529     * @param srcPts the array containing the source point coordinates.2530     * Each point is stored as a pair of x, y coordinates.2531     * @param dstPts the array into which the transformed point2532     * coordinates are returned. Each point is stored as a pair of 2533     * x, y coordinates.2534     * @param srcOff the offset to the first point to be transformed2535     * in the source array2536     * @param dstOff the offset to the location of the first2537     * transformed point that is stored in the destination array2538     * @param numPts the number of point objects to be transformed2539     */2540    public void transform(double[] srcPts, int srcOff,2541              float[] dstPts, int dstOff,2542              int numPts) {2543    double M00, M01, M02, M10, M11, M12; // For caching2544 switch (state) {2545    default:2546        stateError();2547        /* NOTREACHED */2548    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):2549        M00 = m00; M01 = m01; M02 = m02;2550        M10 = m10; M11 = m11; M12 = m12;2551        while (--numPts >= 0) {2552        double x = srcPts[srcOff++];2553        double y = srcPts[srcOff++];2554        dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);2555        dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);2556        }2557        return;2558    case (APPLY_SHEAR | APPLY_SCALE):2559        M00 = m00; M01 = m01;2560        M10 = m10; M11 = m11;2561        while (--numPts >= 0) {2562        double x = srcPts[srcOff++];2563        double y = srcPts[srcOff++];2564        dstPts[dstOff++] = (float) (M00 * x + M01 * y);2565        dstPts[dstOff++] = (float) (M10 * x + M11 * y);2566        }2567        return;2568    case (APPLY_SHEAR | APPLY_TRANSLATE):2569        M01 = m01; M02 = m02;2570        M10 = m10; M12 = m12;2571        while (--numPts >= 0) {2572        double x = srcPts[srcOff++];2573        dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);2574        dstPts[dstOff++] = (float) (M10 * x + M12);2575        }2576        return;2577    case (APPLY_SHEAR):2578        M01 = m01; M10 = m10;2579        while (--numPts >= 0) {2580        double x = srcPts[srcOff++];2581        dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);2582        dstPts[dstOff++] = (float) (M10 * x);2583        }2584        return;2585    case (APPLY_SCALE | APPLY_TRANSLATE):2586        M00 = m00; M02 = m02;2587        M11 = m11; M12 = m12;2588        while (--numPts >= 0) {2589        dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);2590        dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);2591        }2592        return;2593    case (APPLY_SCALE):2594        M00 = m00; M11 = m11;2595        while (--numPts >= 0) {2596        dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);2597        dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);2598        }2599        return;2600    case (APPLY_TRANSLATE):2601        M02 = m02; M12 = m12;2602        while (--numPts >= 0) {2603        dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);2604        dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);2605        }2606        return;2607    case (APPLY_IDENTITY):2608        while (--numPts >= 0) {2609        dstPts[dstOff++] = (float) (srcPts[srcOff++]);2610        dstPts[dstOff++] = (float) (srcPts[srcOff++]);2611        }2612        return;2613    }26142615    /* NOTREACHED */2616    }26172618    /**2619     * Inverse transforms the specified ptSrc and stores the2620     * result in ptDst.2621     * If ptDst is null, a new2622     * Point2D object is allocated and then the result of the2623     * transform is stored in this object.2624     * In either case, ptDst, which contains the transformed2625     * point, is returned for convenience.2626     * If ptSrc and ptDst are the same2627     * object, the input point is correctly overwritten with the2628     * transformed point.2629     * @param ptSrc the point to be inverse transformed2630     * @param ptDst the resulting transformed point2631     * @return ptDst, which contains the result of the 2632     * inverse transform.2633     * @exception NoninvertibleTransformException if the matrix cannot be2634     * inverted.2635     */2636    public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst)2637    throws NoninvertibleTransformException 2638    {2639    if (ptDst == null) {2640        if (ptSrc instanceof Point2D.Double ) {2641        ptDst = new Point2D.Double ();2642        } else {2643        ptDst = new Point2D.Float ();2644        }2645    }2646    // Copy source coords into local variables in case src == dst2647 double x = ptSrc.getX();2648    double y = ptSrc.getY();2649    switch (state) {2650    default:2651        stateError();2652        /* NOTREACHED */2653    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):2654        x -= m02;2655        y -= m12;2656        /* NOBREAK */2657    case (APPLY_SHEAR | APPLY_SCALE):2658        double det = m00 * m11 - m01 * m10;2659        if (Math.abs(det) <= Double.MIN_VALUE) {2660        throw new NoninvertibleTransformException ("Determinant is "+2661                              det);2662        }2663        ptDst.setLocation((x * m11 - y * m01) / det,2664                  (y * m00 - x * m10) / det);2665        return ptDst;2666    case (APPLY_SHEAR | APPLY_TRANSLATE):2667        x -= m02;2668        y -= m12;2669        /* NOBREAK */2670    case (APPLY_SHEAR):2671        if (m01 == 0.0 || m10 == 0.0) {2672        throw new NoninvertibleTransformException ("Determinant is 0");2673        }2674        ptDst.setLocation(y / m10, x / m01);2675        return ptDst;2676    case (APPLY_SCALE | APPLY_TRANSLATE):2677        x -= m02;2678        y -= m12;2679        /* NOBREAK */2680    case (APPLY_SCALE):2681        if (m00 == 0.0 || m11 == 0.0) {2682        throw new NoninvertibleTransformException ("Determinant is 0");2683        }2684        ptDst.setLocation(x / m00, y / m11);2685        return ptDst;2686    case (APPLY_TRANSLATE):2687        ptDst.setLocation(x - m02, y - m12);2688        return ptDst;2689    case (APPLY_IDENTITY):2690        ptDst.setLocation(x, y);2691        return ptDst;2692    }26932694    /* NOTREACHED */2695    }26962697    /**2698     * Inverse transforms an array of double precision coordinates by2699     * this transform. 2700     * The two coordinate array sections can be exactly the same or2701     * can be overlapping sections of the same array without affecting the2702     * validity of the results.2703     * This method ensures that no source coordinates are2704     * overwritten by a previous operation before they can be transformed.2705     * The coordinates are stored in the arrays starting at the specified2706     * offset in the order [x0, y0, x1, y1, ..., xn, yn].2707     * @param srcPts the array containing the source point coordinates.2708     * Each point is stored as a pair of x, y coordinates.2709     * @param dstPts the array into which the transformed point2710     * coordinates are returned. Each point is stored as a pair of 2711     * x, y coordinates.2712     * @param srcOff the offset to the first point to be transformed2713     * in the source array2714     * @param dstOff the offset to the location of the first2715     * transformed point that is stored in the destination array2716     * @param numPts the number of point objects to be transformed2717     * @exception NoninvertibleTransformException if the matrix cannot be2718     * inverted.2719     */2720    public void inverseTransform(double[] srcPts, int srcOff,2721                                 double[] dstPts, int dstOff,2722                                 int numPts)2723    throws NoninvertibleTransformException 2724    {2725    double M00, M01, M02, M10, M11, M12; // For caching2726 double det;2727    if (dstPts == srcPts &&2728        dstOff > srcOff && dstOff < srcOff + numPts * 2)2729    {2730        // If the arrays overlap partially with the destination higher2731 // than the source and we transform the coordinates normally2732 // we would overwrite some of the later source coordinates2733 // with results of previous transformations.2734 // To get around this we use arraycopy to copy the points2735 // to their final destination with correct overwrite2736 // handling and then transform them in place in the new2737 // safer location.2738 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);2739        // srcPts = dstPts; // They are known to be equal.2740 srcOff = dstOff;2741    }2742    switch (state) {2743    default:2744        stateError();2745        /* NOTREACHED */2746    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):2747        M00 = m00; M01 = m01; M02 = m02;2748        M10 = m10; M11 = m11; M12 = m12;2749        det = M00 * M11 - M01 * M10;2750        if (Math.abs(det) <= Double.MIN_VALUE) {2751        throw new NoninvertibleTransformException ("Determinant is "+2752                              det);2753        }2754        while (--numPts >= 0) {2755        double x = srcPts[srcOff++] - M02;2756        double y = srcPts[srcOff++] - M12;2757        dstPts[dstOff++] = (x * M11 - y * M01) / det;2758        dstPts[dstOff++] = (y * M00 - x * M10) / det;2759        }2760        return;2761    case (APPLY_SHEAR | APPLY_SCALE):2762        M00 = m00; M01 = m01;2763        M10 = m10; M11 = m11;2764        det = M00 * M11 - M01 * M10;2765        if (Math.abs(det) <= Double.MIN_VALUE) {2766        throw new NoninvertibleTransformException ("Determinant is "+2767                              det);2768        }2769        while (--numPts >= 0) {2770        double x = srcPts[srcOff++];2771        double y = srcPts[srcOff++];2772        dstPts[dstOff++] = (x * M11 - y * M01) / det;2773        dstPts[dstOff++] = (y * M00 - x * M10) / det;2774        }2775        return;2776    case (APPLY_SHEAR | APPLY_TRANSLATE):2777        M01 = m01; M02 = m02;2778        M10 = m10; M12 = m12;2779        if (M01 == 0.0 || M10 == 0.0) {2780        throw new NoninvertibleTransformException ("Determinant is 0");2781        }2782        while (--numPts >= 0) {2783        double x = srcPts[srcOff++] - M02;2784        dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M10;2785        dstPts[dstOff++] = x / M01;2786        }2787        return;2788    case (APPLY_SHEAR):2789        M01 = m01; M10 = m10;2790        if (M01 == 0.0 || M10 == 0.0) {2791        throw new NoninvertibleTransformException ("Determinant is 0");2792        }2793        while (--numPts >= 0) {2794        double x = srcPts[srcOff++];2795        dstPts[dstOff++] = srcPts[srcOff++] / M10;2796        dstPts[dstOff++] = x / M01;2797        }2798        return;2799    case (APPLY_SCALE | APPLY_TRANSLATE):2800        M00 = m00; M02 = m02;2801        M11 = m11; M12 = m12;2802        if (M00 == 0.0 || M11 == 0.0) {2803        throw new NoninvertibleTransformException ("Determinant is 0");2804        }2805        while (--numPts >= 0) {2806        dstPts[dstOff++] = (srcPts[srcOff++] - M02) / M00;2807        dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M11;2808        }2809        return;2810    case (APPLY_SCALE):2811        M00 = m00; M11 = m11;2812        if (M00 == 0.0 || M11 == 0.0) {2813        throw new NoninvertibleTransformException ("Determinant is 0");2814        }2815        while (--numPts >= 0) {2816        dstPts[dstOff++] = srcPts[srcOff++] / M00;2817        dstPts[dstOff++] = srcPts[srcOff++] / M11;2818        }2819        return;2820    case (APPLY_TRANSLATE):2821        M02 = m02; M12 = m12;2822        while (--numPts >= 0) {2823        dstPts[dstOff++] = srcPts[srcOff++] - M02;2824        dstPts[dstOff++] = srcPts[srcOff++] - M12;2825        }2826        return;2827    case (APPLY_IDENTITY):2828        if (srcPts != dstPts || srcOff != dstOff) {2829        System.arraycopy(srcPts, srcOff, dstPts, dstOff,2830                 numPts * 2);2831        }2832        return;2833    }28342835    /* NOTREACHED */2836    }28372838    /**2839     * Transforms the relative distance vector specified by 2840     * ptSrc and stores the result in ptDst.2841     * A relative distance vector is transformed without applying the2842     * translation components of the affine transformation matrix2843     * using the following equations:2844     *

2845     *  [  x' ]   [  m00  m01 (m02) ] [  x  ]   [ m00x + m01y ]2846     *  [  y' ] = [  m10  m11 (m12) ] [  y  ] = [ m10x + m11y ]2847     *  [ (1) ]   [  (0)  (0) ( 1 ) ] [ (1) ]   [     (1)     ]2848     *
2849     * If ptDst is null, a new2850     * Point2D object is allocated and then the result of the2851     * transform is stored in this object.2852     * In either case, ptDst, which contains the2853     * transformed point, is returned for convenience.2854     * If ptSrc and ptDst are the same object,2855     * the input point is correctly overwritten with the transformed2856     * point.2857     * @param ptSrc the distance vector to be delta transformed2858     * @param ptDst the resulting transformed distance vector2859     * @return ptDst, which contains the result of the2860     * transformation.2861     */2862    public Point2D deltaTransform(Point2D ptSrc, Point2D ptDst) {2863    if (ptDst == null) {2864        if (ptSrc instanceof Point2D.Double ) {2865        ptDst = new Point2D.Double ();2866        } else {2867        ptDst = new Point2D.Float ();2868        }2869    }2870    // Copy source coords into local variables in case src == dst2871 double x = ptSrc.getX();2872    double y = ptSrc.getY();2873    switch (state) {2874    default:2875        stateError();2876        /* NOTREACHED */2877    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):2878    case (APPLY_SHEAR | APPLY_SCALE):2879        ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11);2880        return ptDst;2881    case (APPLY_SHEAR | APPLY_TRANSLATE):2882    case (APPLY_SHEAR):2883        ptDst.setLocation(y * m01, x * m10);2884        return ptDst;2885    case (APPLY_SCALE | APPLY_TRANSLATE):2886    case (APPLY_SCALE):2887        ptDst.setLocation(x * m00, y * m11);2888        return ptDst;2889    case (APPLY_TRANSLATE):2890    case (APPLY_IDENTITY):2891        ptDst.setLocation(x, y);2892        return ptDst;2893    }28942895    /* NOTREACHED */2896    }28972898    /**2899     * Transforms an array of relative distance vectors by this2900     * transform.2901     * A relative distance vector is transformed without applying the2902     * translation components of the affine transformation matrix2903     * using the following equations:2904     *
2905     *  [  x' ]   [  m00  m01 (m02) ] [  x  ]   [ m00x + m01y ]2906     *  [  y' ] = [  m10  m11 (m12) ] [  y  ] = [ m10x + m11y ]2907     *  [ (1) ]   [  (0)  (0) ( 1 ) ] [ (1) ]   [     (1)     ]2908     *
2909     * The two coordinate array sections can be exactly the same or2910     * can be overlapping sections of the same array without affecting the2911     * validity of the results.2912     * This method ensures that no source coordinates are2913     * overwritten by a previous operation before they can be transformed.2914     * The coordinates are stored in the arrays starting at the indicated2915     * offset in the order [x0, y0, x1, y1, ..., xn, yn].2916     * @param srcPts the array containing the source distance vectors.2917     * Each vector is stored as a pair of relative x, y coordinates.2918     * @param dstPts the array into which the transformed distance vectors2919     * are returned. Each vector is stored as a pair of relative2920     * x, y coordinates.2921     * @param srcOff the offset to the first vector to be transformed2922     * in the source array2923     * @param dstOff the offset to the location of the first2924     * transformed vector that is stored in the destination array2925     * @param numPts the number of vector coordinate pairs to be2926     * transformed2927     */2928    public void deltaTransform(double[] srcPts, int srcOff,2929                   double[] dstPts, int dstOff,2930                   int numPts) {2931    double M00, M01, M10, M11; // For caching2932 if (dstPts == srcPts &&2933        dstOff > srcOff && dstOff < srcOff + numPts * 2)2934    {2935        // If the arrays overlap partially with the destination higher2936 // than the source and we transform the coordinates normally2937 // we would overwrite some of the later source coordinates2938 // with results of previous transformations.2939 // To get around this we use arraycopy to copy the points2940 // to their final destination with correct overwrite2941 // handling and then transform them in place in the new2942 // safer location.2943 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);2944        // srcPts = dstPts; // They are known to be equal.2945 srcOff = dstOff;2946    }2947    switch (state) {2948    default:2949        stateError();2950        /* NOTREACHED */2951    case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):2952    case (APPLY_SHEAR | APPLY_SCALE):2953        M00 = m00; M01 = m01;2954        M10 = m10; M11 = m11;2955        while (--numPts >= 0) {2956        double x = srcPts[srcOff++];2957        double y = srcPts[srcOff++];2958        dstPts[dstOff++] = x * M00 + y * M01;2959        dstPts[dstOff++] = x * M10 + y * M11;2960        }2961        return;2962    case (APPLY_SHEAR | APPLY_TRANSLATE):2963    case (APPLY_SHEAR):2964        M01 = m01; M10 = m10;2965        while (--numPts >= 0) {2966        double x = srcPts[srcOff++];2967        dstPts[dstOff++] = srcPts[srcOff++] * M01;2968        dstPts[dstOff++] = x * M10;2969        }2970        return;2971    case (APPLY_SCALE | APPLY_TRANSLATE):2972    case (APPLY_SCALE):2973        M00 = m00; M11 = m11;2974        while (--numPts >= 0) {2975        dstPts[dstOff++] = srcPts[srcOff++] * M00;2976        dstPts[dstOff++] = srcPts[srcOff++] * M11;2977        }2978        return;2979    case (APPLY_TRANSLATE):2980    case (APPLY_IDENTITY):2981        if (srcPts != dstPts || srcOff != dstOff) {2982        System.arraycopy(srcPts, srcOff, dstPts, dstOff,2983                 numPts * 2);2984        }2985        return;2986    }29872988    /* NOTREACHED */2989    }29902991    /**2992     * Returns a new {@link Shape} object defined by the geometry of the2993     * specified Shape after it has been transformed by2994     * this transform.2995     * @param pSrc the specified Shape object to be2996     * transformed by this transform.2997     * @return a new Shape object that defines the geometry2998     * of the transformed Shape.2999     */3000    public Shape createTransformedShape(Shape pSrc) {3001        if (pSrc == null) {3002            return null;3003        }3004        3005        if (pSrc instanceof GeneralPath ) {3006            return ((GeneralPath )pSrc).createTransformedShape(this);3007        } else {3008            PathIterator pi = pSrc.getPathIterator(this);3009            GeneralPath gp = new GeneralPath (pi.getWindingRule());3010        gp.append(pi, false);3011            return gp;3012        }30133014    /* NOTREACHED */3015    }30163017    // Round values to sane precision for printing3018 // Note that Math.sin(Math.PI) has an error of about 10^-163019 private static double _matround(double matval) {3020    return Math.rint(matval * 1E15) / 1E15;3021    }30223023    /**3024     * Returns a String that represents the value of this3025     * {@link Object}.3026     * @return a String representing the value of this3027     * Object.3028     */3029    public String toString() {3030    return ("AffineTransform[["3031        + _matround(m00) + ", "3032        + _matround(m01) + ", "3033        + _matround(m02) + "], ["3034        + _matround(m10) + ", "3035        + _matround(m11) + ", "3036        + _matround(m12) + "]]");3037    }30383039    /**3040     * Returns true if this AffineTransform is3041     * an identity transform.3042     * @return true if this AffineTransform is3043     * an identity transform; false otherwise.3044     */3045    public boolean isIdentity() {3046        return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY));3047    }30483049    /**3050     * Returns a copy of this AffineTransform object.3051     * @return an Object that is a copy of this3052     * AffineTransform object.3053     */3054    public Object clone() {3055    try {3056        return super.clone();3057    } catch (CloneNotSupportedException e) { 3058        // this shouldn't happen, since we are Cloneable3059 throw new InternalError ();3060    }3061    }30623063    /**3064     * Returns the hashcode for this transform.3065     * @return a hash code for this transform.3066     */3067    public int hashCode() {3068    long bits = Double.doubleToLongBits(m00);3069    bits = bits * 31 + Double.doubleToLongBits(m01);3070    bits = bits * 31 + Double.doubleToLongBits(m02);3071    bits = bits * 31 + Double.doubleToLongBits(m10);3072    bits = bits * 31 + Double.doubleToLongBits(m11);3073    bits = bits * 31 + Double.doubleToLongBits(m12);3074    return (((int) bits) ^ ((int) (bits >> 32)));3075    }30763077    /**3078     * Returns true if this AffineTransform 3079     * represents the same affine coordinate transform as the specified3080     * argument.3081     * @param obj the Object to test for equality with this3082     * AffineTransform3083     * @return true if obj equals this3084     * AffineTransform object; false otherwise.3085     */3086    public boolean equals(Object obj) {3087        if (!(obj instanceof AffineTransform )) {3088            return false;3089        }30903091        AffineTransform a = (AffineTransform )obj;30923093    return ((m00 == a.m00) && (m01 == a.m01) && (m02 == a.m02) &&3094        (m10 == a.m10) && (m11 == a.m11) && (m12 == a.m12));3095    }30963097    /* Serialization support. A readObject method is neccessary because3098     * the state field is part of the implementation of this particular3099     * AffineTransform and not part of the public specification. The3100     * state variable's value needs to be recalculated on the fly by the3101     * readObject method as it is in the 6-argument matrix constructor.3102     */31033104    private void writeObject(java.io.ObjectOutputStream s)3105    throws java.lang.ClassNotFoundException , java.io.IOException 3106    {3107    s.defaultWriteObject();3108    }31093110    private void readObject(java.io.ObjectInputStream s)3111    throws java.lang.ClassNotFoundException , java.io.IOException 3112    {3113    s.defaultReadObject();3114    updateState();3115    }3116}3117` Popular Tags