// Matrix3D.java
// This class implements the basic matrix operations for 3D graphics
// Much of this code is based on code or hints provided by Prof. Ken Perlin (NYU)

/*	// constructors
	Matrix3D();
	
	// canonical matrices
	identityMatrix();
	translationMatrix(double a, double b, double c);
	xRotationMatrix(double theta);
	yRotationMatrix(double theta);
	zRotationMatrix(double theta);
	scaleMatrix(double a, double b, double c);
	zeroMatrix();
	
	// transformation matrices
	translate(double a, double b, double c);
	rotateX(double theta);
	rotateY(double theta);
	rotateZ(double theta);
	scale(double a, double b, double c);
	
	// additional operations
	double get(int i, int j);
	set(int i, int j, double a);
	copy(Matrix3D src);
	add(Matrix3D src);
	multiply(Matrix3D src);
	print();
	
	// variables
	double data[4][4];
	Matrix3D temp;
*/


public class Matrix3D {
	
	double[][] data = new double[4][4];
	Matrix3D temp;
	
	public void Matrix3D( ) {
		identityMatrix();
	}
	
	public void zeroMatrix() {
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				data[i][j] = 0;
			}
		}
	}
	
	public void identityMatrix() {
		zeroMatrix();
		data[0][0] = 1;
		data[1][1] = 1;
		data[2][2] = 1;
		data[3][3] = 1;
	}
	
	public void identity() {
		this.identityMatrix();
	}
	
	public void translationMatrix(double a, double b, double c) {
		identity();
		data[0][3] = a;
		data[1][3] = b;
		data[2][3] = c;
	}
	
	public void xRotationMatrix(double theta) {
		identityMatrix();
		data[1][1] = Math.cos(theta);
		data[1][2] = Math.sin(theta);
		data[2][2] = data[1][1];
		data[2][1] = -1*data[1][2];
	}
	
	public void yRotationMatrix(double theta) {
		identityMatrix();
		data[0][0] = Math.cos(theta);
		data[0][2] = Math.sin(theta);
		data[2][2] = data[0][0];
		data[2][0] = -1*data[0][2];
	}

	public void zRotationMatrix(double theta) {
		identityMatrix();
		data[0][0] = Math.cos(theta);
		data[0][1] = Math.sin(theta);
		data[1][1] = data[0][0];
		data[1][0] = -1*data[0][1];
	}
	
	public void scaleMatrix(double a, double b, double c) {
		identityMatrix();
		data[0][0] = a;
		data[1][1] = b;
		data[2][2] = c;
	}
	
	public void set(int i, int j, double a) { data[i][j] = a; }
	public double get(int i, int j) { return data[i][j]; }
	
	public void copy(Matrix3D src) {
		for (int i = 0 ; i < 4 ; i++) {
			for (int j = 0 ; j < 4 ; j++) {
	 			set(i,j, src.get(i,j));
	 		}
	 	}
	}
   
   	public void add(Matrix3D src) {
		for (int i = 0 ; i < 4 ; i++) {
			for (int j = 0 ; j < 4 ; j++) {
	 			set(i,j, this.get(i,j) + src.get(i,j));
	 		}
	 	}
	}
	
	public void multiply(Matrix3D M) {
		temp = new Matrix3D();
		temp.copy(this);		
		for (int i = 0 ; i < 4 ; i++) {
			for (int j = 0 ; j < 4 ; j++) {
				double sum = 0;
				for (int k = 0 ; k < 4 ; k++) {
					sum += temp.get(i,k) * M.get(k,j);
				}
				data[i][j] = sum;
			}
		}
	}
	
	public void transform(double src[], double dst[]) {
		double w = data[3][0] * src[0] + data[3][1] * src[1] + data[3][1] * src[1] + data[3][3];
		
		for (int i = 0 ; i < 3 ; i++) {
         	dst[i] = (data[i][0] * src[0] + data[i][1] * src[1] + data[i][2] * src[2] + data[i][3]) / w;
   		}
   	}

	public void translate(double a, double b, double c) {
		temp = new Matrix3D();
		temp.translationMatrix(a, b, c);
		this.multiply(temp);
//		this.print();
	}
	
	public void rotateX(double theta) {
		temp = new Matrix3D();
		temp.xRotationMatrix(theta);
		this.multiply(temp);
	}

	public void rotateY(double theta) {
		temp = new Matrix3D();
		temp.yRotationMatrix(theta);
		this.multiply(temp);
	}

	public void rotateZ(double theta) {
		temp = new Matrix3D();
		temp.zRotationMatrix(theta);
		this.multiply(temp);
	}

	public void scale(double a, double b, double c) {
		temp = new Matrix3D();
		temp.scaleMatrix(a, b, c);
		this.multiply(temp);
	}
	
	public void print( ) {
		for (int i = 0; i < 4; i++) {
			System.out.println(this.get(i,0) + "\t" + this.get(i,1) + "\t" + this.get(i,2) + "\t" + this.get(i,3) + "\t");
		}
	}

	
}

