浏览代码

initial quaternion unit tests, add Quaterion.toEuler (untested for now.)

Ben Houston 12 年之前
父节点
当前提交
835d6b13f4
共有 6 个文件被更改,包括 257 次插入56 次删除
  1. 78 53
      src/math/Quaternion.js
  2. 171 0
      test/math/Quaternion.js
  3. 3 1
      test/sources.html
  4. 2 1
      test/three-math.html
  5. 1 0
      test/three.html
  6. 2 1
      test/three.min.html

+ 78 - 53
src/math/Quaternion.js

@@ -174,6 +174,28 @@ 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;
+
+	},
+
 	inverse: function () {
 	inverse: function () {
 
 
 		this.conjugate().normalize();
 		this.conjugate().normalize();
@@ -277,6 +299,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 ) {
 	slerpSelf: function ( qb, t ) {
 
 
 		var x = this.x, y = this.y, z = this.z, w = this.w;
 		var x = this.x, y = this.y, z = this.z, w = this.w;
@@ -337,6 +408,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 () {
 	clone: function () {
 
 
 		return new THREE.Quaternion( this.x, this.y, this.z, this.w );
 		return new THREE.Quaternion( this.x, this.y, this.z, this.w );
@@ -347,58 +424,6 @@ THREE.Quaternion.prototype = {
 
 
 THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
 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 );
 
 
 }
 }

+ 171 - 0
test/math/Quaternion.js

@@ -0,0 +1,171 @@
+/**
+ * @author bhouston / http://exocortex.com
+ */
+
+module( "Quaternion" );
+
+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( "setFromRotationMatrix", function() {
+
+	// TODO: find cases to validate.
+	ok( true, "Passed!" );
+
+});
+
+test( "fromEuler/toEuler", function() {
+
+	// TODO: find cases to validate.
+	ok( true, "Passed!" );
+
+});
+
+test( "add", 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( "normalize/length", function() {
+	var a = new THREE.Quaternion( x, y, z, w );
+	var b = new THREE.Quaternion( -x, -y, -z, -w );
+
+	ok( a.length() != 1, "Passed!");
+	a.normalize();
+	ok( a.length() == 1, "Passed!");
+
+	a.set( 0, 0, 0, 0 );
+	ok( a.length() == 0, "Passed!");
+	a.normalize();
+	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() {
+
+	// TODO: find cases to validate.
+	ok( true, "Passed!" );
+
+});
+
+test( "multiplyVector3", function() {
+	
+	// TODO: find cases to validate.
+	ok( true, "Passed!" );
+
+});
+
+test( "slerpSelf/slerp", function() {
+
+	// TODO: find cases to validate.
+	ok( true, "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!" );
+});

+ 3 - 1
test/sources.html

@@ -23,7 +23,8 @@
   <script src="../src/math/Triangle.js"></script>
   <script src="../src/math/Triangle.js"></script>
   <script src="../src/math/Matrix3.js"></script>
   <script src="../src/math/Matrix3.js"></script>
   <script src="../src/math/Matrix4.js"></script>
   <script src="../src/math/Matrix4.js"></script>
-
+  <script src="../src/math/Quaternion.js"></script>
+  
   <!-- add class-based unit tests below -->
   <!-- add class-based unit tests below -->
 
 
   <script src="math/Constants.js"></script>
   <script src="math/Constants.js"></script>
@@ -36,6 +37,7 @@
   <script src="math/Vector2.js"></script>
   <script src="math/Vector2.js"></script>
   <script src="math/Vector3.js"></script>
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
   <script src="math/Vector4.js"></script>
+  <script src="math/Quaternion.js"></script>
   
   
 </body>
 </body>
 </html>
 </html>

+ 2 - 1
test/three-math.html

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

+ 1 - 0
test/three.html

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

+ 2 - 1
test/three.min.html

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