/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.javafx.geom.transform;

import com.sun.javafx.geom.Point2D;

The Affine2D class represents a 2D affine transform that performs a linear mapping from 2D coordinates to other 2D coordinates that preserves the "straightness" and "parallelness" of lines. Affine transformations can be constructed using sequences of translations, scales, flips, rotations, and shears.

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

 [ x']   [  m00  m01  m02  ] [ x ]   [ m00x + m01y + m02 ]
 [ y'] = [  m10  m11  m12  ] [ y ] = [ m10x + m11y + m12 ]
 [ 1 ]   [   0    0    1   ] [ 1 ]   [         1         ]

Handling 90-Degree Rotations

In some variations of the rotate methods in the Affine2D class, a double-precision argument specifies the angle of rotation in radians. These methods have special handling for rotations of approximately 90 degrees (including multiples such as 180, 270, and 360 degrees), so that the common case of quadrant rotation is handled more efficiently. This special handling can cause angles very close to multiples of 90 degrees to be treated as if they were exact multiples of 90 degrees. For small multiples of 90 degrees the range of angles treated as a quadrant rotation is approximately 0.00000121 degrees wide. This section explains why such special care is needed and how it is implemented.

Since 90 degrees is represented as PI/2 in radians, and since PI is a transcendental (and therefore irrational) number, it is not possible to exactly represent a multiple of 90 degrees as an exact double precision value measured in radians. As a result it is theoretically impossible to describe quadrant rotations (90, 180, 270 or 360 degrees) using these values. Double precision floating point values can get very close to non-zero multiples of PI/2 but never close enough for the sine or cosine to be exactly 0.0, 1.0 or -1.0. The implementations of Math.sin() and Math.cos() correspondingly never return 0.0 for any case other than Math.sin(0.0). These same implementations do, however, return exactly 1.0 and -1.0 for some range of numbers around each multiple of 90 degrees since the correct answer is so close to 1.0 or -1.0 that the double precision significand cannot represent the difference as accurately as it can for numbers that are near 0.0.

The net result of these issues is that if the Math.sin() and Math.cos() methods are used to directly generate the values for the matrix modifications during these radian-based rotation operations then the resulting transform is never strictly classifiable as a quadrant rotation even for a simple case like rotate(Math.PI/2.0), due to minor variations in the matrix caused by the non-0.0 values obtained for the sine and cosine. If these transforms are not classified as quadrant rotations then subsequent code which attempts to optimize further operations based upon the type of the transform will be relegated to its most general implementation.

Because quadrant rotations are fairly common, this class should handle these cases reasonably quickly, both in applying the rotations to the transform and in applying the resulting transform to the coordinates. To facilitate this optimal handling, the methods which take an angle of rotation measured in radians attempt to detect angles that are intended to be quadrant rotations and treat them as such. These methods therefore treat an angle theta as a quadrant rotation if either Math.sin(theta) or Math.cos(theta) returns exactly 1.0 or -1.0. As a rule of thumb, this property holds true for a range of approximately 0.0000000211 radians (or 0.00000121 degrees) around small multiples of Math.PI/2.0.

Version:1.83, 05/05/07
/** * The <code>Affine2D</code> class represents a 2D affine transform * that performs a linear mapping from 2D coordinates to other 2D * coordinates that preserves the "straightness" and * "parallelness" of lines. Affine transformations can be constructed * using sequences of translations, scales, flips, rotations, and shears. * <p> * Such a coordinate transformation can be represented by a 3 row by * 3 column matrix with an implied last row of [ 0 0 1 ]. This matrix * transforms source coordinates {@code (x,y)} into * destination coordinates {@code (x',y')} by considering * them to be a column vector and multiplying the coordinate vector * by the matrix according to the following process: * <pre> * [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ] * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ] * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ] * </pre> * <p> * <a name="quadrantapproximation"><h4>Handling 90-Degree Rotations</h4></a> * <p> * In some variations of the <code>rotate</code> methods in the * <code>Affine2D</code> class, a double-precision argument * specifies the angle of rotation in radians. * These methods have special handling for rotations of approximately * 90 degrees (including multiples such as 180, 270, and 360 degrees), * so that the common case of quadrant rotation is handled more * efficiently. * This special handling can cause angles very close to multiples of * 90 degrees to be treated as if they were exact multiples of * 90 degrees. * For small multiples of 90 degrees the range of angles treated * as a quadrant rotation is approximately 0.00000121 degrees wide. * This section explains why such special care is needed and how * it is implemented. * <p> * Since 90 degrees is represented as <code>PI/2</code> in radians, * and since PI is a transcendental (and therefore irrational) number, * it is not possible to exactly represent a multiple of 90 degrees as * an exact double precision value measured in radians. * As a result it is theoretically impossible to describe quadrant * rotations (90, 180, 270 or 360 degrees) using these values. * Double precision floating point values can get very close to * non-zero multiples of <code>PI/2</code> but never close enough * for the sine or cosine to be exactly 0.0, 1.0 or -1.0. * The implementations of <code>Math.sin()</code> and * <code>Math.cos()</code> correspondingly never return 0.0 * for any case other than <code>Math.sin(0.0)</code>. * These same implementations do, however, return exactly 1.0 and * -1.0 for some range of numbers around each multiple of 90 * degrees since the correct answer is so close to 1.0 or -1.0 that * the double precision significand cannot represent the difference * as accurately as it can for numbers that are near 0.0. * <p> * The net result of these issues is that if the * <code>Math.sin()</code> and <code>Math.cos()</code> methods * are used to directly generate the values for the matrix modifications * during these radian-based rotation operations then the resulting * transform is never strictly classifiable as a quadrant rotation * even for a simple case like <code>rotate(Math.PI/2.0)</code>, * due to minor variations in the matrix caused by the non-0.0 values * obtained for the sine and cosine. * If these transforms are not classified as quadrant rotations then * subsequent code which attempts to optimize further operations based * upon the type of the transform will be relegated to its most general * implementation. * <p> * Because quadrant rotations are fairly common, * this class should handle these cases reasonably quickly, both in * applying the rotations to the transform and in applying the resulting * transform to the coordinates. * To facilitate this optimal handling, the methods which take an angle * of rotation measured in radians attempt to detect angles that are * intended to be quadrant rotations and treat them as such. * These methods therefore treat an angle <em>theta</em> as a quadrant * rotation if either <code>Math.sin(<em>theta</em>)</code> or * <code>Math.cos(<em>theta</em>)</code> returns exactly 1.0 or -1.0. * As a rule of thumb, this property holds true for a range of * approximately 0.0000000211 radians (or 0.00000121 degrees) around * small multiples of <code>Math.PI/2.0</code>. * * @version 1.83, 05/05/07 */
public class Affine2D extends AffineBase { private Affine2D(double mxx, double myx, double mxy, double myy, double mxt, double myt, int state) { this.mxx = mxx; this.myx = myx; this.mxy = mxy; this.myy = myy; this.mxt = mxt; this.myt = myt; this.state = state; this.type = TYPE_UNKNOWN; }
Constructs a new Affine2D representing the Identity transformation.
/** * Constructs a new <code>Affine2D</code> representing the * Identity transformation. */
public Affine2D() { mxx = myy = 1.0; // m01 = m10 = m02 = m12 = 0.0; /* Not needed. */ // state = APPLY_IDENTITY; /* Not needed. */ // type = TYPE_IDENTITY; /* Not needed. */ }
Constructs a new Affine2D that uses the same transform as the specified BaseTransform object.
Params:
  • Tx – the BaseTransform object to copy
/** * Constructs a new <code>Affine2D</code> that uses the same transform * as the specified <code>BaseTransform</code> object. * @param Tx the <code>BaseTransform</code> object to copy */
public Affine2D(BaseTransform Tx) { setTransform(Tx); }
Constructs a new Affine2D from 6 floating point values representing the 6 specifiable entries of the 3x3 transformation matrix.
Params:
  • mxx – the X coordinate scaling element of the 3x3 matrix
  • myx – the Y coordinate shearing element of the 3x3 matrix
  • mxy – the X coordinate shearing element of the 3x3 matrix
  • myy – the Y coordinate scaling element of the 3x3 matrix
  • mxt – the X coordinate translation element of the 3x3 matrix
  • myt – the Y coordinate translation element of the 3x3 matrix
/** * Constructs a new <code>Affine2D</code> from 6 floating point * values representing the 6 specifiable entries of the 3x3 * transformation matrix. * * @param mxx the X coordinate scaling element of the 3x3 matrix * @param myx the Y coordinate shearing element of the 3x3 matrix * @param mxy the X coordinate shearing element of the 3x3 matrix * @param myy the Y coordinate scaling element of the 3x3 matrix * @param mxt the X coordinate translation element of the 3x3 matrix * @param myt the Y coordinate translation element of the 3x3 matrix */
public Affine2D(float mxx, float myx, float mxy, float myy, float mxt, float myt) { this.mxx = mxx; this.myx = myx; this.mxy = mxy; this.myy = myy; this.mxt = mxt; this.myt = myt; updateState2D(); }
Constructs a new Affine2D from 6 double precision values representing the 6 specifiable entries of the 3x3 transformation matrix.
Params:
  • mxx – the X coordinate scaling element of the 3x3 matrix
  • myx – the Y coordinate shearing element of the 3x3 matrix
  • mxy – the X coordinate shearing element of the 3x3 matrix
  • myy – the Y coordinate scaling element of the 3x3 matrix
  • mxt – the X coordinate translation element of the 3x3 matrix
  • myt – the Y coordinate translation element of the 3x3 matrix
/** * Constructs a new <code>Affine2D</code> from 6 double * precision values representing the 6 specifiable entries of the 3x3 * transformation matrix. * * @param mxx the X coordinate scaling element of the 3x3 matrix * @param myx the Y coordinate shearing element of the 3x3 matrix * @param mxy the X coordinate shearing element of the 3x3 matrix * @param myy the Y coordinate scaling element of the 3x3 matrix * @param mxt the X coordinate translation element of the 3x3 matrix * @param myt the Y coordinate translation element of the 3x3 matrix */
public Affine2D(double mxx, double myx, double mxy, double myy, double mxt, double myt) { this.mxx = mxx; this.myx = myx; this.mxy = mxy; this.myy = myy; this.mxt = mxt; this.myt = myt; updateState2D(); } @Override public Degree getDegree() { return Degree.AFFINE_2D; } @Override protected void reset3Delements() { /* NOP for Affine2D */ }
Concatenates this transform with a transform that rotates coordinates around an anchor point. This operation is equivalent to translating the coordinates so that the anchor point is at the origin (S1), then rotating them about the new origin (S2), and finally translating so that the intermediate origin is restored to the coordinates of the original anchor point (S3).

This operation is equivalent to the following sequence of calls:

    translate(anchorx, anchory);      // S3: final translation
    rotate(theta);                    // S2: rotate around anchor
    translate(-anchorx, -anchory);    // S1: translate anchor to origin
Rotating by a positive angle theta rotates points on the positive X axis toward the positive Y axis. Note also the discussion of Handling 90-Degree Rotations above.
Params:
  • theta – the angle of rotation measured in radians
  • anchorx – the X coordinate of the rotation anchor point
  • anchory – the Y coordinate of the rotation anchor point
/** * Concatenates this transform with a transform that rotates * coordinates around an anchor point. * This operation is equivalent to translating the coordinates so * that the anchor point is at the origin (S1), then rotating them * about the new origin (S2), and finally translating so that the * intermediate origin is restored to the coordinates of the original * anchor point (S3). * <p> * This operation is equivalent to the following sequence of calls: * <pre> * translate(anchorx, anchory); // S3: final translation * rotate(theta); // S2: rotate around anchor * translate(-anchorx, -anchory); // S1: translate anchor to origin * </pre> * Rotating by a positive angle theta rotates points on the positive * X axis toward the positive Y axis. * Note also the discussion of * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> * above. * * @param theta the angle of rotation measured in radians * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point */
public void rotate(double theta, double anchorx, double anchory) { // REMIND: Simple for now - optimize later translate(anchorx, anchory); rotate(theta); translate(-anchorx, -anchory); }
Concatenates this transform with a transform that rotates coordinates according to a rotation vector. All coordinates rotate about the origin by the same amount. The amount of rotation is such that coordinates along the former positive X axis will subsequently align with the vector pointing from the origin to the specified vector coordinates. If both vecx and vecy are 0.0, no additional rotation is added to this transform. This operation is equivalent to calling:
         rotate(Math.atan2(vecy, vecx));
Params:
  • vecx – the X coordinate of the rotation vector
  • vecy – the Y coordinate of the rotation vector
/** * Concatenates this transform with a transform that rotates * coordinates according to a rotation vector. * All coordinates rotate about the origin by the same amount. * The amount of rotation is such that coordinates along the former * positive X axis will subsequently align with the vector pointing * from the origin to the specified vector coordinates. * If both <code>vecx</code> and <code>vecy</code> are 0.0, * no additional rotation is added to this transform. * This operation is equivalent to calling: * <pre> * rotate(Math.atan2(vecy, vecx)); * </pre> * * @param vecx the X coordinate of the rotation vector * @param vecy the Y coordinate of the rotation vector */
public void rotate(double vecx, double vecy) { if (vecy == 0.0) { if (vecx < 0.0) { rotate180(); } // If vecx > 0.0 - no rotation // If vecx == 0.0 - undefined rotation - treat as no rotation } else if (vecx == 0.0) { if (vecy > 0.0) { rotate90(); } else { // vecy must be < 0.0 rotate270(); } } else { double len = Math.sqrt(vecx * vecx + vecy * vecy); double sin = vecy / len; double cos = vecx / len; double M0, M1; M0 = mxx; M1 = mxy; mxx = cos * M0 + sin * M1; mxy = -sin * M0 + cos * M1; M0 = myx; M1 = myy; myx = cos * M0 + sin * M1; myy = -sin * M0 + cos * M1; updateState2D(); } }
Concatenates this transform with a transform that rotates coordinates around an anchor point according to a rotation vector. All coordinates rotate about the specified anchor coordinates by the same amount. The amount of rotation is such that coordinates along the former positive X axis will subsequently align with the vector pointing from the origin to the specified vector coordinates. If both vecx and vecy are 0.0, the transform is not modified in any way. This method is equivalent to calling:
    rotate(Math.atan2(vecy, vecx), anchorx, anchory);
Params:
  • vecx – the X coordinate of the rotation vector
  • vecy – the Y coordinate of the rotation vector
  • anchorx – the X coordinate of the rotation anchor point
  • anchory – the Y coordinate of the rotation anchor point
/** * Concatenates this transform with a transform that rotates * coordinates around an anchor point according to a rotation * vector. * All coordinates rotate about the specified anchor coordinates * by the same amount. * The amount of rotation is such that coordinates along the former * positive X axis will subsequently align with the vector pointing * from the origin to the specified vector coordinates. * If both <code>vecx</code> and <code>vecy</code> are 0.0, * the transform is not modified in any way. * This method is equivalent to calling: * <pre> * rotate(Math.atan2(vecy, vecx), anchorx, anchory); * </pre> * * @param vecx the X coordinate of the rotation vector * @param vecy the Y coordinate of the rotation vector * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point */
public void rotate(double vecx, double vecy, double anchorx, double anchory) { // REMIND: Simple for now - optimize later translate(anchorx, anchory); rotate(vecx, vecy); translate(-anchorx, -anchory); }
Concatenates this transform with a transform that rotates coordinates by the specified number of quadrants. This is equivalent to calling:
    rotate(numquadrants * Math.PI / 2.0);
Rotating by a positive number of quadrants rotates points on the positive X axis toward the positive Y axis.
Params:
  • numquadrants – the number of 90 degree arcs to rotate by
/** * Concatenates this transform with a transform that rotates * coordinates by the specified number of quadrants. * This is equivalent to calling: * <pre> * rotate(numquadrants * Math.PI / 2.0); * </pre> * Rotating by a positive number of quadrants rotates points on * the positive X axis toward the positive Y axis. * @param numquadrants the number of 90 degree arcs to rotate by */
public void quadrantRotate(int numquadrants) { switch (numquadrants & 3) { case 0: break; case 1: rotate90(); break; case 2: rotate180(); break; case 3: rotate270(); break; } }
Concatenates this transform with a transform that rotates coordinates by the specified number of quadrants around the specified anchor point. This method is equivalent to calling:
    rotate(numquadrants * Math.PI / 2.0, anchorx, anchory);
Rotating by a positive number of quadrants rotates points on the positive X axis toward the positive Y axis.
Params:
  • numquadrants – the number of 90 degree arcs to rotate by
  • anchorx – the X coordinate of the rotation anchor point
  • anchory – the Y coordinate of the rotation anchor point
/** * Concatenates this transform with a transform that rotates * coordinates by the specified number of quadrants around * the specified anchor point. * This method is equivalent to calling: * <pre> * rotate(numquadrants * Math.PI / 2.0, anchorx, anchory); * </pre> * Rotating by a positive number of quadrants rotates points on * the positive X axis toward the positive Y axis. * * @param numquadrants the number of 90 degree arcs to rotate by * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point */
public void quadrantRotate(int numquadrants, double anchorx, double anchory) { switch (numquadrants & 3) { case 0: return; case 1: mxt += anchorx * (mxx - mxy) + anchory * (mxy + mxx); myt += anchorx * (myx - myy) + anchory * (myy + myx); rotate90(); break; case 2: mxt += anchorx * (mxx + mxx) + anchory * (mxy + mxy); myt += anchorx * (myx + myx) + anchory * (myy + myy); rotate180(); break; case 3: mxt += anchorx * (mxx + mxy) + anchory * (mxy - mxx); myt += anchorx * (myx + myy) + anchory * (myy - myx); rotate270(); break; } if (mxt == 0.0 && myt == 0.0) { state &= ~APPLY_TRANSLATE; if (type != TYPE_UNKNOWN) { type &= ~TYPE_TRANSLATION; } } else { state |= APPLY_TRANSLATE; type |= TYPE_TRANSLATION; } }
Sets this transform to a translation transformation. The matrix representing this transform becomes:
     [   1    0    tx  ]
     [   0    1    ty  ]
     [   0    0    1   ]
Params:
  • tx – the distance by which coordinates are translated in the X axis direction
  • ty – the distance by which coordinates are translated in the Y axis direction
/** * Sets this transform to a translation transformation. * The matrix representing this transform becomes: * <pre> * [ 1 0 tx ] * [ 0 1 ty ] * [ 0 0 1 ] * </pre> * @param tx the distance by which coordinates are translated in the * X axis direction * @param ty the distance by which coordinates are translated in the * Y axis direction */
public void setToTranslation(double tx, double ty) { mxx = 1.0; myx = 0.0; mxy = 0.0; myy = 1.0; mxt = tx; myt = ty; if (tx != 0.0 || ty != 0.0) { state = APPLY_TRANSLATE; type = TYPE_TRANSLATION; } else { state = APPLY_IDENTITY; type = TYPE_IDENTITY; } }
Sets this transform to a rotation transformation. The matrix representing this transform becomes:
     [   cos(theta)    -sin(theta)    0   ]
     [   sin(theta)     cos(theta)    0   ]
     [       0              0         1   ]
Rotating by a positive angle theta rotates points on the positive X axis toward the positive Y axis. Note also the discussion of Handling 90-Degree Rotations above.
Params:
  • theta – the angle of rotation measured in radians
/** * Sets this transform to a rotation transformation. * The matrix representing this transform becomes: * <pre> * [ cos(theta) -sin(theta) 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 0 1 ] * </pre> * Rotating by a positive angle theta rotates points on the positive * X axis toward the positive Y axis. * Note also the discussion of * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> * above. * @param theta the angle of rotation measured in radians */
public void setToRotation(double theta) { double sin = Math.sin(theta); double cos; if (sin == 1.0 || sin == -1.0) { cos = 0.0; state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; } else { cos = Math.cos(theta); if (cos == -1.0) { sin = 0.0; state = APPLY_SCALE; type = TYPE_QUADRANT_ROTATION; } else if (cos == 1.0) { sin = 0.0; state = APPLY_IDENTITY; type = TYPE_IDENTITY; } else { state = APPLY_SHEAR | APPLY_SCALE; type = TYPE_GENERAL_ROTATION; } } mxx = cos; myx = sin; mxy = -sin; myy = cos; mxt = 0.0; myt = 0.0; }
Sets this transform to a translated rotation transformation. This operation is equivalent to translating the coordinates so that the anchor point is at the origin (S1), then rotating them about the new origin (S2), and finally translating so that the intermediate origin is restored to the coordinates of the original anchor point (S3).

This operation is equivalent to the following sequence of calls:

    setToTranslation(anchorx, anchory); // S3: final translation
    rotate(theta);                      // S2: rotate around anchor
    translate(-anchorx, -anchory);      // S1: translate anchor to origin
The matrix representing this transform becomes:
     [   cos(theta)    -sin(theta)    x-x*cos+y*sin  ]
     [   sin(theta)     cos(theta)    y-x*sin-y*cos  ]
     [       0              0               1        ]
Rotating by a positive angle theta rotates points on the positive X axis toward the positive Y axis. Note also the discussion of Handling 90-Degree Rotations above.
Params:
  • theta – the angle of rotation measured in radians
  • anchorx – the X coordinate of the rotation anchor point
  • anchory – the Y coordinate of the rotation anchor point
/** * Sets this transform to a translated rotation transformation. * This operation is equivalent to translating the coordinates so * that the anchor point is at the origin (S1), then rotating them * about the new origin (S2), and finally translating so that the * intermediate origin is restored to the coordinates of the original * anchor point (S3). * <p> * This operation is equivalent to the following sequence of calls: * <pre> * setToTranslation(anchorx, anchory); // S3: final translation * rotate(theta); // S2: rotate around anchor * translate(-anchorx, -anchory); // S1: translate anchor to origin * </pre> * The matrix representing this transform becomes: * <pre> * [ cos(theta) -sin(theta) x-x*cos+y*sin ] * [ sin(theta) cos(theta) y-x*sin-y*cos ] * [ 0 0 1 ] * </pre> * Rotating by a positive angle theta rotates points on the positive * X axis toward the positive Y axis. * Note also the discussion of * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> * above. * * @param theta the angle of rotation measured in radians * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point */
public void setToRotation(double theta, double anchorx, double anchory) { setToRotation(theta); double sin = myx; double oneMinusCos = 1.0 - mxx; mxt = anchorx * oneMinusCos + anchory * sin; myt = anchory * oneMinusCos - anchorx * sin; if (mxt != 0.0 || myt != 0.0) { state |= APPLY_TRANSLATE; type |= TYPE_TRANSLATION; } }
Sets this transform to a rotation transformation that rotates coordinates according to a rotation vector. All coordinates rotate about the origin by the same amount. The amount of rotation is such that coordinates along the former positive X axis will subsequently align with the vector pointing from the origin to the specified vector coordinates. If both vecx and vecy are 0.0, the transform is set to an identity transform. This operation is equivalent to calling:
    setToRotation(Math.atan2(vecy, vecx));
Params:
  • vecx – the X coordinate of the rotation vector
  • vecy – the Y coordinate of the rotation vector
/** * Sets this transform to a rotation transformation that rotates * coordinates according to a rotation vector. * All coordinates rotate about the origin by the same amount. * The amount of rotation is such that coordinates along the former * positive X axis will subsequently align with the vector pointing * from the origin to the specified vector coordinates. * If both <code>vecx</code> and <code>vecy</code> are 0.0, * the transform is set to an identity transform. * This operation is equivalent to calling: * <pre> * setToRotation(Math.atan2(vecy, vecx)); * </pre> * * @param vecx the X coordinate of the rotation vector * @param vecy the Y coordinate of the rotation vector */
public void setToRotation(double vecx, double vecy) { double sin, cos; if (vecy == 0) { sin = 0.0; if (vecx < 0.0) { cos = -1.0; state = APPLY_SCALE; type = TYPE_QUADRANT_ROTATION; } else { cos = 1.0; state = APPLY_IDENTITY; type = TYPE_IDENTITY; } } else if (vecx == 0) { cos = 0.0; sin = (vecy > 0.0) ? 1.0 : -1.0; state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; } else { double len = Math.sqrt(vecx * vecx + vecy * vecy); cos = vecx / len; sin = vecy / len; state = APPLY_SHEAR | APPLY_SCALE; type = TYPE_GENERAL_ROTATION; } mxx = cos; myx = sin; mxy = -sin; myy = cos; mxt = 0.0; myt = 0.0; }
Sets this transform to a rotation transformation that rotates coordinates around an anchor point according to a rotation vector. All coordinates rotate about the specified anchor coordinates by the same amount. The amount of rotation is such that coordinates along the former positive X axis will subsequently align with the vector pointing from the origin to the specified vector coordinates. If both vecx and vecy are 0.0, the transform is set to an identity transform. This operation is equivalent to calling:
    setToTranslation(Math.atan2(vecy, vecx), anchorx, anchory);
Params:
  • vecx – the X coordinate of the rotation vector
  • vecy – the Y coordinate of the rotation vector
  • anchorx – the X coordinate of the rotation anchor point
  • anchory – the Y coordinate of the rotation anchor point
/** * Sets this transform to a rotation transformation that rotates * coordinates around an anchor point according to a rotation * vector. * All coordinates rotate about the specified anchor coordinates * by the same amount. * The amount of rotation is such that coordinates along the former * positive X axis will subsequently align with the vector pointing * from the origin to the specified vector coordinates. * If both <code>vecx</code> and <code>vecy</code> are 0.0, * the transform is set to an identity transform. * This operation is equivalent to calling: * <pre> * setToTranslation(Math.atan2(vecy, vecx), anchorx, anchory); * </pre> * * @param vecx the X coordinate of the rotation vector * @param vecy the Y coordinate of the rotation vector * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point */
public void setToRotation(double vecx, double vecy, double anchorx, double anchory) { setToRotation(vecx, vecy); double sin = myx; double oneMinusCos = 1.0 - mxx; mxt = anchorx * oneMinusCos + anchory * sin; myt = anchory * oneMinusCos - anchorx * sin; if (mxt != 0.0 || myt != 0.0) { state |= APPLY_TRANSLATE; type |= TYPE_TRANSLATION; } }
Sets this transform to a rotation transformation that rotates coordinates by the specified number of quadrants. This operation is equivalent to calling:
    setToRotation(numquadrants * Math.PI / 2.0);
Rotating by a positive number of quadrants rotates points on the positive X axis toward the positive Y axis.
Params:
  • numquadrants – the number of 90 degree arcs to rotate by
/** * Sets this transform to a rotation transformation that rotates * coordinates by the specified number of quadrants. * This operation is equivalent to calling: * <pre> * setToRotation(numquadrants * Math.PI / 2.0); * </pre> * Rotating by a positive number of quadrants rotates points on * the positive X axis toward the positive Y axis. * @param numquadrants the number of 90 degree arcs to rotate by */
public void setToQuadrantRotation(int numquadrants) { switch (numquadrants & 3) { case 0: mxx = 1.0; myx = 0.0; mxy = 0.0; myy = 1.0; mxt = 0.0; myt = 0.0; state = APPLY_IDENTITY; type = TYPE_IDENTITY; break; case 1: mxx = 0.0; myx = 1.0; mxy = -1.0; myy = 0.0; mxt = 0.0; myt = 0.0; state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; break; case 2: mxx = -1.0; myx = 0.0; mxy = 0.0; myy = -1.0; mxt = 0.0; myt = 0.0; state = APPLY_SCALE; type = TYPE_QUADRANT_ROTATION; break; case 3: mxx = 0.0; myx = -1.0; mxy = 1.0; myy = 0.0; mxt = 0.0; myt = 0.0; state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; break; } }
Sets this transform to a translated rotation transformation that rotates coordinates by the specified number of quadrants around the specified anchor point. This operation is equivalent to calling:
    setToRotation(numquadrants * Math.PI / 2.0, anchorx, anchory);
Rotating by a positive number of quadrants rotates points on the positive X axis toward the positive Y axis.
Params:
  • numquadrants – the number of 90 degree arcs to rotate by
  • anchorx – the X coordinate of the rotation anchor point
  • anchory – the Y coordinate of the rotation anchor point
/** * Sets this transform to a translated rotation transformation * that rotates coordinates by the specified number of quadrants * around the specified anchor point. * This operation is equivalent to calling: * <pre> * setToRotation(numquadrants * Math.PI / 2.0, anchorx, anchory); * </pre> * Rotating by a positive number of quadrants rotates points on * the positive X axis toward the positive Y axis. * * @param numquadrants the number of 90 degree arcs to rotate by * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point */
public void setToQuadrantRotation(int numquadrants, double anchorx, double anchory) { switch (numquadrants & 3) { case 0: mxx = 1.0; myx = 0.0; mxy = 0.0; myy = 1.0; mxt = 0.0; myt = 0.0; state = APPLY_IDENTITY; type = TYPE_IDENTITY; break; case 1: mxx = 0.0; myx = 1.0; mxy = -1.0; myy = 0.0; mxt = anchorx + anchory; myt = anchory - anchorx; if (mxt == 0.0 && myt == 0.0) { state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; } else { state = APPLY_SHEAR | APPLY_TRANSLATE; type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; } break; case 2: mxx = -1.0; myx = 0.0; mxy = 0.0; myy = -1.0; mxt = anchorx + anchorx; myt = anchory + anchory; if (mxt == 0.0 && myt == 0.0) { state = APPLY_SCALE; type = TYPE_QUADRANT_ROTATION; } else { state = APPLY_SCALE | APPLY_TRANSLATE; type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; } break; case 3: mxx = 0.0; myx = -1.0; mxy = 1.0; myy = 0.0; mxt = anchorx - anchory; myt = anchory + anchorx; if (mxt == 0.0 && myt == 0.0) { state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; } else { state = APPLY_SHEAR | APPLY_TRANSLATE; type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; } break; } }
Sets this transform to a scaling transformation. The matrix representing this transform becomes:
     [   sx   0    0   ]
     [   0    sy   0   ]
     [   0    0    1   ]
Params:
  • sx – the factor by which coordinates are scaled along the X axis direction
  • sy – the factor by which coordinates are scaled along the Y axis direction
/** * Sets this transform to a scaling transformation. * The matrix representing this transform becomes: * <pre> * [ sx 0 0 ] * [ 0 sy 0 ] * [ 0 0 1 ] * </pre> * @param sx the factor by which coordinates are scaled along the * X axis direction * @param sy the factor by which coordinates are scaled along the * Y axis direction */
public void setToScale(double sx, double sy) { mxx = sx; myx = 0.0; mxy = 0.0; myy = sy; mxt = 0.0; myt = 0.0; if (sx != 1.0 || sy != 1.0) { state = APPLY_SCALE; type = TYPE_UNKNOWN; } else { state = APPLY_IDENTITY; type = TYPE_IDENTITY; } }
Sets this transform to a copy of the transform in the specified BaseTransform object.
Params:
  • Tx – the BaseTransform object from which to copy the transform
/** * Sets this transform to a copy of the transform in the specified * <code>BaseTransform</code> object. * @param Tx the <code>BaseTransform</code> object from which to * copy the transform */
public void setTransform(BaseTransform Tx) { switch (Tx.getDegree()) { case IDENTITY: setToIdentity(); break; case TRANSLATE_2D: setToTranslation(Tx.getMxt(), Tx.getMyt()); break; default: if (Tx.getType() > TYPE_AFFINE2D_MASK) { System.out.println(Tx+" is "+Tx.getType()); System.out.print(" "+Tx.getMxx()); System.out.print(", "+Tx.getMxy()); System.out.print(", "+Tx.getMxz()); System.out.print(", "+Tx.getMxt()); System.out.println(); System.out.print(" "+Tx.getMyx()); System.out.print(", "+Tx.getMyy()); System.out.print(", "+Tx.getMyz()); System.out.print(", "+Tx.getMyt()); System.out.println(); System.out.print(" "+Tx.getMzx()); System.out.print(", "+Tx.getMzy()); System.out.print(", "+Tx.getMzz()); System.out.print(", "+Tx.getMzt()); System.out.println(); // TODO: Should this be thrown before we modify anything? // (RT-26801) degreeError(Degree.AFFINE_2D); } /* No Break */ case AFFINE_2D: this.mxx = Tx.getMxx(); this.myx = Tx.getMyx(); this.mxy = Tx.getMxy(); this.myy = Tx.getMyy(); this.mxt = Tx.getMxt(); this.myt = Tx.getMyt(); if (Tx instanceof AffineBase) { this.state = ((AffineBase) Tx).state; this.type = ((AffineBase) Tx).type; } else { updateState2D(); } break; } }
Concatenates a BaseTransform Tx to this Affine2D Cx in a less commonly used way such that Tx modifies the coordinate transformation relative to the absolute pixel space rather than relative to the existing user space. Cx is updated to perform the combined transformation. Transforming a point p by the updated transform Cx' is equivalent to first transforming p by the original transform Cx and then transforming the result by Tx like this: Cx'(p) = Tx(Cx(p)) In matrix notation, if this transform Cx is represented by the matrix [this] and Tx is represented by the matrix [Tx] then this method does the following:
     [this] = [Tx] x [this]
Params:
  • Tx – the BaseTransform object to be concatenated with this Affine2D object.
See Also:
/** * Concatenates a <code>BaseTransform</code> <code>Tx</code> to * this <code>Affine2D</code> Cx * in a less commonly used way such that <code>Tx</code> modifies the * coordinate transformation relative to the absolute pixel * space rather than relative to the existing user space. * Cx is updated to perform the combined transformation. * Transforming a point p by the updated transform Cx' is * equivalent to first transforming p by the original transform * Cx and then transforming the result by * <code>Tx</code> like this: * Cx'(p) = Tx(Cx(p)) * In matrix notation, if this transform Cx * is represented by the matrix [this] and <code>Tx</code> is * represented by the matrix [Tx] then this method does the * following: * <pre> * [this] = [Tx] x [this] * </pre> * @param Tx the <code>BaseTransform</code> object to be * concatenated with this <code>Affine2D</code> object. * @see #concatenate */
public void preConcatenate(BaseTransform Tx) { switch (Tx.getDegree()) { case IDENTITY: return; case TRANSLATE_2D: translate(Tx.getMxt(), Tx.getMyt()); return; case AFFINE_2D: break; default: degreeError(Degree.AFFINE_2D); } double M0, M1; double Txx, Txy, Tyx, Tyy; double Txt, Tyt; int mystate = state; Affine2D at = (Affine2D) Tx; int txstate = at.state; switch ((txstate << HI_SHIFT) | mystate) { case (HI_IDENTITY | APPLY_IDENTITY): case (HI_IDENTITY | APPLY_TRANSLATE): case (HI_IDENTITY | APPLY_SCALE): case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE): case (HI_IDENTITY | APPLY_SHEAR): case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE): case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE): case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // Tx is IDENTITY... return; case (HI_TRANSLATE | APPLY_IDENTITY): case (HI_TRANSLATE | APPLY_SCALE): case (HI_TRANSLATE | APPLY_SHEAR): case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE): // Tx is TRANSLATE, this has no TRANSLATE mxt = at.mxt; myt = at.myt; state = mystate | APPLY_TRANSLATE; type |= TYPE_TRANSLATION; return; case (HI_TRANSLATE | APPLY_TRANSLATE): case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE): case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE): case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // Tx is TRANSLATE, this has one too mxt = mxt + at.mxt; myt = myt + at.myt; return; case (HI_SCALE | APPLY_TRANSLATE): case (HI_SCALE | APPLY_IDENTITY): // Only these two existing states need a new state state = mystate | APPLY_SCALE; /* NOBREAK */ case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE): case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE): case (HI_SCALE | APPLY_SHEAR): case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE): case (HI_SCALE | APPLY_SCALE): // Tx is SCALE, this is anything Txx = at.mxx; Tyy = at.myy; if ((mystate & APPLY_SHEAR) != 0) { mxy = mxy * Txx; myx = myx * Tyy; if ((mystate & APPLY_SCALE) != 0) { mxx = mxx * Txx; myy = myy * Tyy; } } else { mxx = mxx * Txx; myy = myy * Tyy; } if ((mystate & APPLY_TRANSLATE) != 0) { mxt = mxt * Txx; myt = myt * Tyy; } type = TYPE_UNKNOWN; return; case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE): case (HI_SHEAR | APPLY_SHEAR): mystate = mystate | APPLY_SCALE; /* NOBREAK */ case (HI_SHEAR | APPLY_TRANSLATE): case (HI_SHEAR | APPLY_IDENTITY): case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (HI_SHEAR | APPLY_SCALE): state = mystate ^ APPLY_SHEAR; /* NOBREAK */ case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE): // Tx is SHEAR, this is anything Txy = at.mxy; Tyx = at.myx; M0 = mxx; mxx = myx * Txy; myx = M0 * Tyx; M0 = mxy; mxy = myy * Txy; myy = M0 * Tyx; M0 = mxt; mxt = myt * Txy; myt = M0 * Tyx; type = TYPE_UNKNOWN; return; } // If Tx has more than one attribute, it is not worth optimizing // all of those cases... Txx = at.mxx; Txy = at.mxy; Txt = at.mxt; Tyx = at.myx; Tyy = at.myy; Tyt = at.myt; switch (mystate) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): M0 = mxt; M1 = myt; Txt += M0 * Txx + M1 * Txy; Tyt += M0 * Tyx + M1 * Tyy; /* NOBREAK */ case (APPLY_SHEAR | APPLY_SCALE): mxt = Txt; myt = Tyt; M0 = mxx; M1 = myx; mxx = M0 * Txx + M1 * Txy; myx = M0 * Tyx + M1 * Tyy; M0 = mxy; M1 = myy; mxy = M0 * Txx + M1 * Txy; myy = M0 * Tyx + M1 * Tyy; break; case (APPLY_SHEAR | APPLY_TRANSLATE): M0 = mxt; M1 = myt; Txt += M0 * Txx + M1 * Txy; Tyt += M0 * Tyx + M1 * Tyy; /* NOBREAK */ case (APPLY_SHEAR): mxt = Txt; myt = Tyt; M0 = myx; mxx = M0 * Txy; myx = M0 * Tyy; M0 = mxy; mxy = M0 * Txx; myy = M0 * Tyx; break; case (APPLY_SCALE | APPLY_TRANSLATE): M0 = mxt; M1 = myt; Txt += M0 * Txx + M1 * Txy; Tyt += M0 * Tyx + M1 * Tyy; /* NOBREAK */ case (APPLY_SCALE): mxt = Txt; myt = Tyt; M0 = mxx; mxx = M0 * Txx; myx = M0 * Tyx; M0 = myy; mxy = M0 * Txy; myy = M0 * Tyy; break; case (APPLY_TRANSLATE): M0 = mxt; M1 = myt; Txt += M0 * Txx + M1 * Txy; Tyt += M0 * Tyx + M1 * Tyy; /* NOBREAK */ case (APPLY_IDENTITY): mxt = Txt; myt = Tyt; mxx = Txx; myx = Tyx; mxy = Txy; myy = Tyy; state = mystate | txstate; type = TYPE_UNKNOWN; return; } updateState2D(); }
Returns an Affine2D object representing the inverse transformation. The inverse transform Tx' of this transform Tx maps coordinates transformed by Tx back to their original coordinates. In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).

If this transform maps all coordinates onto a point or a line then it will not have an inverse, since coordinates that do not lie on the destination point or line will not have an inverse mapping. The getDeterminant method can be used to determine if this transform has no inverse, in which case an exception will be thrown if the createInverse method is called.

Throws:
See Also:
Returns:a new Affine2D object representing the inverse transformation.
/** * Returns an <code>Affine2D</code> object representing the * inverse transformation. * The inverse transform Tx' of this transform Tx * maps coordinates transformed by Tx back * to their original coordinates. * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)). * <p> * If this transform maps all coordinates onto a point or a line * then it will not have an inverse, since coordinates that do * not lie on the destination point or line will not have an inverse * mapping. * The <code>getDeterminant</code> method can be used to determine if this * transform has no inverse, in which case an exception will be * thrown if the <code>createInverse</code> method is called. * @return a new <code>Affine2D</code> object representing the * inverse transformation. * @see #getDeterminant * @exception NoninvertibleTransformException * if the matrix cannot be inverted. */
public Affine2D createInverse() throws NoninvertibleTransformException { double det; switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): det = mxx * myy - mxy * myx; if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NoninvertibleTransformException("Determinant is "+ det); } return new Affine2D( myy / det, -myx / det, -mxy / det, mxx / det, (mxy * myt - myy * mxt) / det, (myx * mxt - mxx * myt) / det, (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE)); case (APPLY_SHEAR | APPLY_SCALE): det = mxx * myy - mxy * myx; if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NoninvertibleTransformException("Determinant is "+ det); } return new Affine2D( myy / det, -myx / det, -mxy / det, mxx / det, 0.0, 0.0, (APPLY_SHEAR | APPLY_SCALE)); case (APPLY_SHEAR | APPLY_TRANSLATE): if (mxy == 0.0 || myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } return new Affine2D( 0.0, 1.0 / mxy, 1.0 / myx, 0.0, -myt / myx, -mxt / mxy, (APPLY_SHEAR | APPLY_TRANSLATE)); case (APPLY_SHEAR): if (mxy == 0.0 || myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } return new Affine2D(0.0, 1.0 / mxy, 1.0 / myx, 0.0, 0.0, 0.0, (APPLY_SHEAR)); case (APPLY_SCALE | APPLY_TRANSLATE): if (mxx == 0.0 || myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } return new Affine2D( 1.0 / mxx, 0.0, 0.0, 1.0 / myy, -mxt / mxx, -myt / myy, (APPLY_SCALE | APPLY_TRANSLATE)); case (APPLY_SCALE): if (mxx == 0.0 || myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } return new Affine2D(1.0 / mxx, 0.0, 0.0, 1.0 / myy, 0.0, 0.0, (APPLY_SCALE)); case (APPLY_TRANSLATE): return new Affine2D( 1.0, 0.0, 0.0, 1.0, -mxt, -myt, (APPLY_TRANSLATE)); case (APPLY_IDENTITY): return new Affine2D(); } /* NOTREACHED */ }
Transforms an array of point objects by this transform. If any element of the ptDst array is null, a new Point2D object is allocated and stored into that element before storing the results of the transformation.

Note that this method does not take any precautions to avoid problems caused by storing results into Point2D objects that will be used as the source for calculations further down the source array. This method does guarantee that if a specified Point2D object is both the source and destination for the same single point transform operation then the results will not be stored until the calculations are complete to avoid storing the results on top of the operands. If, however, the destination Point2D object for one operation is the same object as the source Point2D object for another operation further down the source array then the original coordinates in that point are overwritten before they can be converted.

Params:
  • ptSrc – the array containing the source point objects
  • ptDst – the array into which the transform point objects are returned
  • srcOff – the offset to the first point object to be transformed in the source array
  • dstOff – the offset to the location of the first transformed point object that is stored in the destination array
  • numPts – the number of point objects to be transformed
/** * Transforms an array of point objects by this transform. * If any element of the <code>ptDst</code> array is * <code>null</code>, a new <code>Point2D</code> object is allocated * and stored into that element before storing the results of the * transformation. * <p> * Note that this method does not take any precautions to * avoid problems caused by storing results into <code>Point2D</code> * objects that will be used as the source for calculations * further down the source array. * This method does guarantee that if a specified <code>Point2D</code> * object is both the source and destination for the same single point * transform operation then the results will not be stored until * the calculations are complete to avoid storing the results on * top of the operands. * If, however, the destination <code>Point2D</code> object for one * operation is the same object as the source <code>Point2D</code> * object for another operation further down the source array then * the original coordinates in that point are overwritten before * they can be converted. * @param ptSrc the array containing the source point objects * @param ptDst the array into which the transform point objects are * returned * @param srcOff the offset to the first point object to be * transformed in the source array * @param dstOff the offset to the location of the first * transformed point object that is stored in the destination array * @param numPts the number of point objects to be transformed */
public void transform(Point2D[] ptSrc, int srcOff, Point2D[] ptDst, int dstOff, int numPts) { int mystate = this.state; while (--numPts >= 0) { // Copy source coords into local variables in case src == dst Point2D src = ptSrc[srcOff++]; double x = src.x; double y = src.y; Point2D dst = ptDst[dstOff++]; if (dst == null) { dst = new Point2D(); ptDst[dstOff - 1] = dst; } switch (mystate) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): dst.setLocation((float)(x * mxx + y * mxy + mxt), (float)(x * myx + y * myy + myt)); break; case (APPLY_SHEAR | APPLY_SCALE): dst.setLocation((float)(x * mxx + y * mxy), (float)(x * myx + y * myy)); break; case (APPLY_SHEAR | APPLY_TRANSLATE): dst.setLocation((float)(y * mxy + mxt), (float)(x * myx + myt)); break; case (APPLY_SHEAR): dst.setLocation((float)(y * mxy), (float)(x * myx)); break; case (APPLY_SCALE | APPLY_TRANSLATE): dst.setLocation((float)(x * mxx + mxt), (float)(y * myy + myt)); break; case (APPLY_SCALE): dst.setLocation((float)(x * mxx), (float)(y * myy)); break; case (APPLY_TRANSLATE): dst.setLocation((float)(x + mxt), (float)(y + myt)); break; case (APPLY_IDENTITY): dst.setLocation((float) x, (float) y); break; } } /* NOTREACHED */ }
Transforms the relative distance vector specified by ptSrc and stores the result in ptDst. A relative distance vector is transformed without applying the translation components of the affine transformation matrix using the following equations:
 [  x' ]   [  m00  m01 (m02) ] [  x  ]   [ m00x + m01y ]
 [  y' ] = [  m10  m11 (m12) ] [  y  ] = [ m10x + m11y ]
 [ (1) ]   [  (0)  (0) ( 1 ) ] [ (1) ]   [     (1)     ]
If ptDst is null, a new Point2D object is allocated and then the result of the transform is stored in this object. In either case, ptDst, which contains the transformed point, is returned for convenience. If ptSrc and ptDst are the same object, the input point is correctly overwritten with the transformed point.
Params:
  • ptSrc – the distance vector to be delta transformed
  • ptDst – the resulting transformed distance vector
Returns:ptDst, which contains the result of the transformation.
/** * Transforms the relative distance vector specified by * <code>ptSrc</code> and stores the result in <code>ptDst</code>. * A relative distance vector is transformed without applying the * translation components of the affine transformation matrix * using the following equations: * <pre> * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ] * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ] * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ] * </pre> * If <code>ptDst</code> is <code>null</code>, a new * <code>Point2D</code> object is allocated and then the result of the * transform is stored in this object. * In either case, <code>ptDst</code>, which contains the * transformed point, is returned for convenience. * If <code>ptSrc</code> and <code>ptDst</code> are the same object, * the input point is correctly overwritten with the transformed * point. * @param ptSrc the distance vector to be delta transformed * @param ptDst the resulting transformed distance vector * @return <code>ptDst</code>, which contains the result of the * transformation. */
public Point2D deltaTransform(Point2D ptSrc, Point2D ptDst) { if (ptDst == null) { ptDst = new Point2D(); } // Copy source coords into local variables in case src == dst double x = ptSrc.x; double y = ptSrc.y; switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SHEAR | APPLY_SCALE): ptDst.setLocation((float)(x * mxx + y * mxy), (float)(x * myx + y * myy)); return ptDst; case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): ptDst.setLocation((float)(y * mxy), (float)(x * myx)); return ptDst; case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): ptDst.setLocation((float)(x * mxx), (float)(y * myy)); return ptDst; case (APPLY_TRANSLATE): case (APPLY_IDENTITY): ptDst.setLocation((float) x, (float) y); return ptDst; } /* NOTREACHED */ } // Round values to sane precision for printing // Note that Math.sin(Math.PI) has an error of about 10^-16 private static double _matround(double matval) { return Math.rint(matval * 1E15) / 1E15; }
Returns a String that represents the value of this Object.
Returns:a String representing the value of this Object.
/** * Returns a <code>String</code> that represents the value of this * {@link Object}. * @return a <code>String</code> representing the value of this * <code>Object</code>. */
@Override public String toString() { return ("Affine2D[[" + _matround(mxx) + ", " + _matround(mxy) + ", " + _matround(mxt) + "], [" + _matround(myx) + ", " + _matround(myy) + ", " + _matround(myt) + "]]"); } @Override public boolean is2D() { return true; } @Override public void restoreTransform(double mxx, double myx, double mxy, double myy, double mxt, double myt) { setTransform(mxx, myx, mxy, myy, mxt, myt); } @Override public void restoreTransform(double mxx, double mxy, double mxz, double mxt, double myx, double myy, double myz, double myt, double mzx, double mzy, double mzz, double mzt) { if ( mxz != 0 || myz != 0 || mzx != 0 || mzy != 0 || mzz != 1 || mzt != 0.0) { degreeError(Degree.AFFINE_2D); } setTransform(mxx, myx, mxy, myy, mxt, myt); } @Override public BaseTransform deriveWithTranslation(double mxt, double myt) { translate(mxt, myt); return this; } @Override public BaseTransform deriveWithTranslation(double mxt, double myt, double mzt) { if (mzt == 0.0) { translate(mxt, myt); return this; } Affine3D a = new Affine3D(this); a.translate(mxt, myt, mzt); return a; } @Override public BaseTransform deriveWithScale(double mxx, double myy, double mzz) { if (mzz == 1.0) { scale(mxx, myy); return this; } Affine3D a = new Affine3D(this); a.scale(mxx, myy, mzz); return a; } @Override public BaseTransform deriveWithRotation(double theta, double axisX, double axisY, double axisZ) { if (theta == 0.0) { return this; } if (almostZero(axisX) && almostZero(axisY)) { if (axisZ > 0) { rotate(theta); } else if (axisZ < 0) { rotate(-theta); } // else rotating about zero vector - NOP return this; } Affine3D a = new Affine3D(this); a.rotate(theta, axisX, axisY, axisZ); return a; } @Override public BaseTransform deriveWithPreTranslation(double mxt, double myt) { this.mxt += mxt; this.myt += myt; if (this.mxt != 0.0 || this.myt != 0.0) { state |= APPLY_TRANSLATE; // if (type != TYPE_UNKNOWN) { type |= TYPE_TRANSLATION; // } } else { state &= ~APPLY_TRANSLATE; if (type != TYPE_UNKNOWN) { type &= ~TYPE_TRANSLATION; } } return this; } @Override public BaseTransform deriveWithConcatenation(double mxx, double myx, double mxy, double myy, double mxt, double myt) { // TODO: Simplify this (RT-26801) BaseTransform tmpTx = getInstance(mxx, myx, mxy, myy, mxt, myt); concatenate(tmpTx); return this; } @Override public BaseTransform deriveWithConcatenation( double mxx, double mxy, double mxz, double mxt, double myx, double myy, double myz, double myt, double mzx, double mzy, double mzz, double mzt) { if ( mxz == 0.0 && myz == 0.0 && mzx == 0.0 && mzy == 0.0 && mzz == 1.0 && mzt == 0.0) { concatenate(mxx, mxy, mxt, myx, myy, myt); return this; } Affine3D t3d = new Affine3D(this); t3d.concatenate(mxx, mxy, mxz, mxt, myx, myy, myz, myt, mzx, mzy, mzz, mzt); return t3d; } @Override public BaseTransform deriveWithConcatenation(BaseTransform tx) { if (tx.is2D()) { concatenate(tx); return this; } Affine3D t3d = new Affine3D(this); t3d.concatenate(tx); return t3d; } @Override public BaseTransform deriveWithPreConcatenation(BaseTransform tx) { if (tx.is2D()) { preConcatenate(tx); return this; } Affine3D t3d = new Affine3D(this); t3d.preConcatenate(tx); return t3d; } @Override public BaseTransform deriveWithNewTransform(BaseTransform tx) { if (tx.is2D()) { setTransform(tx); return this; } return getInstance(tx); } @Override public BaseTransform copy() { return new Affine2D(this); } private static final long BASE_HASH; static { long bits = 0; bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMzz()); bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMzy()); bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMzx()); bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMyz()); bits = bits * 31 + Double.doubleToLongBits(IDENTITY_TRANSFORM.getMxz()); BASE_HASH = bits; }
Returns the hashcode for this transform. The base algorithm for computing the hashcode is defined by the implementation in the BaseTransform class. This implementation is just a faster way of computing the same value knowing which elements of the transform matrix are populated.
Returns: a hash code for this transform.
/** * Returns the hashcode for this transform. The base algorithm for * computing the hashcode is defined by the implementation in * the {@code BaseTransform} class. This implementation is just a * faster way of computing the same value knowing which elements of * the transform matrix are populated. * @return a hash code for this transform. */
@Override public int hashCode() { if (isIdentity()) return 0; long bits = BASE_HASH; bits = bits * 31 + Double.doubleToLongBits(getMyy()); bits = bits * 31 + Double.doubleToLongBits(getMyx()); bits = bits * 31 + Double.doubleToLongBits(getMxy()); bits = bits * 31 + Double.doubleToLongBits(getMxx()); bits = bits * 31 + Double.doubleToLongBits(0.0); // mzt bits = bits * 31 + Double.doubleToLongBits(getMyt()); bits = bits * 31 + Double.doubleToLongBits(getMxt()); return (((int) bits) ^ ((int) (bits >> 32))); }
Returns true if this Affine2D represents the same coordinate transform as the specified argument.
Params:
  • obj – the Object to test for equality with this Affine2D
Returns:true if obj equals this Affine2D object; false otherwise.
/** * Returns <code>true</code> if this <code>Affine2D</code> * represents the same coordinate transform as the specified * argument. * @param obj the <code>Object</code> to test for equality with this * <code>Affine2D</code> * @return <code>true</code> if <code>obj</code> equals this * <code>Affine2D</code> object; <code>false</code> otherwise. */
@Override public boolean equals(Object obj) { if (obj instanceof BaseTransform) { BaseTransform a = (BaseTransform) obj; return (a.getType() <= TYPE_AFFINE2D_MASK && a.getMxx() == this.mxx && a.getMxy() == this.mxy && a.getMxt() == this.mxt && a.getMyx() == this.myx && a.getMyy() == this.myy && a.getMyt() == this.myt); } return false; } }