blob: 04e0f51a23fc8af4e20a1f018a72c1cc48906f65 [file] [log] [blame]
/*
* 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.
*/
package org.apache.sis.referencing.operation.matrix;
import org.opengis.referencing.operation.Matrix;
import org.apache.sis.internal.util.Numerics;
/**
* A matrix of fixed {@value #SIZE}×{@value #SIZE} size,
* typically resulting from {@link org.opengis.referencing.operation.MathTransform2D} derivative computation.
* The matrix members are:
*
* <blockquote><pre> ┌ ┐
* │ {@linkplain #m00} {@linkplain #m01} │
* │ {@linkplain #m10} {@linkplain #m11} │
* └ ┘</pre></blockquote>
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 0.4
*
* @see Matrix1
* @see Matrix3
* @see Matrix4
*
* @since 0.4
* @module
*/
@SuppressWarnings("CloneableClassWithoutClone") // No field in this class needs clone.
public final class Matrix2 extends MatrixSIS {
/**
* Serial number for inter-operability 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 filled with only zero values.
*
* @param ignore shall always be {@code false} in current version.
*/
Matrix2(final boolean ignore) {
}
/**
* Creates a new matrix initialized to the specified values.
*
* @param m00 the first matrix element in the first row.
* @param m01 the second matrix element in the first row.
* @param m10 the first matrix element in the second row.
* @param m11 the second matrix element in the second row.
*/
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 specified values.
* The length of the given array must be 4 and the values in the same order than the above constructor.
*
* @param elements elements of the matrix. Column indices vary fastest.
* @throws IllegalArgumentException if the given array does not have the expected length.
*
* @see #setElements(double[])
* @see Matrices#create(int, int, double[])
*/
public Matrix2(final double[] elements) throws IllegalArgumentException {
setElements(elements);
}
/**
* Creates a new matrix initialized to the same value than the specified one.
* The specified matrix size must be {@value #SIZE}×{@value #SIZE}.
* This is not verified by this constructor, since it shall be verified by {@link Matrices}.
*
* @param matrix the matrix to copy.
*/
Matrix2(final Matrix matrix) {
m00 = matrix.getElement(0,0);
m01 = matrix.getElement(0,1);
m10 = matrix.getElement(1,0);
m11 = matrix.getElement(1,1);
}
/**
* Casts or copies the given matrix to a {@code Matrix2} implementation. If the given {@code matrix}
* is already an instance of {@code Matrix2}, then it is returned unchanged. Otherwise this method
* verifies the matrix size, then copies all elements in a new {@code Matrix2} object.
*
* @param matrix the matrix to cast or copy, or {@code null}.
* @return the matrix argument if it can be safely casted (including {@code null} argument),
* or a copy of the given matrix otherwise.
* @throws MismatchedMatrixSizeException if the size of the given matrix is not {@value #SIZE}×{@value #SIZE}.
*/
public static Matrix2 castOrCopy(final Matrix matrix) throws MismatchedMatrixSizeException {
if (matrix == null || matrix instanceof Matrix2) {
return (Matrix2) matrix;
}
ensureSizeMatch(SIZE, SIZE, matrix);
return new Matrix2(matrix);
}
/*
* The 'final' modifier in following method declarations is redundant with the 'final' modifier
* in this class declaration, but we keep them as a reminder of which methods should stay final
* if this class was modified to a non-final class. Some methods should stay final because:
*
* - returning a different value would make no-sense for this class (e.g. 'getNumRow()');
* - they are invoked by a constructor or by an other method expecting this exact semantic.
*/
/**
* Returns the number of rows in this matrix, which is always {@value #SIZE} in this implementation.
*
* @return always {@value #SIZE}.
*/
@Override
public final int getNumRow() {
return SIZE;
}
/**
* Returns the number of columns in this matrix, which is always {@value #SIZE} in this implementation.
*
* @return always {@value #SIZE}.
*/
@Override
public final int getNumCol() {
return SIZE;
}
/**
* Retrieves the value at the specified row and column of this matrix.
* This method can be invoked when the matrix size or type is unknown.
* If the matrix is known to be an instance of {@code Matrix2},
* then the {@link #m00} … {@link #m11} fields can be read directly for efficiency.
*
* @param row the row index, which can only be 0 or 1.
* @param column the column index, which can only be 0 or 1.
* @return the current value at the given row and column.
*/
@Override
public final double getElement(final int row, final int column) {
if (row >= 0 && row < SIZE && column >= 0 && column < SIZE) {
switch (row*SIZE + column) {
case 0: return m00;
case 1: return m01;
case 2: return m10;
case 3: return m11;
}
}
throw indexOutOfBounds(row, column);
}
/**
* Modifies the value at the specified row and column of this matrix.
* This method can be invoked when the matrix size or type is unknown.
* If the matrix is known to be an instance of {@code Matrix2},
* then the {@link #m00} … {@link #m11} fields can be set directly for efficiency.
*
* @param row the row index, which can only be 0 or 1.
* @param column the column index, which can only be 0 or 1.
* @param value the new value to set at the given row and column.
*/
@Override
public final void setElement(final int row, final int column, final double value) {
if (row >= 0 && row < SIZE && column >= 0 && column < SIZE) {
switch (row*SIZE + column) {
case 0: m00 = value; return;
case 1: m01 = value; return;
case 2: m10 = value; return;
case 3: m11 = value; return;
}
}
throw indexOutOfBounds(row, column);
}
/**
* Returns all matrix elements in a flat, row-major (column indices vary fastest) array.
* The array length is 4.
*
* @return {@inheritDoc}
*/
@Override
public final double[] getElements() {
final double[] elements = new double[SIZE*SIZE];
getElements(elements);
return elements;
}
/**
* Copies the matrix elements in the given flat array.
* The array length shall be at least 4, may also be 8.
*/
@Override
final void getElements(final double[] elements) {
elements[0] = m00; elements[1] = m01;
elements[2] = m10; elements[3] = m11;
}
/**
* Sets all matrix elements from a flat, row-major (column indices vary fastest) array.
* The array length shall be 4.
*/
@Override
public final void setElements(final double[] elements) {
ensureLengthMatch(SIZE*SIZE, elements);
m00 = elements[0]; m01 = elements[1];
m10 = elements[2]; m11 = elements[3];
}
/**
* {@inheritDoc}
*
* @return {@inheritDoc}
*/
@Override
public final boolean isAffine() {
return m10 == 0 && m11 == 1;
}
/**
* {@inheritDoc}
*
* @return {@inheritDoc}
*/
@Override
public final boolean isIdentity() {
return m00 == 1 && m10 == 0 &&
m01 == 0 && m11 == 1;
}
/**
* {@inheritDoc}
*/
@Override
public void transpose() {
final double swap = m10;
m10 = m01;
m01 = swap;
}
/**
* {@inheritDoc}
*/
@Override
public void normalizeColumns() {
double m;
m = Math.hypot(m00, m10); m00 /= m; m10 /= m;
m = Math.hypot(m01, m11); m01 /= m; m11 /= m;
}
/**
* 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.
*
* @param object the object to compare with this matrix for equality.
* @return {@code true} if the given object is equal to this matrix.
*/
@Override
public boolean equals(final Object object) {
if (object instanceof Matrix2) {
final Matrix2 that = (Matrix2) object;
return Numerics.equals(this.m00, that.m00) &&
Numerics.equals(this.m01, that.m01) &&
Numerics.equals(this.m10, that.m10) &&
Numerics.equals(this.m11, that.m11);
}
return false;
}
/**
* Returns a hash code value based on the data values in this object.
*
* @return {@inheritDoc}
*/
@Override
public int hashCode() {
return Long.hashCode(serialVersionUID ^
(((Double.doubleToLongBits(m00) +
31 * Double.doubleToLongBits(m01)) +
31 * Double.doubleToLongBits(m10)) +
31 * Double.doubleToLongBits(m11)));
}
}