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] = (int)(px * getItem(0,0) + py* getItem(1,0)); result[1] = (int)(px * getItem(0,1) + py* getItem(1,1)); } else if(x==3){ result[0] = (int)(px * getItem(0,0) + py* getItem(1,0) + getItem(2,0)); result[1] = (int)(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; } }