1268 lines
42 KiB
Java
1268 lines
42 KiB
Java
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
* (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/**
|
|
* @author Denis M. Kishenko
|
|
* @version $Revision$
|
|
*/
|
|
|
|
package java.awt.geom;
|
|
|
|
import java.awt.Shape;
|
|
import java.io.IOException;
|
|
import java.io.Serializable;
|
|
|
|
import org.apache.harmony.awt.internal.nls.Messages;
|
|
import org.apache.harmony.misc.HashCode;
|
|
|
|
/**
|
|
* The Class AffineTransform represents a linear transformation (rotation,
|
|
* scaling, or shear) followed by a translation that acts on a coordinate space.
|
|
* It preserves collinearity of points and ratios of distances between collinear
|
|
* points: so if A, B, and C are on a line, then after the space has been
|
|
* transformed via the affine transform, the images of the three points will
|
|
* still be on a line, and the ratio of the distance from A to B with the
|
|
* distance from B to C will be the same as the corresponding ratio in the image
|
|
* space.
|
|
*
|
|
* @since Android 1.0
|
|
*/
|
|
public class AffineTransform implements Cloneable, Serializable {
|
|
|
|
/**
|
|
* The Constant serialVersionUID.
|
|
*/
|
|
private static final long serialVersionUID = 1330973210523860834L;
|
|
|
|
/**
|
|
* The Constant TYPE_IDENTITY.
|
|
*/
|
|
public static final int TYPE_IDENTITY = 0;
|
|
|
|
/**
|
|
* The Constant TYPE_TRANSLATION.
|
|
*/
|
|
public static final int TYPE_TRANSLATION = 1;
|
|
|
|
/**
|
|
* The Constant TYPE_UNIFORM_SCALE.
|
|
*/
|
|
public static final int TYPE_UNIFORM_SCALE = 2;
|
|
|
|
/**
|
|
* The Constant TYPE_GENERAL_SCALE.
|
|
*/
|
|
public static final int TYPE_GENERAL_SCALE = 4;
|
|
|
|
/**
|
|
* The Constant TYPE_QUADRANT_ROTATION.
|
|
*/
|
|
public static final int TYPE_QUADRANT_ROTATION = 8;
|
|
|
|
/**
|
|
* The Constant TYPE_GENERAL_ROTATION.
|
|
*/
|
|
public static final int TYPE_GENERAL_ROTATION = 16;
|
|
|
|
/**
|
|
* The Constant TYPE_GENERAL_TRANSFORM.
|
|
*/
|
|
public static final int TYPE_GENERAL_TRANSFORM = 32;
|
|
|
|
/**
|
|
* The Constant TYPE_FLIP.
|
|
*/
|
|
public static final int TYPE_FLIP = 64;
|
|
|
|
/**
|
|
* The Constant TYPE_MASK_SCALE.
|
|
*/
|
|
public static final int TYPE_MASK_SCALE = TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE;
|
|
|
|
/**
|
|
* The Constant TYPE_MASK_ROTATION.
|
|
*/
|
|
public static final int TYPE_MASK_ROTATION = TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION;
|
|
|
|
/**
|
|
* The <code>TYPE_UNKNOWN</code> is an initial type value.
|
|
*/
|
|
static final int TYPE_UNKNOWN = -1;
|
|
|
|
/**
|
|
* The min value equivalent to zero. If absolute value less then ZERO it
|
|
* considered as zero.
|
|
*/
|
|
static final double ZERO = 1E-10;
|
|
|
|
/**
|
|
* The values of transformation matrix.
|
|
*/
|
|
double m00;
|
|
|
|
/**
|
|
* The m10.
|
|
*/
|
|
double m10;
|
|
|
|
/**
|
|
* The m01.
|
|
*/
|
|
double m01;
|
|
|
|
/**
|
|
* The m11.
|
|
*/
|
|
double m11;
|
|
|
|
/**
|
|
* The m02.
|
|
*/
|
|
double m02;
|
|
|
|
/**
|
|
* The m12.
|
|
*/
|
|
double m12;
|
|
|
|
/**
|
|
* The transformation <code>type</code>.
|
|
*/
|
|
transient int type;
|
|
|
|
/**
|
|
* Instantiates a new affine transform of type <code>TYPE_IDENTITY</code>
|
|
* (which leaves coordinates unchanged).
|
|
*/
|
|
public AffineTransform() {
|
|
type = TYPE_IDENTITY;
|
|
m00 = m11 = 1.0;
|
|
m10 = m01 = m02 = m12 = 0.0;
|
|
}
|
|
|
|
/**
|
|
* Instantiates a new affine transform that has the same data as the given
|
|
* AffineTransform.
|
|
*
|
|
* @param t
|
|
* the transform to copy.
|
|
*/
|
|
public AffineTransform(AffineTransform t) {
|
|
this.type = t.type;
|
|
this.m00 = t.m00;
|
|
this.m10 = t.m10;
|
|
this.m01 = t.m01;
|
|
this.m11 = t.m11;
|
|
this.m02 = t.m02;
|
|
this.m12 = t.m12;
|
|
}
|
|
|
|
/**
|
|
* Instantiates a new affine transform by specifying the values of the 2x3
|
|
* transformation matrix as floats. The type is set to the default type:
|
|
* <code>TYPE_UNKNOWN</code>
|
|
*
|
|
* @param m00
|
|
* the m00 entry in the transformation matrix.
|
|
* @param m10
|
|
* the m10 entry in the transformation matrix.
|
|
* @param m01
|
|
* the m01 entry in the transformation matrix.
|
|
* @param m11
|
|
* the m11 entry in the transformation matrix.
|
|
* @param m02
|
|
* the m02 entry in the transformation matrix.
|
|
* @param m12
|
|
* the m12 entry in the transformation matrix.
|
|
*/
|
|
public AffineTransform(float m00, float m10, float m01, float m11, float m02, float m12) {
|
|
this.type = TYPE_UNKNOWN;
|
|
this.m00 = m00;
|
|
this.m10 = m10;
|
|
this.m01 = m01;
|
|
this.m11 = m11;
|
|
this.m02 = m02;
|
|
this.m12 = m12;
|
|
}
|
|
|
|
/**
|
|
* Instantiates a new affine transform by specifying the values of the 2x3
|
|
* transformation matrix as doubles. The type is set to the default type:
|
|
* <code>TYPE_UNKNOWN</code>
|
|
*
|
|
* @param m00
|
|
* the m00 entry in the transformation matrix.
|
|
* @param m10
|
|
* the m10 entry in the transformation matrix.
|
|
* @param m01
|
|
* the m01 entry in the transformation matrix.
|
|
* @param m11
|
|
* the m11 entry in the transformation matrix.
|
|
* @param m02
|
|
* the m02 entry in the transformation matrix.
|
|
* @param m12
|
|
* the m12 entry in the transformation matrix.
|
|
*/
|
|
public AffineTransform(double m00, double m10, double m01, double m11, double m02, double m12) {
|
|
this.type = TYPE_UNKNOWN;
|
|
this.m00 = m00;
|
|
this.m10 = m10;
|
|
this.m01 = m01;
|
|
this.m11 = m11;
|
|
this.m02 = m02;
|
|
this.m12 = m12;
|
|
}
|
|
|
|
/**
|
|
* Instantiates a new affine transform by reading the values of the
|
|
* transformation matrix from an array of floats. The mapping from the array
|
|
* to the matrix starts with <code>matrix[0]</code> giving the top-left
|
|
* entry of the matrix and proceeds with the usual left-to-right and
|
|
* top-down ordering.
|
|
* <p>
|
|
* If the array has only four entries, then the two entries of the last row
|
|
* of the transformation matrix default to zero.
|
|
*
|
|
* @param matrix
|
|
* the array of four or six floats giving the values of the
|
|
* matrix.
|
|
* @throws ArrayIndexOutOfBoundsException
|
|
* if the size of the array is 0, 1, 2, 3, or 5.
|
|
*/
|
|
public AffineTransform(float[] matrix) {
|
|
this.type = TYPE_UNKNOWN;
|
|
m00 = matrix[0];
|
|
m10 = matrix[1];
|
|
m01 = matrix[2];
|
|
m11 = matrix[3];
|
|
if (matrix.length > 4) {
|
|
m02 = matrix[4];
|
|
m12 = matrix[5];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Instantiates a new affine transform by reading the values of the
|
|
* transformation matrix from an array of doubles. The mapping from the
|
|
* array to the matrix starts with <code>matrix[0]</code> giving the
|
|
* top-left entry of the matrix and proceeds with the usual left-to-right
|
|
* and top-down ordering.
|
|
* <p>
|
|
* If the array has only four entries, then the two entries of the last row
|
|
* of the transformation matrix default to zero.
|
|
*
|
|
* @param matrix
|
|
* the array of four or six doubles giving the values of the
|
|
* matrix.
|
|
* @throws ArrayIndexOutOfBoundsException
|
|
* if the size of the array is 0, 1, 2, 3, or 5.
|
|
*/
|
|
public AffineTransform(double[] matrix) {
|
|
this.type = TYPE_UNKNOWN;
|
|
m00 = matrix[0];
|
|
m10 = matrix[1];
|
|
m01 = matrix[2];
|
|
m11 = matrix[3];
|
|
if (matrix.length > 4) {
|
|
m02 = matrix[4];
|
|
m12 = matrix[5];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns type of the affine transformation.
|
|
* <p>
|
|
* The type is computed as follows: Label the entries of the transformation
|
|
* matrix as three rows (m00, m01), (m10, m11), and (m02, m12). Then if the
|
|
* original basis vectors are (1, 0) and (0, 1), the new basis vectors after
|
|
* transformation are given by (m00, m01) and (m10, m11), and the
|
|
* translation vector is (m02, m12).
|
|
* <p>
|
|
* The types are classified as follows: <br/> TYPE_IDENTITY - no change<br/>
|
|
* TYPE_TRANSLATION - The translation vector isn't zero<br/>
|
|
* TYPE_UNIFORM_SCALE - The new basis vectors have equal length<br/>
|
|
* TYPE_GENERAL_SCALE - The new basis vectors dont' have equal length<br/>
|
|
* TYPE_FLIP - The new basis vector orientation differs from the original
|
|
* one<br/> TYPE_QUADRANT_ROTATION - The new basis is a rotation of the
|
|
* original by 90, 180, 270, or 360 degrees<br/> TYPE_GENERAL_ROTATION - The
|
|
* new basis is a rotation of the original by an arbitrary angle<br/>
|
|
* TYPE_GENERAL_TRANSFORM - The transformation can't be inverted.<br/>
|
|
* <p>
|
|
* Note that multiple types are possible, thus the types can be combined
|
|
* using bitwise combinations.
|
|
*
|
|
* @return the type of the Affine Transform.
|
|
*/
|
|
public int getType() {
|
|
if (type != TYPE_UNKNOWN) {
|
|
return type;
|
|
}
|
|
|
|
int type = 0;
|
|
|
|
if (m00 * m01 + m10 * m11 != 0.0) {
|
|
type |= TYPE_GENERAL_TRANSFORM;
|
|
return type;
|
|
}
|
|
|
|
if (m02 != 0.0 || m12 != 0.0) {
|
|
type |= TYPE_TRANSLATION;
|
|
} else if (m00 == 1.0 && m11 == 1.0 && m01 == 0.0 && m10 == 0.0) {
|
|
type = TYPE_IDENTITY;
|
|
return type;
|
|
}
|
|
|
|
if (m00 * m11 - m01 * m10 < 0.0) {
|
|
type |= TYPE_FLIP;
|
|
}
|
|
|
|
double dx = m00 * m00 + m10 * m10;
|
|
double dy = m01 * m01 + m11 * m11;
|
|
if (dx != dy) {
|
|
type |= TYPE_GENERAL_SCALE;
|
|
} else if (dx != 1.0) {
|
|
type |= TYPE_UNIFORM_SCALE;
|
|
}
|
|
|
|
if ((m00 == 0.0 && m11 == 0.0) || (m10 == 0.0 && m01 == 0.0 && (m00 < 0.0 || m11 < 0.0))) {
|
|
type |= TYPE_QUADRANT_ROTATION;
|
|
} else if (m01 != 0.0 || m10 != 0.0) {
|
|
type |= TYPE_GENERAL_ROTATION;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
* Gets the scale x entry of the transformation matrix (the upper left
|
|
* matrix entry).
|
|
*
|
|
* @return the scale x value.
|
|
*/
|
|
public double getScaleX() {
|
|
return m00;
|
|
}
|
|
|
|
/**
|
|
* Gets the scale y entry of the transformation matrix (the lower right
|
|
* entry of the linear transformation).
|
|
*
|
|
* @return the scale y value.
|
|
*/
|
|
public double getScaleY() {
|
|
return m11;
|
|
}
|
|
|
|
/**
|
|
* Gets the shear x entry of the transformation matrix (the upper right
|
|
* entry of the linear transformation).
|
|
*
|
|
* @return the shear x value.
|
|
*/
|
|
public double getShearX() {
|
|
return m01;
|
|
}
|
|
|
|
/**
|
|
* Gets the shear y entry of the transformation matrix (the lower left entry
|
|
* of the linear transformation).
|
|
*
|
|
* @return the shear y value.
|
|
*/
|
|
public double getShearY() {
|
|
return m10;
|
|
}
|
|
|
|
/**
|
|
* Gets the x coordinate of the translation vector.
|
|
*
|
|
* @return the x coordinate of the translation vector.
|
|
*/
|
|
public double getTranslateX() {
|
|
return m02;
|
|
}
|
|
|
|
/**
|
|
* Gets the y coordinate of the translation vector.
|
|
*
|
|
* @return the y coordinate of the translation vector.
|
|
*/
|
|
public double getTranslateY() {
|
|
return m12;
|
|
}
|
|
|
|
/**
|
|
* Checks if the AffineTransformation is the identity.
|
|
*
|
|
* @return true, if the AffineTransformation is the identity.
|
|
*/
|
|
public boolean isIdentity() {
|
|
return getType() == TYPE_IDENTITY;
|
|
}
|
|
|
|
/**
|
|
* Writes the values of the transformation matrix into the given array of
|
|
* doubles. If the array has length 4, only the linear transformation part
|
|
* will be written into it. If it has length greater than 4, the translation
|
|
* vector will be included as well.
|
|
*
|
|
* @param matrix
|
|
* the array to fill with the values of the matrix.
|
|
* @throws ArrayIndexOutOfBoundsException
|
|
* if the size of the array is 0, 1, 2, 3, or 5.
|
|
*/
|
|
public void getMatrix(double[] matrix) {
|
|
matrix[0] = m00;
|
|
matrix[1] = m10;
|
|
matrix[2] = m01;
|
|
matrix[3] = m11;
|
|
if (matrix.length > 4) {
|
|
matrix[4] = m02;
|
|
matrix[5] = m12;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the determinant of the linear transformation matrix.
|
|
*
|
|
* @return the determinant of the linear transformation matrix.
|
|
*/
|
|
public double getDeterminant() {
|
|
return m00 * m11 - m01 * m10;
|
|
}
|
|
|
|
/**
|
|
* Sets the transform in terms of a list of double values.
|
|
*
|
|
* @param m00
|
|
* the m00 coordinate of the transformation matrix.
|
|
* @param m10
|
|
* the m10 coordinate of the transformation matrix.
|
|
* @param m01
|
|
* the m01 coordinate of the transformation matrix.
|
|
* @param m11
|
|
* the m11 coordinate of the transformation matrix.
|
|
* @param m02
|
|
* the m02 coordinate of the transformation matrix.
|
|
* @param m12
|
|
* the m12 coordinate of the transformation matrix.
|
|
*/
|
|
public void setTransform(double m00, double m10, double m01, double m11, double m02, double m12) {
|
|
this.type = TYPE_UNKNOWN;
|
|
this.m00 = m00;
|
|
this.m10 = m10;
|
|
this.m01 = m01;
|
|
this.m11 = m11;
|
|
this.m02 = m02;
|
|
this.m12 = m12;
|
|
}
|
|
|
|
/**
|
|
* Sets the transform's data to match the data of the transform sent as a
|
|
* parameter.
|
|
*
|
|
* @param t
|
|
* the transform that gives the new values.
|
|
*/
|
|
public void setTransform(AffineTransform t) {
|
|
type = t.type;
|
|
setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12);
|
|
}
|
|
|
|
/**
|
|
* Sets the transform to the identity transform.
|
|
*/
|
|
public void setToIdentity() {
|
|
type = TYPE_IDENTITY;
|
|
m00 = m11 = 1.0;
|
|
m10 = m01 = m02 = m12 = 0.0;
|
|
}
|
|
|
|
/**
|
|
* Sets the transformation to a translation alone. Sets the linear part of
|
|
* the transformation to identity and the translation vector to the values
|
|
* sent as parameters. Sets the type to <code>TYPE_IDENTITY</code> if the
|
|
* resulting AffineTransformation is the identity transformation, otherwise
|
|
* sets it to <code>TYPE_TRANSLATION</code>.
|
|
*
|
|
* @param mx
|
|
* the distance to translate in the x direction.
|
|
* @param my
|
|
* the distance to translate in the y direction.
|
|
*/
|
|
public void setToTranslation(double mx, double my) {
|
|
m00 = m11 = 1.0;
|
|
m01 = m10 = 0.0;
|
|
m02 = mx;
|
|
m12 = my;
|
|
if (mx == 0.0 && my == 0.0) {
|
|
type = TYPE_IDENTITY;
|
|
} else {
|
|
type = TYPE_TRANSLATION;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the transformation to being a scale alone, eliminating rotation,
|
|
* shear, and translation elements. Sets the type to
|
|
* <code>TYPE_IDENTITY</code> if the resulting AffineTransformation is the
|
|
* identity transformation, otherwise sets it to <code>TYPE_UNKNOWN</code>.
|
|
*
|
|
* @param scx
|
|
* the scaling factor in the x direction.
|
|
* @param scy
|
|
* the scaling factor in the y direction.
|
|
*/
|
|
public void setToScale(double scx, double scy) {
|
|
m00 = scx;
|
|
m11 = scy;
|
|
m10 = m01 = m02 = m12 = 0.0;
|
|
if (scx != 1.0 || scy != 1.0) {
|
|
type = TYPE_UNKNOWN;
|
|
} else {
|
|
type = TYPE_IDENTITY;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the transformation to being a shear alone, eliminating rotation,
|
|
* scaling, and translation elements. Sets the type to
|
|
* <code>TYPE_IDENTITY</code> if the resulting AffineTransformation is the
|
|
* identity transformation, otherwise sets it to <code>TYPE_UNKNOWN</code>.
|
|
*
|
|
* @param shx
|
|
* the shearing factor in the x direction.
|
|
* @param shy
|
|
* the shearing factor in the y direction.
|
|
*/
|
|
public void setToShear(double shx, double shy) {
|
|
m00 = m11 = 1.0;
|
|
m02 = m12 = 0.0;
|
|
m01 = shx;
|
|
m10 = shy;
|
|
if (shx != 0.0 || shy != 0.0) {
|
|
type = TYPE_UNKNOWN;
|
|
} else {
|
|
type = TYPE_IDENTITY;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the transformation to being a rotation alone, eliminating shearing,
|
|
* scaling, and translation elements. Sets the type to
|
|
* <code>TYPE_IDENTITY</code> if the resulting AffineTransformation is the
|
|
* identity transformation, otherwise sets it to <code>TYPE_UNKNOWN</code>.
|
|
*
|
|
* @param angle
|
|
* the angle of rotation in radians.
|
|
*/
|
|
public void setToRotation(double angle) {
|
|
double sin = Math.sin(angle);
|
|
double cos = Math.cos(angle);
|
|
if (Math.abs(cos) < ZERO) {
|
|
cos = 0.0;
|
|
sin = sin > 0.0 ? 1.0 : -1.0;
|
|
} else if (Math.abs(sin) < ZERO) {
|
|
sin = 0.0;
|
|
cos = cos > 0.0 ? 1.0 : -1.0;
|
|
}
|
|
m00 = m11 = cos;
|
|
m01 = -sin;
|
|
m10 = sin;
|
|
m02 = m12 = 0.0;
|
|
type = TYPE_UNKNOWN;
|
|
}
|
|
|
|
/**
|
|
* Sets the transformation to being a rotation followed by a translation.
|
|
* Sets the type to <code>TYPE_UNKNOWN</code>.
|
|
*
|
|
* @param angle
|
|
* the angle of rotation in radians.
|
|
* @param px
|
|
* the distance to translate in the x direction.
|
|
* @param py
|
|
* the distance to translate in the y direction.
|
|
*/
|
|
public void setToRotation(double angle, double px, double py) {
|
|
setToRotation(angle);
|
|
m02 = px * (1.0 - m00) + py * m10;
|
|
m12 = py * (1.0 - m00) - px * m10;
|
|
type = TYPE_UNKNOWN;
|
|
}
|
|
|
|
/**
|
|
* Creates a new AffineTransformation that is a translation alone with the
|
|
* translation vector given by the values sent as parameters. The new
|
|
* transformation's type is <code>TYPE_IDENTITY</code> if the
|
|
* AffineTransformation is the identity transformation, otherwise it's
|
|
* <code>TYPE_TRANSLATION</code>.
|
|
*
|
|
* @param mx
|
|
* the distance to translate in the x direction.
|
|
* @param my
|
|
* the distance to translate in the y direction.
|
|
* @return the new AffineTransformation.
|
|
*/
|
|
public static AffineTransform getTranslateInstance(double mx, double my) {
|
|
AffineTransform t = new AffineTransform();
|
|
t.setToTranslation(mx, my);
|
|
return t;
|
|
}
|
|
|
|
/**
|
|
* Creates a new AffineTransformation that is a scale alone. The new
|
|
* transformation's type is <code>TYPE_IDENTITY</code> if the
|
|
* AffineTransformation is the identity transformation, otherwise it's
|
|
* <code>TYPE_UNKNOWN</code>.
|
|
*
|
|
* @param scx
|
|
* the scaling factor in the x direction.
|
|
* @param scY
|
|
* the scaling factor in the y direction.
|
|
* @return the new AffineTransformation.
|
|
*/
|
|
public static AffineTransform getScaleInstance(double scx, double scY) {
|
|
AffineTransform t = new AffineTransform();
|
|
t.setToScale(scx, scY);
|
|
return t;
|
|
}
|
|
|
|
/**
|
|
* Creates a new AffineTransformation that is a shear alone. The new
|
|
* transformation's type is <code>TYPE_IDENTITY</code> if the
|
|
* AffineTransformation is the identity transformation, otherwise it's
|
|
* <code>TYPE_UNKNOWN</code>.
|
|
*
|
|
* @param shx
|
|
* the shearing factor in the x direction.
|
|
* @param shy
|
|
* the shearing factor in the y direction.
|
|
* @return the new AffineTransformation.
|
|
*/
|
|
public static AffineTransform getShearInstance(double shx, double shy) {
|
|
AffineTransform m = new AffineTransform();
|
|
m.setToShear(shx, shy);
|
|
return m;
|
|
}
|
|
|
|
/**
|
|
* Creates a new AffineTransformation that is a rotation alone. The new
|
|
* transformation's type is <code>TYPE_IDENTITY</code> if the
|
|
* AffineTransformation is the identity transformation, otherwise it's
|
|
* <code>TYPE_UNKNOWN</code>.
|
|
*
|
|
* @param angle
|
|
* the angle of rotation in radians.
|
|
* @return the new AffineTransformation.
|
|
*/
|
|
public static AffineTransform getRotateInstance(double angle) {
|
|
AffineTransform t = new AffineTransform();
|
|
t.setToRotation(angle);
|
|
return t;
|
|
}
|
|
|
|
/**
|
|
* Creates a new AffineTransformation that is a rotation followed by a
|
|
* translation. Sets the type to <code>TYPE_UNKNOWN</code>.
|
|
*
|
|
* @param angle
|
|
* the angle of rotation in radians.
|
|
* @param x
|
|
* the distance to translate in the x direction.
|
|
* @param y
|
|
* the distance to translate in the y direction.
|
|
* @return the new AffineTransformation.
|
|
*/
|
|
public static AffineTransform getRotateInstance(double angle, double x, double y) {
|
|
AffineTransform t = new AffineTransform();
|
|
t.setToRotation(angle, x, y);
|
|
return t;
|
|
}
|
|
|
|
/**
|
|
* Applies a translation to this AffineTransformation.
|
|
*
|
|
* @param mx
|
|
* the distance to translate in the x direction.
|
|
* @param my
|
|
* the distance to translate in the y direction.
|
|
*/
|
|
public void translate(double mx, double my) {
|
|
concatenate(AffineTransform.getTranslateInstance(mx, my));
|
|
}
|
|
|
|
/**
|
|
* Applies a scaling transformation to this AffineTransformation.
|
|
*
|
|
* @param scx
|
|
* the scaling factor in the x direction.
|
|
* @param scy
|
|
* the scaling factor in the y direction.
|
|
*/
|
|
public void scale(double scx, double scy) {
|
|
concatenate(AffineTransform.getScaleInstance(scx, scy));
|
|
}
|
|
|
|
/**
|
|
* Applies a shearing transformation to this AffineTransformation.
|
|
*
|
|
* @param shx
|
|
* the shearing factor in the x direction.
|
|
* @param shy
|
|
* the shearing factor in the y direction.
|
|
*/
|
|
public void shear(double shx, double shy) {
|
|
concatenate(AffineTransform.getShearInstance(shx, shy));
|
|
}
|
|
|
|
/**
|
|
* Applies a rotation transformation to this AffineTransformation.
|
|
*
|
|
* @param angle
|
|
* the angle of rotation in radians.
|
|
*/
|
|
public void rotate(double angle) {
|
|
concatenate(AffineTransform.getRotateInstance(angle));
|
|
}
|
|
|
|
/**
|
|
* Applies a rotation and translation transformation to this
|
|
* AffineTransformation.
|
|
*
|
|
* @param angle
|
|
* the angle of rotation in radians.
|
|
* @param px
|
|
* the distance to translate in the x direction.
|
|
* @param py
|
|
* the distance to translate in the y direction.
|
|
*/
|
|
public void rotate(double angle, double px, double py) {
|
|
concatenate(AffineTransform.getRotateInstance(angle, px, py));
|
|
}
|
|
|
|
/**
|
|
* Multiplies the matrix representations of two AffineTransform objects.
|
|
*
|
|
* @param t1
|
|
* - the AffineTransform object is a multiplicand
|
|
* @param t2
|
|
* - the AffineTransform object is a multiplier
|
|
* @return an AffineTransform object that is the result of t1 multiplied by
|
|
* the matrix t2.
|
|
*/
|
|
AffineTransform multiply(AffineTransform t1, AffineTransform t2) {
|
|
return new AffineTransform(t1.m00 * t2.m00 + t1.m10 * t2.m01, // m00
|
|
t1.m00 * t2.m10 + t1.m10 * t2.m11, // m01
|
|
t1.m01 * t2.m00 + t1.m11 * t2.m01, // m10
|
|
t1.m01 * t2.m10 + t1.m11 * t2.m11, // m11
|
|
t1.m02 * t2.m00 + t1.m12 * t2.m01 + t2.m02, // m02
|
|
t1.m02 * t2.m10 + t1.m12 * t2.m11 + t2.m12);// m12
|
|
}
|
|
|
|
/**
|
|
* Applies the given AffineTransform to this AffineTransform via matrix
|
|
* multiplication.
|
|
*
|
|
* @param t
|
|
* the AffineTransform to apply to this AffineTransform.
|
|
*/
|
|
public void concatenate(AffineTransform t) {
|
|
setTransform(multiply(t, this));
|
|
}
|
|
|
|
/**
|
|
* Changes the current AffineTransform the one obtained by taking the
|
|
* transform t and applying this AffineTransform to it.
|
|
*
|
|
* @param t
|
|
* the AffineTransform that this AffineTransform is multiplied
|
|
* by.
|
|
*/
|
|
public void preConcatenate(AffineTransform t) {
|
|
setTransform(multiply(this, t));
|
|
}
|
|
|
|
/**
|
|
* Creates an AffineTransform that is the inverse of this transform.
|
|
*
|
|
* @return the affine transform that is the inverse of this AffineTransform.
|
|
* @throws NoninvertibleTransformException
|
|
* if this AffineTransform cannot be inverted (the determinant
|
|
* of the linear transformation part is zero).
|
|
*/
|
|
public AffineTransform createInverse() throws NoninvertibleTransformException {
|
|
double det = getDeterminant();
|
|
if (Math.abs(det) < ZERO) {
|
|
// awt.204=Determinant is zero
|
|
throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$
|
|
}
|
|
return new AffineTransform(m11 / det, // m00
|
|
-m10 / det, // m10
|
|
-m01 / det, // m01
|
|
m00 / det, // m11
|
|
(m01 * m12 - m11 * m02) / det, // m02
|
|
(m10 * m02 - m00 * m12) / det // m12
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Apply the current AffineTransform to the point.
|
|
*
|
|
* @param src
|
|
* the original point.
|
|
* @param dst
|
|
* Point2D object to be filled with the destination coordinates
|
|
* (where the original point is sent by this AffineTransform).
|
|
* May be null.
|
|
* @return the point in the AffineTransform's image space where the original
|
|
* point is sent.
|
|
*/
|
|
public Point2D transform(Point2D src, Point2D dst) {
|
|
if (dst == null) {
|
|
if (src instanceof Point2D.Double) {
|
|
dst = new Point2D.Double();
|
|
} else {
|
|
dst = new Point2D.Float();
|
|
}
|
|
}
|
|
|
|
double x = src.getX();
|
|
double y = src.getY();
|
|
|
|
dst.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12);
|
|
return dst;
|
|
}
|
|
|
|
/**
|
|
* Applies this AffineTransform to an array of points.
|
|
*
|
|
* @param src
|
|
* the array of points to be transformed.
|
|
* @param srcOff
|
|
* the offset in the source point array of the first point to be
|
|
* transformed.
|
|
* @param dst
|
|
* the point array where the images of the points (after applying
|
|
* the AffineTransformation) should be placed.
|
|
* @param dstOff
|
|
* the offset in the destination array where the new values
|
|
* should be written.
|
|
* @param length
|
|
* the number of points to transform.
|
|
* @throws ArrayIndexOutOfBoundsException
|
|
* if <code>srcOff + length > src.length</code> or
|
|
* <code>dstOff + length > dst.length</code>.
|
|
*/
|
|
public void transform(Point2D[] src, int srcOff, Point2D[] dst, int dstOff, int length) {
|
|
while (--length >= 0) {
|
|
Point2D srcPoint = src[srcOff++];
|
|
double x = srcPoint.getX();
|
|
double y = srcPoint.getY();
|
|
Point2D dstPoint = dst[dstOff];
|
|
if (dstPoint == null) {
|
|
if (srcPoint instanceof Point2D.Double) {
|
|
dstPoint = new Point2D.Double();
|
|
} else {
|
|
dstPoint = new Point2D.Float();
|
|
}
|
|
}
|
|
dstPoint.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12);
|
|
dst[dstOff++] = dstPoint;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Applies this AffineTransform to a set of points given as an array of
|
|
* double values where every two values in the array give the coordinates of
|
|
* a point; the even-indexed values giving the x coordinates and the
|
|
* odd-indexed values giving the y coordinates.
|
|
*
|
|
* @param src
|
|
* the array of points to be transformed.
|
|
* @param srcOff
|
|
* the offset in the source point array of the first point to be
|
|
* transformed.
|
|
* @param dst
|
|
* the point array where the images of the points (after applying
|
|
* the AffineTransformation) should be placed.
|
|
* @param dstOff
|
|
* the offset in the destination array where the new values
|
|
* should be written.
|
|
* @param length
|
|
* the number of points to transform.
|
|
* @throws ArrayIndexOutOfBoundsException
|
|
* if <code>srcOff + length*2 > src.length</code> or
|
|
* <code>dstOff + length*2 > dst.length</code>.
|
|
*/
|
|
public void transform(double[] src, int srcOff, double[] dst, int dstOff, int length) {
|
|
int step = 2;
|
|
if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) {
|
|
srcOff = srcOff + length * 2 - 2;
|
|
dstOff = dstOff + length * 2 - 2;
|
|
step = -2;
|
|
}
|
|
while (--length >= 0) {
|
|
double x = src[srcOff + 0];
|
|
double y = src[srcOff + 1];
|
|
dst[dstOff + 0] = x * m00 + y * m01 + m02;
|
|
dst[dstOff + 1] = x * m10 + y * m11 + m12;
|
|
srcOff += step;
|
|
dstOff += step;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Applies this AffineTransform to a set of points given as an array of
|
|
* float values where every two values in the array give the coordinates of
|
|
* a point; the even-indexed values giving the x coordinates and the
|
|
* odd-indexed values giving the y coordinates.
|
|
*
|
|
* @param src
|
|
* the array of points to be transformed.
|
|
* @param srcOff
|
|
* the offset in the source point array of the first point to be
|
|
* transformed.
|
|
* @param dst
|
|
* the point array where the images of the points (after applying
|
|
* the AffineTransformation) should be placed.
|
|
* @param dstOff
|
|
* the offset in the destination array where the new values
|
|
* should be written.
|
|
* @param length
|
|
* the number of points to transform.
|
|
* @throws ArrayIndexOutOfBoundsException
|
|
* if <code>srcOff + length*2 > src.length</code> or
|
|
* <code>dstOff + length*2 > dst.length</code>.
|
|
*/
|
|
public void transform(float[] src, int srcOff, float[] dst, int dstOff, int length) {
|
|
int step = 2;
|
|
if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) {
|
|
srcOff = srcOff + length * 2 - 2;
|
|
dstOff = dstOff + length * 2 - 2;
|
|
step = -2;
|
|
}
|
|
while (--length >= 0) {
|
|
float x = src[srcOff + 0];
|
|
float y = src[srcOff + 1];
|
|
dst[dstOff + 0] = (float)(x * m00 + y * m01 + m02);
|
|
dst[dstOff + 1] = (float)(x * m10 + y * m11 + m12);
|
|
srcOff += step;
|
|
dstOff += step;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Applies this AffineTransform to a set of points given as an array of
|
|
* float values where every two values in the array give the coordinates of
|
|
* a point; the even-indexed values giving the x coordinates and the
|
|
* odd-indexed values giving the y coordinates. The destination coordinates
|
|
* are given as values of type <code>double</code>.
|
|
*
|
|
* @param src
|
|
* the array of points to be transformed.
|
|
* @param srcOff
|
|
* the offset in the source point array of the first point to be
|
|
* transformed.
|
|
* @param dst
|
|
* the point array where the images of the points (after applying
|
|
* the AffineTransformation) should be placed.
|
|
* @param dstOff
|
|
* the offset in the destination array where the new values
|
|
* should be written.
|
|
* @param length
|
|
* the number of points to transform.
|
|
* @throws ArrayIndexOutOfBoundsException
|
|
* if <code>srcOff + length*2 > src.length</code> or
|
|
* <code>dstOff + length*2 > dst.length</code>.
|
|
*/
|
|
public void transform(float[] src, int srcOff, double[] dst, int dstOff, int length) {
|
|
while (--length >= 0) {
|
|
float x = src[srcOff++];
|
|
float y = src[srcOff++];
|
|
dst[dstOff++] = x * m00 + y * m01 + m02;
|
|
dst[dstOff++] = x * m10 + y * m11 + m12;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Applies this AffineTransform to a set of points given as an array of
|
|
* double values where every two values in the array give the coordinates of
|
|
* a point; the even-indexed values giving the x coordinates and the
|
|
* odd-indexed values giving the y coordinates. The destination coordinates
|
|
* are given as values of type <code>float</code>.
|
|
*
|
|
* @param src
|
|
* the array of points to be transformed.
|
|
* @param srcOff
|
|
* the offset in the source point array of the first point to be
|
|
* transformed.
|
|
* @param dst
|
|
* the point array where the images of the points (after applying
|
|
* the AffineTransformation) should be placed.
|
|
* @param dstOff
|
|
* the offset in the destination array where the new values
|
|
* should be written.
|
|
* @param length
|
|
* the number of points to transform.
|
|
* @throws ArrayIndexOutOfBoundsException
|
|
* if <code>srcOff + length*2 > src.length</code> or
|
|
* <code>dstOff + length*2 > dst.length</code>.
|
|
*/
|
|
public void transform(double[] src, int srcOff, float[] dst, int dstOff, int length) {
|
|
while (--length >= 0) {
|
|
double x = src[srcOff++];
|
|
double y = src[srcOff++];
|
|
dst[dstOff++] = (float)(x * m00 + y * m01 + m02);
|
|
dst[dstOff++] = (float)(x * m10 + y * m11 + m12);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Transforms the point according to the linear transformation part of this
|
|
* AffineTransformation (without applying the translation).
|
|
*
|
|
* @param src
|
|
* the original point.
|
|
* @param dst
|
|
* the point object where the result of the delta transform is
|
|
* written.
|
|
* @return the result of applying the delta transform (linear part only) to
|
|
* the original point.
|
|
*/
|
|
// TODO: is this right? if dst is null, we check what it's an
|
|
// instance of? Shouldn't it be src instanceof Point2D.Double?
|
|
public Point2D deltaTransform(Point2D src, Point2D dst) {
|
|
if (dst == null) {
|
|
if (dst instanceof Point2D.Double) {
|
|
dst = new Point2D.Double();
|
|
} else {
|
|
dst = new Point2D.Float();
|
|
}
|
|
}
|
|
|
|
double x = src.getX();
|
|
double y = src.getY();
|
|
|
|
dst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
|
|
return dst;
|
|
}
|
|
|
|
/**
|
|
* Applies the linear transformation part of this AffineTransform (ignoring
|
|
* the translation part) to a set of points given as an array of double
|
|
* values where every two values in the array give the coordinates of a
|
|
* point; the even-indexed values giving the x coordinates and the
|
|
* odd-indexed values giving the y coordinates.
|
|
*
|
|
* @param src
|
|
* the array of points to be transformed.
|
|
* @param srcOff
|
|
* the offset in the source point array of the first point to be
|
|
* transformed.
|
|
* @param dst
|
|
* the point array where the images of the points (after applying
|
|
* the delta transformation) should be placed.
|
|
* @param dstOff
|
|
* the offset in the destination array where the new values
|
|
* should be written.
|
|
* @param length
|
|
* the number of points to transform.
|
|
* @throws ArrayIndexOutOfBoundsException
|
|
* if <code>srcOff + length*2 > src.length</code> or
|
|
* <code>dstOff + length*2 > dst.length</code>.
|
|
*/
|
|
public void deltaTransform(double[] src, int srcOff, double[] dst, int dstOff, int length) {
|
|
while (--length >= 0) {
|
|
double x = src[srcOff++];
|
|
double y = src[srcOff++];
|
|
dst[dstOff++] = x * m00 + y * m01;
|
|
dst[dstOff++] = x * m10 + y * m11;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Transforms the point according to the inverse of this
|
|
* AffineTransformation.
|
|
*
|
|
* @param src
|
|
* the original point.
|
|
* @param dst
|
|
* the point object where the result of the inverse transform is
|
|
* written (may be null).
|
|
* @return the result of applying the inverse transform. Inverse transform.
|
|
* @throws NoninvertibleTransformException
|
|
* if this AffineTransform cannot be inverted (the determinant
|
|
* of the linear transformation part is zero).
|
|
*/
|
|
public Point2D inverseTransform(Point2D src, Point2D dst)
|
|
throws NoninvertibleTransformException {
|
|
double det = getDeterminant();
|
|
if (Math.abs(det) < ZERO) {
|
|
// awt.204=Determinant is zero
|
|
throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$
|
|
}
|
|
|
|
if (dst == null) {
|
|
if (src instanceof Point2D.Double) {
|
|
dst = new Point2D.Double();
|
|
} else {
|
|
dst = new Point2D.Float();
|
|
}
|
|
}
|
|
|
|
double x = src.getX() - m02;
|
|
double y = src.getY() - m12;
|
|
|
|
dst.setLocation((x * m11 - y * m01) / det, (y * m00 - x * m10) / det);
|
|
return dst;
|
|
}
|
|
|
|
/**
|
|
* Applies the inverse of this AffineTransform to a set of points given as
|
|
* an array of double values where every two values in the array give the
|
|
* coordinates of a point; the even-indexed values giving the x coordinates
|
|
* and the odd-indexed values giving the y coordinates.
|
|
*
|
|
* @param src
|
|
* the array of points to be transformed.
|
|
* @param srcOff
|
|
* the offset in the source point array of the first point to be
|
|
* transformed.
|
|
* @param dst
|
|
* the point array where the images of the points (after applying
|
|
* the inverse of the AffineTransformation) should be placed.
|
|
* @param dstOff
|
|
* the offset in the destination array where the new values
|
|
* should be written.
|
|
* @param length
|
|
* the number of points to transform.
|
|
* @throws ArrayIndexOutOfBoundsException
|
|
* if <code>srcOff + length*2 > src.length</code> or
|
|
* <code>dstOff + length*2 > dst.length</code>.
|
|
* @throws NoninvertibleTransformException
|
|
* if this AffineTransform cannot be inverted (the determinant
|
|
* of the linear transformation part is zero).
|
|
*/
|
|
public void inverseTransform(double[] src, int srcOff, double[] dst, int dstOff, int length)
|
|
throws NoninvertibleTransformException {
|
|
double det = getDeterminant();
|
|
if (Math.abs(det) < ZERO) {
|
|
// awt.204=Determinant is zero
|
|
throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$
|
|
}
|
|
|
|
while (--length >= 0) {
|
|
double x = src[srcOff++] - m02;
|
|
double y = src[srcOff++] - m12;
|
|
dst[dstOff++] = (x * m11 - y * m01) / det;
|
|
dst[dstOff++] = (y * m00 - x * m10) / det;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a new shape whose data is given by applying this AffineTransform
|
|
* to the specified shape.
|
|
*
|
|
* @param src
|
|
* the original shape whose data is to be transformed.
|
|
* @return the new shape found by applying this AffineTransform to the
|
|
* original shape.
|
|
*/
|
|
public Shape createTransformedShape(Shape src) {
|
|
if (src == null) {
|
|
return null;
|
|
}
|
|
if (src instanceof GeneralPath) {
|
|
return ((GeneralPath)src).createTransformedShape(this);
|
|
}
|
|
PathIterator path = src.getPathIterator(this);
|
|
GeneralPath dst = new GeneralPath(path.getWindingRule());
|
|
dst.append(path, false);
|
|
return dst;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return getClass().getName() + "[[" + m00 + ", " + m01 + ", " + m02 + "], [" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
|
|
+ m10 + ", " + m11 + ", " + m12 + "]]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
|
}
|
|
|
|
@Override
|
|
public Object clone() {
|
|
try {
|
|
return super.clone();
|
|
} catch (CloneNotSupportedException e) {
|
|
throw new InternalError();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
HashCode hash = new HashCode();
|
|
hash.append(m00);
|
|
hash.append(m01);
|
|
hash.append(m02);
|
|
hash.append(m10);
|
|
hash.append(m11);
|
|
hash.append(m12);
|
|
return hash.hashCode();
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (obj == this) {
|
|
return true;
|
|
}
|
|
if (obj instanceof AffineTransform) {
|
|
AffineTransform t = (AffineTransform)obj;
|
|
return m00 == t.m00 && m01 == t.m01 && m02 == t.m02 && m10 == t.m10 && m11 == t.m11
|
|
&& m12 == t.m12;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Writes the AffineTrassform object to the output steam.
|
|
*
|
|
* @param stream
|
|
* - the output stream.
|
|
* @throws IOException
|
|
* - if there are I/O errors while writing to the output stream.
|
|
*/
|
|
private void writeObject(java.io.ObjectOutputStream stream) throws IOException {
|
|
stream.defaultWriteObject();
|
|
}
|
|
|
|
/**
|
|
* Read the AffineTransform object from the input stream.
|
|
*
|
|
* @param stream
|
|
* - the input stream.
|
|
* @throws IOException
|
|
* - if there are I/O errors while reading from the input
|
|
* stream.
|
|
* @throws ClassNotFoundException
|
|
* - if class could not be found.
|
|
*/
|
|
private void readObject(java.io.ObjectInputStream stream) throws IOException,
|
|
ClassNotFoundException {
|
|
stream.defaultReadObject();
|
|
type = TYPE_UNKNOWN;
|
|
}
|
|
|
|
}
|