/*

## Java classes to implement 3D transformation matrices

by Ken Perlin @ NYU, 1998.

You have my permission to use freely, as long as you keep the attribution. - Ken Perlin

Note: this Matrix3D.html file also works as a legal Matrix3D.java file. If you save the source under that name, you can just run javac on it.

### Why does this class exist?

I created this class to support general purpose 3D transformations. I use it in a number of the demos that run on my Web page.

### What does the class do?

You can use it to create 3D points and homogeneous vectors, and also to create transformation matrices with these. There are methods to rotate, translate, and scale transformations, and to apply transformations to vectors. You can also get and set the elements of matrices and vectors.

The classes Vector3D and Matrix3D are extended from respective generic classes VectorN and MatrixN, which do most of the bookkeeping for arithmetic vectors of length N and square matrices of size N × N, respectively.

```*/

// Homogeneous transformation matrices in three dimensions

public class Matrix3D extends MatrixN {

Matrix3D() { // create a new identity transformation
super(4);
identity();
}

void rotateX(double theta) { // rotate transformation about the X axis

Matrix3D tmp = new Matrix3D();
double c = Math.cos(theta);
double s = Math.sin(theta);

tmp.set(1,1, c);
tmp.set(1,2,-s);
tmp.set(2,1, s);
tmp.set(2,2, c);

postMultiply(tmp);
}
void rotateY(double theta) { // rotate transformation about the Y axis

Matrix3D tmp = new Matrix3D();
double c = Math.cos(theta);
double s = Math.sin(theta);

tmp.set(2,2, c);
tmp.set(2,0,-s);
tmp.set(0,2, s);
tmp.set(0,0, c);

postMultiply(tmp);
}
void rotateZ(double theta) { // rotate transformation about the Z axis

Matrix3D tmp = new Matrix3D();
double c = Math.cos(theta);
double s = Math.sin(theta);

tmp.set(0,0, c);
tmp.set(0,1,-s);
tmp.set(1,0, s);
tmp.set(1,1, c);

postMultiply(tmp);
}

void translate(double a, double b, double c) { // translate

Matrix3D tmp = new Matrix3D();

tmp.set(0,3, a);
tmp.set(1,3, b);
tmp.set(2,3, c);

postMultiply(tmp);
}
void translate(Vector3D v) { translate(v.get(0), v.get(1), v.get(2)); }

void scale(double s) { // scale uniformly

Matrix3D tmp = new Matrix3D();

tmp.set(0,0, s);
tmp.set(1,1, s);
tmp.set(2,2, s);

postMultiply(tmp);
}
void scale(double r, double s, double t) { // scale non-uniformly

Matrix3D tmp = new Matrix3D();

tmp.set(0,0, r);
tmp.set(1,1, s);
tmp.set(2,2, t);

postMultiply(tmp);
}
void scale(Vector3D v) { scale(v.get(0), v.get(1), v.get(2)); }
}

// Homogeneous vectors in three dimensions

class Vector3D extends VectorN {

Vector3D() { super(4); }                                    // create a new 3D homogeneous vector

void set(double x, double y, double z, double w) {          // set value of vector
set(0, x);
set(1, y);
set(2, z);
set(3, w);
}
void set(double x, double y, double z) { set(x, y, z, 1); } // set value of a 3D point
}

// Geometric vectors of size N

class VectorN {
private double v[];

VectorN(int n) { v = new double[n]; }    // create a new vector

int size() { return v.length; }          // return vector size

double get(int j) { return v[j]; }       // get one element

void set(int j, double f) { v[j] = f; }  // set one element

void set(VectorN vec) {                  // copy from another vector
for (int j = 0 ; j < size() ; j++)
set(j, vec.get(j));
}

public String toString() {               // convert to string representation
String s = "{";
for (int j = 0 ; j < size() ; j++)
s += (j == 0 ? "" : ",") + get(j);
return s + "}";
}

void transform(MatrixN mat) {            // multiply by an N × N matrix
VectorN tmp = new VectorN(size());
double f;

for (int i = 0 ; i < size() ; i++) {
f = 0.;
for (int j = 0 ; j < size() ; j++)
f += mat.get(i,j) * get(j);
tmp.set(i, f);
}
set(tmp);
}

double distance(VectorN vec) {          // euclidean distance
double x, y, d = 0;
for (int i = 0 ; i < size() ; i++) {
x = vec.get(0) - get(0);
y = vec.get(1) - get(1);
d += x * x + y * y;
}
return Math.sqrt(d);
}
}

// Geometric matrices of size N × N

class MatrixN { // N × N matrices
private VectorN v[];
private MatrixN stack[] = new MatrixN[5];
int stackPtr = 0;

MatrixN(int n) {                                    // make a new square matrix
v = new VectorN[n];
for (int i = 0 ; i < n ; i++)
v[i] = new VectorN(n);
}

int size() { return v.length; }                     // return no. of rows

double get(int i, int j) { return get(i).get(j); }  // get one element

void set(int i, int j, double f) { v[i].set(j,f); } // set one element

VectorN get(int i) { return v[i]; }                 // get one row

void set(int i, VectorN vec) { v[i].set(vec); }     // set one row

void push() {
if (stack[stackPtr] == null)
stack[stackPtr] = new MatrixN(size());
stack[stackPtr++].set(this);
}

void pop() {
set(stack[--stackPtr]);
}

void set(MatrixN mat) {                             // copy from another matrix
for (int i = 0 ; i < size() ; i++)
set(i, mat.get(i));
}

public String toString() {                   // convert to string representation
String s = "{";
for (int i = 0 ; i < size() ; i++)
s += (i == 0 ? "" : ",") + get(i);
return s + "}";
}

void identity() {                            // set to identity matrix
for (int j = 0 ; j < size() ; j++)
for (int i = 0 ; i < size() ; i++)
set(i, j, (i == j ? 1 : 0));
}

void preMultiply(MatrixN mat) {              // mat × this
MatrixN tmp = new MatrixN(size());
double f;

for (int j = 0 ; j < size() ; j++)
for (int i = 0 ; i < size() ; i++) {
f = 0.;
for (int k = 0 ; k < size() ; k++)
f += mat.get(i,k) * get(k,j);
tmp.set(i, j, f);
}
set(tmp);
}

void postMultiply(MatrixN mat) {             // this × mat
MatrixN tmp = new MatrixN(size());
double f;

for (int j = 0 ; j < size() ; j++)
for (int i = 0 ; i < size() ; i++) {
f = 0.;
for (int k = 0 ; k < size() ; k++)
f += get(i,k) * mat.get(k,j);
tmp.set(i, j, f);
}
set(tmp);
}
}

```