org.mini2Dx.gdx.math.Affine2 Maven / Gradle / Ivy
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed 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.
******************************************************************************/
package org.mini2Dx.gdx.math;
import java.io.Serializable;
import org.mini2Dx.gdx.utils.GdxRuntimeException;
/** A specialized 3x3 matrix that can represent sequences of 2D translations, scales, flips, rotations, and shears. Affine transformations preserve straight lines, and
* parallel lines remain parallel after the transformation. Operations on affine matrices are faster because the last row can
* always be assumed (0, 0, 1).
*
* @author vmilea */
public final class Affine2 implements Serializable {
private static final long serialVersionUID = 1524569123485049187L;
public float m00 = 1, m01 = 0, m02 = 0;
public float m10 = 0, m11 = 1, m12 = 0;
// constant: m21 = 0, m21 = 1, m22 = 1
/** Constructs an identity matrix. */
public Affine2 () {
}
/** Constructs a matrix from the given affine matrix.
*
* @param other The affine matrix to copy. This matrix will not be modified. */
public Affine2 (Affine2 other) {
set(other);
}
/** Sets this matrix to the identity matrix
* @return This matrix for the purpose of chaining operations. */
public Affine2 idt () {
m00 = 1;
m01 = 0;
m02 = 0;
m10 = 0;
m11 = 1;
m12 = 0;
return this;
}
/** Copies the values from the provided affine matrix to this matrix.
* @param other The affine matrix to copy.
* @return This matrix for the purposes of chaining. */
public Affine2 set (Affine2 other) {
m00 = other.m00;
m01 = other.m01;
m02 = other.m02;
m10 = other.m10;
m11 = other.m11;
m12 = other.m12;
return this;
}
/** Copies the values from the provided matrix to this matrix.
* @param matrix The matrix to copy, assumed to be an affine transformation.
* @return This matrix for the purposes of chaining. */
public Affine2 set (Matrix3 matrix) {
float[] other = matrix.val;
m00 = other[Matrix3.M00];
m01 = other[Matrix3.M01];
m02 = other[Matrix3.M02];
m10 = other[Matrix3.M10];
m11 = other[Matrix3.M11];
m12 = other[Matrix3.M12];
return this;
}
/** Copies the 2D transformation components from the provided 4x4 matrix. The values are mapped as follows:
*
*
* [ M00 M01 M03 ]
* [ M10 M11 M13 ]
* [ 0 0 1 ]
*
* @param matrix The source matrix, assumed to be an affine transformation within XY plane. This matrix will not be modified.
* @return This matrix for the purpose of chaining operations. */
public Affine2 set (Matrix4 matrix) {
float[] other = matrix.val;
m00 = other[Matrix4.M00];
m01 = other[Matrix4.M01];
m02 = other[Matrix4.M03];
m10 = other[Matrix4.M10];
m11 = other[Matrix4.M11];
m12 = other[Matrix4.M13];
return this;
}
/** Sets this matrix to a translation matrix.
* @param x The translation in x
* @param y The translation in y
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToTranslation (float x, float y) {
m00 = 1;
m01 = 0;
m02 = x;
m10 = 0;
m11 = 1;
m12 = y;
return this;
}
/** Sets this matrix to a translation matrix.
* @param trn The translation vector.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToTranslation (Vector2 trn) {
return setToTranslation(trn.x, trn.y);
}
/** Sets this matrix to a scaling matrix.
* @param scaleX The scale in x.
* @param scaleY The scale in y.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToScaling (float scaleX, float scaleY) {
m00 = scaleX;
m01 = 0;
m02 = 0;
m10 = 0;
m11 = scaleY;
m12 = 0;
return this;
}
/** Sets this matrix to a scaling matrix.
* @param scale The scale vector.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToScaling (Vector2 scale) {
return setToScaling(scale.x, scale.y);
}
/** Sets this matrix to a rotation matrix that will rotate any vector in counter-clockwise direction around the z-axis.
* @param degrees The angle in degrees.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToRotation (float degrees) {
float cos = MathUtils.cosDeg(degrees);
float sin = MathUtils.sinDeg(degrees);
m00 = cos;
m01 = -sin;
m02 = 0;
m10 = sin;
m11 = cos;
m12 = 0;
return this;
}
/** Sets this matrix to a rotation matrix that will rotate any vector in counter-clockwise direction around the z-axis.
* @param radians The angle in radians.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToRotationRad (float radians) {
float cos = MathUtils.cos(radians);
float sin = MathUtils.sin(radians);
m00 = cos;
m01 = -sin;
m02 = 0;
m10 = sin;
m11 = cos;
m12 = 0;
return this;
}
/** Sets this matrix to a rotation matrix that will rotate any vector in counter-clockwise direction around the z-axis.
* @param cos The angle cosine.
* @param sin The angle sine.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToRotation (float cos, float sin) {
m00 = cos;
m01 = -sin;
m02 = 0;
m10 = sin;
m11 = cos;
m12 = 0;
return this;
}
/** Sets this matrix to a shearing matrix.
* @param shearX The shear in x direction.
* @param shearY The shear in y direction.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToShearing (float shearX, float shearY) {
m00 = 1;
m01 = shearX;
m02 = 0;
m10 = shearY;
m11 = 1;
m12 = 0;
return this;
}
/** Sets this matrix to a shearing matrix.
* @param shear The shear vector.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToShearing (Vector2 shear) {
return setToShearing(shear.x, shear.y);
}
/** Sets this matrix to a concatenation of translation, rotation and scale. It is a more efficient form for:
* idt().translate(x, y).rotate(degrees).scale(scaleX, scaleY)
* @param x The translation in x.
* @param y The translation in y.
* @param degrees The angle in degrees.
* @param scaleX The scale in y.
* @param scaleY The scale in x.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToTrnRotScl (float x, float y, float degrees, float scaleX, float scaleY) {
m02 = x;
m12 = y;
if (degrees == 0) {
m00 = scaleX;
m01 = 0;
m10 = 0;
m11 = scaleY;
} else {
float sin = MathUtils.sinDeg(degrees);
float cos = MathUtils.cosDeg(degrees);
m00 = cos * scaleX;
m01 = -sin * scaleY;
m10 = sin * scaleX;
m11 = cos * scaleY;
}
return this;
}
/** Sets this matrix to a concatenation of translation, rotation and scale. It is a more efficient form for:
* idt().translate(trn).rotate(degrees).scale(scale)
* @param trn The translation vector.
* @param degrees The angle in degrees.
* @param scale The scale vector.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToTrnRotScl (Vector2 trn, float degrees, Vector2 scale) {
return setToTrnRotScl(trn.x, trn.y, degrees, scale.x, scale.y);
}
/** Sets this matrix to a concatenation of translation, rotation and scale. It is a more efficient form for:
* idt().translate(x, y).rotateRad(radians).scale(scaleX, scaleY)
* @param x The translation in x.
* @param y The translation in y.
* @param radians The angle in radians.
* @param scaleX The scale in y.
* @param scaleY The scale in x.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToTrnRotRadScl (float x, float y, float radians, float scaleX, float scaleY) {
m02 = x;
m12 = y;
if (radians == 0) {
m00 = scaleX;
m01 = 0;
m10 = 0;
m11 = scaleY;
} else {
float sin = MathUtils.sin(radians);
float cos = MathUtils.cos(radians);
m00 = cos * scaleX;
m01 = -sin * scaleY;
m10 = sin * scaleX;
m11 = cos * scaleY;
}
return this;
}
/** Sets this matrix to a concatenation of translation, rotation and scale. It is a more efficient form for:
* idt().translate(trn).rotateRad(radians).scale(scale)
* @param trn The translation vector.
* @param radians The angle in radians.
* @param scale The scale vector.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToTrnRotRadScl (Vector2 trn, float radians, Vector2 scale) {
return setToTrnRotRadScl(trn.x, trn.y, radians, scale.x, scale.y);
}
/** Sets this matrix to a concatenation of translation and scale. It is a more efficient form for:
* idt().translate(x, y).scale(scaleX, scaleY)
* @param x The translation in x.
* @param y The translation in y.
* @param scaleX The scale in y.
* @param scaleY The scale in x.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToTrnScl (float x, float y, float scaleX, float scaleY) {
m00 = scaleX;
m01 = 0;
m02 = x;
m10 = 0;
m11 = scaleY;
m12 = y;
return this;
}
/** Sets this matrix to a concatenation of translation and scale. It is a more efficient form for:
* idt().translate(trn).scale(scale)
* @param trn The translation vector.
* @param scale The scale vector.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToTrnScl (Vector2 trn, Vector2 scale) {
return setToTrnScl(trn.x, trn.y, scale.x, scale.y);
}
/** Sets this matrix to the product of two matrices.
* @param l Left matrix.
* @param r Right matrix.
* @return This matrix for the purpose of chaining operations. */
public Affine2 setToProduct (Affine2 l, Affine2 r) {
m00 = l.m00 * r.m00 + l.m01 * r.m10;
m01 = l.m00 * r.m01 + l.m01 * r.m11;
m02 = l.m00 * r.m02 + l.m01 * r.m12 + l.m02;
m10 = l.m10 * r.m00 + l.m11 * r.m10;
m11 = l.m10 * r.m01 + l.m11 * r.m11;
m12 = l.m10 * r.m02 + l.m11 * r.m12 + l.m12;
return this;
}
/** Inverts this matrix given that the determinant is != 0.
* @return This matrix for the purpose of chaining operations.
* @throws GdxRuntimeException if the matrix is singular (not invertible) */
public Affine2 inv () {
float det = det();
if (det == 0) throw new GdxRuntimeException("Can't invert a singular affine matrix");
float invDet = 1.0f / det;
float tmp00 = m11;
float tmp01 = -m01;
float tmp02 = m01 * m12 - m11 * m02;
float tmp10 = -m10;
float tmp11 = m00;
float tmp12 = m10 * m02 - m00 * m12;
m00 = invDet * tmp00;
m01 = invDet * tmp01;
m02 = invDet * tmp02;
m10 = invDet * tmp10;
m11 = invDet * tmp11;
m12 = invDet * tmp12;
return this;
}
/** Postmultiplies this matrix with the provided matrix and stores the result in this matrix. For example:
*
*
* A.mul(B) results in A := AB
*
* @param other Matrix to multiply by.
* @return This matrix for the purpose of chaining operations together. */
public Affine2 mul (Affine2 other) {
float tmp00 = m00 * other.m00 + m01 * other.m10;
float tmp01 = m00 * other.m01 + m01 * other.m11;
float tmp02 = m00 * other.m02 + m01 * other.m12 + m02;
float tmp10 = m10 * other.m00 + m11 * other.m10;
float tmp11 = m10 * other.m01 + m11 * other.m11;
float tmp12 = m10 * other.m02 + m11 * other.m12 + m12;
m00 = tmp00;
m01 = tmp01;
m02 = tmp02;
m10 = tmp10;
m11 = tmp11;
m12 = tmp12;
return this;
}
/** Premultiplies this matrix with the provided matrix and stores the result in this matrix. For example:
*
*
* A.preMul(B) results in A := BA
*
* @param other The other Matrix to multiply by
* @return This matrix for the purpose of chaining operations. */
public Affine2 preMul (Affine2 other) {
float tmp00 = other.m00 * m00 + other.m01 * m10;
float tmp01 = other.m00 * m01 + other.m01 * m11;
float tmp02 = other.m00 * m02 + other.m01 * m12 + other.m02;
float tmp10 = other.m10 * m00 + other.m11 * m10;
float tmp11 = other.m10 * m01 + other.m11 * m11;
float tmp12 = other.m10 * m02 + other.m11 * m12 + other.m12;
m00 = tmp00;
m01 = tmp01;
m02 = tmp02;
m10 = tmp10;
m11 = tmp11;
m12 = tmp12;
return this;
}
/** Postmultiplies this matrix by a translation matrix.
* @param x The x-component of the translation vector.
* @param y The y-component of the translation vector.
* @return This matrix for the purpose of chaining. */
public Affine2 translate (float x, float y) {
m02 += m00 * x + m01 * y;
m12 += m10 * x + m11 * y;
return this;
}
/** Postmultiplies this matrix by a translation matrix.
* @param trn The translation vector.
* @return This matrix for the purpose of chaining. */
public Affine2 translate (Vector2 trn) {
return translate(trn.x, trn.y);
}
/** Premultiplies this matrix by a translation matrix.
* @param x The x-component of the translation vector.
* @param y The y-component of the translation vector.
* @return This matrix for the purpose of chaining. */
public Affine2 preTranslate (float x, float y) {
m02 += x;
m12 += y;
return this;
}
/** Premultiplies this matrix by a translation matrix.
* @param trn The translation vector.
* @return This matrix for the purpose of chaining. */
public Affine2 preTranslate (Vector2 trn) {
return preTranslate(trn.x, trn.y);
}
/** Postmultiplies this matrix with a scale matrix.
* @param scaleX The scale in the x-axis.
* @param scaleY The scale in the y-axis.
* @return This matrix for the purpose of chaining. */
public Affine2 scale (float scaleX, float scaleY) {
m00 *= scaleX;
m01 *= scaleY;
m10 *= scaleX;
m11 *= scaleY;
return this;
}
/** Postmultiplies this matrix with a scale matrix.
* @param scale The scale vector.
* @return This matrix for the purpose of chaining. */
public Affine2 scale (Vector2 scale) {
return scale(scale.x, scale.y);
}
/** Premultiplies this matrix with a scale matrix.
* @param scaleX The scale in the x-axis.
* @param scaleY The scale in the y-axis.
* @return This matrix for the purpose of chaining. */
public Affine2 preScale (float scaleX, float scaleY) {
m00 *= scaleX;
m01 *= scaleX;
m02 *= scaleX;
m10 *= scaleY;
m11 *= scaleY;
m12 *= scaleY;
return this;
}
/** Premultiplies this matrix with a scale matrix.
* @param scale The scale vector.
* @return This matrix for the purpose of chaining. */
public Affine2 preScale (Vector2 scale) {
return preScale(scale.x, scale.y);
}
/** Postmultiplies this matrix with a (counter-clockwise) rotation matrix.
* @param degrees The angle in degrees
* @return This matrix for the purpose of chaining. */
public Affine2 rotate (float degrees) {
if (degrees == 0) return this;
float cos = MathUtils.cosDeg(degrees);
float sin = MathUtils.sinDeg(degrees);
float tmp00 = m00 * cos + m01 * sin;
float tmp01 = m00 * -sin + m01 * cos;
float tmp10 = m10 * cos + m11 * sin;
float tmp11 = m10 * -sin + m11 * cos;
m00 = tmp00;
m01 = tmp01;
m10 = tmp10;
m11 = tmp11;
return this;
}
/** Postmultiplies this matrix with a (counter-clockwise) rotation matrix.
* @param radians The angle in radians
* @return This matrix for the purpose of chaining. */
public Affine2 rotateRad (float radians) {
if (radians == 0) return this;
float cos = MathUtils.cos(radians);
float sin = MathUtils.sin(radians);
float tmp00 = m00 * cos + m01 * sin;
float tmp01 = m00 * -sin + m01 * cos;
float tmp10 = m10 * cos + m11 * sin;
float tmp11 = m10 * -sin + m11 * cos;
m00 = tmp00;
m01 = tmp01;
m10 = tmp10;
m11 = tmp11;
return this;
}
/** Premultiplies this matrix with a (counter-clockwise) rotation matrix.
* @param degrees The angle in degrees
* @return This matrix for the purpose of chaining. */
public Affine2 preRotate (float degrees) {
if (degrees == 0) return this;
float cos = MathUtils.cosDeg(degrees);
float sin = MathUtils.sinDeg(degrees);
float tmp00 = cos * m00 - sin * m10;
float tmp01 = cos * m01 - sin * m11;
float tmp02 = cos * m02 - sin * m12;
float tmp10 = sin * m00 + cos * m10;
float tmp11 = sin * m01 + cos * m11;
float tmp12 = sin * m02 + cos * m12;
m00 = tmp00;
m01 = tmp01;
m02 = tmp02;
m10 = tmp10;
m11 = tmp11;
m12 = tmp12;
return this;
}
/** Premultiplies this matrix with a (counter-clockwise) rotation matrix.
* @param radians The angle in radians
* @return This matrix for the purpose of chaining. */
public Affine2 preRotateRad (float radians) {
if (radians == 0) return this;
float cos = MathUtils.cos(radians);
float sin = MathUtils.sin(radians);
float tmp00 = cos * m00 - sin * m10;
float tmp01 = cos * m01 - sin * m11;
float tmp02 = cos * m02 - sin * m12;
float tmp10 = sin * m00 + cos * m10;
float tmp11 = sin * m01 + cos * m11;
float tmp12 = sin * m02 + cos * m12;
m00 = tmp00;
m01 = tmp01;
m02 = tmp02;
m10 = tmp10;
m11 = tmp11;
m12 = tmp12;
return this;
}
/** Postmultiplies this matrix by a shear matrix.
* @param shearX The shear in x direction.
* @param shearY The shear in y direction.
* @return This matrix for the purpose of chaining. */
public Affine2 shear (float shearX, float shearY) {
float tmp0 = m00 + shearY * m01;
float tmp1 = m01 + shearX * m00;
m00 = tmp0;
m01 = tmp1;
tmp0 = m10 + shearY * m11;
tmp1 = m11 + shearX * m10;
m10 = tmp0;
m11 = tmp1;
return this;
}
/** Postmultiplies this matrix by a shear matrix.
* @param shear The shear vector.
* @return This matrix for the purpose of chaining. */
public Affine2 shear (Vector2 shear) {
return shear(shear.x, shear.y);
}
/** Premultiplies this matrix by a shear matrix.
* @param shearX The shear in x direction.
* @param shearY The shear in y direction.
* @return This matrix for the purpose of chaining. */
public Affine2 preShear (float shearX, float shearY) {
float tmp00 = m00 + shearX * m10;
float tmp01 = m01 + shearX * m11;
float tmp02 = m02 + shearX * m12;
float tmp10 = m10 + shearY * m00;
float tmp11 = m11 + shearY * m01;
float tmp12 = m12 + shearY * m02;
m00 = tmp00;
m01 = tmp01;
m02 = tmp02;
m10 = tmp10;
m11 = tmp11;
m12 = tmp12;
return this;
}
/** Premultiplies this matrix by a shear matrix.
* @param shear The shear vector.
* @return This matrix for the purpose of chaining. */
public Affine2 preShear (Vector2 shear) {
return preShear(shear.x, shear.y);
}
/** Calculates the determinant of the matrix.
* @return The determinant of this matrix. */
public float det () {
return m00 * m11 - m01 * m10;
}
/** Get the x-y translation component of the matrix.
* @param position Output vector.
* @return Filled position. */
public Vector2 getTranslation (Vector2 position) {
position.x = m02;
position.y = m12;
return position;
}
/** Check if the this is a plain translation matrix.
* @return True if scale is 1 and rotation is 0. */
public boolean isTranslation () {
return (m00 == 1 && m11 == 1 && m01 == 0 && m10 == 0);
}
/** Check if this is an indentity matrix.
* @return True if scale is 1 and rotation is 0. */
public boolean isIdt () {
return (m00 == 1 && m02 == 0 && m12 == 0 && m11 == 1 && m01 == 0 && m10 == 0);
}
/** Applies the affine transformation on a vector. */
public void applyTo (Vector2 point) {
float x = point.x;
float y = point.y;
point.x = m00 * x + m01 * y + m02;
point.y = m10 * x + m11 * y + m12;
}
@Override
public String toString () {
return "[" + m00 + "|" + m01 + "|" + m02 + "]\n[" + m10 + "|" + m11 + "|" + m12 + "]\n[0.0|0.0|0.1]";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy