/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-2015, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.referencing.operation.matrix;
import java.io.Serializable;
import org.opengis.referencing.operation.Matrix;
import org.ejml.alg.fixed.FixedOps3;
import org.geotools.resources.i18n.Errors;
import org.geotools.resources.i18n.ErrorKeys;
/**
* A matrix of fixed {@value #SIZE}×{@value #SIZE} size.
*
* @since 2.2
* @version 13.0
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
*/
public class Matrix2 implements XMatrix, Serializable {
/** Serial number for interoperability with different versions. */
private static final long serialVersionUID = 7116561372481474290L;
/** The matrix size, which is {@value}. */
public static final int SIZE = 2;
/** The first matrix element in the first row. */
public double m00;
/** The second matrix element in the first row. */
public double m01;
/** The first matrix element in the second row. */
public double m10;
/** The second matrix element in the second row. */
public double m11;
/**
* Creates a new identity matrix.
*/
public Matrix2() {
m00 = m11 = 1;
}
/**
* Creates a new matrix initialized to the specified values.
*/
public Matrix2(final double m00, final double m01,
final double m10, final double m11)
{
this.m00 = m00;
this.m01 = m01;
this.m10 = m10;
this.m11 = m11;
}
/**
* Creates a new matrix initialized to the same value than the specified one.
* The specified matrix size must be {@value #SIZE}×{@value #SIZE}.
*/
public Matrix2(final Matrix matrix) {
if (matrix.getNumRow()!=SIZE || matrix.getNumCol()!=SIZE) {
throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_MATRIX_SIZE));
}
m00 = matrix.getElement(0,0);
m01 = matrix.getElement(0,1);
m10 = matrix.getElement(1,0);
m11 = matrix.getElement(1,1);
}
/** Used to cast/copy matrix to Matrix2 */
Matrix2 internal( Matrix matrix ){
if (matrix instanceof Matrix2) {
return (Matrix2) matrix;
} else {
if (matrix.getNumRow()!=SIZE || matrix.getNumCol()!=SIZE) {
throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_MATRIX_SIZE));
}
return new Matrix2(matrix);
}
}
/**
* Returns the number of rows in this matrix, which is always {@value #SIZE}
* in this implementation.
*/
public final int getNumRow() {
return SIZE;
}
/**
* Returns the number of colmuns in this matrix, which is always {@value #SIZE}
* in this implementation.
*/
public final int getNumCol() {
return SIZE;
}
/**
* {@inheritDoc}
*/
public final double getElement(final int row, final int col) {
switch (row) {
case 0: {
switch (col) {
case 0: return m00;
case 1: return m01;
}
break;
}
case 1: {
switch (col) {
case 0: return m10;
case 1: return m11;
}
break;
}
}
throw new IndexOutOfBoundsException();
}
/**
* {@inheritDoc}
*/
public final void setElement(final int row, final int col, final double value) {
switch (row) {
case 0: {
switch (col) {
case 0: m00 = value; return;
case 1: m01 = value; return;
}
break;
}
case 1: {
switch (col) {
case 0: m10 = value; return;
case 1: m11 = value; return;
}
break;
}
}
throw new IndexOutOfBoundsException();
}
/**
* {@inheritDoc}
*/
public final void setZero() {
m00 = m01 = m10 = m11 = 0;
}
/**
* {@inheritDoc}
*/
public final void setIdentity() {
m01 = m10 = 0;
m00 = m11 = 1;
assert isIdentity();
}
/**
* {@inheritDoc}
*/
public final boolean isIdentity() {
return m01==0 && m10==0 && m00==1 && m11==1;
}
/**
* {@inheritDoc}
*/
public final boolean isIdentity(double tolerance) {
return GeneralMatrix.isIdentity(this, tolerance);
}
/**
* {@inheritDoc}
*/
public final boolean isAffine() {
return m10==0 && m11==1;
}
/**
* {@inheritDoc}
*/
public final void negate() {
m00 = -m00;
m01 = -m01;
m10 = -m10;
m11 = -m11;
}
@Override
public void negate(Matrix matrix) {
Matrix2 k = internal( matrix );
m00 = -k.m00;
m01 = -k.m01;
m10 = -k.m10;
m11 = -k.m11;
}
/**
* {@inheritDoc}
*/
public final void transpose() {
final double swap = m10;
m10 = m01;
m01 = swap;
}
@Override
public void transpose(Matrix matrix) {
Matrix2 k = internal( matrix );
m00 = k.m00;
m01 = k.m10;
m10 = k.m01;
m11 = k.m11;
}
/**
* Inverts this matrix in place.
*/
public final void invert() {
final double det = m00*m11 - m01*m10;
if (det == 0) {
throw new SingularMatrixException("Determinate is zero, cannot invert matrix");
}
final double swap = m00;
m00 = m11 / det;
m11 = swap / det;
m10 = -m10 / det;
m01 = -m01 / det;
}
@Override
public void invert(Matrix matrix) throws SingularMatrixException {
Matrix2 k = internal( matrix );
final double det = k.m00*k.m11 - k.m01*k.m10;
if (det == 0) {
throw new SingularMatrixException("Determinate is zero, cannot invert matrix");
}
m00 = k.m11 / det;
m11 = k.m00 / det;
m10 = -k.m10 / det;
m01 = -k.m01 / det;
}
public final void multiply(final Matrix matrix) {
mul(matrix);
}
/**
* {@inheritDoc}
*/
public boolean equals(final Matrix matrix, final double tolerance) {
return GeneralMatrix.epsilonEquals(this, matrix, tolerance);
}
/**
* Returns {@code true} if the specified object is of type {@code Matrix2} and
* all of the data members are equal to the corresponding data members in this matrix.
*/
@Override
public boolean equals(final Object object) {
if (object!=null && object.getClass().equals(getClass())) {
final Matrix2 that = (Matrix2) object;
return Double.doubleToLongBits(this.m00) == Double.doubleToLongBits(that.m00) &&
Double.doubleToLongBits(this.m01) == Double.doubleToLongBits(that.m01) &&
Double.doubleToLongBits(this.m10) == Double.doubleToLongBits(that.m10) &&
Double.doubleToLongBits(this.m11) == Double.doubleToLongBits(that.m11);
}
return false;
}
/**
* Returns a hash code value based on the data values in this object.
*/
@Override
public int hashCode() {
return (int)((((Double.doubleToLongBits(m00) +
37*Double.doubleToLongBits(m01)) +
37*Double.doubleToLongBits(m10)) +
37*Double.doubleToLongBits(m11)) ^
serialVersionUID);
}
/**
* Returns a string representation of this matrix. The returned string is implementation
* dependent. It is usually provided for debugging purposes only.
*/
@Override
public String toString() {
return GeneralMatrix.toString(this);
}
/**
* Returns a clone of this matrix.
*/
@Override
public Matrix2 clone() {
try {
return (Matrix2) super.clone();
} catch (CloneNotSupportedException e) {
// Should not happen, since we are cloneable.
throw new AssertionError(e);
}
}
@Override
public void getRow(int row, double[] array) {
if (array.length != SIZE) {
throw new IllegalArgumentException("Call getRow received an array of length "
+ array.length + ". " + "The dimensions of the matrix is 2 by 2.");
}
if (row == 0) {
array[0] = m00;
array[1] = m01;
} else if (row == 1) {
array[0] = m10;
array[1] = m11;
} else {
throw new IllegalArgumentException("Specified element is out of bounds: (" +row
+ ", 0)");
}
}
@Override
public void setRow(int row, double... values) {
if (values.length != SIZE) {
throw new IllegalArgumentException("Call setRow received an array of length "
+ values.length + ". " + "The dimensions of the matrix is 2 by 2.");
}
if (row == 0) {
m00 = values[0];
m01 = values[1];
} else if (row == 1) {
m10 = values[0];
m11 = values[1];
} else {
throw new IllegalArgumentException("Specified element is out of bounds: (" + row
+ " , 0)");
}
}
@Override
public void getColumn(int column, double[] array) {
if (array.length != SIZE) {
throw new IllegalArgumentException("Call getColumn received an array of length "
+ array.length + ". " + "The dimensions of the matrix is 2 by 2.");
}
if (column == 0) {
array[0] = m00;
array[1] = m10;
} else if (column == 1) {
array[0] = m01;
array[1] = m11;
} else {
throw new IllegalArgumentException("Specified element is out of bounds: (0 , " + column
+ ")");
}
}
@Override
public void setColumn(int column, double... values) {
if (values.length != SIZE) {
throw new IllegalArgumentException("Call setColumn received an array of length "
+ values.length + ". " + "The dimensions of the matrix is 2 by 2.");
}
if (column == 0) {
m00 = values[0];
m10 = values[1];
} else if (column == 1) {
m01 = values[0];
m11 = values[1];
} else {
throw new IllegalArgumentException("Specified element is out of bounds: (0 , " + column
+ ")");
}
}
@Override
public void add(double scalar) {
m00 += scalar;
m01 += scalar;
m10 += scalar;
m11 += scalar;
}
@Override
public void add(double scalar, XMatrix matrix) {
final Matrix2 k = internal( matrix );
m00 = scalar + k.m00;
m01 = scalar + k.m01;
m10 = scalar + k.m10;
m11 = scalar + k.m11;
}
@Override
public void add(XMatrix matrix) {
final Matrix2 k = internal(matrix);
m00 += k.m00;
m01 += k.m01;
m10 += k.m10;
m11 += k.m11;
}
@Override
public void add(XMatrix matrix1, XMatrix matrix2) {
final Matrix2 a = internal(matrix1);
final Matrix2 b = internal(matrix2);
m00 = a.m00 + b.m00;
m01 = a.m01 + b.m01;
m10 = a.m10 + b.m10;
m11 = a.m11 + b.m11;
}
@Override
public double determinate() {
return (m00 * m11) - (m01 * m10);
}
@Override
public void mul(double scalar) {
m00 *= scalar;
m01 *= scalar;
m10 *= scalar;
m11 *= scalar;
}
@Override
public void mul(double scalar, Matrix matrix) {
final Matrix2 k = internal(matrix);
m00 = scalar * k.m00;
m01 = scalar * k.m01;
m10 = scalar * k.m10;
m11 = scalar * k.m11;
}
@Override
public void mul(Matrix matrix) {
final Matrix2 k = internal(matrix);
double m0, m1;
m0 = m00;
m1 = m01;
m00 = m0 * k.m00 + m1 * k.m10;
m01 = m0 * k.m01 + m1 * k.m11;
m0 = m10;
m1 = m11;
m10 = m0 * k.m00 + m1 * k.m10;
m11 = m0 * k.m01 + m1 * k.m11;
}
@Override
public void mul(Matrix matrix1, Matrix matrix2) {
final Matrix2 a = internal(matrix1);
final Matrix2 b = internal(matrix2);
m00 = a.m00 * b.m00 + a.m10 * b.m01;
m01 = a.m00 * b.m10 + a.m10 * b.m11;
m10 = a.m01 * b.m00 + a.m11 * b.m01;
m11 = a.m01 * b.m10 + a.m11 * b.m11;
}
@Override
public void sub(double scalar) {
m00 -= scalar;
m01 -= scalar;
m10 -= scalar;
m11 -= scalar;
}
@Override
public void sub(double scalar, Matrix matrix) {
final Matrix2 k = internal( matrix );
m00 = scalar - k.m00;
m01 = scalar - k.m01;
m10 = scalar - k.m10;
m11 = scalar - k.m11;
}
@Override
public void sub(Matrix matrix) {
final Matrix2 k = internal( matrix );
m00 -= k.m00;
m01 -= k.m01;
m10 -= k.m10;
m11 -= k.m11;
}
@Override
public void sub(Matrix matrix1, Matrix matrix2) {
final Matrix2 a = internal(matrix1);
final Matrix2 b = internal(matrix2);
m00 = a.m00 - b.m00;
m01 = a.m01 - b.m01;
m10 = a.m10 - b.m10;
m11 = a.m11 - b.m11;
}
}