Browse Source

expand Matrix3 with set, identity, copy, multiplyScalar, deteminant, clone; Matrix3/4.getInverse will now optionally throw an exception; start Matrix3/4 unit tests

Ben Houston 12 years ago
parent
commit
f52aa6303d

+ 119 - 21
src/math/Matrix3.js

@@ -3,16 +3,65 @@
  * @author WestLangley / http://github.com/WestLangley
  */
 
-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 +98,75 @@ 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 ];
+
+		console.log( "optimized det: " + det );
+		console.log( "matrix.determinant(): " + matrix.determinant() );
 
 		// 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 +202,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]
+
+		);
+
 	}
 
 };

+ 25 - 3
src/math/Matrix4.js

@@ -544,7 +544,29 @@ THREE.Matrix4.prototype = {
 
 	},
 
-	getInverse: function ( m ) {
+	getInverse: function ( m, throwOnInvertible ) {
+
+		var det = m.determinant();
+
+		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;
+		}
 
 		// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
 		var te = this.elements;
@@ -571,7 +593,8 @@ 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() );
+
+		this.multiplyScalar( 1 / det );
 
 		return this;
 
@@ -1042,7 +1065,6 @@ THREE.Matrix4.prototype = {
 
 	},
 
-
 	clone: function () {
 
 		var te = this.elements;

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

@@ -0,0 +1,161 @@
+/**
+ * @author bhouston / http://exocortex.com
+ */
+
+module( "Matrix3" );
+
+var matrixEquals = function( a, b, tolerance ) {
+	tolerance = tolerance || 0;
+	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.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( ! matrixEquals( 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( matrixEquals( a, b ), "Passed!" );
+
+	// ensure that it is a true copy
+	a.elements[0] = 2;
+	ok( ! matrixEquals( 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( ! matrixEquals( a, b ), "Passed!" );
+
+	b.identity();
+	ok( matrixEquals( 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!" );
+});
+
+test( "getInverse", function() {
+	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( ! matrixEquals( a, b ), "Passed!" );
+	b.getInverse( a, false );
+	console.log( a );
+	console.log( b );
+	ok( matrixEquals( b, new THREE.Matrix3() ), "Passed!" );
+
+	try { 
+		b.getInverse( c, true );
+		ok( false, "Passed!" ); // should never get here.
+	}
+	catch( err ) {
+		ok( true, "Passed!" );
+	}
+});
+
+test( "transpose", function() {
+	var a = new THREE.Matrix3();
+	var b = a.clone().transpose();
+	ok( matrixEquals( a, b ), "Passed!" );
+
+	b = new THREE.Matrix3( 0, 1, 2, 3, 4, 5, 6, 7, 8 );
+	c = b.clone().transpose();
+	ok( ! matrixEquals( b, c ), "Passed!" ); 
+	c.transpose();
+	ok( matrixEquals( 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( matrixEquals( a, b ), "Passed!" );
+
+	// ensure that it is a true copy
+	a.elements[0] = 2;
+	ok( ! matrixEquals( a, b ), "Passed!" );
+});

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

@@ -0,0 +1,194 @@
+/**
+ * @author bhouston / http://exocortex.com
+ */
+
+module( "Matrix4" );
+
+var matrixEquals = function( a, b, tolerance ) {
+	tolerance = tolerance || 0;
+	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( ! matrixEquals( 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( matrixEquals( a, b ), "Passed!" );
+
+	// ensure that it is a true copy
+	a.elements[0] = 2;
+	ok( ! matrixEquals( 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( ! matrixEquals( a, b ), "Passed!" );
+
+	b.identity();
+	ok( matrixEquals( 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!" );
+});
+
+test( "getInverse", function() {
+	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( ! matrixEquals( a, b ), "Passed!" );
+	b.getInverse( a, false );
+	ok( matrixEquals( b, new THREE.Matrix4() ), "Passed!" );
+
+	try { 
+		b.getInverse( c, true );
+		ok( false, "Passed!" ); // should never get here.
+	}
+	catch( err ) {
+		ok( true, "Passed!" );
+	}
+});
+
+test( "transpose", function() {
+	var a = new THREE.Matrix4();
+	var b = a.clone().transpose();
+	ok( matrixEquals( 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( ! matrixEquals( b, c ), "Passed!" ); 
+	c.transpose();
+	ok( matrixEquals( 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( matrixEquals( a, b ), "Passed!" );
+
+	// ensure that it is a true copy
+	a.elements[0] = 2;
+	ok( ! matrixEquals( a, b ), "Passed!" );
+});

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

@@ -159,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>