/*
* Copyright 2009 Hao Nguyen
*
* 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 gwt.g2d.client.math;
import gwt.g2d.shared.math.Vector2;
import java.util.Arrays;
/**
* Represents a 3x3 matrix for 2D transformation.
*
* The matrix has the following structure:
*
* <pre>
* m11 m21 dx
* m12 m22 dy
* 0 0 1
* </pre>
*
* @author hao1300@gmail.com
*/
public class Matrix {
private double m11, m12, m21, m22, dx, dy;
/**
* Constructs an identity matrix.
*/
public Matrix() {
m11 = 1;
m22 = 1;
}
/**
* Creates a new matrix using the given values (the last row is implicit).
*
* @param m11
* @param m12
* @param m21
* @param m22
* @param dx
* @param dy
*/
public Matrix(double m11, double m12, double m21, double m22, double dx,
double dy) {
set(m11, m12, m21, m22, dx, dy);
}
/**
* Copy constructor.
*
* @param rhs
*/
public Matrix(Matrix rhs) {
set(rhs);
}
/**
* Sets this matrix to have the given values.
*
* @param m11
* @param m12
* @param m21
* @param m22
* @param dx
* @param dy
*/
public final void set(double m11, double m12, double m21, double m22,
double dx, double dy) {
this.m11 = m11;
this.m12 = m12;
this.m21 = m21;
this.m22 = m22;
this.dx = dx;
this.dy = dy;
}
/**
* Sets the value of this matrix to be the given matrix.
*
* @param rhs
*/
public final void set(Matrix rhs) {
set(rhs.m11, rhs.m12, rhs.m21, rhs.m22, rhs.dx, rhs.dy);
}
/**
* Sets this matrix values to be a clockwise rotation transformation.
* This will override all existing values in the matrix. If you want to
* transform this matrix by a rotation transformation, considers using
* {@link #rotate(double)} instead.
*
* @param angle
*/
public final void setRotate(double angle) {
m11 = Math.cos(angle);
m12 = Math.sin(angle);
m21 = -m12;
m22 = m11;
dx = 0;
dy = 0;
}
/**
* Sets this matrix values to be a counter-clockwise rotation transformation.
* This will override all existing values in the matrix. If you want to
* transform this matrix by a rotation transformation, considers using
* {@link #rotate(double)} instead.
*
* @param angle
*/
public final void setRotateCcw(double angle) {
setRotate(-angle);
}
/**
* Sets this matrix values to be a scale transformation.
* This will override all existing values in the matrix. If you want to
* transform this matrix by a scale transformation, considers using
* {@link #scale(double, double)} instead.
*
* @param x
* @param y
*/
public final void setScale(double x, double y) {
set(x, 0, 0, y, 0, 0);
}
/**
* Sets this matrix values to be a translation transformation.
* This will override all existing values in the matrix. If you want to
* transform this matrix by a translation transformation, considers using
* {@link #translate(double, double)} instead.
*
* @param x
* @param y
*/
public final void setTranslate(double x, double y) {
set(1, 0, 0, 1, x, y);
}
/**
* Sets this matrix to be the identity matrix.
*/
public final void setIdentity() {
set(1, 0, 0, 1, 0, 0);
}
/**
* Returns a new matrix that is the result of this * rhs.
*
* @param rhs
* @return the new matrix
*/
public final Matrix multiply(Matrix rhs) {
return new Matrix(this).mutableMultiply(rhs);
}
/**
* Multiply this by rhs. Unlike {@link #multiply(Matrix)}, the returned Matrix
* is this, so no new Matrix is allocated.
*
* @param rhs
* the vector to multiply.
* @return self to support chaining.
*/
public final Matrix mutableMultiply(Matrix rhs) {
set(this.m11 * rhs.m11 + this.m12 * rhs.m21,
this.m11 * rhs.m12 + this.m12 * rhs.m22,
this.m21 * rhs.m11 + this.m22 * rhs.m21,
this.m21 * rhs.m12 + this.m22 * rhs.m22,
this.m11 * rhs.dx + this.m12 * rhs.dy + this.dx,
this.m21 * rhs.dx + this.m22 * rhs.dy + this.dy);
return this;
}
/**
* Returns a new matrix that is the result of this rotated clockwise by the
* given angle.
*
* @param angle
* @return the new matrix
*/
public final Matrix rotate(double angle) {
return new Matrix(this).mutableRotate(angle);
}
/**
* Returns a new matrix that is the result of this rotated counter-clockwise
* by the given angle.
*
* @param angle
* @return the new matrix
*/
public final Matrix rotateCcw(double angle) {
return new Matrix(this).mutableRotateCcw(angle);
}
/**
* Rotates this matrix clockwise by angle.
* Unlike {@link #rotate(double)}, the returned Matrix is this, so no new
* Matrix is allocated.
*
* @param angle
* @return self to support chaining.
*/
public final Matrix mutableRotate(double angle) {
double c = Math.cos(angle);
double s = Math.sin(angle);
set(m11 * c + m12 * -s, m11 * s + m12 * c,
m21 * c + m22 * -s, m21 * s + m22 * c,
dx, dy);
return this;
}
/**
* Rotates this matrix anti-clockwise by angle.
* Unlike {@link #rotateCcw(double)}, the returned Matrix is this,
* so no new Matrix is allocated.
*
* @param angle
* @return self to support chaining.
*/
public final Matrix mutableRotateCcw(double angle) {
return mutableRotate(-angle);
}
/**
* Returns a new matrix that is this matrix scaled by x and y.
*
* @param x
* @param y
* @return the new scaled matrix.
*/
public final Matrix scale(double x, double y) {
return new Matrix(this).mutableScale(x, y);
}
/**
* Scales this matrix by x and y.
* Unlike {@link #scale(double, double)}, the returned Matrix is this, so no
* new Matrix is allocated.
*
* @param x
* @param y
* @return self to support chaining.
*/
public final Matrix mutableScale(double x, double y) {
set(m11 * x, m12 * y, m21 * x, m22 * y, dx, dy);
return this;
}
/**
* Returns a new matrix that is this matrix translated by x and y.
*
* @param x
* @param y
* @return a new translated matrix.
*/
public final Matrix translate(double x, double y) {
return new Matrix(this).mutableTranslate(x, y);
}
/**
* Translates this matrix by x and y.
* Unlike {@link #translate(double, double)}, the returned Matrix is this, so
* no new Matrix is allocated.
*
* @param x
* @param y
* @return self to support chaining.
*/
public final Matrix mutableTranslate(double x, double y) {
set(m11, m12, m21, m22, m11 * x + m12 * y + dx, m21 * x + m22 * y + dy);
return this;
}
/**
* Transform the given vector by this matrix.
*
* @param vector
* @return a new Vector2
*/
public final Vector2 transform(Vector2 vector) {
return mutableTransform(new Vector2(vector));
}
/**
* Transform the given vector by this matrix.
* Unlike {@link #transform(Vector2)}, the returned Vector2 is the given
* vector, so no new V is allocated.
*
* @param vector
* @return the transformed vector
*/
public final Vector2 mutableTransform(Vector2 vector) {
vector.set(m11 * vector.getX() + m12 * vector.getY() + dx,
m21 * vector.getX() + m22 * vector.getY() + dy);
return vector;
}
/**
* @param m11 the m11 to set
*/
public final void setM11(double m11) {
this.m11 = m11;
}
/**
* @return the m11
*/
public final double getM11() {
return m11;
}
/**
* @param m12 the m12 to set
*/
public final void setM12(double m12) {
this.m12 = m12;
}
/**
* @return the m12
*/
public final double getM12() {
return m12;
}
/**
* @param m21 the m21 to set
*/
public final void setM21(double m21) {
this.m21 = m21;
}
/**
* @return the m21
*/
public final double getM21() {
return m21;
}
/**
* @param m22 the m22 to set
*/
public final void setM22(double m22) {
this.m22 = m22;
}
/**
* @return the m22
*/
public final double getM22() {
return m22;
}
/**
* @param dx the dx to set
*/
public final void setDx(double dx) {
this.dx = dx;
}
/**
* @return the dx
*/
public final double getDx() {
return dx;
}
/**
* @param dy the dy to set
*/
public final void setDy(double dy) {
this.dy = dy;
}
/**
* @return the dy
*/
public final double getDy() {
return dy;
}
@Override
public final boolean equals(Object obj) {
return (obj instanceof Matrix) ? equals((Matrix) obj) : false;
}
public final boolean equals(Matrix rhs) {
return (m11 == rhs.m11) && (m12 == rhs.m12) && (dx == rhs.dx)
&& (m21 == rhs.m21) && (m22 == rhs.m22) && (dy == rhs.dy);
}
@Override
public final int hashCode() {
return Arrays.hashCode(new double[] { m11, m12, dx, m21, m22, dy });
}
}