Răsfoiți Sursa

Merge remote-tracking branch 'bhouston/math-additions' into dev

Mr.doob 12 ani în urmă
părinte
comite
3b1ec3dbea

+ 117 - 21
src/math/Matrix3.js

@@ -1,18 +1,68 @@
 /**
  * @author alteredq / http://alteredqualia.com/
  * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://exocortex.com
  */
 
-THREE.Matrix3 = function () {
+THREE.Matrix3 = function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
 
 	this.elements = new Float32Array(9);
 
+	this.set(
+
+		( n11 !== undefined ) ? n11 : 1, n12 || 0, n13 || 0,
+		n21 || 0, ( n22 !== undefined ) ? n22 : 1, n23 || 0,
+		n31 || 0, n32 || 0, ( n33 !== undefined ) ? n33 : 1
+
+	);
 };
 
 THREE.Matrix3.prototype = {
 
 	constructor: THREE.Matrix3,
 
+	set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
+
+		var te = this.elements;
+
+		te[0] = n11; te[3] = n12; te[6] = n13;
+		te[1] = n21; te[4] = n22; te[7] = n23;
+		te[2] = n31; te[5] = n32; te[8] = n33;
+
+		return this;
+
+	},
+
+	identity: function () {
+
+		this.set(
+
+			1, 0, 0,
+			0, 1, 0,
+			0, 0, 1
+
+		);
+
+		return this;
+
+	},
+
+	copy: function ( m ) {
+
+		var me = m.elements;
+
+		this.set(
+
+			me[0], me[3], me[6],
+			me[1], me[4], me[7],
+			me[2], me[5], me[8]
+
+		);
+
+		return this;
+
+	},
+
 	multiplyVector3: function ( v ) {
 
 		var te = this.elements;
@@ -49,40 +99,72 @@ THREE.Matrix3.prototype = {
 
 	},
 
-	getInverse: function ( matrix ) {
+	multiplyScalar: function ( s ) {
+
+		var te = this.elements;
+
+		te[0] *= s; te[3] *= s; te[6] *= s;
+		te[1] *= s; te[4] *= s; te[7] *= s;
+		te[2] *= s; te[5] *= s; te[8] *= s;
+
+		return this;
+
+	},
+
+	determinant: function () {
+		var te = this.elements;
+		
+		var a = te[0], b = te[1], c = te[2],
+			d = te[3], e = te[4], f = te[5],
+			g = te[6], h = te[7], i = te[8];
+
+		return a*e*i - a*f*h - b*d*i + b*f*g + c*d*h - c*e*g;
+	},
+
+	getInverse: function ( matrix, throwOnInvertible ) {
 
 		// input: THREE.Matrix4
 		// ( based on http://code.google.com/p/webgl-mjs/ )
 
 		var me = matrix.elements;
-
-		var a11 =   me[10] * me[5] - me[6] * me[9];
-		var a21 = - me[10] * me[1] + me[2] * me[9];
-		var a31 =   me[6] * me[1] - me[2] * me[5];
-		var a12 = - me[10] * me[4] + me[6] * me[8];
-		var a22 =   me[10] * me[0] - me[2] * me[8];
-		var a32 = - me[6] * me[0] + me[2] * me[4];
-		var a13 =   me[9] * me[4] - me[5] * me[8];
-		var a23 = - me[9] * me[0] + me[1] * me[8];
-		var a33 =   me[5] * me[0] - me[1] * me[4];
-
-		var det = me[0] * a11 + me[1] * a12 + me[2] * a13;
+		var te = this.elements;
+	
+		te[ 0 ] =   me[10] * me[5] - me[6] * me[9];
+		te[ 1 ] = - me[10] * me[1] + me[2] * me[9];
+		te[ 2 ] =   me[6] * me[1] - me[2] * me[5];
+		te[ 3 ] = - me[10] * me[4] + me[6] * me[8];
+		te[ 4 ] =   me[10] * me[0] - me[2] * me[8];
+		te[ 5 ] = - me[6] * me[0] + me[2] * me[4];
+		te[ 6 ] =   me[9] * me[4] - me[5] * me[8];
+		te[ 7 ] = - me[9] * me[0] + me[1] * me[8];
+		te[ 8 ] =   me[5] * me[0] - me[1] * me[4];
+
+		var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ];
 
 		// no inverse
 
 		if ( det === 0 ) {
 
-			console.warn( "Matrix3.getInverse(): determinant == 0" );
+			var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0";
 
-		}
+			if( throwOnInvertible || false ) {
 
-		var idet = 1.0 / det;
+				throw new Error( msg ); 
 
-		var m = this.elements;
+			}
+			else {
+
+				console.warn( msg );
 
-		m[ 0 ] = idet * a11; m[ 1 ] = idet * a21; m[ 2 ] = idet * a31;
-		m[ 3 ] = idet * a12; m[ 4 ] = idet * a22; m[ 5 ] = idet * a32;
-		m[ 6 ] = idet * a13; m[ 7 ] = idet * a23; m[ 8 ] = idet * a33;
+			}
+
+			this.identity();
+
+			return this;
+			
+		}
+
+		this.multiplyScalar( 1.0 / det );
 
 		return this;
 
@@ -118,6 +200,20 @@ THREE.Matrix3.prototype = {
 
 		return this;
 
+	},
+
+	clone: function () {
+
+		var te = this.elements;
+
+		return new THREE.Matrix3(
+
+			te[0], te[3], te[6],
+			te[1], te[4], te[7],
+			te[2], te[5], te[8]
+
+		);
+
 	}
 
 };

