Newer
Older
EPQ-3D-renderer / src / main / java / uk / org / floop / epq3d / Matrix.java
package uk.org.floop.epq3d;

public class Matrix {
    protected int x;
    protected int y;
    private double[][] items;

    public Matrix(int _x, int _y){
        x = _x; y= _y;
        items = new double[y][x];
    }
    public double getItem(int _x, int _y){
        return items[_y][_x];
    }
    public void setItem(int _x, int _y, double val){
        items[_y][_x] = val;
    }
    public double[][] getItems() {
        return items;
    }
    public void setItems(double[][] newItems){
        items = newItems;
    }
    public Matrix multiplyGetResult(Matrix multiplier) {
        Matrix result = new Matrix(multiplier.x, this.y);
        double newItem;
        if(x== multiplier.y){
            for(int rx = 0; rx< result.x; rx+=1){
                for(int ry = 0; ry< result.y; ry+=1){
                    newItem = 0;
                    for(int i = 0; i<x; i+=1){
                        newItem += this.getItem(i, ry)* multiplier.getItem(rx, i);
                    }
                    result.setItem(rx, ry, newItem);
            }}
        } else {
            throw new RuntimeException("wrong dimensions");
        }
        return result;
    }
    public void multiply(Matrix multiplier) {
        Matrix result = new Matrix(multiplier.x, this.y);
        double newItem;
        if(x== multiplier.y){
            for(int rx = 0; rx< result.x; rx+=1){
                for(int ry = 0; ry< result.y; ry+=1){
                    newItem = 0;
                    for(int i = 0; i<x; i+=1){
                        newItem += this.getItem(i, ry)* multiplier.getItem(rx, i);
                    }
                    result.setItem(rx, ry, newItem);
                }}
        } else {
            throw new RuntimeException("wrong dimensions");
        }
        setItems(result.getItems());
    }
    public void multiplyTo(Matrix multiplier, Matrix result) {
        if(!(result.x == multiplier.x && result.y == this.y)){
            throw new RuntimeException("wrong dimensions");
        }
        double newItem;
        if(x== multiplier.y){
            for(int rx = 0; rx< result.x; rx+=1){
                for(int ry = 0; ry< result.y; ry+=1){
                    newItem = 0;
                    for(int i = 0; i<x; i+=1){
                        newItem += this.getItem(i, ry)* multiplier.getItem(rx, i);
                    }
                    result.setItem(rx, ry, newItem);
                }}
        } else {
            throw new RuntimeException("wrong dimensions");
        }
    }

