/** * Converts a single coordinate and optionally computes the derivative. */ @Override public Matrix transform(final double[] srcPts, final int srcOff, final double[] dstPts, final int dstOff, final boolean derivate) { final double X = srcPts[srcOff ]; final double Y = srcPts[srcOff+1]; final double Z = srcPts[srcOff+2]; final double ρ2 = X*X + Y*Y; final double r2 = Z*Z + ρ2; final double r = sqrt(r2); if (dstPts != null) { dstPts[dstOff ] = atan2(Y, X); // Spherical longitude (θ) dstPts[dstOff+1] = (r == 0) ? Z : asin(Z / r); // Spherical latitude (Ω). If (X,Y,Z) is (0,0,0) take the sign of Z. dstPts[dstOff+2] = r; } if (!derivate) { return null; } final double d = r2 * sqrt(r2 - Z*Z); return new Matrix3(-Y/ρ2, X/ρ2, 0, // ∂θ/∂X, ∂θ/∂Y, ∂θ/∂Z -X*Z/d, -Y*Z/d, ρ2/d, // ∂Ω/∂X, ∂Ω/∂Y, ∂Ω/∂Z X/r, Y/r, Z/r); // ∂r/∂X, ∂r/∂Y, ∂r/∂Z }
/** * Returns all matrix elements in a flat, row-major (column indices vary fastest) array. * The array length is 9. * * @return {@inheritDoc} */ @Override public final double[] getElements() { final double[] elements = new double[SIZE*SIZE]; getElements(elements); return elements; }
/** * Sets all matrix elements from a flat, row-major (column indices vary fastest) array. * The array length shall be 9. */ @Override public final void setElements(final double[] elements) { ensureLengthMatch(SIZE*SIZE, elements); m00 = elements[0]; m01 = elements[1]; m02 = elements[2]; m10 = elements[3]; m11 = elements[4]; m12 = elements[5]; m20 = elements[6]; m21 = elements[7]; m22 = elements[8]; }
/** * Casts or copies the given matrix to a {@code Matrix3} implementation. If the given {@code matrix} * is already an instance of {@code Matrix3}, then it is returned unchanged. Otherwise this method * verifies the matrix size, then copies all elements in a new {@code Matrix3} 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 Matrix3 castOrCopy(final Matrix matrix) throws MismatchedMatrixSizeException { if (matrix == null || matrix instanceof Matrix3) { return (Matrix3) matrix; } ensureSizeMatch(SIZE, SIZE, matrix); return new Matrix3(matrix); }
/** * Verifies our claim that {@code A.solve(B)} is equivalent to {@code A.inverse().multiply(B)}. * This claim is documented in {@link MatrixSIS#solve(Matrix)} javadoc. * * @throws NoninvertibleMatrixException should not happen. */ @Test public void testSolveEquivalence() throws NoninvertibleMatrixException { final Matrix3 A = new Matrix3( 0.5, 0, 0, 0, 0.5, 0, 0, 0, 1); final Matrix3 B = new Matrix3( 0, 3, 0, 3, 0, 0, 0, 0, 1); // Verify the result of A.inverse().multiply(B) as a matter of principle. final double[] expected = A.inverse().multiply(B).getElements(); assertArrayEquals(new double[] { 0, 6, 0, 6, 0, 0, 0, 0, 1}, expected, TOLERANCE); // Now the actual test. assertEqualsElements(expected, SIZE, SIZE, A.solve(B), TOLERANCE); }
/** * Tests the {@link Matrix3#Matrix3(double, double, double, * double, double, double, double, double, double)} constructor. * This constructor is specific to the implementation class. */ @Test public void testConstructor() { initialize(-2078758443421995879L); final double[] elements = createRandomPositiveValues(SIZE * SIZE); final Matrix3 matrix = new Matrix3( elements[0], elements[1], elements[2], elements[3], elements[4], elements[5], elements[6], elements[7], elements[8]); validate(matrix); assertArrayEquals(elements, matrix.getElements(), STRICT); }
final double SENTINEL_VALUE = Double.MIN_VALUE; final int SIZE = Matrix3.SIZE; final Matrix3 expected = new Matrix3( 1, 2, 3, 0.1, 0.2, 0.3, assertFalse(expected.equals(matrix)); // Because not the same type. assertTrue(Matrices.equals(expected, matrix, ComparisonMode.BY_CONTRACT)); final double[] errors = ((GeneralMatrix) matrix).elements;
/** * Compares this affine transform with the given object for equality. This method behaves as documented * in the {@link LenientComparable#equals(Object, ComparisonMode) LenientComparable.equals(…)} method, * except for the following case: if the given mode is {@link ComparisonMode#STRICT}, then this method * delegates to {@link #equals(Object)}. The later method has different rules than the ones documented * in the {@code LenientComparable} interface, because of the {@code AffineTransform} inheritance. * * @param object the object to compare to {@code this}. * @param mode the strictness level of the comparison. * @return {@code true} if both objects are equal. */ @Override public boolean equals(final Object object, final ComparisonMode mode) { if (object == this) { // Slight optimization return true; } if (mode == ComparisonMode.STRICT) { return equals(object); } if (object instanceof LinearTransform) { final Matrix m2 = ((LinearTransform) object).getMatrix(); return AffineTransforms2D.toMatrix(this).equals(m2, mode); } return false; }
/** * 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 Matrix3}, * then the {@link #m00} … {@link #m22} fields can be set directly for efficiency. * * @param row the row index, from 0 inclusive to {@value #SIZE} exclusive. * @param column the column index, from 0 inclusive to {@value #SIZE} exclusive. * @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: m02 = value; return; case 3: m10 = value; return; case 4: m11 = value; return; case 5: m12 = value; return; case 6: m20 = value; return; case 7: m21 = value; return; case 8: m22 = value; return; } } throw indexOutOfBounds(row, column); }
/** * {@inheritDoc} * * @return {@inheritDoc} */ @Override public final boolean isIdentity() { return m00 == 1 && m01 == 0 && m02 == 0 && m10 == 0 && m11 == 1 && m12 == 0 && isAffine(); }
/** * 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. */ Matrix3(final Matrix matrix) { for (int j=0; j<SIZE; j++) { for (int i=0; i<SIZE; i++) { setElement(j,i, matrix.getElement(j,i)); } } }
/** * Casts or copies the given matrix to a {@code Matrix3} implementation. If the given {@code matrix} * is already an instance of {@code Matrix3}, then it is returned unchanged. Otherwise this method * verifies the matrix size, then copies all elements in a new {@code Matrix3} 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 Matrix3 castOrCopy(final Matrix matrix) throws MismatchedMatrixSizeException { if (matrix == null || matrix instanceof Matrix3) { return (Matrix3) matrix; } ensureSizeMatch(SIZE, SIZE, matrix); return new Matrix3(matrix); }
/** * Compares this affine transform with the given object for equality. This method behaves as documented * in the {@link LenientComparable#equals(Object, ComparisonMode) LenientComparable.equals(…)} method, * except for the following case: if the given mode is {@link ComparisonMode#STRICT}, then this method * delegates to {@link #equals(Object)}. The later method has different rules than the ones documented * in the {@code LenientComparable} interface, because of the {@code AffineTransform} inheritance. * * @param object the object to compare to {@code this}. * @param mode the strictness level of the comparison. * @return {@code true} if both objects are equal. */ @Override public boolean equals(final Object object, final ComparisonMode mode) { if (object == this) { // Slight optimization return true; } if (mode == ComparisonMode.STRICT) { return equals(object); } if (object instanceof LinearTransform) { final Matrix m2 = ((LinearTransform) object).getMatrix(); return AffineTransforms2D.toMatrix(this).equals(m2, mode); } return false; }
/** * 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 Matrix3}, * then the {@link #m00} … {@link #m22} fields can be read directly for efficiency. * * @param row the row index, from 0 inclusive to {@value #SIZE} exclusive. * @param column the column index, from 0 inclusive to {@value #SIZE} exclusive. * @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 m02; case 3: return m10; case 4: return m11; case 5: return m12; case 6: return m20; case 7: return m21; case 8: return m22; } } throw indexOutOfBounds(row, column); }
/** * {@inheritDoc} * * @return {@inheritDoc} */ @Override public final boolean isIdentity() { return m00 == 1 && m01 == 0 && m02 == 0 && m10 == 0 && m11 == 1 && m12 == 0 && isAffine(); }
/** * 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. */ Matrix3(final Matrix matrix) { for (int j=0; j<SIZE; j++) { for (int i=0; i<SIZE; i++) { setElement(j,i, matrix.getElement(j,i)); } } }
/** * Converts a single coordinate and optionally computes the derivative. */ @Override public Matrix transform(final double[] srcPts, final int srcOff, final double[] dstPts, final int dstOff, final boolean derivate) { final double X = srcPts[srcOff ]; final double Y = srcPts[srcOff+1]; final double Z = srcPts[srcOff+2]; final double ρ2 = X*X + Y*Y; final double r2 = Z*Z + ρ2; final double r = sqrt(r2); if (dstPts != null) { dstPts[dstOff ] = atan2(Y, X); // Spherical longitude (θ) dstPts[dstOff+1] = (r == 0) ? Z : asin(Z / r); // Spherical latitude (Ω). If (X,Y,Z) is (0,0,0) take the sign of Z. dstPts[dstOff+2] = r; } if (!derivate) { return null; } final double d = r2 * sqrt(r2 - Z*Z); return new Matrix3(-Y/ρ2, X/ρ2, 0, // ∂θ/∂X, ∂θ/∂Y, ∂θ/∂Z -X*Z/d, -Y*Z/d, ρ2/d, // ∂Ω/∂X, ∂Ω/∂Y, ∂Ω/∂Z X/r, Y/r, Z/r); // ∂r/∂X, ∂r/∂Y, ∂r/∂Z }
/** * Returns all matrix elements in a flat, row-major (column indices vary fastest) array. * The array length is 9. * * @return {@inheritDoc} */ @Override public final double[] getElements() { final double[] elements = new double[SIZE*SIZE]; getElements(elements); return elements; }
/** * 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 Matrix3}, * then the {@link #m00} … {@link #m22} fields can be set directly for efficiency. * * @param row the row index, from 0 inclusive to {@value #SIZE} exclusive. * @param column the column index, from 0 inclusive to {@value #SIZE} exclusive. * @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: m02 = value; return; case 3: m10 = value; return; case 4: m11 = value; return; case 5: m12 = value; return; case 6: m20 = value; return; case 7: m21 = value; return; case 8: m22 = value; return; } } throw indexOutOfBounds(row, column); }