Jelajahi Sumber

Merge branch 'quaternion_polish' into three_math_degreesToRadians

Conflicts:
	test/sources.html
Ben Houston 12 tahun lalu
induk
melakukan
56a1e4fca1

+ 137 - 138
src/math/Matrix4.js

@@ -74,6 +74,143 @@ THREE.Matrix4.prototype = {
 
 	},
 
+	setRotationFromEuler: function ( v, order ) {
+
+		var te = this.elements;
+
+		var x = v.x, y = v.y, z = v.z;
+		var a = Math.cos( x ), b = Math.sin( x );
+		var c = Math.cos( y ), d = Math.sin( y );
+		var e = Math.cos( z ), f = Math.sin( z );
+
+		if ( order === undefined || order === 'XYZ' ) {
+
+			var ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+			te[0] = c * e;
+			te[4] = - c * f;
+			te[8] = d;
+
+			te[1] = af + be * d;
+			te[5] = ae - bf * d;
+			te[9] = - b * c;
+
+			te[2] = bf - ae * d;
+			te[6] = be + af * d;
+			te[10] = a * c;
+
+		} else if ( order === 'YXZ' ) {
+
+			var ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+			te[0] = ce + df * b;
+			te[4] = de * b - cf;
+			te[8] = a * d;
+
+			te[1] = a * f;
+			te[5] = a * e;
+			te[9] = - b;
+
+			te[2] = cf * b - de;
+			te[6] = df + ce * b;
+			te[10] = a * c;
+
+		} else if ( order === 'ZXY' ) {
+
+			var ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+			te[0] = ce - df * b;
+			te[4] = - a * f;
+			te[8] = de + cf * b;
+
+			te[1] = cf + de * b;
+			te[5] = a * e;
+			te[9] = df - ce * b;
+
+			te[2] = - a * d;
+			te[6] = b;
+			te[10] = a * c;
+
+		} else if ( order === 'ZYX' ) {
+
+			var ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+			te[0] = c * e;
+			te[4] = be * d - af;
+			te[8] = ae * d + bf;
+
+			te[1] = c * f;
+			te[5] = bf * d + ae;
+			te[9] = af * d - be;
+
+			te[2] = - d;
+			te[6] = b * c;
+			te[10] = a * c;
+
+		} else if ( order === 'YZX' ) {
+
+			var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+			te[0] = c * e;
+			te[4] = bd - ac * f;
+			te[8] = bc * f + ad;
+
+			te[1] = f;
+			te[5] = a * e;
+			te[9] = - b * e;
+
+			te[2] = - d * e;
+			te[6] = ad * f + bc;
+			te[10] = ac - bd * f;
+
+		} else if ( order === 'XZY' ) {
+
+			var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+			te[0] = c * e;
+			te[4] = - f;
+			te[8] = d * e;
+
+			te[1] = ac * f + bd;
+			te[5] = a * e;
+			te[9] = ad * f - bc;
+
+			te[2] = bc * f - ad;
+			te[6] = b * e;
+			te[10] = bd * f + ac;
+
+		}
+
+		return this;
+
+	},
+
+	setRotationFromQuaternion: function ( q ) {
+
+		var te = this.elements;
+
+		var x = q.x, y = q.y, z = q.z, w = q.w;
+		var x2 = x + x, y2 = y + y, z2 = z + z;
+		var xx = x * x2, xy = x * y2, xz = x * z2;
+		var yy = y * y2, yz = y * z2, zz = z * z2;
+		var wx = w * x2, wy = w * y2, wz = w * z2;
+
+		te[0] = 1 - ( yy + zz );
+		te[4] = xy - wz;
+		te[8] = xz + wy;
+
+		te[1] = xy + wz;
+		te[5] = 1 - ( xx + zz );
+		te[9] = yz - wx;
+
+		te[2] = xz - wy;
+		te[6] = yz + wx;
+		te[10] = 1 - ( xx + yy );
+
+		return this;
+
+	},
+
 	lookAt: function ( eye, target, up ) {
 
 		var te = this.elements;
@@ -440,144 +577,6 @@ THREE.Matrix4.prototype = {
 
 	},
 
-	setRotationFromEuler: function ( v, order ) {
-
-		var te = this.elements;
-
-		var x = v.x, y = v.y, z = v.z;
-		var a = Math.cos( x ), b = Math.sin( x );
-		var c = Math.cos( y ), d = Math.sin( y );
-		var e = Math.cos( z ), f = Math.sin( z );
-
-		if ( order === undefined || order === 'XYZ' ) {
-
-			var ae = a * e, af = a * f, be = b * e, bf = b * f;
-
-			te[0] = c * e;
-			te[4] = - c * f;
-			te[8] = d;
-
-			te[1] = af + be * d;
-			te[5] = ae - bf * d;
-			te[9] = - b * c;
-
-			te[2] = bf - ae * d;
-			te[6] = be + af * d;
-			te[10] = a * c;
-
-		} else if ( order === 'YXZ' ) {
-
-			var ce = c * e, cf = c * f, de = d * e, df = d * f;
-
-			te[0] = ce + df * b;
-			te[4] = de * b - cf;
-			te[8] = a * d;
-
-			te[1] = a * f;
-			te[5] = a * e;
-			te[9] = - b;
-
-			te[2] = cf * b - de;
-			te[6] = df + ce * b;
-			te[10] = a * c;
-
-		} else if ( order === 'ZXY' ) {
-
-			var ce = c * e, cf = c * f, de = d * e, df = d * f;
-
-			te[0] = ce - df * b;
-			te[4] = - a * f;
-			te[8] = de + cf * b;
-
-			te[1] = cf + de * b;
-			te[5] = a * e;
-			te[9] = df - ce * b;
-
-			te[2] = - a * d;
-			te[6] = b;
-			te[10] = a * c;
-
-		} else if ( order === 'ZYX' ) {
-
-			var ae = a * e, af = a * f, be = b * e, bf = b * f;
-
-			te[0] = c * e;
-			te[4] = be * d - af;
-			te[8] = ae * d + bf;
-
-			te[1] = c * f;
-			te[5] = bf * d + ae;
-			te[9] = af * d - be;
-
-			te[2] = - d;
-			te[6] = b * c;
-			te[10] = a * c;
-
-		} else if ( order === 'YZX' ) {
-
-			var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
-
-			te[0] = c * e;
-			te[4] = bd - ac * f;
-			te[8] = bc * f + ad;
-
-			te[1] = f;
-			te[5] = a * e;
-			te[9] = - b * e;
-
-			te[2] = - d * e;
-			te[6] = ad * f + bc;
-			te[10] = ac - bd * f;
-
-		} else if ( order === 'XZY' ) {
-
-			var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
-
-			te[0] = c * e;
-			te[4] = - f;
-			te[8] = d * e;
-
-			te[1] = ac * f + bd;
-			te[5] = a * e;
-			te[9] = ad * f - bc;
-
-			te[2] = bc * f - ad;
-			te[6] = b * e;
-			te[10] = bd * f + ac;
-
-		}
-
-		return this;
-
-	},
-
-
-	setRotationFromQuaternion: function ( q ) {
-
-		var te = this.elements;
-
-		var x = q.x, y = q.y, z = q.z, w = q.w;
-		var x2 = x + x, y2 = y + y, z2 = z + z;
-		var xx = x * x2, xy = x * y2, xz = x * z2;
-		var yy = y * y2, yz = y * z2, zz = z * z2;
-		var wx = w * x2, wy = w * y2, wz = w * z2;
-
-		te[0] = 1 - ( yy + zz );
-		te[4] = xy - wz;
-		te[8] = xz + wy;
-
-		te[1] = xy + wz;
-		te[5] = 1 - ( xx + zz );
-		te[9] = yz - wx;
-
-		te[2] = xz - wy;
-		te[6] = yz + wx;
-		te[10] = 1 - ( xx + yy );
-
-		return this;
-
-	},
-
 	compose: function ( translation, rotation, scale ) {
 
 		var te = this.elements;

+ 111 - 65
src/math/Quaternion.js

@@ -176,6 +176,50 @@ THREE.Quaternion.prototype = {
 
 	},
 
+	add: function ( a, b ) {
+
+		this.x = a.x + b.x;
+		this.y = a.y + b.y;
+		this.z = a.z + b.z;
+		this.w = a.w + b.w;
+
+		return this;
+
+	},
+
+	addSelf: function ( v ) {
+
+		this.x += v.x;
+		this.y += v.y;
+		this.z += v.z;
+		this.w += v.w;
+
+		return this;
+
+	},
+
+	sub: function ( a, b ) {
+
+		this.x = a.x - b.x;
+		this.y = a.y - b.y;
+		this.z = a.z - b.z;
+		this.w = a.w - b.w;
+
+		return this;
+
+	},
+
+	subSelf: function ( v ) {
+
+		this.x -= v.x;
+		this.y -= v.y;
+		this.z -= v.z;
+		this.w -= v.w;
+
+		return this;
+
+	},
+
 	inverse: function () {
 
 		this.conjugate().normalize();
@@ -194,15 +238,21 @@ THREE.Quaternion.prototype = {
 
 	},
 
+	lengthSq: function () {
+
+		return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
+
+	},
+
 	length: function () {
 
-		return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
+		return Math.sqrt( this.lengthSq() );
 
 	},
 
 	normalize: function () {
 
-		var l = Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
+		var l = this.length();
 
 		if ( l === 0 ) {
 
@@ -228,21 +278,14 @@ THREE.Quaternion.prototype = {
 
 	multiply: function ( a, b ) {
 
-		// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
-		var qax = a.x, qay = a.y, qaz = a.z, qaw = a.w,
-		qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w;
-
-		this.x =  qax * qbw + qay * qbz - qaz * qby + qaw * qbx;
-		this.y = -qax * qbz + qay * qbw + qaz * qbx + qaw * qby;
-		this.z =  qax * qby - qay * qbx + qaz * qbw + qaw * qbz;
-		this.w = -qax * qbx - qay * qby - qaz * qbz + qaw * qbw;
-
-		return this;
+		this.copy( a );
+		return this.multiplySelf( b );
 
 	},
 
 	multiplySelf: function ( b ) {
 
+		// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
 		var qax = this.x, qay = this.y, qaz = this.z, qaw = this.w,
 		qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w;
 
@@ -279,6 +322,55 @@ THREE.Quaternion.prototype = {
 
 	},
 
+	toEuler: function ( order, optionalTarget ) {
+
+	    var result = optionalTarget || new THREE.Vector3();
+
+	    var qx = this.x,
+	    	qy = this.y,
+	    	qz = this.z,
+	    	qw = this.w;
+	    var sqx = qx*qx,
+	        sqy = qy*qy,
+	        sqz = qz*qz,
+	        sqw = qw*qw;
+
+		if ( order === undefined || order === 'XYZ' ) {
+
+	        var test = qw*qy - qx*qz;
+
+		    if (test > 0.4999) {
+
+		        result.x = 0;
+		        result.y = 90;
+		        result.z = -2 * Math.atan2(qx, qw);
+
+		    } else if (test < -0.4999) {
+
+		        result.x = 0;
+		        result.y = -90;
+		        result.z = 2 * Math.atan2(qx, qw);
+
+		    } else {
+
+		        result.x = Math.atan2(2 * (qw*qx + qy*qz), sqw - sqx - sqy + sqz);
+		        result.y = Math.asin(2 * (qw*qy - qx*qz));
+		        result.z = Math.atan2(2 * (qx*qy + qw*qz), sqw + sqx - sqy - sqz);
+
+		    }
+
+		}
+		else {
+
+			// TODO: support more Euler orders.			
+			throw new Error( "Euler order not supported: " + order );
+
+		}
+
+	    return result;
+
+	},
+
 	slerpSelf: function ( qb, t ) {
 
 		var x = this.x, y = this.y, z = this.z, w = this.w;
@@ -339,6 +431,12 @@ THREE.Quaternion.prototype = {
 
 	},
 
+	equals: function ( v ) {
+
+		return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
+
+	},
+
 	clone: function () {
 
 		return new THREE.Quaternion( this.x, this.y, this.z, this.w );
@@ -349,58 +447,6 @@ THREE.Quaternion.prototype = {
 
 THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
 
-	// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
-
-	var cosHalfTheta = qa.w * qb.w + qa.x * qb.x + qa.y * qb.y + qa.z * qb.z;
-
-	if ( cosHalfTheta < 0 ) {
-
-		qm.w = -qb.w;
-		qm.x = -qb.x;
-		qm.y = -qb.y;
-		qm.z = -qb.z;
-
-		cosHalfTheta = -cosHalfTheta;
-
-	} else {
-
-		qm.copy( qb );
-
-	}
-
-	if ( Math.abs( cosHalfTheta ) >= 1.0 ) {
-
-		qm.w = qa.w;
-		qm.x = qa.x;
-		qm.y = qa.y;
-		qm.z = qa.z;
-
-		return qm;
-
-	}
-
-	var halfTheta = Math.acos( cosHalfTheta );
-	var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
-
-	if ( Math.abs( sinHalfTheta ) < 0.001 ) {
-
-		qm.w = 0.5 * ( qa.w + qm.w );
-		qm.x = 0.5 * ( qa.x + qm.x );
-		qm.y = 0.5 * ( qa.y + qm.y );
-		qm.z = 0.5 * ( qa.z + qm.z );
-
-		return qm;
-
-	}
-
-	var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta;
-	var ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
-
-	qm.w = ( qa.w * ratioA + qm.w * ratioB );
-	qm.x = ( qa.x * ratioA + qm.x * ratioB );
-	qm.y = ( qa.y * ratioA + qm.y * ratioB );
-	qm.z = ( qa.z * ratioA + qm.z * ratioB );
-
-	return qm;
+	return qm.copy( qa ).slerpSelf( qb, t );
 
 }

+ 228 - 0
test/unit/math/Quaternion.js

@@ -0,0 +1,228 @@
+/**
+ * @author bhouston / http://exocortex.com
+ */
+
+module( "Quaternion" );
+
+var orders = [ 'XYZ', 'YXZ', 'ZXY', 'ZYX', 'YZX', 'XZY' ];
+var eulerAngles = new THREE.Vector3( 0.1, -0.3, 0.25 );
+
+
+test( "constructor", function() {
+	var a = new THREE.Quaternion();
+	ok( a.x == 0, "Passed!" );
+	ok( a.y == 0, "Passed!" );
+	ok( a.z == 0, "Passed!" );
+	ok( a.w == 1, "Passed!" );
+
+	a = new THREE.Quaternion( x, y, z, w );
+	ok( a.x === x, "Passed!" );
+	ok( a.y === y, "Passed!" );
+	ok( a.z === z, "Passed!" );
+	ok( a.w === w, "Passed!" );
+});
+
+test( "copy", function() {
+	var a = new THREE.Quaternion( x, y, z, w );
+	var b = new THREE.Quaternion().copy( a );
+	ok( b.x == x, "Passed!" );
+	ok( b.y == y, "Passed!" );
+	ok( b.z == z, "Passed!" );
+	ok( b.w == w, "Passed!" );
+
+	// ensure that it is a true copy
+	a.x = 0;
+	a.y = -1;
+	a.z = 0;
+	a.w = -1;
+	ok( b.x == x, "Passed!" );
+	ok( b.y == y, "Passed!" );
+});
+
+test( "set", function() {
+	var a = new THREE.Quaternion();
+	ok( a.x == 0, "Passed!" );
+	ok( a.y == 0, "Passed!" );
+	ok( a.z == 0, "Passed!" );
+	ok( a.w == 1, "Passed!" );
+
+	a.set( x, y, z, w );
+	ok( a.x == x, "Passed!" );
+	ok( a.y == y, "Passed!" );
+	ok( a.z === z, "Passed!" );
+	ok( a.w === w, "Passed!" );
+});
+
+test( "setFromAxisAngle", function() {
+
+	// TODO: find cases to validate.
+	ok( true, "Passed!" );
+
+	var zero = new THREE.Quaternion();
+
+	var a = new THREE.Quaternion().setFromAxisAngle( new THREE.Vector3( 1, 0, 0 ), 0 );
+	ok( a.equals( zero ), "Passed!" );
+	a = new THREE.Quaternion().setFromAxisAngle( new THREE.Vector3( 0, 1, 0 ), 0 );
+	ok( a.equals( zero ), "Passed!" );
+	a = new THREE.Quaternion().setFromAxisAngle( new THREE.Vector3( 0, 0, 1 ), 0 );
+	ok( a.equals( zero ), "Passed!" );
+
+	var b1 = new THREE.Quaternion().setFromAxisAngle( new THREE.Vector3( 1, 0, 0 ), Math.PI );
+	ok( ! a.equals( b1 ), "Passed!" );
+	var b2 = new THREE.Quaternion().setFromAxisAngle( new THREE.Vector3( 1, 0, 0 ), -Math.PI );
+	ok( ! a.equals( b2 ), "Passed!" );
+
+	b1.multiplySelf( b2 );
+	ok( a.equals( b1 ), "Passed!" );
+});
+
+
+test( "setFromEuler/toEuler", function() {
+
+	var angles = [ new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ) ];
+
+	for( var i = 0; i < angles.length; i ++ ) {
+		// check only supported toEuler format
+		var eulers2 = new THREE.Quaternion().setFromEuler( angles[i], "XYZ" ).toEuler( "XYZ" );
+		ok( eulers2.distanceTo( angles[i] ) < 0.001, "Passed!" );
+	}
+
+});
+
+test( "setFromEuler/setFromRotationMatrix", function() {
+
+	// ensure euler conversion for Quaternion matches that of Matrix4
+	for( var i = 0; i < orders.length; i ++ ) {
+		var q = new THREE.Quaternion().setFromEuler( eulerAngles, orders[i] );
+		var m = new THREE.Matrix4().setRotationFromEuler( eulerAngles, orders[i] );
+		var q2 = new THREE.Quaternion().setFromRotationMatrix( m );
+
+		ok( q.subSelf( q2 ).length() < 0.001, "Passed!" );
+	}
+
+});
+
+test( "add/addSelf", function() {
+	var a = new THREE.Quaternion( x, y, z, w );
+	var b = new THREE.Quaternion( -x, -y, -z, -w );
+
+	a.addSelf( b );
+	ok( a.x == 0, "Passed!" );
+	ok( a.y == 0, "Passed!" );
+	ok( a.z == 0, "Passed!" );
+	ok( a.w == 0, "Passed!" );
+
+	var c = new THREE.Quaternion().add( b, b );
+	ok( c.x == -2*x, "Passed!" );
+	ok( c.y == -2*y, "Passed!" );	
+	ok( c.z == -2*z, "Passed!" );
+	ok( c.w == -2*w, "Passed!" );	
+});
+
+test( "sub/subSelf", function() {
+	var a = new THREE.Quaternion( x, y, z, w );
+	var b = new THREE.Quaternion( -x, -y, -z, -w );
+
+	a.subSelf( b );
+	ok( a.x == 2*x, "Passed!" );
+	ok( a.y == 2*y, "Passed!" );
+	ok( a.z == 2*z, "Passed!" );
+	ok( a.w == 2*w, "Passed!" );
+
+	var c = new THREE.Quaternion().sub( b, b );
+	ok( c.x == 0, "Passed!" );
+	ok( c.y == 0, "Passed!" );	
+	ok( c.z == 0, "Passed!" );
+	ok( c.w == 0, "Passed!" );	
+});
+
+test( "normalize/length/lengthSq", function() {
+	var a = new THREE.Quaternion( x, y, z, w );
+	var b = new THREE.Quaternion( -x, -y, -z, -w );
+
+	ok( a.length() != 1, "Passed!");
+	ok( a.lengthSq() != 1, "Passed!");
+	a.normalize();
+	ok( a.length() == 1, "Passed!");
+	ok( a.lengthSq() == 1, "Passed!");
+
+	a.set( 0, 0, 0, 0 );
+	ok( a.lengthSq() == 0, "Passed!");
+	ok( a.length() == 0, "Passed!");
+	a.normalize();
+	ok( a.lengthSq() == 1, "Passed!");
+	ok( a.length() == 1, "Passed!");
+});
+
+test( "inverse/conjugate", function() {
+	var a = new THREE.Quaternion( x, y, z, w );
+
+	// TODO: add better validation here.
+
+	var b = a.clone().conjugate();
+
+	ok( a.x == -b.x, "Passed!" );
+	ok( a.y == -b.y, "Passed!" );
+	ok( a.z == -b.z, "Passed!" );
+	ok( a.w == b.w, "Passed!" );
+});
+
+
+test( "multiply/multiplySelf", function() {
+	
+	var angles = [ new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ) ];
+
+	var q1 = new THREE.Quaternion().setFromEuler( angles[0], "XYZ" );
+	var q2 = new THREE.Quaternion().setFromEuler( angles[1], "XYZ" );
+	var q3 = new THREE.Quaternion().setFromEuler( angles[2], "XYZ" );
+
+	var q = new THREE.Quaternion().multiply( q1, q2 ).multiplySelf( q3 );
+
+	var m1 = new THREE.Matrix4().setRotationFromEuler( angles[0], "XYZ" );
+	var m2 = new THREE.Matrix4().setRotationFromEuler( angles[1], "XYZ" );
+	var m3 = new THREE.Matrix4().setRotationFromEuler( angles[2], "XYZ" );
+
+	var m = new THREE.Matrix4().multiply( m1, m2 ).multiplySelf( m3 );
+
+	var qFromM = new THREE.Quaternion().setFromRotationMatrix( m );
+
+	ok( q.subSelf( qFromM ).length() < 0.001, "Passed!" );
+});
+
+test( "multiplyVector3", function() {
+	
+	var angles = [ new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ) ];
+
+	// ensure euler conversion for Quaternion matches that of Matrix4
+	for( var i = 0; i < orders.length; i ++ ) {
+		for( var j = 0; j < angles.length; j ++ ) {
+			var q = new THREE.Quaternion().setFromEuler( angles[j], orders[i] );
+			var m = new THREE.Matrix4().setRotationFromEuler( angles[j], orders[i] );
+
+			var v0 = new THREE.Vector3(1, 0, 0);
+			var qv = q.multiplyVector3( v0.clone() );
+			var mv = m.multiplyVector3( v0.clone() );
+		
+			ok( qv.distanceTo( mv ) < 0.001, "Passed!" );
+		}
+	}
+
+});
+
+test( "equals", function() {
+	var a = new THREE.Quaternion( x, y, z, w );
+	var b = new THREE.Quaternion( -x, -y, -z, -w );
+	
+	ok( a.x != b.x, "Passed!" );
+	ok( a.y != b.y, "Passed!" );
+
+	ok( ! a.equals( b ), "Passed!" );
+	ok( ! b.equals( a ), "Passed!" );
+
+	a.copy( b );
+	ok( a.x == b.x, "Passed!" );
+	ok( a.y == b.y, "Passed!" );
+
+	ok( a.equals( b ), "Passed!" );
+	ok( b.equals( a ), "Passed!" );
+});

+ 43 - 0
test/unit/sources.html

@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>ThreeJS Test Suite - Using Files in /src</title>
+  <link rel="stylesheet" href="qunit-1.10.0.css">
+</head>
+<body>
+  <div id="qunit"></div>
+  <script src="qunit-1.10.0.js"></script>
+
+  <!-- add ThreeJS sources to test below -->
+
+  <script src="../../src/Three.js"></script>
+  <script src="../../src/math/Vector2.js"></script>
+  <script src="../../src/math/Vector3.js"></script>
+  <script src="../../src/math/Vector4.js"></script>
+  <script src="../../src/math/Box2.js"></script>
+  <script src="../../src/math/Box3.js"></script>
+  <script src="../../src/math/Plane.js"></script>
+  <script src="../../src/math/Ray.js"></script>
+  <script src="../../src/math/Sphere.js"></script>
+  <script src="../../src/math/Triangle.js"></script>
+  <script src="../../src/math/Matrix3.js"></script>
+  <script src="../../src/math/Matrix4.js"></script>
+  <script src="../../src/math/Quaternion.js"></script>
+  
+  <!-- add class-based unit tests below -->
+
+  <script src="math/Constants.js"></script>
+  <script src="math/Box2.js"></script>
+  <script src="math/Box3.js"></script>
+  <script src="math/Plane.js"></script>
+  <script src="math/Ray.js"></script>
+  <script src="math/Sphere.js"></script>
+  <script src="math/Triangle.js"></script>
+  <script src="math/Vector2.js"></script>
+  <script src="math/Vector3.js"></script>
+  <script src="math/Vector4.js"></script>
+  <script src="math/Quaternion.js"></script>
+  
+</body>
+</html>

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

@@ -25,6 +25,7 @@
   <script src="math/Vector2.js"></script>
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
-  
+  <script src="math/Quaternion.js"></script>
+
 </body>
 </html>

+ 1 - 0
test/unit/unittests_three.html

@@ -25,6 +25,7 @@
   <script src="math/Vector2.js"></script>
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
+  <script src="math/Quaternion.js"></script>
   
 </body>
 </html>

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

@@ -25,6 +25,7 @@
   <script src="math/Vector2.js"></script>
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
-  
+  <script src="math/Quaternion.js"></script>
+
 </body>
 </html>