+ 59 - 32
src/math/Matrix4.js

@@ -7,6 +7,7 @@
  * @author alteredq / http://alteredqualia.com/
  * @author mikael emtinger / http://gomo.se/
  * @author timknip / http://www.floorplanner.com/
+ * @author bhouston / http://exocortex.com
  */
 
 
@@ -415,35 +416,39 @@ THREE.Matrix4.prototype = {
 		//( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
 
 		return (
-			n14 * n23 * n32 * n41-
-			n13 * n24 * n32 * n41-
-			n14 * n22 * n33 * n41+
-			n12 * n24 * n33 * n41+
-
-			n13 * n22 * n34 * n41-
-			n12 * n23 * n34 * n41-
-			n14 * n23 * n31 * n42+
-			n13 * n24 * n31 * n42+
-
-			n14 * n21 * n33 * n42-
-			n11 * n24 * n33 * n42-
-			n13 * n21 * n34 * n42+
-			n11 * n23 * n34 * n42+
-
-			n14 * n22 * n31 * n43-
-			n12 * n24 * n31 * n43-
-			n14 * n21 * n32 * n43+
-			n11 * n24 * n32 * n43+
-
-			n12 * n21 * n34 * n43-
-			n11 * n22 * n34 * n43-
-			n13 * n22 * n31 * n44+
-			n12 * n23 * n31 * n44+
-
-			n13 * n21 * n32 * n44-
-			n11 * n23 * n32 * n44-
-			n12 * n21 * n33 * n44+
-			n11 * n22 * n33 * n44
+			n41 * (
+		      +n14 * n23 * n32
+		      -n13 * n24 * n32
+		      -n14 * n22 * n33
+		      +n12 * n24 * n33
+		      +n13 * n22 * n34
+		      -n12 * n23 * n34
+		      ) +
+			n42 * (
+		      +n11 * n23 * n34
+		      -n11 * n24 * n33
+		      +n14 * n21 * n33
+		      -n13 * n21 * n34
+		      +n13 * n24 * n31
+		      -n14 * n23 * n31
+		      ) +
+			n43 * (
+		      +n11 * n24 * n32
+		      -n11 * n22 * n34
+		      -n14 * n21 * n32
+		      +n12 * n21 * n34
+		      +n14 * n22 * n31
+		      -n12 * n24 * n31
+		      ) +
+		    n44 * (
+		      -n13 * n22 * n31
+		      -n11 * n23 * n32
+		      +n11 * n22 * n33
+		      +n13 * n21 * n32
+		      -n12 * n21 * n33
+		      +n12 * n23 * n31
+		      )
+
 		);
 
 	},
