/*
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.
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.
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);
}
}