(function() {
'use strict';
var Vec3 = require('./Vec3');
var EPSILON = require('./Epsilon');
/**
* Instantiates a Mat33 object.
* @class Mat33
* @classdesc A 3x3 column-major matrix.
*/
function Mat33( that ) {
that = that || [
1, 0, 0,
0, 1, 0,
0, 0, 1
];
if ( that instanceof Array ) {
this.data = that;
} else {
this.data = new Array( 9 );
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];
}
}
/**
* Returns a row of the matrix as a Vec3 object.
* @memberof Mat33
*
* @param {number} index - The 0-based row index.
* @param {Vec3|Array} vec - The vector to replace the row. Optional.
*
* @returns {Vec3} The row vector.
*/
Mat33.prototype.row = function( index, vec ) {
if ( vec ) {
this.data[0+index] = vec[0] || vec.x;
this.data[3+index] = vec[1] || vec.y;
this.data[6+index] = vec[2] || vec.z;
return this;
}
return new Vec3(
this.data[0+index],
this.data[3+index],
this.data[6+index] );
};
/**
* Returns a column of the matrix as a Vec3 object.
* @memberof Mat33
*
* @param {number} index - The 0-based col index.
* @param {Vec3|Array} vec - The vector to replace the col. Optional.
*
* @returns {Vec3} The column vector.
*/
Mat33.prototype.col = function( index, vec ) {
if ( vec ) {
this.data[0+index*3] = vec[0] || vec.x;
this.data[1+index*3] = vec[1] || vec.y;
this.data[2+index*3] = vec[2] || vec.z;
return this;
}
return new Vec3(
this.data[0+index*3],
this.data[1+index*3],
this.data[2+index*3] );
};
/**
* Returns the identity matrix.
* @memberof Mat33
*
* @returns {Mat33} The identiy matrix.
*/
Mat33.identity = function() {
return new Mat33();
};
/**
* Returns a scale matrix.
* @memberof Mat33
*
* @param {Vec3|Array|number} scale - The scalar or vector scaling factor.
*
* @returns {Mat33} The scale matrix.
*/
Mat33.scale = function( scale ) {
if ( typeof scale === 'number' ) {
return new Mat33([
scale, 0, 0,
0, scale, 0,
0, 0, scale
]);
} else if ( scale instanceof Array ) {
return new Mat33([
scale[0], 0, 0,
0, scale[1], 0,
0, 0, scale[2]
]);
}
return new Mat33([
scale.x, 0, 0,
0, scale.y, 0,
0, 0, scale.z
]);
};
/**
* Returns a rotation matrix defined by an axis and an angle.
* @memberof Mat33
*
* @param {number} angle - The angle of the rotation, in radians.
* @param {Vec3} axis - The axis of the rotation.
*
* @returns {Mat33} The rotation matrix.
*/
Mat33.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 Mat33([
(one_c * xx) + c, (one_c * xy) + zs, (one_c * zx) - ys,
(one_c * xy) - zs, (one_c * yy) + c, (one_c * yz) + xs,
(one_c * zx) + ys, (one_c * yz) - xs, (one_c * zz) + c
]);
};
/**
* Returns a rotation matrix to rotate a vector from one direction to
* another.
* @memberof Mat33
*
* @param {Vec3} from - The starting direction.
* @param {Vec3} to - The ending direction.
*
* @returns {Mat33} The matrix representing the rotation.
*/
Mat33.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 Mat33([
-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,
-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,
-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
]);
}
// 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 Mat33([
e + ux * v.x,
c1 + v.z,
c2 - v.y,
c1 - v.z,
e + u * v.y * v.y,
c3 + v.x,
c2 + v.y,
c3 - v.x,
e + uz * v.z
]);
};
/**
* Adds the matrix with the provided matrix argument, returning a new Ma33
* object.
* @memberof Mat33
*
* @param {Mat33|Array} that - The matrix to add.
*
* @returns {Mat33} The sum of the two matrices.
*/
Mat33.prototype.addMat33 = function( that ) {
that = ( that instanceof Array ) ? that : that.data;
return new Mat33([
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]
]);
};
/**
* Adds the matrix with the provided matrix argument, returning a new Ma33
* object.
* @memberof Mat33
*
* @param {Mat44|Array} that - The matrix to add.
*
* @returns {Mat33} The sum of the two matrices.
*/
Mat33.prototype.addMat44 = function( that ) {
that = ( that instanceof Array ) ? that : that.data;
return new Mat33([
this.data[0] + that[0],
this.data[1] + that[1],
this.data[2] + that[2],
this.data[3] + that[4],
this.data[4] + that[5],
this.data[5] + that[6],
this.data[6] + that[8],
this.data[7] + that[9],
this.data[8] + that[10]
]);
};
/**
* Subtracts the provided matrix argument from the matrix, returning a new
* Mat33 object.
* @memberof Mat33
*
* @param {Mat33|Array} that - The matrix to add.
*
* @returns {Mat33} The difference of the two matrices.
*/
Mat33.prototype.subMat33 = function( that ) {
that = ( that instanceof Array ) ? that : that.data;
return new Mat33([
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]
]);
};
/**
* Subtracts the provided matrix argument from the matrix, returning a new
* Mat33 object.
* @memberof Mat33
*
* @param {Mat44|Array} that - The matrix to add.
*
* @returns {Mat33} The difference of the two matrices.
*/
Mat33.prototype.subMat44 = function( that ) {
that = ( that instanceof Array ) ? that : that.data;
return new Mat33([
this.data[0] - that[0],
this.data[1] - that[1],
this.data[2] - that[2],
this.data[3] - that[4],
this.data[4] - that[5],
this.data[5] - that[6],
this.data[6] - that[8],
this.data[7] - that[9],
this.data[8] - that[10]
]);
};
/**
* Multiplies the provded vector argument by the matrix, returning a new
* Vec3 object.
* @memberof Mat33
*
* @param {Vec3|Vec4|Array} - The vector to be multiplied by the matrix.
*
* @returns {Vec3} The resulting vector.
*/
Mat33.prototype.multVec3 = function( that ) {
// ensure 'that' is a Vec3
// it is safe to only cast if Array since the .w of a Vec4 is not used
if ( that instanceof Array ) {
return new Vec3(
this.data[0] * that[0] + this.data[3] * that[1] + this.data[6] * that[2],
this.data[1] * that[0] + this.data[4] * that[1] + this.data[7] * that[2],
this.data[2] * that[0] + this.data[5] * that[1] + this.data[8] * that[2] );
}
return new Vec3(
this.data[0] * that.x + this.data[3] * that.y + this.data[6] * that.z,
this.data[1] * that.x + this.data[4] * that.y + this.data[7] * that.z,
this.data[2] * that.x + this.data[5] * that.y + this.data[8] * that.z );
};
/**
* Multiplies all components of the matrix by the provded scalar argument,
* returning a new Mat33 object.
* @memberof Mat33
*
* @param {number} - The scalar to multiply the matrix by.
*
* @returns {Mat33} The resulting matrix.
*/
Mat33.prototype.multScalar = function( that ) {
return new Mat33([
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
]);
};
/**
* Multiplies the provded matrix argument by the matrix, returning a new
* Mat33 object.
* @memberof Mat33
*
* @param {Mat33|Array} - The matrix to be multiplied by the matrix.
*
* @returns {Mat33} The resulting matrix.
*/
Mat33.prototype.multMat33 = function( that ) {
that = ( that instanceof Array ) ? that : that.data;
return new Mat33([
this.data[0] * that[0] + this.data[3] * that[1] + this.data[6] * that[2],
this.data[1] * that[0] + this.data[4] * that[1] + this.data[7] * that[2],
this.data[2] * that[0] + this.data[5] * that[1] + this.data[8] * that[2],
this.data[0] * that[3] + this.data[3] * that[4] + this.data[6] * that[5],
this.data[1] * that[3] + this.data[4] * that[4] + this.data[7] * that[5],
this.data[2] * that[3] + this.data[5] * that[4] + this.data[8] * that[5],
this.data[0] * that[6] + this.data[3] * that[7] + this.data[6] * that[8],
this.data[1] * that[6] + this.data[4] * that[7] + this.data[7] * that[8],
this.data[2] * that[6] + this.data[5] * that[7] + this.data[8] * that[8]
]);
};
/**
* Multiplies the provded matrix argument by the matrix, returning a new
* Mat33 object.
* @memberof Mat33
*
* @param {Mat44|Array} - The matrix to be multiplied by the matrix.
*
* @returns {Mat33} The resulting matrix.
*/
Mat33.prototype.multMat44 = function( that ) {
that = ( that instanceof Array ) ? that : that.data;
return new Mat33([
this.data[0] * that[0] + this.data[3] * that[1] + this.data[6] * that[2],
this.data[1] * that[0] + this.data[4] * that[1] + this.data[7] * that[2],
this.data[2] * that[0] + this.data[5] * that[1] + this.data[8] * that[2],
this.data[0] * that[4] + this.data[3] * that[5] + this.data[6] * that[6],
this.data[1] * that[4] + this.data[4] * that[5] + this.data[7] * that[6],
this.data[2] * that[4] + this.data[5] * that[5] + this.data[8] * that[6],
this.data[0] * that[8] + this.data[3] * that[9] + this.data[6] * that[10],
this.data[1] * that[8] + this.data[4] * that[9] + this.data[7] * that[10],
this.data[2] * that[8] + this.data[5] * that[9] + this.data[8] * that[10]
]);
};
/**
* Divides all components of the matrix by the provded scalar argument,
* returning a new Mat33 object.
* @memberof Mat33
*
* @param {number} - The scalar to divide the matrix by.
*
* @returns {Mat33} The resulting matrix.
*/
Mat33.prototype.divScalar = function( that ) {
return new Mat33([
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
]);
};
/**
* Returns true if the all components match those of a provided matrix.
* An optional epsilon value may be provided.
* @memberof Mat33
*
* @param {Mat33|Array} that - The matrix to test equality with.
* @param {number} epsilon - The epsilon value. Optional.
*
* @returns {boolean} Whether or not the matrix components match.
*/
Mat33.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 ) );
};
/**
* Returns the transpose of the matrix.
* @memberof Mat33
*
* @returns {Mat33} The transposed matrix.
*/
Mat33.prototype.transpose = function() {
return new Mat33([
this.data[0],
this.data[3],
this.data[6],
this.data[1],
this.data[4],
this.data[7],
this.data[2],
this.data[5],
this.data[8]
]);
};
/**
* Returns the inverse of the matrix.
* @memberof Mat33
*
* @returns {Mat33} The inverted matrix.
*/
Mat33.prototype.inverse = function() {
var inv = new Mat33([
// col 0
this.data[4]*this.data[8] - this.data[7]*this.data[5],
-this.data[1]*this.data[8] + this.data[7]*this.data[2],
this.data[1]*this.data[5] - this.data[4]*this.data[2],
// col 1
-this.data[3]*this.data[8] + this.data[6]*this.data[5],
this.data[0]*this.data[8] - this.data[6]*this.data[2],
-this.data[0]*this.data[5] + this.data[3]*this.data[2],
// col 2
this.data[3]*this.data[7] - this.data[6]*this.data[4],
-this.data[0]*this.data[7] + this.data[6]*this.data[1],
this.data[0]*this.data[4] - this.data[3]*this.data[1]
]);
// calculate determinant
var det = this.data[0]*inv.data[0] + this.data[1]*inv.data[3] + this.data[2]*inv.data[6];
// return
return inv.multScalar( 1 / det );
};
/**
* Returns the rotation matrix from the affine-transformation.
* @memberof Mat33
*
* @returns {Mat33} The rotation matrix.
*/
Mat33.prototype.rotation = function() {
var x = new Vec3( this.data[0], this.data[1], this.data[2] ).normalize();
var y = new Vec3( this.data[3], this.data[4], this.data[5] ).normalize();
var z = new Vec3( this.data[6], this.data[7], this.data[8] ).normalize();
return new Mat33([
x.x, x.y, x.z,
y.x, y.y, y.z,
z.x, z.y, z.z
]);
};
/**
* Returns the scale matrix from the affine-transformation.
* @memberof Mat33
*
* @returns {Mat33} The scale matrix.
*/
Mat33.prototype.scale = function() {
var x = new Vec3( this.data[0], this.data[1], this.data[2] );
var y = new Vec3( this.data[3], this.data[4], this.data[5] );
var z = new Vec3( this.data[6], this.data[7], this.data[8] );
return Mat33.scale([ x.length(), y.length(), z.length() ]);
};
/**
* Returns the inverse of the transform's rotation matrix.
* @memberof Mat33
*
* @returns {Mat33} The inverse rotation matrix.
*/
Mat33.prototype.inverseRotation = function() {
var x = new Vec3( this.data[0], this.data[1], this.data[2] ).normalize();
var y = new Vec3( this.data[3], this.data[4], this.data[5] ).normalize();
var z = new Vec3( this.data[6], this.data[7], this.data[8] ).normalize();
return new Mat33([
x.x, y.x, z.x,
x.y, y.y, z.y,
x.z, y.z, z.z
]);
};
/**
* Returns the inverse of the transform's scale matrix.
* @memberof Mat33
*
* @returns {Mat33} The inverse scale matrix.
*/
Mat33.prototype.inverseScale = function() {
var x = new Vec3( this.data[0], this.data[1], this.data[2] );
var y = new Vec3( this.data[3], this.data[4], this.data[5] );
var z = new Vec3( this.data[6], this.data[7], this.data[8] );
var scale = new Vec3( x.length(), y.length(), z.length() );
return Mat33.scale([
1 / scale.x,
1 / scale.y,
1 / scale.z
]);
};
/**
* Returns a random transform matrix composed of a rotation and scale.
* @memberof Mat33
*
* @returns {Mat33} A random transform matrix.
*/
Mat33.random = function() {
var r = Mat33.rotation( Math.random() * 360, Vec3.random() );
var s = Mat33.scale( Math.random() * 10 );
return r.multMat33( s );
};
/**
* Returns a string representation of the matrix.
* @memberof Mat33
*
* @returns {String} The string representation of the matrix.
*/
Mat33.prototype.toString = function() {
return this.data[0] +', '+ this.data[3] +', '+ this.data[6] +',\n' +
this.data[1] +', '+ this.data[4] +', '+ this.data[7] +',\n' +
this.data[2] +', '+ this.data[5] +', '+ this.data[8];
};
/**
* Returns an array representation of the matrix.
* @memberof Mat33
*
* @returns {Array} The matrix as an array.
*/
Mat33.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],
];
};
module.exports = Mat33;
}());