@@ -544,7 +549,7 @@ THREE.Matrix4.prototype = {
 
 	},
 
-	getInverse: function ( m ) {
+	getInverse: function ( m, throwOnInvertible ) {
 
 		// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
 		var te = this.elements;
@@ -571,7 +576,30 @@ THREE.Matrix4.prototype = {
 		te[7] = n12*n33*n41 - n13*n32*n41 + n13*n31*n42 - n11*n33*n42 - n12*n31*n43 + n11*n32*n43;
 		te[11] = n13*n22*n41 - n12*n23*n41 - n13*n21*n42 + n11*n23*n42 + n12*n21*n43 - n11*n22*n43;
 		te[15] = n12*n23*n31 - n13*n22*n31 + n13*n21*n32 - n11*n23*n32 - n12*n21*n33 + n11*n22*n33;
-		this.multiplyScalar( 1 / m.determinant() );
+
+		var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 4 ] + me[ 2 ] * te[ 8 ] + me[ 3 ] * te[ 12 ];
+
+		if( det == 0 ) {
+
+			var msg = "Matrix4.getInverse(): can't invert matrix, determinant is 0";
+
+			if( throwOnInvertible || false ) {
+
+				throw new Error( msg ); 
+
+			}
+			else {
+
+				console.warn( msg );
+
+			}
+
+			this.identity();
+
+			return this;
+		}
+
+		this.multiplyScalar( 1 / det );
 
 		return this;
 
@@ -1042,7 +1070,6 @@ THREE.Matrix4.prototype = {
 
 	},
 
-
 	clone: function () {
 
 		var te = this.elements;

+ 34 - 0
src/math/Plane.js

@@ -112,6 +112,40 @@ THREE.Plane.prototype = {
 
 	},
 
+	intersectLine: function ( startPoint, endPoint, optionalTarget ) {
+
+		var result = optionalTarget || new THREE.Vector3();
+
+		var direction = THREE.Plane.__v1.sub( endPoint, startPoint );
+
+		var denominator = this.normal.dot( direction );
+
+		if ( denominator == 0 ) {
+
+			// line is coplanar, return origin
+			if( this.distanceToPoint( startPoint ) == 0 ) {
+
+				return result.copy( startPoint );
+
+			}
+
+			// Unsure if this is the correct method to handle this case.
+			return undefined;
+
+		}
+
+		var t = - ( startPoint.dot( this.normal ) + this.constant ) / denominator;
+
+		if( t < 0 || t > 1 ) {
+
+			return undefined;
+
+		}
+
+		return result.copy( direction ).multiplyScalar( t ).addSelf( startPoint );
+
+	},
+
 	coplanarPoint: function ( optionalTarget ) {
 
 		var result = optionalTarget || new THREE.Vector3();

+ 209 - 0
test/unit/math/Matrix3.js

@@ -0,0 +1,209 @@
+/**
+ * @author bhouston / http://exocortex.com
+ */
+
+module( "Matrix3" );
+
+var matrixEquals3 = function( a, b, tolerance ) {
+	tolerance = tolerance || 0.0001;
+	if( a.elements.length != b.elements.length ) {
+		return false;
+	}	
+	for( var i = 0, il = a.elements.length; i < il; i ++ ) {
+		var delta = a.elements[i] - b.elements[i];
+		if( delta > tolerance ) {
+			return false;
+		}
+	}
+	return true;
+};
+
+
+var toMatrix4 = function( m3 ) {
+	var result = new THREE.Matrix4();
+	var re = result.elements;
+	var me = m3.elements;
+	re[0] = me[0];
+	re[1] = me[1];
+	re[2] = me[2];
+	re[4] = me[3];
+	re[5] = me[4];
+	re[6] = me[5];
+	re[8] = me[6];
+	re[9] = me[7];
+	re[10] = me[8];
+
+	return result;
+};
+
+test( "constructor", function() {
+	var a = new THREE.Matrix3();
+	ok( a.determinant() == 1, "Passed!" );
+
+	var b = new THREE.Matrix3( 0, 1, 2, 3, 4, 5, 6, 7, 8 );
+	ok( b.elements[0] == 0 );
+	ok( b.elements[1] == 3 );
+	ok( b.elements[2] == 6 );
+	ok( b.elements[3] == 1 );
+	ok( b.elements[4] == 4 );
+	ok( b.elements[5] == 7 );
+	ok( b.elements[6] == 2 );
+	ok( b.elements[7] == 5 );
+	ok( b.elements[8] == 8 );
+
+	ok( ! matrixEquals3( a, b ), "Passed!" );
+});
+
+test( "copy", function() {
+	var a = new THREE.Matrix3( 0, 1, 2, 3, 4, 5, 6, 7, 8 );
+	var b = new THREE.Matrix3().copy( a );
+
+	ok( matrixEquals3( a, b ), "Passed!" );
+
+	// ensure that it is a true copy
+	a.elements[0] = 2;
+	ok( ! matrixEquals3( a, b ), "Passed!" );
+});
+
+test( "set", function() {
+	var b = new THREE.Matrix3();
+	ok( b.determinant() == 1, "Passed!" );
+
+	b.set( 0, 1, 2, 3, 4, 5, 6, 7, 8 );
+	ok( b.elements[0] == 0 );
+	ok( b.elements[1] == 3 );
+	ok( b.elements[2] == 6 );
+	ok( b.elements[3] == 1 );
+	ok( b.elements[4] == 4 );
+	ok( b.elements[5] == 7 );
+	ok( b.elements[6] == 2 );
+	ok( b.elements[7] == 5 );
+	ok( b.elements[8] == 8 );
+});
+
+test( "identity", function() {
+	var b = new THREE.Matrix3( 0, 1, 2, 3, 4, 5, 6, 7, 8 );
+	ok( b.elements[0] == 0 );
+	ok( b.elements[1] == 3 );
+	ok( b.elements[2] == 6 );
+	ok( b.elements[3] == 1 );
+	ok( b.elements[4] == 4 );
+	ok( b.elements[5] == 7 );
+	ok( b.elements[6] == 2 );
+	ok( b.elements[7] == 5 );
+	ok( b.elements[8] == 8 );
+
+	var a = new THREE.Matrix3();
+	ok( ! matrixEquals3( a, b ), "Passed!" );
+
+	b.identity();
+	ok( matrixEquals3( a, b ), "Passed!" );
+});
+
+test( "multiplyScalar", function() {
+	var b = new THREE.Matrix3( 0, 1, 2, 3, 4, 5, 6, 7, 8 );
+	ok( b.elements[0] == 0 );
+	ok( b.elements[1] == 3 );
+	ok( b.elements[2] == 6 );
+	ok( b.elements[3] == 1 );
+	ok( b.elements[4] == 4 );
+	ok( b.elements[5] == 7 );
+	ok( b.elements[6] == 2 );
+	ok( b.elements[7] == 5 );
+	ok( b.elements[8] == 8 );
+
+	b.multiplyScalar( 2 );
+	ok( b.elements[0] == 0*2 );
+	ok( b.elements[1] == 3*2 );
+	ok( b.elements[2] == 6*2 );
+	ok( b.elements[3] == 1*2 );
+	ok( b.elements[4] == 4*2 );
+	ok( b.elements[5] == 7*2 );
+	ok( b.elements[6] == 2*2 );
+	ok( b.elements[7] == 5*2 );
+	ok( b.elements[8] == 8*2 );
+});
+
+test( "determinant", function() {
+	var a = new THREE.Matrix3();
+	ok( a.determinant() == 1, "Passed!" );
+
+	a.elements[0] = 2;
+	ok( a.determinant() == 2, "Passed!" );
+
+	a.elements[0] = 0;
+	ok( a.determinant() == 0, "Passed!" );
+
+	// calculated via http://www.euclideanspace.com/maths/algebra/matrix/functions/determinant/threeD/index.htm
+	a.set( 2, 3, 4, 5, 13, 7, 8, 9, 11 );
+	ok( a.determinant() == -73, "Passed!" );
+});
+
+
+test( "getInverse", function() {
+	var identity = new THREE.Matrix4();
+	var a = new THREE.Matrix4();
+	var b = new THREE.Matrix3( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+	var c = new THREE.Matrix4( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+	
+	ok( ! matrixEquals3( a, b ), "Passed!" );
+	b.getInverse( a, false );
+	ok( matrixEquals3( b, new THREE.Matrix3() ), "Passed!" );
+
+	try { 
+		b.getInverse( c, true );
+		ok( false, "Passed!" ); // should never get here.
+	}
+	catch( err ) {
+		ok( true, "Passed!" );
+	}
+
+	var testMatrices = [
+		new THREE.Matrix4().makeRotationX( 0.3 ),
+		new THREE.Matrix4().makeRotationX( -0.3 ),
+		new THREE.Matrix4().makeRotationY( 0.3 ),
+		new THREE.Matrix4().makeRotationY( -0.3 ),
+		new THREE.Matrix4().makeRotationZ( 0.3 ),
+		new THREE.Matrix4().makeRotationZ( -0.3 ),
+		new THREE.Matrix4().makeScale( new THREE.Vector3( 1, 2, 3 ) ),
+		new THREE.Matrix4().makeScale( new THREE.Vector3( 1/8, 1/2, 1/3 ) )
+		];
+
+	for( var i = 0, il = testMatrices.length; i < il; i ++ ) {
+		var m = testMatrices[i];
+		var mInverse3 = new THREE.Matrix3().getInverse( m );
+	
+		var mInverse = toMatrix4( mInverse3 );
+		
+		// the determinant of the inverse should be the reciprocal
+		ok( Math.abs( m.determinant() * mInverse3.determinant() - 1 ) < 0.0001, "Passed!" );
+		ok( Math.abs( m.determinant() * mInverse.determinant() - 1 ) < 0.0001, "Passed!" );
+
+		var mProduct = new THREE.Matrix4().multiply( m, mInverse );
+		ok( Math.abs( mProduct.determinant() - 1 ) < 0.0001, "Passed!" );
+		ok( matrixEquals3( mProduct, identity ), "Passed!" );
+	}
+});
+
+test( "transpose", function() {
+	var a = new THREE.Matrix3();
+	var b = a.clone().transpose();
+	ok( matrixEquals3( a, b ), "Passed!" );
+
+	b = new THREE.Matrix3( 0, 1, 2, 3, 4, 5, 6, 7, 8 );
+	c = b.clone().transpose();
+	ok( ! matrixEquals3( b, c ), "Passed!" ); 
+	c.transpose();
+	ok( matrixEquals3( b, c ), "Passed!" ); 
+});
+
+test( "clone", function() {
+	var a = new THREE.Matrix3( 0, 1, 2, 3, 4, 5, 6, 7, 8 );
+	var b = a.clone();
+
+	ok( matrixEquals3( a, b ), "Passed!" );
+
+	// ensure that it is a true copy
+	a.elements[0] = 2;
+	ok( ! matrixEquals3( a, b ), "Passed!" );
+});

+ 229 - 0
test/unit/math/Matrix4.js

@@ -0,0 +1,229 @@
+/**
+ * @author bhouston / http://exocortex.com
+ */
+
+module( "Matrix4" );
+
+var matrixEquals4 = function( a, b, tolerance ) {
+	tolerance = tolerance || 0.0001;
+	if( a.elements.length != b.elements.length ) {
+		return false;
+	}	
+	for( var i = 0, il = a.elements.length; i < il; i ++ ) {
+		var delta = a.elements[i] - b.elements[i];
+		if( delta > tolerance ) {
+			return false;
+		}
+	}
+	return true;
+};
+
+test( "constructor", function() {
+	var a = new THREE.Matrix4();
+	ok( a.determinant() == 1, "Passed!" );
+
+	var b = new THREE.Matrix4( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+	ok( b.elements[0] == 0 );
+	ok( b.elements[1] == 4 );
+	ok( b.elements[2] == 8 );
+	ok( b.elements[3] == 12 );
+	ok( b.elements[4] == 1 );
+	ok( b.elements[5] == 5 );
+	ok( b.elements[6] == 9 );
+	ok( b.elements[7] == 13 );
+	ok( b.elements[8] == 2 );
+	ok( b.elements[9] == 6 );
+	ok( b.elements[10] == 10 );
+	ok( b.elements[11] == 14 );
+	ok( b.elements[12] == 3 );
+	ok( b.elements[13] == 7 );
+	ok( b.elements[14] == 11 );
+	ok( b.elements[15] == 15 );
+
+	ok( ! matrixEquals4( a, b ), "Passed!" );
+});
+
+test( "copy", function() {
+	var a = new THREE.Matrix4( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+	var b = new THREE.Matrix4().copy( a );
+
+	ok( matrixEquals4( a, b ), "Passed!" );
+
+	// ensure that it is a true copy
+	a.elements[0] = 2;
+	ok( ! matrixEquals4( a, b ), "Passed!" );
+});
+
+test( "set", function() {
+	var b = new THREE.Matrix4();
+	ok( b.determinant() == 1, "Passed!" );
+
+	b.set( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+	ok( b.elements[0] == 0 );
+	ok( b.elements[1] == 4 );
+	ok( b.elements[2] == 8 );
+	ok( b.elements[3] == 12 );
+	ok( b.elements[4] == 1 );
+	ok( b.elements[5] == 5 );
+	ok( b.elements[6] == 9 );
+	ok( b.elements[7] == 13 );
+	ok( b.elements[8] == 2 );
+	ok( b.elements[9] == 6 );
+	ok( b.elements[10] == 10 );
+	ok( b.elements[11] == 14 );
+	ok( b.elements[12] == 3 );
+	ok( b.elements[13] == 7 );
+	ok( b.elements[14] == 11 );
+	ok( b.elements[15] == 15 );
+});
+
+test( "identity", function() {
+	var b = new THREE.Matrix4( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+	ok( b.elements[0] == 0 );
+	ok( b.elements[1] == 4 );
+	ok( b.elements[2] == 8 );
+	ok( b.elements[3] == 12 );
+	ok( b.elements[4] == 1 );
+	ok( b.elements[5] == 5 );
+	ok( b.elements[6] == 9 );
+	ok( b.elements[7] == 13 );
+	ok( b.elements[8] == 2 );
+	ok( b.elements[9] == 6 );
+	ok( b.elements[10] == 10 );
+	ok( b.elements[11] == 14 );
+	ok( b.elements[12] == 3 );
+	ok( b.elements[13] == 7 );
+	ok( b.elements[14] == 11 );
+	ok( b.elements[15] == 15 );
+
+	var a = new THREE.Matrix4();
+	ok( ! matrixEquals4( a, b ), "Passed!" );
+
+	b.identity();
+	ok( matrixEquals4( a, b ), "Passed!" );
+});
+
+test( "multiplyScalar", function() {
+	var b = new THREE.Matrix4( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+	ok( b.elements[0] == 0 );
+	ok( b.elements[1] == 4 );
+	ok( b.elements[2] == 8 );
+	ok( b.elements[3] == 12 );
+	ok( b.elements[4] == 1 );
+	ok( b.elements[5] == 5 );
+	ok( b.elements[6] == 9 );
+	ok( b.elements[7] == 13 );
+	ok( b.elements[8] == 2 );
+	ok( b.elements[9] == 6 );
+	ok( b.elements[10] == 10 );
+	ok( b.elements[11] == 14 );
+	ok( b.elements[12] == 3 );
+	ok( b.elements[13] == 7 );
+	ok( b.elements[14] == 11 );
+	ok( b.elements[15] == 15 );
+
+	b.multiplyScalar( 2 );
+	ok( b.elements[0] == 0*2 );
+	ok( b.elements[1] == 4*2 );
+	ok( b.elements[2] == 8*2 );
+	ok( b.elements[3] == 12*2 );
+	ok( b.elements[4] == 1*2 );
+	ok( b.elements[5] == 5*2 );
+	ok( b.elements[6] == 9*2 );
+	ok( b.elements[7] == 13*2 );
+	ok( b.elements[8] == 2*2 );
+	ok( b.elements[9] == 6*2 );
+	ok( b.elements[10] == 10*2 );
+	ok( b.elements[11] == 14*2 );
+	ok( b.elements[12] == 3*2 );
+	ok( b.elements[13] == 7*2 );
+	ok( b.elements[14] == 11*2 );
+	ok( b.elements[15] == 15*2 );
+});
+
+test( "determinant", function() {
+	var a = new THREE.Matrix4();
+	ok( a.determinant() == 1, "Passed!" );
+
+	a.elements[0] = 2;
+	ok( a.determinant() == 2, "Passed!" );
+
+	a.elements[0] = 0;
+	ok( a.determinant() == 0, "Passed!" );
+
+	// calculated via http://www.euclideanspace.com/maths/algebra/matrix/functions/determinant/fourD/index.htm
+	a.set( 2, 3, 4, 5, -1, -21, -3, -4, 6, 7, 8, 10, -8, -9, -10, -12 );
+	ok( a.determinant() == 76, "Passed!" );
+});
+
+test( "getInverse", function() {
+	var identity = new THREE.Matrix4();
+
+	var a = new THREE.Matrix4();
+	var b = new THREE.Matrix4( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+	var c = new THREE.Matrix4( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+	
+	ok( ! matrixEquals4( a, b ), "Passed!" );
+	b.getInverse( a, false );
+	ok( matrixEquals4( b, new THREE.Matrix4() ), "Passed!" );
+
+	try { 
+		b.getInverse( c, true );
+		ok( false, "Passed!" ); // should never get here.
+	}
+	catch( err ) {
+		ok( true, "Passed!" );
+	}
+
+	var testMatrices = [
+		new THREE.Matrix4().makeRotationX( 0.3 ),
+		new THREE.Matrix4().makeRotationX( -0.3 ),
+		new THREE.Matrix4().makeRotationY( 0.3 ),
+		new THREE.Matrix4().makeRotationY( -0.3 ),
+		new THREE.Matrix4().makeRotationZ( 0.3 ),
+		new THREE.Matrix4().makeRotationZ( -0.3 ),
+		new THREE.Matrix4().makeScale( new THREE.Vector3( 1, 2, 3 ) ),
+		new THREE.Matrix4().makeScale( new THREE.Vector3( 1/8, 1/2, 1/3 ) ),
+		new THREE.Matrix4().makeFrustum( -1, 1, -1, 1, 1, 1000 ),
+		new THREE.Matrix4().makeFrustum( -16, 16, -9, 9, 0.1, 10000 ),
+		new THREE.Matrix4().makeTranslation( new THREE.Vector3( 1, 2, 3 ) )
+		];
+
+	for( var i = 0, il = testMatrices.length; i < il; i ++ ) {
+		var m = testMatrices[i];
+
+		var mInverse = new THREE.Matrix4().getInverse( m );
+
+		// the determinant of the inverse should be the reciprocal
+		ok( Math.abs( m.determinant() * mInverse.determinant() - 1  ) < 0.0001, "Passed!" );
+
+		var mProduct = new THREE.Matrix4().multiply( m, mInverse );
+
+		// the determinant of the identity matrix is 1
+		ok( Math.abs( mProduct.determinant() - 1 ) < 0.0001, "Passed!" );
+		ok( matrixEquals4( mProduct, identity ), "Passed!" );
+	}
+});
+
+test( "transpose", function() {
+	var a = new THREE.Matrix4();
+	var b = a.clone().transpose();
+	ok( matrixEquals4( a, b ), "Passed!" );
+
+	b = new THREE.Matrix4( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+	c = b.clone().transpose();
+	ok( ! matrixEquals4( b, c ), "Passed!" ); 
+	c.transpose();
+	ok( matrixEquals4( b, c ), "Passed!" ); 
+});
+
+test( "clone", function() {
+	var a = new THREE.Matrix4( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
+	var b = a.clone();
+
+	ok( matrixEquals4( a, b ), "Passed!" );
+
+	// ensure that it is a true copy
+	a.elements[0] = 2;
+	ok( ! matrixEquals4( a, b ), "Passed!" );
+});

+ 25 - 5
test/unit/math/Plane.js

@@ -115,6 +115,31 @@ test( "distanceToSphere", function() {
 	ok( a.distanceToSphere( b ) === -1, "Passed!" );
 });
 
+test( "isInterestionLine/intersectLine", function() {
+	var a = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), 0 );
+
+	ok( a.isIntersectionLine( new THREE.Vector3( -10, 0, 0 ), new THREE.Vector3( 10, 0, 0 ) ), "Passed!" );
+	ok( a.intersectLine( new THREE.Vector3( -10, 0, 0 ), new THREE.Vector3( 10, 0, 0 ) ).equals( new THREE.Vector3( 0, 0, 0 ) ), "Passed!" );
+
+	a = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), -3 );
+
+	ok( a.isIntersectionLine( new THREE.Vector3( -10, 0, 0 ), new THREE.Vector3( 10, 0, 0 ) ), "Passed!" );
+	ok( a.intersectLine( new THREE.Vector3( -10, 0, 0 ), new THREE.Vector3( 10, 0, 0 ) ).equals( new THREE.Vector3( 3, 0, 0 ) ), "Passed!" );
+
+
+	a = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), -11 );
+
+	ok( ! a.isIntersectionLine( new THREE.Vector3( -10, 0, 0 ), new THREE.Vector3( 10, 0, 0 ) ), "Passed!" );
+	ok( a.intersectLine( new THREE.Vector3( -10, 0, 0 ), new THREE.Vector3( 10, 0, 0 ) ) === undefined, "Passed!" );
+	
+	a = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), 11 );
+
+	ok( ! a.isIntersectionLine( new THREE.Vector3( -10, 0, 0 ), new THREE.Vector3( 10, 0, 0 ) ), "Passed!" );
+	ok( a.intersectLine( new THREE.Vector3( -10, 0, 0 ), new THREE.Vector3( 10, 0, 0 ) ) === undefined, "Passed!" );
+
+});
+
+
 test( "projectPoint", function() {
 	var a = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), 0 );
 
@@ -134,11 +159,6 @@ test( "orthoPoint", function() {
 	ok( a.orthoPoint( new THREE.Vector3( -10, 0, 0 ) ).equals( new THREE.Vector3( -10, 0, 0 ) ), "Passed!" );
 });
 
-/*
-test( "isIntersectionLine", function() {
-});
-*/
-
 test( "coplanarPoint", function() {
 	var a = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), 0 );
 	ok( a.distanceToPoint( a.coplanarPoint() ) === 0, "Passed!" );

+ 3 - 0
test/unit/unittests_sources.html

@@ -12,6 +12,7 @@
   <!-- add ThreeJS sources to test below -->
 
   <script src="../../src/Three.js"></script>
+  <script src="../../src/math/Math.js"></script>
   <script src="../../src/math/Vector2.js"></script>
   <script src="../../src/math/Vector3.js"></script>
   <script src="../../src/math/Vector4.js"></script>
@@ -39,6 +40,8 @@
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
   <script src="math/Quaternion.js"></script>
+  <script src="math/Matrix3.js"></script>
+  <script src="math/Matrix4.js"></script>
   
 </body>
 </html>

+ 3 - 1
test/unit/unittests_three-math.html

@@ -26,6 +26,8 @@
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
   <script src="math/Quaternion.js"></script>
-
+  <script src="math/Matrix3.js"></script>
+  <script src="math/Matrix4.js"></script>
+  
 </body>
 </html>

+ 3 - 1
test/unit/unittests_three.html

@@ -26,6 +26,8 @@
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
   <script src="math/Quaternion.js"></script>
-  
+  <script src="math/Matrix3.js"></script>
+  <script src="math/Matrix4.js"></script>
+
 </body>
 </html>

+ 2 - 0
test/unit/unittests_three.min.html

@@ -26,6 +26,8 @@
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
   <script src="math/Quaternion.js"></script>
+  <script src="math/Matrix3.js"></script>
+  <script src="math/Matrix4.js"></script>
 
 </body>
 </html>