/*
* 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.awt.geom.AffineTransform;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Serializable;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
import org.opengis.referencing.operation.Matrix;
import org.ejml.UtilEjml;
import org.ejml.alg.fixed.FixedOps3;
import org.ejml.data.DenseMatrix64F;
import org.ejml.data.FixedMatrix2x2_64F;
import org.ejml.data.FixedMatrix3x3_64F;
import org.geotools.io.ContentFormatException;
import org.geotools.io.LineFormat;
import org.geotools.resources.XArray;
import org.geotools.resources.i18n.Errors;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.util.Utilities;
/**
* 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 Matrix3 implements XMatrix, Serializable {
/**
* Serial number for interoperability with different versions.
*/
private static final long serialVersionUID = 8902061778871586612L;
/**
* The matrix size, which is {@value}.
*/
public static final int SIZE = 3;
FixedMatrix3x3_64F mat;
/**
* Creates a new identity matrix.
*/
public Matrix3() {
mat = new FixedMatrix3x3_64F();
setIdentity();
}
/**
* Creates a new matrix initialized to the specified values.
*/
public Matrix3(double m00, double m01, double m02,
double m10, double m11, double m12,
double m20, double m21, double m22)
{
mat = new FixedMatrix3x3_64F(
m00, m01, m02,
m10, m11, m12,
m20, m21, m22);
}
/**
* Constructs a 3×3 matrix from the specified affine transform.
*/
public Matrix3(final AffineTransform transform) {
mat = new FixedMatrix3x3_64F();
setMatrix(transform);
}
/**
* Creates a new matrix initialized to the same value than the specified one.
* The specified matrix size must be {@value #SIZE}×{@value #SIZE}.
*/
public Matrix3(final Matrix matrix) {
mat = new FixedMatrix3x3_64F();
if (matrix.getNumRow()!=SIZE || matrix.getNumCol()!=SIZE) {
throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_MATRIX_SIZE));
}
for (int j=0; j<SIZE; j++) {
for (int i=0; i<SIZE; i++) {
setElement(j,i, matrix.getElement(j,i));
}
}
}
/**
* Cast (or convert) Matrix to internal DenseMatrix64F representation required for FixedOps3.
* @param matrix
* @return
*/
private FixedMatrix3x3_64F internal( Matrix matrix ){
if( matrix instanceof Matrix3 ){
return ((Matrix3)matrix).mat;
}
else {
FixedMatrix3x3_64F a = new FixedMatrix3x3_64F(
matrix.getElement(0,0), matrix.getElement(0,1), matrix.getElement(0,2),
matrix.getElement(1,0), matrix.getElement(1,1), matrix.getElement(1,2),
matrix.getElement(2,0), matrix.getElement(2,1), matrix.getElement(2,2));
return a;
}
}
/**
* 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;
}
/**
* Returns {@code true} if at least one value is {@code NaN}.
*
* @since 2.3
*/
public final boolean isNaN() {
return Double.isNaN(mat.a11) || Double.isNaN(mat.a12) || Double.isNaN(mat.a13) ||
Double.isNaN(mat.a21) || Double.isNaN(mat.a22) || Double.isNaN(mat.a23) ||
Double.isNaN(mat.a31) || Double.isNaN(mat.a32) || Double.isNaN(mat.a33);
}
/**
* Sets this matrix to the specified affine transform.
*
* @since 2.3
*/
public void setMatrix(final AffineTransform transform) {
mat.a11=transform.getScaleX(); mat.a12=transform.getShearX(); mat.a13=transform.getTranslateX();
mat.a21=transform.getShearY(); mat.a22=transform.getScaleY(); mat.a23=transform.getTranslateY();
mat.a31=0; mat.a32=0; mat.a33=1;
}
/**
* Returns {@code true} if this matrix is equals to the specified affine transform.
*
* @since 2.3
*/
public boolean equalsAffine(final AffineTransform transform) {
return mat.a11==transform.getScaleX() && mat.a12==transform.getShearX() && mat.a13==transform.getTranslateX() &&
mat.a21==transform.getShearY() && mat.a22==transform.getScaleY() && mat.a23==transform.getTranslateY() &&
mat.a31==0 && mat.a32==0 && mat.a33==1;
}
/**
* Returns a clone of this matrix.
*/
@Override
public Matrix3 clone() {
return new Matrix3(this);
}
/**
* {@inheritDoc}
*/
public final boolean isAffine() {
return mat.a31 == 0 && mat.a32 == 0 && mat.a33 == 1;
}
/**
* Changes the sign of each element in the matrix.
*/
@Override
public void negate() {
FixedOps3.changeSign(mat);
}
@Override
public void negate(Matrix matrix) {
FixedMatrix3x3_64F a = internal(matrix);
FixedOps3.changeSign(a);
this.mat = a;
}
/**
* Transposes the matrix.
*/
@Override
public void transpose() {
FixedOps3.transpose(mat);
}
@Override
public void transpose(Matrix matrix) {
FixedMatrix3x3_64F a = internal(matrix);
FixedOps3.transpose(a, mat);
}
@Override
public void invert() {
FixedMatrix3x3_64F ret = new FixedMatrix3x3_64F();
boolean success = FixedOps3.invert(mat,ret);
if(!success){
throw new SingularMatrixException("Could not invert, possible singular matrix?");
}
mat = ret;
}
@Override
public void invert(Matrix matrix) throws SingularMatrixException {
FixedMatrix3x3_64F a = internal(matrix);
boolean success = FixedOps3.invert(a, mat);
if(!success){
throw new SingularMatrixException("Could not invert, possible singular matrix?");
}
this.mat = a;
}
/**
* Returns the value at the row, column position in the matrix.
* @param row
* @param column
* @return Matrix value at the given row and column.
*/
@Override
public double getElement(int row, int column) {
return mat.get(row, column);
}
public void setColumn( int column, double ... values ){
if ( values.length != mat.getNumCols() ) {
throw new IllegalArgumentException("Call setRow received an array of length " + values.length + ". " +
"The dimensions of the matrix is " + mat.getNumRows() + " by " + mat.getNumCols() + ".");
}
for( int i = 0; i < values.length; i++) {
mat.set(i, column, values[i]);
}
}
public void setRow(int row, double ... values) {
if ( values.length != mat.getNumCols() ) {
throw new IllegalArgumentException("Call setRow received an array of length " + values.length + ". " +
"The dimensions of the matrix is " + mat.getNumRows() + " by " + mat.getNumCols() + ".");
}
for( int i = 0; i < values.length; i++) {
mat.set(row, i, values[i]);
}
}
/**
* Sets the value of the row, column position in the matrix.
* @param row
* @param column
* @param value
*/
@Override
public void setElement(int row, int column, double value) {
mat.set(row, column, value);
}
/**
* Sets each value of the matrix to 0.0.
*/
@Override
public void setZero() {
FixedOps3.fill(mat, 0);
}
/**
* Sets the main diagonal of this matrix to be 1.0.
*/
@Override
public void setIdentity() {
FixedOps3.setIdentity(mat);
}
/**
* Returns {@code true} if this matrix is an identity matrix.
*/
public final boolean isIdentity() {
final int numRow = getNumRow();
final int numCol = getNumCol();
if (numRow != numCol) {
return false;
}
for (int j=0; j<numRow; j++) {
for (int i=0; i<numCol; i++) {
if (getElement(j,i) != (i==j ? 1.0 : 0.0)) {
return false;
}
}
}
return true;
}
/**
* {@inheritDoc}
*
* @since 2.3.1
*/
public final boolean isIdentity(double tolerance) {
return GeneralMatrix.isIdentity(this, tolerance);
}
/**
* {@inheritDoc}
*/
public final void multiply(final Matrix matrix) {
mul(matrix);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
if (mat == null) {
return prime * result;
}
result = prime * result + SIZE; // for hashCode compatibility with GeneralMatrix
result = prime * result + SIZE;
long bits = Double.doubleToRawLongBits(mat.a11);
result = prime * result + ((int)(bits ^ (bits >>> 32)));
bits = Double.doubleToRawLongBits(mat.a12);
result = prime * result + ((int)(bits ^ (bits >>> 32)));
bits = Double.doubleToRawLongBits(mat.a13);
result = prime * result + ((int)(bits ^ (bits >>> 32)));
bits = Double.doubleToRawLongBits(mat.a21);
result = prime * result + ((int)(bits ^ (bits >>> 32)));
bits = Double.doubleToRawLongBits(mat.a22);
result = prime * result + ((int)(bits ^ (bits >>> 32)));
bits = Double.doubleToRawLongBits(mat.a23);
result = prime * result + ((int)(bits ^ (bits >>> 32)));
bits = Double.doubleToRawLongBits(mat.a31);
result = prime * result + ((int)(bits ^ (bits >>> 32)));
bits = Double.doubleToRawLongBits(mat.a32);
result = prime * result + ((int)(bits ^ (bits >>> 32)));
bits = Double.doubleToRawLongBits(mat.a33);
result = prime * result + ((int)(bits ^ (bits >>> 32)));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Matrix3 other = (Matrix3) obj;
return equals(other,0);
}
public boolean equals(final Matrix matrix, final double tolerance) {
return GeneralMatrix.epsilonEquals(this, matrix, tolerance);
}
/**
* Returns an affine transform for this matrix.
* This is a convenience method for interoperability with Java2D.
*
* @return The affine transform for this matrix.
* @throws IllegalStateException if this matrix is not 3×3,
* or if the last row is not {@code [0 0 1]}.
*/
public final AffineTransform toAffineTransform2D() throws IllegalStateException {
int check;
if (isAffine()) {
return new AffineTransform(getElement(0,0), getElement(1,0),
getElement(0,1), getElement(1,1),
getElement(0,2), getElement(1,2));
}
throw new IllegalStateException(Errors.format(ErrorKeys.NOT_AN_AFFINE_TRANSFORM));
}
/**
* 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);
}
/**
* Extract col to provided array.
*
* @param col
* @param array
*/
public void getColumn(int col, double[] array) {
for (int j = 0; j < array.length; j++) {
array[j] = mat.get(j, col);
}
}
@Override
public void mul(double scalar) {
FixedOps3.scale(scalar, this.mat);
}
@Override
public void mul(double scalar, Matrix matrix) {
FixedMatrix3x3_64F ret = new FixedMatrix3x3_64F();
FixedOps3.scale(scalar, internal( matrix ), ret );
mat = ret;
}
/**
* Extract row to provided array
* @param row
* @param array
*/
public void getRow(int row, double[] array) {
for (int i = 0; i < array.length; i++) {
array[i] = mat.get(row, i);
}
}
//
// In-place operations
//
/**
* In-place multiply with provided matrix.
* @param matrix
*
*/
public final void mul(Matrix matrix){
FixedMatrix3x3_64F b = internal(matrix);
FixedMatrix3x3_64F ret = new FixedMatrix3x3_64F();
FixedOps3.mult(mat,b,ret);
mat = ret;
}
/**
* In-place update from matrix1 * matrix2.
* @param matrix1
* @param matrix2
*/
public void mul(Matrix matrix1, Matrix matrix2) {
FixedMatrix3x3_64F a = internal(matrix1);
FixedMatrix3x3_64F b = internal(matrix2);
if( a == mat || b == mat ){
mat = new FixedMatrix3x3_64F();
}
FixedOps3.mult(a, b, mat);
}
@Override
public void sub(double scalar) {
mat.a11 -= scalar; mat.a12 -= scalar; mat.a12 -= scalar;
mat.a21 -= scalar; mat.a22 -= scalar; mat.a22 -= scalar;
mat.a31 -= scalar; mat.a32 -= scalar; mat.a32 -= scalar;
}
@Override
public void sub(double scalar, Matrix matrix) {
FixedMatrix3x3_64F a = internal(matrix);
mat.a11 = scalar - a.a11;
mat.a12 = scalar - a.a12;
mat.a12 = scalar - a.a13;
mat.a21 = scalar - a.a21;
mat.a22 = scalar - a.a22;
mat.a22 = scalar - a.a23;
mat.a31 = scalar - a.a31;
mat.a32 = scalar - a.a32;
mat.a32 = scalar - a.a33;
}
public void sub(Matrix matrix) {
FixedMatrix3x3_64F a = internal(matrix);
mat.a11 -= a.a11;
mat.a12 -= a.a12;
mat.a12 -= a.a13;
mat.a21 -= a.a21;
mat.a22 -= a.a22;
mat.a22 -= a.a23;
mat.a31 -= a.a31;
mat.a32 -= a.a32;
mat.a32 -= a.a33;
}
public void sub(Matrix matrix1, Matrix matrix2) {
FixedMatrix3x3_64F a = internal(matrix1);
FixedMatrix3x3_64F b = internal(matrix2);
mat.a11 = a.a11 - b.a11;
mat.a12 = a.a12 - b.a12;
mat.a12 = a.a13 - b.a13;
mat.a21 = a.a21 - b.a21;
mat.a22 = a.a22 - b.a22;
mat.a22 = a.a23 - b.a23;
mat.a31 = a.a31 - b.a31;
mat.a32 = a.a32 - b.a32;
mat.a32 = a.a33 - b.a33;
}
/**
* Update in place to the provided matrix (row-order).
* @param matrix
*/
public void set(double[] matrix) {
mat.a11 = matrix[0]; mat.a12 = matrix[1]; mat.a13 = matrix[2];
mat.a21 = matrix[3]; mat.a22 = matrix[4]; mat.a23 = matrix[5];
mat.a31 = matrix[6]; mat.a32 = matrix[7]; mat.a33 = matrix[8];
}
@Override
public void add(double scalar) {
mat.a11 += scalar; mat.a12 += scalar; mat.a12 += scalar;
mat.a21 += scalar; mat.a22 += scalar; mat.a22 += scalar;
mat.a31 += scalar; mat.a32 += scalar; mat.a32 += scalar;
}
@Override
public void add(double scalar, XMatrix matrix) {
FixedMatrix3x3_64F a = internal(matrix);
mat.a11 = scalar + a.a11;
mat.a12 = scalar + a.a12;
mat.a12 = scalar + a.a13;
mat.a21 = scalar + a.a21;
mat.a22 = scalar + a.a22;
mat.a22 = scalar + a.a23;
mat.a31 = scalar + a.a31;
mat.a32 = scalar + a.a32;
mat.a32 = scalar + a.a33;
}
@Override
public void add(XMatrix matrix) {
FixedOps3.add(mat, internal(matrix), mat);
}
@Override
public void add(XMatrix matrix1, XMatrix matrix2) {
FixedMatrix3x3_64F a = internal(matrix1);
FixedMatrix3x3_64F b = internal(matrix2);
FixedOps3.add(a, b, mat);
}
@Override
public double determinate() {
double det = FixedOps3.det(mat);
// if the decomposition silently failed then the matrix is most likely singular
if(UtilEjml.isUncountable(det))
return 0;
return det;
}
}