    public void multiplyPoint3to(Point3D point, Point3D result) {
        if(x==3){
            result.x = point.x * getItem(0,0) + point.y* getItem(1,0) + point.z* getItem(2,0);
            result.y = point.x * getItem(0,1) + point.y* getItem(1,1) + point.z* getItem(2,1);
            result.z = point.x * getItem(0,2) + point.y* getItem(1,2) + point.z* getItem(2,2);
        } else if(x == 4){
            result.x = point.x * getItem(0,0) + point.y* getItem(1,0) + point.z* getItem(2,0) + getItem(3,0);
            result.y = point.x * getItem(0,1) + point.y* getItem(1,1) + point.z* getItem(2,1) + getItem(3,1);
            result.z = point.x * getItem(0,2) + point.y* getItem(1,2) + point.z* getItem(2,2) + getItem(3,2);
        } else {throw new RuntimeException("wrong-dimensions");}
    }
    public void multiplyVec3to(Vector3D vector, Vector3D result){
        if(x==3){
            double x = vector.x * getItem(0,0) + vector.y* getItem(1,0) + vector.z* getItem(2,0);
            double y = vector.x * getItem(0,1) + vector.y* getItem(1,1) + vector.z* getItem(2,1);
            double z = vector.x * getItem(0,2) + vector.y* getItem(1,2) + vector.z* getItem(2,2);
            result.x = x; result.y = y; result.z = z;
        } else if(x == 4){
            double x = vector.x * getItem(0,0) + vector.y* getItem(1,0) + vector.z* getItem(2,0) + getItem(3,0);
            double y = vector.x * getItem(0,1) + vector.y* getItem(1,1) + vector.z* getItem(2,1) + getItem(3,1);
            double z = vector.x * getItem(0,2) + vector.y* getItem(1,2) + vector.z* getItem(2,2) + getItem(3,2);
            result.x = x; result.y = y; result.z = z;
        } else {throw new RuntimeException("wrong-dimensions");}
    }
    public double[] multiplyPoint3raw(double _x, double _y, double _z) {
        double[] result = new double[3];
        if(x ==3){
            result[0] = _x * getItem(0,0) + _y * getItem(1,0) + _z * getItem(2,0);
            result[1] = _x * getItem(0,1) + _y * getItem(1,1) + _z * getItem(2,1);
            result[2] = _x * getItem(0,2) + _y * getItem(1,2) + _z * getItem(2,2);
        } else if(x == 4){
            result[0] = _x * getItem(0,0) + _y * getItem(1,0) + _z * getItem(2,0) + getItem(3,0);
            result[1] = _x * getItem(0,1) + _y * getItem(1,1) + _z * getItem(2,1) + getItem(3,1);
            result[2] = _x * getItem(0,2) + _y * getItem(1,2) + _z * getItem(2,2) + getItem(3,2);
        } else {throw new RuntimeException("wrong-dimensions");}
        return result;
    }
    public void multiplyPoint2to(Point2D point, Point2D result) {
        if(x==2){
            result.x = (int)(point.x * getItem(0,0) + point.y* getItem(1,0));
            result.y = (int)(point.x * getItem(0,1) + point.y* getItem(1,1));
        } else if(x == 3){
            result.x = (int)(point.x * getItem(0,0) + point.y* getItem(1,0) + getItem(2,0));
            result.y = (int)(point.x * getItem(0,1) + point.y* getItem(1,1) + getItem(2,1));
        } else {throw new RuntimeException("wrong-dimensions");}
    }
    public double[] multiplyPoint2raw(double px, double py) {
        double[] result = new double[2];
        if(x==2){
            result[0] = (px * getItem(0,0) + py* getItem(1,0));
            result[1] = (px * getItem(0,1) + py* getItem(1,1));
        } else if(x==3){
            result[0] = (px * getItem(0,0) + py* getItem(1,0) + getItem(2,0));
            result[1] = (px * getItem(0,1) + py* getItem(1,1) + getItem(2,1));
        } else {throw new RuntimeException("wrong-dimensions");}
        return result;
    }
    public Matrix getInverse() {
        // handles the first level of determinant /
        // adjoint finding, and then uses getDeterminant for finding the determinants of sub-matrices
        Matrix result = new Matrix(x, y);
        /* find determinant and adjoint
         since both these calculations use some of the same values, I'm hardcoding the first level here for an extra
         2% performance gain*/
        Matrix adjoint = new Matrix(x, y);
        double determinant = 0;

        double[] COFResult;
        for(int origX = 0; origX < x; origX +=1) {
            for (int origY = 0; origY < x; origY += 1) {
                if (origX == 0){
                    COFResult = getCofactor(origX, origY, true);
                    determinant += COFResult[1];
                }else{
                    COFResult = getCofactor(origX, origY, false);
                }
                adjoint.setItem(origX, origY, COFResult[0]);
            }
        }
        // transpose the adjoint matrix (unused because it's easier to do it while copying across
        //{
        //     double temp;
        //     for(int mx = 0; mx < x; mx+=1){
        //         for(int my = 0; my <= mx; my += 1){
        //             if (my != mx){
        //                 temp = adjoint.getItem(mx, my);
        //                 adjoint.setItem(mx, my, adjoint.getItem(my, mx));
        //                 adjoint.setItem(my, mx, temp);
        //             }
        //         }
        //     }
        // }
        // copy the matrix to the result and divide by the determinant
        for(int mx = 0; mx<x;mx+=1){
            for(int my = 0; my<y;my+=1){
                result.setItem(my, mx, adjoint.getItem(mx, my) / determinant);
            }
        }
        return result;
    }
    // gets the cofactor element of a matrix specified by x and y
    // if given true, it also returns the determinant
    public double[] getCofactor(int origX, int origY, Boolean getDeterminant){
        Matrix minor = new Matrix(x-1, y-1);
        int setX = 0;
        for (int getX = 0; getX < x; getX += 1) {
            // if newX is the same as the x value of the selected element, skip that column
            if (getX == origX) {
                getX += 1;
                // bug where if the selected element is also the final element, we 'skip' it to a point outside the matrix
                // there's probably a better fix but this works
                if (!(getX < x)) {
                    break;
                }
            }
            int setY = 0;
            for (int getY = 0; getY < y; getY += 1) {
                if (getY == origY) {
                    getY += 1;
                    // bug where if the selected element is also the final element, we 'skip' it to a point outside the matrix
                    // there's probably a better fix but this works
                    if (!(getY < y)) {
                        break;
                    }
                }
                minor.setItem(setX, setY, getItem(getX, getY));
                setY += 1;
            }
            setX += 1;
        }
        /*
         multiplying the determinant of a minor, by -1^(i+j), gives the co factor.
         the sum of all the cofactors in a row or column equals the determinant
         */
        double coFactor = Math.pow(-1, origX + origY) * minor.getDeterminant();
        if (getDeterminant){
            double determinant = getItem(origX, origY) * coFactor;
            return new double[]{coFactor, determinant};
        }
        else{
            return new double[]{coFactor};

        }
    }
    /*
    gets the determinant of a matrix
    recursively calls getCoFactor, which in turn calls getDeterminant, for handling of any sized matrix
    probably really inefficient, it's n^n or something ridiculous,
    but hopefully I'll only need it for 3x3 mats, maybe some 4x4
    */
    public double getDeterminant() {
        double determinant = 0;
        if (x != y) {
            throw new RuntimeException("Determinants are only defined for square matrices, given: x=" + x + " y=" + y);
        }
        else if (x == 2){
            return getItem(0,0)*getItem(1,1) -
                    getItem(1,0)*getItem(0,1);
        }
        else{
            for(int i = 0; i<x; i+=1) {
                determinant += getCofactor(i, 0, true)[1];
            }
        }
        return determinant;
    }
}