Transform3D.java
package com.gwief.jTrace;
/* Transfrom3D.java by Damian Newport
Allows a Ray/Vector3D to be transfrom3D in 3D space.
Relatively inefficent when creating / building the transfrom but
once built reasonably good efficiency at tranforming a given object
note :
Once built allows no further changes.
To do further changes make a copy and change that.
*/
class Transform3D {
double matrix[][] = new double[4][4];
double imatrix[][] = new double[4][4];
boolean identity = false;
boolean built = false;
/*
Grid Naming
===========
matrix[row][column]
*/
/* creates a identity matrix */
public Transform3D(){
matrix[0][0] = 1;
matrix[1][1] = 1;
matrix[2][2] = 1;
matrix[3][3] = 1;
identity = true;
}
/* copy contructor */
public Transform3D(Transform3D a){
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
matrix[i][j] = a.matrix[i][j];
}
}
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
imatrix[i][j] = a.imatrix[i][j];
}
}
identity = a.identity;
built = false; /* so that you can change this if nessessary */
}
/* translates by x,y,z */
public final void addTranslate(double x, double y, double z){
Transform3D temp = new Transform3D();
temp.matrix[3][0] = -x;
temp.matrix[3][1] = -y;
temp.matrix[3][2] = -z;
multiply(temp);
}
/* translates by x,y,z from vector3D */
public final void addTranslate(Vector3D a){
addTranslate(a.x,a.y,a.z);
}
/* scales by x,y,z */
public final void addScale(double x, double y, double z){
Transform3D temp = new Transform3D();
temp.matrix[0][0] = 1/x;
temp.matrix[1][1] = 1/y;
temp.matrix[2][2] = 1/z;
multiply(temp);
}
public final void addScale(double a){
addScale(a,a,a);
}
/* scales by x,y,z from Vector3D */
public final void addScale(Vector3D a){
addScale(a.x,a.y,a.z);
}
/* totates around x axis */
public final void addRotateX(double angle){
angle = Math.toRadians(angle);
Transform3D temp = new Transform3D();
temp.matrix[1][1] = Math.cos(angle);
temp.matrix[1][2] = Math.sin(angle);
temp.matrix[2][1] = (-1 * Math.sin(angle));
temp.matrix[2][2] = Math.cos(angle);
multiply(temp);
}
/* rotates around y axis */
public final void addRotateY(double angle){
angle = Math.toRadians(angle);
Transform3D temp = new Transform3D();
temp.matrix[0][0] = Math.cos(angle);
temp.matrix[0][2] = Math.sin(angle);
temp.matrix[2][0] = (-1 * Math.sin(angle));
temp.matrix[2][2] = Math.cos(angle);
multiply(temp);
}
/* rotates around z axis*/
public final void addRotateZ(double angle){
angle = Math.toRadians(angle);
Transform3D temp = new Transform3D();
temp.matrix[0][0] = Math.cos(angle);
temp.matrix[0][1] = Math.sin(angle);
temp.matrix[1][0] = (-1 * Math.sin(angle));
temp.matrix[1][1] = Math.cos(angle);
multiply(temp);
}
public final void addRotate(double x, double y, double z){
addRotateX(x);
addRotateY(y);
addRotateZ(z);
}
/* does matrix multiplication for 2 4x4 matrixies */
public final void multiply(Transform3D a){
if (built == false){ /* means you can't change an alrady built Transfrom */
Transform3D temp = new Transform3D();
temp.toZero();
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
for(int k=0;k<4;k++){
temp.matrix[i][j] += matrix[i][k] * a.matrix[k][j];
}
}
}
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
matrix[i][j] = temp.matrix[i][j];
}
}
identity = false;
}
}
/* fills imatrix with the invert of matrix */
private final void invert() {
if (identity == false) {
double temp;
double m00 = matrix[0][0];
double m01 = matrix[0][1];
double m02 = matrix[0][2];
double m03 = matrix[0][3];
double m10 = matrix[1][0];
double m11 = matrix[1][1];
double m12 = matrix[1][2];
double m13 = matrix[1][3];
double m20 = matrix[2][0];
double m21 = matrix[2][1];
double m22 = matrix[2][2];
double m23 = matrix[2][3];
double m30 = matrix[3][0];
double m31 = matrix[3][1];
double m32 = matrix[3][2];
double m33 = matrix[3][3];
/* calculate determinent */
temp =
m03 * m12 * m21 * m30-m02 * m13 * m21 * m30-m03 * m11 * m22 * m30+m01 * m13 * m22 * m30+
m02 * m11 * m23 * m30-m01 * m12 * m23 * m30-m03 * m12 * m20 * m31+m02 * m13 * m20 * m31+
m03 * m10 * m22 * m31-m00 * m13 * m22 * m31-m02 * m10 * m23 * m31+m00 * m12 * m23 * m31+
m03 * m11 * m20 * m32-m01 * m13 * m20 * m32-m03 * m10 * m21 * m32+m00 * m13 * m21 * m32+
m01 * m10 * m23 * m32-m00 * m11 * m23 * m32-m02 * m11 * m20 * m33+m01 * m12 * m20 * m33+
m02 * m10 * m21 * m33-m00 * m12 * m21 * m33-m01 * m10 * m22 * m33+m00 * m11 * m22 * m33;
temp = 1/temp;
/* do inversion */
imatrix[0][0] = m12*m23*m31 - m13*m22*m31 + m13*m21*m32 - m11*m23*m32 - m12*m21*m33 + m11*m22*m33;
imatrix[0][1] = m03*m22*m31 - m02*m23*m31 - m03*m21*m32 + m01*m23*m32 + m02*m21*m33 - m01*m22*m33;
imatrix[0][2] = m02*m13*m31 - m03*m12*m31 + m03*m11*m32 - m01*m13*m32 - m02*m11*m33 + m01*m12*m33;
imatrix[0][3] = m03*m12*m21 - m02*m13*m21 - m03*m11*m22 + m01*m13*m22 + m02*m11*m23 - m01*m12*m23;
imatrix[1][0] = m13*m22*m30 - m12*m23*m30 - m13*m20*m32 + m10*m23*m32 + m12*m20*m33 - m10*m22*m33;
imatrix[1][1] = m02*m23*m30 - m03*m22*m30 + m03*m20*m32 - m00*m23*m32 - m02*m20*m33 + m00*m22*m33;
imatrix[1][2] = m03*m12*m30 - m02*m13*m30 - m03*m10*m32 + m00*m13*m32 + m02*m10*m33 - m00*m12*m33;
imatrix[1][3] = m02*m13*m20 - m03*m12*m20 + m03*m10*m22 - m00*m13*m22 - m02*m10*m23 + m00*m12*m23;
imatrix[2][0] = m11*m23*m30 - m13*m21*m30 + m13*m20*m31 - m10*m23*m31 - m11*m20*m33 + m10*m21*m33;
imatrix[2][1] = m03*m21*m30 - m01*m23*m30 - m03*m20*m31 + m00*m23*m31 + m01*m20*m33 - m00*m21*m33;
imatrix[2][2] = m01*m13*m30 - m03*m11*m30 + m03*m10*m31 - m00*m13*m31 - m01*m10*m33 + m00*m11*m33;
imatrix[2][3] = m03*m11*m20 - m01*m13*m20 - m03*m10*m21 + m00*m13*m21 + m01*m10*m23 - m00*m11*m23;
imatrix[3][0] = m12*m21*m30 - m11*m22*m30 - m12*m20*m31 + m10*m22*m31 + m11*m20*m32 - m10*m21*m32;
imatrix[3][1] = m01*m22*m30 - m02*m21*m30 + m02*m20*m31 - m00*m22*m31 - m01*m20*m32 + m00*m21*m32;
imatrix[3][2] = m02*m11*m30 - m01*m12*m30 - m02*m10*m31 + m00*m12*m31 + m01*m10*m32 - m00*m11*m32;
imatrix[3][3] = m01*m12*m20 - m02*m11*m20 + m02*m10*m21 - m00*m12*m21 - m01*m10*m22 + m00*m11*m22;
/* devide by invarient */
for (int i = 0; i<4; i++){
for (int j = 0; j<4; j++){
imatrix[i][j] *= temp;
}
}
}
}
/* sets the matrix to zero */
private void toZero(){
for(int i = 0;i < 4; i++){
for (int j = 0; j < 4; j++){
matrix[i][j] = 0;
}
}
}
/* performs the transfrom - reasonably efficient */
public final Vector3D Transform(Vector3D a){
if (built == false){
build();
}
if (identity) return a;
Vector3D temp = new Vector3D();
temp.x = a.x * matrix[0][0] + a.y * matrix[1][0] + a.z * matrix[2][0] + matrix[3][0];
temp.y = a.x * matrix[0][1] + a.y * matrix[1][1] + a.z * matrix[2][1] + matrix[3][1];
temp.z = a.x * matrix[0][2] + a.y * matrix[1][2] + a.z * matrix[2][2] + matrix[3][2];
return temp;
}
/* performs the transform on a Ray */
public final Ray Transform(Ray a){
if (identity) return a;
Ray temp = new Ray();
temp.origin = Transform(a.origin);
temp.direction = pTransform(a.direction);
return temp;
}
/* perfroms the inverse transform - will build the transform if not already built */
public final Vector3D iTransform(Vector3D a){
if (built == false){
build();
}
if (identity) return a;
Vector3D temp = new Vector3D();
temp.x = a.x * imatrix[0][0] + a.y * imatrix[1][0] + a.z * imatrix[2][0] + imatrix[3][0];
temp.y = a.x * imatrix[0][1] + a.y * imatrix[1][1] + a.z * imatrix[2][1] + imatrix[3][1];
temp.z = a.x * imatrix[0][2] + a.y * imatrix[1][2] + a.z * imatrix[2][2] + imatrix[3][2];
return temp;
}
/* performs the inverse transfrom on a Ray */
public final Ray iTransform(Ray a){
if (identity) return a;
Ray temp = new Ray();
temp.origin = iTransform(a.origin);
temp.direction = piTransform(a.direction);
return temp;
}
/* builds the transfrom */
/* note - once built no further changes are possible */
public final void build() {
invert();
built = true;
}
/* applies only the rotation & scaling */
public final Vector3D pTransform(Vector3D a){
Vector3D temp = new Vector3D();
if (identity) return a;
temp.x = a.x * matrix[0][0] + a.y * matrix[1][0] + a.z * matrix[2][0];
temp.y = a.x * matrix[0][1] + a.y * matrix[1][1] + a.z * matrix[2][1];
temp.z = a.x * matrix[0][2] + a.y * matrix[1][2] + a.z * matrix[2][2];
return temp;
}
/* applies only the rotation & scaling of the inverse transform */
public final Vector3D piTransform(Vector3D a){
if (built == false){
build();
}
if (identity) return a;
Vector3D temp = new Vector3D();
temp.x = a.x * imatrix[0][0] + a.y * imatrix[1][0] + a.z * imatrix[2][0];
temp.y = a.x * imatrix[0][1] + a.y * imatrix[1][1] + a.z * imatrix[2][1];
temp.z = a.x * imatrix[0][2] + a.y * imatrix[1][2] + a.z * imatrix[2][2];
return temp;
}
public final String out(){
return out(matrix);
}
public final String out(double[][] m){
if (m.length != 4){
return "Error! Invalid matrix";
}
if (m[0].length != 4){
return "Error! Invalid matrix";
}
String a = "";
a += "( " + m[0][0] + " , " + m[0][1] + " , " + m[0][2] + " , " + m[0][3] + " )\n";
a += "( " + m[1][0] + " , " + m[1][1] + " , " + m[1][2] + " , " + m[1][3] + " )\n";
a += "( " + m[2][0] + " , " + m[2][1] + " , " + m[2][2] + " , " + m[2][3] + " )\n";
a += "( " + m[3][0] + " , " + m[3][1] + " , " + m[3][2] + " , " + m[3][3] + " )\n";
return a;
}
}