GwieF.com : Project : Classes : Transform3D
logo

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;
    }
}