yaphyre.geometry.Matrix.java Source code

Java tutorial

Introduction

Here is the source code for yaphyre.geometry.Matrix.java

Source

/*
 * Copyright 2012 Michael Bieri
 *
 * 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 yaphyre.geometry;

import org.apache.commons.math3.linear.LUDecomposition;
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.linear.SingularMatrixException;

import java.io.Serializable;
import java.util.Arrays;

import static com.google.common.base.Preconditions.checkArgument;
import static yaphyre.geometry.MathUtils.equalsWithTolerance;

/**
 * Rudimentary implementation of some essential matrix operations.
 *
 * @author Michael Bieri
 * @author $LastChangedBy$
 * @version $Revision$
 */
@SuppressWarnings("ProtectedField")
public class Matrix implements Serializable {

    private static final long serialVersionUID = 1125712454842709925L;

    private static final int DIMENSION = 4;

    public static final Matrix IDENTITY = new Matrix(MatrixUtils.createRealIdentityMatrix(DIMENSION).getData());

    private double determinant = Double.NaN;

    private boolean invertible = true;

    private Matrix inverse = null;

    private Matrix transposed = null;

    protected final double[][] m;

    public Matrix(double[][] values) {
        checkArgument(DIMENSION == values.length);
        for (double[] row : values) {
            checkArgument(DIMENSION == row.length);
        }
        m = values;
    }

    public Matrix(double... values) {
        checkArgument(DIMENSION * DIMENSION == values.length);
        m = new double[DIMENSION][DIMENSION];
        for (int row = 0; row < DIMENSION; row++) {
            System.arraycopy(values, row * DIMENSION, m[row], 0, DIMENSION);
        }
    }

    private Matrix() {
        m = new double[DIMENSION][DIMENSION];
    }

    @Override
    public String toString() {
        return Arrays.deepToString(m);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + Arrays.hashCode(m);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Matrix other = (Matrix) obj;
        if (m.length != other.m.length) {
            return false;
        }
        for (int row = 0; row < DIMENSION; row++) {
            if (m[row].length != other.m[row].length) {
                return false;
            }
            for (int col = 0; col < DIMENSION; col++) {
                // compare with tolerance of 1e-10...
                if (!equalsWithTolerance(m[row][col], other.m[row][col])) {
                    return false;
                }
            }
        }
        return true;
    }

    public double get(int row, int col) {
        return m[row][col];
    }

    public Matrix add(Matrix other) {
        Matrix result = new Matrix();
        for (int row = 0; row < DIMENSION; row++) {
            for (int col = 0; col < DIMENSION; col++) {
                result.m[row][col] = m[row][col] + other.m[row][col];
            }
        }
        return result;
    }

    public Matrix mul(double s) {
        Matrix result = new Matrix();
        for (int row = 0; row < DIMENSION; row++) {
            for (int col = 0; col < DIMENSION; col++) {
                result.m[row][col] = m[row][col] * s;
            }
        }
        return result;
    }

    public Matrix mul(Matrix M) {
        if (M.equals(IDENTITY)) {
            return this;
        }

        Matrix result = new Matrix();

        for (int i = 0; i < DIMENSION; i++) {
            for (int j = 0; j < DIMENSION; j++) {
                result.m[i][j] = m[i][0] * M.m[0][j] + m[i][1] * M.m[1][j] + m[i][2] * M.m[2][j]
                        + m[i][3] * M.m[3][j];
            }
        }

        return result;
    }

    double[] mul(double[] vect) {
        double[] result = new double[DIMENSION];

        result[0] = m[0][0] * vect[0] + m[0][1] * vect[1] + m[0][2] * vect[2] + m[0][3] * vect[3];
        result[1] = m[1][0] * vect[0] + m[1][1] * vect[1] + m[1][2] * vect[2] + m[1][3] * vect[3];
        result[2] = m[2][0] * vect[0] + m[2][1] * vect[1] + m[2][2] * vect[2] + m[2][3] * vect[3];
        result[3] = m[3][0] * vect[0] + m[3][1] * vect[1] + m[3][2] * vect[2] + m[3][3] * vect[3];

        return result;
    }

    public Matrix transpose() {
        if (transposed == null) {
            transposed = new Matrix();
            for (int row = 0; row < DIMENSION; row++) {
                for (int col = 0; col < DIMENSION; col++) {
                    transposed.m[row][col] = m[col][row];
                }
            }
        }
        return transposed;
    }

    public double getDeterminat() {
        if (inverse == null && invertible) {
            calculateInternals();
        }
        return determinant;
    }

    public Matrix inverse() {
        if (inverse == null && invertible) {
            calculateInternals();
        }
        return inverse;
    }

    public boolean isInvertible() {
        if (inverse == null && invertible) {
            calculateInternals();
        }
        return invertible;
    }

    private void calculateInternals() {
        try {
            RealMatrix rm = MatrixUtils.createRealMatrix(m);
            LUDecomposition decomp = new LUDecomposition(rm);
            determinant = decomp.getDeterminant();
            inverse = new Matrix(decomp.getSolver().getInverse().getData());
            invertible = true;
        } catch (SingularMatrixException e) {
            inverse = null;
            invertible = false;
        }
    }

}