#

API Docs for: 0.6.0
Show: Inherited Protected Private Deprecated

# File: src/math/Mat3.js

    module.exports = Mat3;

    var Vec3 = require('./Vec3');

    /**
     * A 3x3 matrix.
     * @class Mat3
     * @constructor
     * @param array elements Array of nine elements. Optional.
     * @author schteppe / http://github.com/schteppe
     */
    function Mat3(elements){
        /**
         * A vector of length 9, containing all matrix elements
         * @property {Array} elements
         */
        if(elements){
            this.elements = elements;
        } else {
            this.elements = [0,0,0,0,0,0,0,0,0];
        }
    }

    /**
     * Sets the matrix to identity
     * @method identity
     * @todo Should perhaps be renamed to setIdentity() to be more clear.
     * @todo Create another function that immediately creates an identity matrix eg. eye()
     */
    Mat3.prototype.identity = function(){
        this.elements[0] = 1;
        this.elements[1] = 0;
        this.elements[2] = 0;

        this.elements[3] = 0;
        this.elements[4] = 1;
        this.elements[5] = 0;

        this.elements[6] = 0;
        this.elements[7] = 0;
        this.elements[8] = 1;
    };

    /**
     * Set all elements to zero
     * @method setZero
     */
    Mat3.prototype.setZero = function(){
        var e = this.elements;
        e[0] = 0;
        e[1] = 0;
        e[2] = 0;
        e[3] = 0;
        e[4] = 0;
        e[5] = 0;
        e[6] = 0;
        e[7] = 0;
        e[8] = 0;
    };

    /**
     * Sets the matrix diagonal elements from a Vec3
     * @method setTrace
     */
    Mat3.prototype.setTrace = function(vec3){
        var e = this.elements;
        e[0] = vec3.x;
        e[4] = vec3.y;
        e[8] = vec3.z;
    };

    /**
     * Sets the matrix diagonal elements from a Vec3
     * @method setTrace
     */
    Mat3.prototype.getTrace = function(target){
        var target = target || new Vec3();
        var e = this.elements;
        target.x = e[0];
        target.y = e[4];
        target.z = e[8];
    };

    /**
     * Matrix-Vector multiplication
     * @method vmult
     * @param {Vec3} v The vector to multiply with
     * @param {Vec3} target Optional, target to save the result in.
     */
    Mat3.prototype.vmult = function(v,target){
        target = target || new Vec3();

        var e = this.elements,
            x = v.x,
            y = v.y,
            z = v.z;
        target.x = e[0]*x + e[1]*y + e[2]*z;
        target.y = e[3]*x + e[4]*y + e[5]*z;
        target.z = e[6]*x + e[7]*y + e[8]*z;

        return target;
    };

    /**
     * Matrix-scalar multiplication
     * @method smult
     * @param {Number} s
     */
    Mat3.prototype.smult = function(s){
        for(var i=0; i<this.elements.length; i++){
            this.elements[i] *= s;
        }
    };

    /**
     * Matrix multiplication
     * @method mmult
     * @param {Mat3} m Matrix to multiply with from left side.
     * @return {Mat3} The result.
     */
    Mat3.prototype.mmult = function(m,target){
        var r = target || new Mat3();
        for(var i=0; i<3; i++){
            for(var j=0; j<3; j++){
                var sum = 0.0;
                for(var k=0; k<3; k++){
                    sum += m.elements[i+k*3] * this.elements[k+j*3];
                }
                r.elements[i+j*3] = sum;
            }
        }
        return r;
    };

    /**
     * Scale each column of the matrix
     * @method scale
     * @param {Vec3} v
     * @return {Mat3} The result.
     */
    Mat3.prototype.scale = function(v,target){
        target = target || new Mat3();
        var e = this.elements,
            t = target.elements;
        for(var i=0; i!==3; i++){
            t[3*i + 0] = v.x * e[3*i + 0];
            t[3*i + 1] = v.y * e[3*i + 1];
            t[3*i + 2] = v.z * e[3*i + 2];
        }
        return target;
    };

    /**
     * Solve Ax=b
     * @method solve
     * @param {Vec3} b The right hand side
     * @param {Vec3} target Optional. Target vector to save in.
     * @return {Vec3} The solution x
     * @todo should reuse arrays
     */
    Mat3.prototype.solve = function(b,target){
        target = target || new Vec3();

        // Construct equations
        var nr = 3; // num rows
        var nc = 4; // num cols
        var eqns = [];
        for(var i=0; i<nr*nc; i++){
            eqns.push(0);
        }
        var i,j;
        for(i=0; i<3; i++){
            for(j=0; j<3; j++){
                eqns[i+nc*j] = this.elements[i+3*j];
            }
        }
        eqns[3+4*0] = b.x;
        eqns[3+4*1] = b.y;
        eqns[3+4*2] = b.z;

        // Compute right upper triangular version of the matrix - Gauss elimination
        var n = 3, k = n, np;
        var kp = 4; // num rows
        var p, els;
        do {
            i = k - n;
            if (eqns[i+nc*i] === 0) {
                // the pivot is null, swap lines
                for (j = i + 1; j < k; j++) {
                    if (eqns[i+nc*j] !== 0) {
                        np = kp;
                        do {  // do ligne( i ) = ligne( i ) + ligne( k )
                            p = kp - np;
                            eqns[p+nc*i] += eqns[p+nc*j];
                        } while (--np);
                        break;
                    }
                }
            }
            if (eqns[i+nc*i] !== 0) {
                for (j = i + 1; j < k; j++) {
                    var multiplier = eqns[i+nc*j] / eqns[i+nc*i];
                    np = kp;
                    do {  // do ligne( k ) = ligne( k ) - multiplier * ligne( i )
                        p = kp - np;
                        eqns[p+nc*j] = p <= i ? 0 : eqns[p+nc*j] - eqns[p+nc*i] * multiplier ;
                    } while (--np);
                }
            }
        } while (--n);

        // Get the solution
        target.z = eqns[2*nc+3] / eqns[2*nc+2];
        target.y = (eqns[1*nc+3] - eqns[1*nc+2]*target.z) / eqns[1*nc+1];
        target.x = (eqns[0*nc+3] - eqns[0*nc+2]*target.z - eqns[0*nc+1]*target.y) / eqns[0*nc+0];

        if(isNaN(target.x) || isNaN(target.y) || isNaN(target.z) || target.x===Infinity || target.y===Infinity || target.z===Infinity){
            throw "Could not solve equation! Got x=["+target.toString()+"], b=["+b.toString()+"], A=["+this.toString()+"]";
        }

        return target;
    };

    /**
     * Get an element in the matrix by index. Index starts at 0, not 1!!!
     * @method e
     * @param {Number} row
     * @param {Number} column
     * @param {Number} value Optional. If provided, the matrix element will be set to this value.
     * @return {Number}
     */
    Mat3.prototype.e = function( row , column ,value){
        if(value===undefined){
            return this.elements[column+3*row];
        } else {
            // Set value
            this.elements[column+3*row] = value;
        }
    };

    /**
     * Copy another matrix into this matrix object.
     * @method copy
     * @param {Mat3} source
     * @return {Mat3} this
     */
    Mat3.prototype.copy = function(source){
        for(var i=0; i < source.elements.length; i++){
            this.elements[i] = source.elements[i];
        }
        return this;
    };

    /**
     * Returns a string representation of the matrix.
     * @method toString
     * @return string
     */
    Mat3.prototype.toString = function(){
        var r = "";
        var sep = ",";
        for(var i=0; i<9; i++){
            r += this.elements[i] + sep;
        }
        return r;
    };

    /**
     * reverse the matrix
     * @method reverse
     * @param {Mat3} target Optional. Target matrix to save in.
     * @return {Mat3} The solution x
     */
    Mat3.prototype.reverse = function(target){

        target = target || new Mat3();

        // Construct equations
        var nr = 3; // num rows
        var nc = 6; // num cols
        var eqns = [];
        for(var i=0; i<nr*nc; i++){
            eqns.push(0);
        }
        var i,j;
        for(i=0; i<3; i++){
            for(j=0; j<3; j++){
                eqns[i+nc*j] = this.elements[i+3*j];
            }
        }
        eqns[3+6*0] = 1;
        eqns[3+6*1] = 0;
        eqns[3+6*2] = 0;
        eqns[4+6*0] = 0;
        eqns[4+6*1] = 1;
        eqns[4+6*2] = 0;
        eqns[5+6*0] = 0;
        eqns[5+6*1] = 0;
        eqns[5+6*2] = 1;

        // Compute right upper triangular version of the matrix - Gauss elimination
        var n = 3, k = n, np;
        var kp = nc; // num rows
        var p;
        do {
            i = k - n;
            if (eqns[i+nc*i] === 0) {
                // the pivot is null, swap lines
                for (j = i + 1; j < k; j++) {
                    if (eqns[i+nc*j] !== 0) {
                        np = kp;
                        do { // do line( i ) = line( i ) + line( k )
                            p = kp - np;
                            eqns[p+nc*i] += eqns[p+nc*j];
                        } while (--np);
                        break;
                    }
                }
            }
            if (eqns[i+nc*i] !== 0) {
                for (j = i + 1; j < k; j++) {
                    var multiplier = eqns[i+nc*j] / eqns[i+nc*i];
                    np = kp;
                    do { // do line( k ) = line( k ) - multiplier * line( i )
                        p = kp - np;
                        eqns[p+nc*j] = p <= i ? 0 : eqns[p+nc*j] - eqns[p+nc*i] * multiplier ;
                    } while (--np);
                }
            }
        } while (--n);

        // eliminate the upper left triangle of the matrix
        i = 2;
        do {
            j = i-1;
            do {
                var multiplier = eqns[i+nc*j] / eqns[i+nc*i];
                np = nc;
                do {
                    p = nc - np;
                    eqns[p+nc*j] =  eqns[p+nc*j] - eqns[p+nc*i] * multiplier ;
                } while (--np);
            } while (j--);
        } while (--i);

        // operations on the diagonal
        i = 2;
        do {
            var multiplier = 1 / eqns[i+nc*i];
            np = nc;
            do {
                p = nc - np;
                eqns[p+nc*i] = eqns[p+nc*i] * multiplier ;
            } while (--np);
        } while (i--);

        i = 2;
        do {
            j = 2;
            do {
                p = eqns[nr+j+nc*i];
                if( isNaN( p ) || p ===Infinity ){
                    throw "Could not reverse! A=["+this.toString()+"]";
                }
                target.e( i , j , p );
            } while (j--);
        } while (i--);

        return target;
    };

    /**
     * Set the matrix from a quaterion
     * @method setRotationFromQuaternion
     * @param {Quaternion} q
     */
    Mat3.prototype.setRotationFromQuaternion = function( q ) {
        var x = q.x, y = q.y, z = q.z, w = q.w,
            x2 = x + x, y2 = y + y, z2 = z + z,
            xx = x * x2, xy = x * y2, xz = x * z2,
            yy = y * y2, yz = y * z2, zz = z * z2,
            wx = w * x2, wy = w * y2, wz = w * z2,
            e = this.elements;

        e[3*0 + 0] = 1 - ( yy + zz );
        e[3*0 + 1] = xy - wz;
        e[3*0 + 2] = xz + wy;

        e[3*1 + 0] = xy + wz;
        e[3*1 + 1] = 1 - ( xx + zz );
        e[3*1 + 2] = yz - wx;

        e[3*2 + 0] = xz - wy;
        e[3*2 + 1] = yz + wx;
        e[3*2 + 2] = 1 - ( xx + yy );

        return this;
    };

    /**
     * Transpose the matrix
     * @method transpose
     * @param  {Mat3} target Where to store the result.
     * @return {Mat3} The target Mat3, or a new Mat3 if target was omitted.
     */
    Mat3.prototype.transpose = function( target ) {
        target = target || new Mat3();

        var Mt = target.elements,
            M = this.elements;

        for(var i=0; i!==3; i++){
            for(var j=0; j!==3; j++){
                Mt[3*i + j] = M[3*j + i];
            }
        }

        return target;
    };