Mat44.js

(function() {

    'use strict';

    var Vec3 = require('./Vec3');
    var Vec4 = require('./Vec4');
    var Mat33 = require('./Mat33');
    var EPSILON = require('./Epsilon');

    /**
     * Instantiates a Mat44 object.
     * @class Mat44
     * @classdesc A 4x4 column-major matrix.
     */
    function Mat44( that ) {
        that = that || [
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        ];
        if ( that instanceof Array ) {
            this.data = that;
        } else {
            this.data = new Array( 16 );
            this.data[0] = that.data[0];
            this.data[1] = that.data[1];
            this.data[2] = that.data[2];
            this.data[3] = that.data[3];
            this.data[4] = that.data[4];
            this.data[5] = that.data[5];
            this.data[6] = that.data[6];
            this.data[7] = that.data[7];
            this.data[8] = that.data[8];
            this.data[9] = that.data[9];
            this.data[10] = that.data[10];
            this.data[11] = that.data[11];
            this.data[12] = that.data[12];
            this.data[13] = that.data[13];
            this.data[14] = that.data[14];
            this.data[15] = that.data[15];
        }
    }

    /**
     * Returns a row of the matrix as a Vec4 object.
     * @memberof Mat44
     *
     * @param {number} index - The 0-based row index.
     * @param {Vec3|Array} vec - The vector to replace the row. Optional.
     *
     * @returns {Vec4} The row vector.
     */
    Mat44.prototype.row = function( index, vec ) {
        if ( vec ) {
            this.data[0+index] = vec[0] || vec.x;
            this.data[4+index] = vec[1] || vec.y;
            this.data[8+index] = vec[2] || vec.z;
            this.data[12+index] = vec[3] || vec.w;
            return this;
        }
        return new Vec4(
            this.data[0+index],
            this.data[4+index],
            this.data[8+index],
            this.data[12+index] );
    };

    /**
     * Returns a column of the matrix as a Vec4 object.
     * @memberof Mat44
     *
     * @param {number} index - The 0-based col index.
     * @param {Vec3|Array} vec - The vector to replace the col. Optional.
     *
     * @returns {Vec4} The column vector.
     */
    Mat44.prototype.col = function( index, vec ) {
        if ( vec ) {
            this.data[0+index*4] = vec[0] || vec.x;
            this.data[1+index*4] = vec[1] || vec.y;
            this.data[2+index*4] = vec[2] || vec.z;
            this.data[3+index*4] = vec[3] || vec.w;
            return this;
        }
        return new Vec4(
            this.data[0+index*4],
            this.data[1+index*4],
            this.data[2+index*4],
            this.data[3+index*4] );
    };

    /**
     * Returns the identity matrix.
     * @memberof Mat44
     *
     * @returns {Mat44} The identiy matrix.
     */
    Mat44.identity = function() {
        return new Mat44();
    };

    /**
     * Returns a scale matrix.
     * @memberof Mat44
     *
     * @param {Vec3|Array|number} scale - The scalar or vector scaling factor.
     *
     * @returns {Mat44} The scale matrix.
     */
    Mat44.scale = function( scale ) {
        if ( typeof scale === 'number' ) {
            return new Mat44([
                scale, 0, 0, 0,
                0, scale, 0, 0,
                0, 0, scale, 0,
                0, 0, 0, 1
            ]);
        } else if ( scale instanceof Array ) {
            return new Mat44([
                scale[0], 0, 0, 0,
                0, scale[1], 0, 0,
                0, 0, scale[2], 0,
                0, 0, 0, 1
            ]);
        }
        return new Mat44([
            scale.x, 0, 0, 0,
            0, scale.y, 0, 0,
            0, 0, scale.z, 0,
            0, 0, 0, 1
        ]);
    };

    /**
     * Returns a translation matrix.
     * @memberof Mat44
     *
     * @param {Vec3|Array} translation - The translation vector.
     *
     * @returns {Mat44} The translation matrix.
     */
    Mat44.translation = function( translation ) {
        if ( translation instanceof Array ) {
            return new Mat44([
                1, 0, 0, 0,
                0, 1, 0, 0,
                0, 0, 1, 0,
                translation[0], translation[1], translation[2], 1
            ]);
        }
        return new Mat44([
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            translation.x, translation.y, translation.z, 1
        ]);
    };

    /**
     * Returns a rotation matrix defined by an axis and an angle.
     * @memberof Mat44
     *
     * @param {number} angle - The angle of the rotation, in radians.
     * @param {Vec3} axis - The axis of the rotation.
     *
     * @returns {Mat44} The rotation matrix.
     */
    Mat44.rotation = function( angle, axis ) {
        if ( axis instanceof Array ) {
            axis = new Vec3( axis );
        }
        // zero vector, return identity
        if ( axis.lengthSquared() === 0 ) {
            throw 'Cannot create rotation matrix for a zero vector axis';
        }
        var normAxis = axis.normalize(),
            x = normAxis.x,
            y = normAxis.y,
            z = normAxis.z,
            modAngle = ( angle > 0 ) ? angle % (2*Math.PI) : angle % (-2*Math.PI),
            s = Math.sin( modAngle ),
            c = Math.cos( modAngle ),
            xx = x * x,
            yy = y * y,
            zz = z * z,
            xy = x * y,
            yz = y * z,
            zx = z * x,
            xs = x * s,
            ys = y * s,
            zs = z * s,
            one_c = 1.0 - c;
        return new Mat44([
            (one_c * xx) + c, (one_c * xy) + zs, (one_c * zx) - ys, 0,
            (one_c * xy) - zs, (one_c * yy) + c, (one_c * yz) + xs, 0,
            (one_c * zx) + ys, (one_c * yz) - xs, (one_c * zz) + c, 0,
            0, 0, 0, 1
        ]);
    };

    /**
     * Returns a rotation matrix to rotate a vector from one direction to
     * another.
     * @memberof Mat44
     *
     * @param {Vec3} from - The starting direction.
     * @param {Vec3} to - The ending direction.
     *
     * @returns {Mat44} The matrix representing the rotation.
     */
    Mat44.rotationFromTo = function( fromVec, toVec ) {
        /*
        This method is based on the code from:
            Tomas Mller, John Hughes
            Efficiently Building a Matrix to Rotate One Vector to Another
            Journal of Graphics Tools, 4(4):1-4, 1999
        */
        fromVec = new Vec3( fromVec ).normalize();
        toVec = new Vec3( toVec ).normalize();
        var e = fromVec.dot( toVec );
        var f = Math.abs( e );
        var x, u, v;
        var fx, fy, fz;
        var ux, uz;
        var c1, c2, c3;
        if ( f > 1.0 - EPSILON ) {
            // 'from' and 'to' almost parallel
            // nearly orthogonal
            fx = Math.abs( fromVec.x );
            fy = Math.abs( fromVec.y );
            fz = Math.abs( fromVec.z );
            if ( fx < fy ) {
                if ( fx < fz ) {
                    x = new Vec3( 1, 0, 0 );
                } else {
                    x = new Vec3( 0, 0, 1 );
                }
            } else {
                if ( fy < fz ) {
                    x = new Vec3( 0, 1, 0 );
                } else {
                    x = new Vec3( 0, 0, 1 );
                }
            }
            u = x.sub( fromVec );
            v = x.sub( toVec );
            c1 = 2.0 / u.dot( u );
            c2 = 2.0 / v.dot( v );
            c3 = c1*c2 * u.dot( v );
            // set matrix entries
            return new Mat44([
                -c1*u.x*u.x - c2*v.x*v.x + c3*v.x*u.x + 1.0,
                -c1*u.y*u.x - c2*v.y*v.x + c3*v.y*u.x,
                -c1*u.z*u.x - c2*v.z*v.x + c3*v.z*u.x,
                0.0,
                -c1*u.x*u.y - c2*v.x*v.y + c3*v.x*u.y,
                -c1*u.y*u.y - c2*v.y*v.y + c3*v.y*u.y + 1.0,
                -c1*u.z*u.y - c2*v.z*v.y + c3*v.z*u.y,
                1.0,
                -c1*u.x*u.z - c2*v.x*v.z + c3*v.x*u.z,
                -c1*u.y*u.z - c2*v.y*v.z + c3*v.y*u.z,
                -c1*u.z*u.z - c2*v.z*v.z + c3*v.z*u.z + 1.0,
                 0.0,
                 0.0,
                 0.0,
                 0.0,
                 1.0
            ]);
        }
        // the most common case, unless 'from'='to', or 'to'=-'from'
        v = fromVec.cross( toVec );
        u = 1.0 / ( 1.0 + e );    // optimization by Gottfried Chen
        ux = u * v.x;
        uz = u * v.z;
        c1 = ux * v.y;
        c2 = ux * v.z;
        c3 = uz * v.y;
        return new Mat44([
            e + ux * v.x,
            c1 + v.z,
            c2 - v.y,
            0.0,
            c1 - v.z,
            e + u * v.y * v.y,
            c3 + v.x,
            0.0,
            c2 + v.y,
            c3 - v.x,
            e + uz * v.z,
            0.0,
            0.0,
            0.0,
            0.0,
            1.0
        ]);
    };

    /**
     * Adds the matrix with the provided matrix argument, returning a new Ma33
     * object.
     * @memberof Mat44
     *
     * @param {Mat33|Array} that - The matrix to add.
     *
     * @returns {Mat44} The sum of the two matrices.
     */
    Mat44.prototype.addMat33 = function( that ) {
        that = ( that instanceof Array ) ? that : that.data;
        return new Mat44([
            this.data[0] + that[0],
            this.data[1] + that[1],
            this.data[2] + that[2],
            this.data[3],
            this.data[4] + that[3],
            this.data[5] + that[4],
            this.data[6] + that[5],
            this.data[7],
            this.data[8] + that[6],
            this.data[9] + that[7],
            this.data[10] + that[8],
            this.data[11],
            this.data[12],
            this.data[13],
            this.data[14],
            this.data[15]
        ]);
    };

    /**
     * Adds the matrix with the provided matrix argument, returning a new Ma33
     * object.
     * @memberof Mat44
     *
     * @param {Mat44|Array} that - The matrix to add.
     *
     * @returns {Mat44} The sum of the two matrices.
     */
    Mat44.prototype.addMat44 = function( that ) {
        that = ( that instanceof Array ) ? that : that.data;
        return new Mat44([
            this.data[0] + that[0],
            this.data[1] + that[1],
            this.data[2] + that[2],
            this.data[3] + that[3],
            this.data[4] + that[4],
            this.data[5] + that[5],
            this.data[6] + that[6],
            this.data[7] + that[7],
            this.data[8] + that[8],
            this.data[9] + that[9],
            this.data[10] + that[10],
            this.data[11] + that[11],
            this.data[12] + that[12],
            this.data[13] + that[13],
            this.data[14] + that[14],
            this.data[15] + that[15]
        ]);
    };

    /**
     * Subtracts the provided matrix argument from the matrix, returning a new
     * Mat44 object.
     * @memberof Mat44
     *
     * @param {Mat33|Array} that - The matrix to add.
     *
     * @returns {Mat44} The difference of the two matrices.
     */
    Mat44.prototype.subMat33 = function( that ) {
        that = ( that instanceof Array ) ? that : that.data;
        return new Mat44([
            this.data[0] - that[0],
            this.data[1] - that[1],
            this.data[2] - that[2],
            this.data[3],
            this.data[4] - that[3],
            this.data[5] - that[4],
            this.data[6] - that[5],
            this.data[7],
            this.data[8] - that[6],
            this.data[9] - that[7],
            this.data[10] - that[8],
            this.data[11],
            this.data[12],
            this.data[13],
            this.data[14],
            this.data[15]
        ]);
    };

    /**
     * Subtracts the provided matrix argument from the matrix, returning a new
     * Mat44 object.
     * @memberof Mat44
     *
     * @param {Mat44|Array} that - The matrix to add.
     *
     * @returns {Mat44} The difference of the two matrices.
     */
    Mat44.prototype.subMat44 = function( that ) {
        that = ( that instanceof Array ) ? that : that.data;
        return new Mat44([
            this.data[0] - that[0],
            this.data[1] - that[1],
            this.data[2] - that[2],
            this.data[3] - that[3],
            this.data[4] - that[4],
            this.data[5] - that[5],
            this.data[6] - that[6],
            this.data[7] - that[7],
            this.data[8] - that[8],
            this.data[9] - that[9],
            this.data[10] - that[10],
            this.data[11] - that[11],
            this.data[12] - that[12],
            this.data[13] - that[13],
            this.data[14] - that[14],
            this.data[15] - that[15]
        ]);
    };

    /**
     * Multiplies the provded vector argument by the matrix, returning a new
     * Vec3 object.
     * @memberof Mat44
     *
     * @param {Vec3|Vec4|Array} - The vector to be multiplied by the matrix.
     *
     * @returns {Vec3} The resulting vector.
     */
    Mat44.prototype.multVec3 = function( that ) {
        // ensure 'that' is a Vec3
        // it is safe to only cast if Array since Vec4 has own method
        if ( that instanceof Array ) {
            return new Vec3(
                this.data[0] * that[0] + this.data[4] * that[1] + this.data[8] * that[2] + this.data[12],
                this.data[1] * that[0] + this.data[5] * that[1] + this.data[9] * that[2] + this.data[13],
                this.data[2] * that[0] + this.data[6] * that[1] + this.data[10] * that[2] + this.data[14]
            );
        }
        return new Vec3(
            this.data[0] * that.x + this.data[4] * that.y + this.data[8] * that.z + this.data[12],
            this.data[1] * that.x + this.data[5] * that.y + this.data[9] * that.z + this.data[13],
            this.data[2] * that.x + this.data[6] * that.y + this.data[10] * that.z + this.data[14]
        );
    };

    /**
     * Multiplies the provded vector argument by the matrix, returning a new
     * Vec3 object.
     * @memberof Mat44
     *
     * @param {Vec3|Vec4|Array} - The vector to be multiplied by the matrix.
     *
     * @returns {Vec4} The resulting vector.
     */
    Mat44.prototype.multVec4 = function( that ) {
        // ensure 'that' is a Vec4
        // it is safe to only cast if Array since Vec3 has own method
        if ( that instanceof Array ) {
            return new Vec4(
                this.data[0] * that[0] + this.data[4] * that[1] + this.data[8] * that[2] + this.data[12] * that[3],
                this.data[1] * that[0] + this.data[5] * that[1] + this.data[9] * that[2] + this.data[13] * that[3],
                this.data[2] * that[0] + this.data[6] * that[1] + this.data[10] * that[2] + this.data[14] * that[3],
                this.data[3] * that[0] + this.data[7] * that[1] + this.data[11] * that[2] + this.data[15] * that[3]
            );
        }
        return new Vec4(
            this.data[0] * that.x + this.data[4] * that.y + this.data[8] * that.z + this.data[12] * that.w,
            this.data[1] * that.x + this.data[5] * that.y + this.data[9] * that.z + this.data[13] * that.w,
            this.data[2] * that.x + this.data[6] * that.y + this.data[10] * that.z + this.data[14] * that.w,
            this.data[3] * that.x + this.data[7] * that.y + this.data[11] * that.z + this.data[15] * that.w
        );
    };

    /**
     * Multiplies all components of the matrix by the provded scalar argument,
     * returning a new Mat44 object.
     * @memberof Mat44
     *
     * @param {number} - The scalar to multiply the matrix by.
     *
     * @returns {Mat44} The resulting matrix.
     */
    Mat44.prototype.multScalar = function( that ) {
        return new Mat44([
            this.data[0] * that,
            this.data[1] * that,
            this.data[2] * that,
            this.data[3] * that,
            this.data[4] * that,
            this.data[5] * that,
            this.data[6] * that,
            this.data[7] * that,
            this.data[8] * that,
            this.data[9] * that,
            this.data[10] * that,
            this.data[11] * that,
            this.data[12] * that,
            this.data[13] * that,
            this.data[14] * that,
            this.data[15] * that
        ]);
    };

    /**
     * Multiplies the provded matrix argument by the matrix, returning a new
     * Mat44 object.
     * @memberof Mat44
     *
     * @param {Mat33|Array} - The matrix to be multiplied by the matrix.
     *
     * @returns {Mat44} The resulting matrix.
     */
    Mat44.prototype.multMat33 = function( that ) {
        that = ( that instanceof Array ) ? that : that.data;
        return new Mat44([
            this.data[0] * that[0] + this.data[4] * that[1] + this.data[8] * that[2],
            this.data[1] * that[0] + this.data[5] * that[1] + this.data[9] * that[2],
            this.data[2] * that[0] + this.data[6] * that[1] + this.data[10] * that[2],
            this.data[3] * that[0] + this.data[7] * that[1] + this.data[11] * that[2],
            this.data[0] * that[3] + this.data[4] * that[4] + this.data[8] * that[5],
            this.data[1] * that[3] + this.data[5] * that[4] + this.data[9] * that[5],
            this.data[2] * that[3] + this.data[6] * that[4] + this.data[10] * that[5],
            this.data[3] * that[3] + this.data[7] * that[4] + this.data[11] * that[5],
            this.data[0] * that[6] + this.data[4] * that[7] + this.data[8] * that[8],
            this.data[1] * that[6] + this.data[5] * that[7] + this.data[9] * that[8],
            this.data[2] * that[6] + this.data[6] * that[7] + this.data[10] * that[8],
            this.data[3] * that[6] + this.data[7] * that[7] + this.data[11] * that[8],
            this.data[12],
            this.data[13],
            this.data[14],
            this.data[15]
        ]);
    };

    /**
     * Multiplies the provded matrix argument by the matrix, returning a new
     * Mat44 object.
     * @memberof Mat44
     *
     * @param {Mat44|Array} - The matrix to be multiplied by the matrix.
     *
     * @returns {Mat44} The resulting matrix.
     */
    Mat44.prototype.multMat44 = function( that ) {
        that = ( that instanceof Array ) ? that : that.data;
        return new Mat44([
            this.data[0] * that[0] + this.data[4] * that[1] + this.data[8] * that[2] + this.data[12] * that[3],
            this.data[1] * that[0] + this.data[5] * that[1] + this.data[9] * that[2] + this.data[13] * that[3],
            this.data[2] * that[0] + this.data[6] * that[1] + this.data[10] * that[2] + this.data[14] * that[3],
            this.data[3] * that[0] + this.data[7] * that[1] + this.data[11] * that[2] + this.data[15] * that[3],
            this.data[0] * that[4] + this.data[4] * that[5] + this.data[8] * that[6] + this.data[12] * that[7],
            this.data[1] * that[4] + this.data[5] * that[5] + this.data[9] * that[6] + this.data[13] * that[7],
            this.data[2] * that[4] + this.data[6] * that[5] + this.data[10] * that[6] + this.data[14] * that[7],
            this.data[3] * that[4] + this.data[7] * that[5] + this.data[11] * that[6] + this.data[15] * that[7],
            this.data[0] * that[8] + this.data[4] * that[9] + this.data[8] * that[10] + this.data[12] * that[11],
            this.data[1] * that[8] + this.data[5] * that[9] + this.data[9] * that[10] + this.data[13] * that[11],
            this.data[2] * that[8] + this.data[6] * that[9] + this.data[10] * that[10] + this.data[14] * that[11],
            this.data[3] * that[8] + this.data[7] * that[9] + this.data[11] * that[10] + this.data[15] * that[11],
            this.data[0] * that[12] + this.data[4] * that[13] + this.data[8] * that[14] + this.data[12] * that[15],
            this.data[1] * that[12] + this.data[5] * that[13] + this.data[9] * that[14] + this.data[13] * that[15],
            this.data[2] * that[12] + this.data[6] * that[13] + this.data[10] * that[14] + this.data[14] * that[15],
            this.data[3] * that[12] + this.data[7] * that[13] + this.data[11] * that[14] + this.data[15] * that[15]
        ]);
    };

    /**
     * Divides all components of the matrix by the provded scalar argument,
     * returning a new Mat44 object.
     * @memberof Mat44
     *
     * @param {number} - The scalar to divide the matrix by.
     *
     * @returns {Mat44} The resulting matrix.
     */
    Mat44.prototype.divScalar = function( that ) {
        return new Mat44([
            this.data[0] / that,
            this.data[1] / that,
            this.data[2] / that,
            this.data[3] / that,
            this.data[4] / that,
            this.data[5] / that,
            this.data[6] / that,
            this.data[7] / that,
            this.data[8] / that,
            this.data[9] / that,
            this.data[10] / that,
            this.data[11] / that,
            this.data[12] / that,
            this.data[13] / that,
            this.data[14] / that,
            this.data[15] / that
        ]);
    };

    /**
     * Returns true if the all components match those of a provided matrix.
     * An optional epsilon value may be provided.
     * @memberof Mat44
     *
     * @param {Mat44|Array} that - The matrix to test equality with.
     * @param {number} epsilon - The epsilon value. Optional.
     *
     * @returns {boolean} Whether or not the matrix components match.
     */
    Mat44.prototype.equals = function( that, epsilon ) {
        epsilon = epsilon === undefined ? 0 : epsilon;
        that = ( that instanceof Array ) ? that : that.data;
        return (( this.data[0] === that[0] ) || ( Math.abs( this.data[0] - that[0] ) <= epsilon ) ) &&
            (( this.data[1] === that[1] ) || ( Math.abs( this.data[1] - that[1] ) <= epsilon ) ) &&
            (( this.data[2] === that[2] ) || ( Math.abs( this.data[2] - that[2] ) <= epsilon ) ) &&
            (( this.data[3] === that[3] ) || ( Math.abs( this.data[3] - that[3] ) <= epsilon ) ) &&
            (( this.data[4] === that[4] ) || ( Math.abs( this.data[4] - that[4] ) <= epsilon ) ) &&
            (( this.data[5] === that[5] ) || ( Math.abs( this.data[5] - that[5] ) <= epsilon ) ) &&
            (( this.data[6] === that[6] ) || ( Math.abs( this.data[6] - that[6] ) <= epsilon ) ) &&
            (( this.data[7] === that[7] ) || ( Math.abs( this.data[7] - that[7] ) <= epsilon ) ) &&
            (( this.data[8] === that[8] ) || ( Math.abs( this.data[8] - that[8] ) <= epsilon ) ) &&
            (( this.data[9] === that[9] ) || ( Math.abs( this.data[9] - that[9] ) <= epsilon ) ) &&
            (( this.data[10] === that[10] ) || ( Math.abs( this.data[10] - that[10] ) <= epsilon ) ) &&
            (( this.data[11] === that[11] ) || ( Math.abs( this.data[11] - that[11] ) <= epsilon ) ) &&
            (( this.data[12] === that[12] ) || ( Math.abs( this.data[12] - that[12] ) <= epsilon ) ) &&
            (( this.data[13] === that[13] ) || ( Math.abs( this.data[13] - that[13] ) <= epsilon ) ) &&
            (( this.data[14] === that[14] ) || ( Math.abs( this.data[14] - that[14] ) <= epsilon ) ) &&
            (( this.data[15] === that[15] ) || ( Math.abs( this.data[15] - that[15] ) <= epsilon ) );
    };

    /**
     * Returns an orthographic projection matrix.
     *
     * @param {number} left - The minimum x extent of the projection.
     * @param {number} right - The maximum x extent of the projection.
     * @param {number} bottom - The minimum y extent of the projection.
     * @param {number} top - The maximum y extent of the projection.
     * @param {number} near - The minimum z extent of the projection.
     * @param {number} far - The maximum z extent of the projection.
     *
     * @returns {Mat44} The orthographic projection matrix.
     */
    Mat44.ortho = function( left, right, bottom, top, near, far ) {
        var mat = Mat44.identity();
        mat.data[0] = 2 / ( right - left );
        mat.data[5] = 2 / ( top - bottom );
        mat.data[10] = -2 / ( far - near );
        mat.data[12] = -( ( right + left ) / ( right - left ) );
        mat.data[13] = -( ( top + bottom ) / ( top - bottom ) );
        mat.data[14] = -( ( far + near ) / ( far - near ) );
        return mat;
    };

    /**
     * Returns a perspective projection matrix.
     *
     * @param {number} fovy - The vertical field of view, in radians.
     * @param {number} aspect - The aspect ratio.
     * @param {number} near - The near clipping plane of the frustum.
     * @param {number} far - The far clipping plane of the frustum.
     *
     * @returns {Mat44} The perspective projection matrix.
     */
    Mat44.perspective = function( fovy, aspect, near, far ) {
        var f = 1.0 / Math.tan( fovy / 2.0 );
        var nf = 1.0 / ( near - far );
        var mat = Mat44.identity();
        mat.data[0] = f / aspect;
        mat.data[5] = f;
        mat.data[10] = ( far + near ) * nf;
        mat.data[11] = -1;
        mat.data[14] = ( 2.0 * far * near ) * nf;
        mat.data[15] = 0;
        return mat;
    };

    /**
     * Returns the a view matrix for the affine-transformation of the current matrix.
     *
     * @returns {Mat44} The view matrix.
     */
    Mat44.prototype.view = function() {
        var x = new Vec3( this.data[0], this.data[1], this.data[2] ).normalize();
        var y = new Vec3( this.data[4], this.data[5], this.data[6] ).normalize();
        var z = new Vec3( this.data[8], this.data[9], this.data[10] ).normalize();
        var t = new Vec3( -this.data[12], -this.data[13], -this.data[14] );
        return new Mat44([
            x.x, y.x, z.x, 0,
            x.y, y.y, z.y, 0,
            x.z, y.z, z.z, 0,
            t.dot( x ), t.dot( y ), t.dot( z ), 1
        ]);
    };

    /**
     * Returns the transpose of the matrix.
     * @memberof Mat44
     *
     * @returns {Mat44} The transposed matrix.
     */
    Mat44.prototype.transpose = function() {
        return new Mat44([
            this.data[0],
            this.data[4],
            this.data[8],
            this.data[12],
            this.data[1],
            this.data[5],
            this.data[9],
            this.data[13],
            this.data[2],
            this.data[6],
            this.data[10],
            this.data[14],
            this.data[3],
            this.data[7],
            this.data[11],
            this.data[15]
        ]);
    };

    /**
     * Returns the inverse of the matrix.
     * @memberof Mat44
     *
     * @returns {Mat44} The inverted matrix.
     */
    Mat44.prototype.inverse = function() {
        var inv = new Mat44([
            // col 0
            this.data[5]*this.data[10]*this.data[15] -
                this.data[5]*this.data[11]*this.data[14] -
                this.data[9]*this.data[6]*this.data[15] +
                this.data[9]*this.data[7]*this.data[14] +
                this.data[13]*this.data[6]*this.data[11] -
                this.data[13]*this.data[7]*this.data[10],
            -this.data[1]*this.data[10]*this.data[15] +
                this.data[1]*this.data[11]*this.data[14] +
                this.data[9]*this.data[2]*this.data[15] -
                this.data[9]*this.data[3]*this.data[14] -
                this.data[13]*this.data[2]*this.data[11] +
                this.data[13]*this.data[3]*this.data[10],
            this.data[1]*this.data[6]*this.data[15] -
                this.data[1]*this.data[7]*this.data[14] -
                this.data[5]*this.data[2]*this.data[15] +
                this.data[5]*this.data[3]*this.data[14] +
                this.data[13]*this.data[2]*this.data[7] -
                this.data[13]*this.data[3]*this.data[6],
            -this.data[1]*this.data[6]*this.data[11] +
                this.data[1]*this.data[7]*this.data[10] +
                this.data[5]*this.data[2]*this.data[11] -
                this.data[5]*this.data[3]*this.data[10] -
                this.data[9]*this.data[2]*this.data[7] +
                this.data[9]*this.data[3]*this.data[6],
            // col 1
            -this.data[4]*this.data[10]*this.data[15] +
                this.data[4]*this.data[11]*this.data[14] +
                this.data[8]*this.data[6]*this.data[15] -
                this.data[8]*this.data[7]*this.data[14] -
                this.data[12]*this.data[6]*this.data[11] +
                this.data[12]*this.data[7]*this.data[10],
            this.data[0]*this.data[10]*this.data[15] -
                this.data[0]*this.data[11]*this.data[14] -
                this.data[8]*this.data[2]*this.data[15] +
                this.data[8]*this.data[3]*this.data[14] +
                this.data[12]*this.data[2]*this.data[11] -
                this.data[12]*this.data[3]*this.data[10],
            -this.data[0]*this.data[6]*this.data[15] +
                this.data[0]*this.data[7]*this.data[14] +
                this.data[4]*this.data[2]*this.data[15] -
                this.data[4]*this.data[3]*this.data[14] -
                this.data[12]*this.data[2]*this.data[7] +
                this.data[12]*this.data[3]*this.data[6],
            this.data[0]*this.data[6]*this.data[11] -
                this.data[0]*this.data[7]*this.data[10] -
                this.data[4]*this.data[2]*this.data[11] +
                this.data[4]*this.data[3]*this.data[10] +
                this.data[8]*this.data[2]*this.data[7] -
                this.data[8]*this.data[3]*this.data[6],
            // col 2
            this.data[4]*this.data[9]*this.data[15] -
                this.data[4]*this.data[11]*this.data[13] -
                this.data[8]*this.data[5]*this.data[15] +
                this.data[8]*this.data[7]*this.data[13] +
                this.data[12]*this.data[5]*this.data[11] -
                this.data[12]*this.data[7]*this.data[9],
            -this.data[0]*this.data[9]*this.data[15] +
                this.data[0]*this.data[11]*this.data[13] +
                this.data[8]*this.data[1]*this.data[15] -
                this.data[8]*this.data[3]*this.data[13] -
                this.data[12]*this.data[1]*this.data[11] +
                this.data[12]*this.data[3]*this.data[9],
            this.data[0]*this.data[5]*this.data[15] -
                this.data[0]*this.data[7]*this.data[13] -
                this.data[4]*this.data[1]*this.data[15] +
                this.data[4]*this.data[3]*this.data[13] +
                this.data[12]*this.data[1]*this.data[7] -
                this.data[12]*this.data[3]*this.data[5],
            -this.data[0]*this.data[5]*this.data[11] +
                this.data[0]*this.data[7]*this.data[9] +
                this.data[4]*this.data[1]*this.data[11] -
                this.data[4]*this.data[3]*this.data[9] -
                this.data[8]*this.data[1]*this.data[7] +
                this.data[8]*this.data[3]*this.data[5],
            // col 3
            -this.data[4]*this.data[9]*this.data[14] +
                this.data[4]*this.data[10]*this.data[13] +
                this.data[8]*this.data[5]*this.data[14] -
                this.data[8]*this.data[6]*this.data[13] -
                this.data[12]*this.data[5]*this.data[10] +
                this.data[12]*this.data[6]*this.data[9],
            this.data[0]*this.data[9]*this.data[14] -
                this.data[0]*this.data[10]*this.data[13] -
                this.data[8]*this.data[1]*this.data[14] +
                this.data[8]*this.data[2]*this.data[13] +
                this.data[12]*this.data[1]*this.data[10] -
                this.data[12]*this.data[2]*this.data[9],
            -this.data[0]*this.data[5]*this.data[14] +
                this.data[0]*this.data[6]*this.data[13] +
                this.data[4]*this.data[1]*this.data[14] -
                this.data[4]*this.data[2]*this.data[13] -
                this.data[12]*this.data[1]*this.data[6] +
                this.data[12]*this.data[2]*this.data[5],
            this.data[0]*this.data[5]*this.data[10] -
                this.data[0]*this.data[6]*this.data[9] -
                this.data[4]*this.data[1]*this.data[10] +
                this.data[4]*this.data[2]*this.data[9] +
                this.data[8]*this.data[1]*this.data[6] -
                this.data[8]*this.data[2]*this.data[5]
        ]);
        // calculate determinant
        var det = this.data[0]*inv.data[0] +
            this.data[1]*inv.data[4] +
            this.data[2]*inv.data[8] +
            this.data[3]*inv.data[12];
        return inv.multScalar( 1 / det );
    };

    /**
     * Returns the rotation matrix from the affine-transformation.
     * @memberof Mat44
     *
     * @returns {Mat44} The rotation matrix.
     */
    Mat44.prototype.rotation = function() {
        var x = new Vec3( this.data[0], this.data[1], this.data[2] ).normalize();
        var y = new Vec3( this.data[4], this.data[5], this.data[6] ).normalize();
        var z = new Vec3( this.data[8], this.data[9], this.data[10] ).normalize();
        return new Mat44([
            x.x, x.y, x.z, 0,
            y.x, y.y, y.z, 0,
            z.x, z.y, z.z, 0,
            0, 0, 0, 1
        ]);
    };

    /**
     * Returns the translation matrix from the affine-transformation.
     * @memberof Mat44
     *
     * @returns {Mat44} The translation matrix.
     */
    Mat44.prototype.translation = function() {
        return Mat44.translation([ this.data[12], this.data[13], this.data[14] ]);
    };

    /**
     * Returns the scale matrix from the affine-transformation.
     * @memberof Mat44
     *
     * @returns {Mat44} The scale matrix.
     */
    Mat44.prototype.scale = function() {
        var x = new Vec3( this.data[0], this.data[1], this.data[2] );
        var y = new Vec3( this.data[4], this.data[5], this.data[6] );
        var z = new Vec3( this.data[8], this.data[9], this.data[10] );
        return Mat44.scale([ x.length(), y.length(), z.length() ]);
    };

    /**
     * Returns the inverse of the transform's rotation matrix.
     * @memberof Mat44
     *
     * @returns {Mat44} The inverse rotation matrix.
     */
    Mat44.prototype.inverseRotation = function() {
        var x = new Vec3( this.data[0], this.data[1], this.data[2] ).normalize();
        var y = new Vec3( this.data[4], this.data[5], this.data[6] ).normalize();
        var z = new Vec3( this.data[8], this.data[9], this.data[10] ).normalize();
        return new Mat44([
            x.x, y.x, z.x, 0,
            x.y, y.y, z.y, 0,
            x.z, y.z, z.z, 0,
            0, 0, 0, 1
        ]);
    };

    /**
     * Returns the inverse of the transform's translation matrix.
     * @memberof Mat44
     *
     * @returns {Mat44} The inverse translation matrix.
     */
    Mat44.prototype.inverseTranslation = function() {
        return Mat44.translation([ -this.data[12], -this.data[13], -this.data[14] ]);
    };

    /**
     * Returns the inverse of the transform's scale matrix.
     * @memberof Mat44
     *
     * @returns {Mat44} The inverse scale matrix.
     */
    Mat44.prototype.inverseScale = function() {
        var x = new Vec3( this.data[0], this.data[1], this.data[2] );
        var y = new Vec3( this.data[4], this.data[5], this.data[6] );
        var z = new Vec3( this.data[8], this.data[9], this.data[10] );
        var scale = new Vec3( x.length(), y.length(), z.length() );
        return Mat44.scale([
            1 / scale.x,
            1 / scale.y,
            1 / scale.z
        ]);
    };

    /**
     * Returns a random transform matrix composed of a rotation and scale.
     * @memberof Mat44
     *
     * @returns {Mat44} A random transform matrix.
     */
    Mat44.random = function() {
        var r = Mat44.rotation( Math.random() * 360, Vec3.random() );
        var s = Mat44.scale( Math.random() * 10 );
        var t = Mat44.translation( Vec3.random() );
        return t.multMat44( r.multMat44( s ) );
    };

    /**
     * Returns a string representation of the matrix.
     * @memberof Mat44
     *
     * @returns {String} The string representation of the matrix.
     */
    Mat44.prototype.toString = function() {
        return this.data[0] +', '+ this.data[4] +', '+ this.data[8] +', '+ this.data[12] +',\n' +
            this.data[1] +', '+ this.data[5] +', '+ this.data[9] +', '+ this.data[13] +',\n' +
            this.data[2] +', '+ this.data[6] +', '+ this.data[10] +', '+ this.data[14] +',\n' +
            this.data[3] +', '+ this.data[7] +', '+ this.data[11] +', '+ this.data[15];
    };

    /**
     * Returns an array representation of the matrix.
     * @memberof Mat44
     *
     * @returns {Array} The matrix as an array.
     */
    Mat44.prototype.toArray = function() {
        return [
            this.data[0],
            this.data[1],
            this.data[2],
            this.data[3],
            this.data[4],
            this.data[5],
            this.data[6],
            this.data[7],
            this.data[8],
            this.data[9],
            this.data[10],
            this.data[11],
            this.data[12],
            this.data[13],
            this.data[14],
            this.data[15]
        ];
    };

    /**
     * Returns an the matrix representation as a 3x3 Mat33 object.
     * @memberof Mat44
     *
     * @returns {Mat33} The matrix as an array.
     */
    Mat44.prototype.toMat33 = function() {
        return new Mat33([
            this.data[0],
            this.data[1],
            this.data[2],
            this.data[4],
            this.data[5],
            this.data[6],
            this.data[8],
            this.data[9],
            this.data[10]
        ]);
    };

    /**
     * Returns an array representation of the matrix.
     * @memberof Mat33
     *
     * @returns {Array} The matrix as an array.
     */
    Mat33.prototype.toMat44 = function() {
        return new Mat44([
            this.data[0],
            this.data[1],
            this.data[2],
            0.0,
            this.data[3],
            this.data[4],
            this.data[5],
            0.0,
            this.data[6],
            this.data[7],
            this.data[8],
            0.0,
            0.0,
            0.0,
            0.0,
            1.0
        ]);
    };

    module.exports = Mat44;

}());