Bläddra i källkod

fix Euler.setFromQuaternion bug - see #5659

Ben Houston 10 år sedan
förälder
incheckning
05e26f97cc
2 ändrade filer med 50 tillägg och 68 borttagningar
  1. 14 65
      src/math/Euler.js
  2. 36 3
      test/unit/math/Euler.js

+ 14 - 65
src/math/Euler.js

@@ -101,7 +101,7 @@ THREE.Euler.prototype = {
 
 	},
 
-	setFromRotationMatrix: function ( m, order ) {
+	setFromRotationMatrix: function ( m, order, update ) {
 
 		var clamp = THREE.Math.clamp;
 
@@ -218,76 +218,26 @@ THREE.Euler.prototype = {
 
 		this._order = order;
 
-		this.onChangeCallback();
+		if ( update !== false ) this.onChangeCallback();
 
 		return this;
 
 	},
 
-	setFromQuaternion: function ( q, order, update ) {
-
-		var clamp = THREE.Math.clamp;
-
-		// q is assumed to be normalized
-
-		// http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
-
-		var sqx = q.x * q.x;
-		var sqy = q.y * q.y;
-		var sqz = q.z * q.z;
-		var sqw = q.w * q.w;
-
-		order = order || this._order;
-
-		if ( order === 'XYZ' ) {
-
-			this._x = Math.atan2( 2 * ( q.x * q.w - q.y * q.z ), ( sqw - sqx - sqy + sqz ) );
-			this._y = Math.asin(  clamp( 2 * ( q.x * q.z + q.y * q.w ), - 1, 1 ) );
-			this._z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw + sqx - sqy - sqz ) );
-
-		} else if ( order ===  'YXZ' ) {
-
-			this._x = Math.asin(  clamp( 2 * ( q.x * q.w - q.y * q.z ), - 1, 1 ) );
-			this._y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw - sqx - sqy + sqz ) );
-			this._z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw - sqx + sqy - sqz ) );
-
-		} else if ( order === 'ZXY' ) {
-
-			this._x = Math.asin(  clamp( 2 * ( q.x * q.w + q.y * q.z ), - 1, 1 ) );
-			this._y = Math.atan2( 2 * ( q.y * q.w - q.z * q.x ), ( sqw - sqx - sqy + sqz ) );
-			this._z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw - sqx + sqy - sqz ) );
+	setFromQuaternion: function() {
 
-		} else if ( order === 'ZYX' ) {
+	   var mIntermediate = null;
+	   
+	   return function( q, order, update ) {
+	   
+	   	  mIntermediate = mIntermediate || new THREE.Matrix4();
+	      mIntermediate.makeRotationFromQuaternion( q );
+	      this.setFromRotationMatrix( mIntermediate, order );
+	   
+	      return this;
+	   };
 
-			this._x = Math.atan2( 2 * ( q.x * q.w + q.z * q.y ), ( sqw - sqx - sqy + sqz ) );
-			this._y = Math.asin(  clamp( 2 * ( q.y * q.w - q.x * q.z ), - 1, 1 ) );
-			this._z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw + sqx - sqy - sqz ) );
-
-		} else if ( order === 'YZX' ) {
-
-			this._x = Math.atan2( 2 * ( q.x * q.w - q.z * q.y ), ( sqw - sqx + sqy - sqz ) );
-			this._y = Math.atan2( 2 * ( q.y * q.w - q.x * q.z ), ( sqw + sqx - sqy - sqz ) );
-			this._z = Math.asin(  clamp( 2 * ( q.x * q.y + q.z * q.w ), - 1, 1 ) );
-
-		} else if ( order === 'XZY' ) {
-
-			this._x = Math.atan2( 2 * ( q.x * q.w + q.y * q.z ), ( sqw - sqx + sqy - sqz ) );
-			this._y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw + sqx - sqy - sqz ) );
-			this._z = Math.asin(  clamp( 2 * ( q.z * q.w - q.x * q.y ), - 1, 1 ) );
-
-		} else {
-
-			console.warn( 'THREE.Euler: .setFromQuaternion() given unsupported order: ' + order )
-
-		}
-
-		this._order = order;
-
-		if ( update !== false ) this.onChangeCallback();
-
-		return this;
-
-	},
+	}(),
 
 	reorder: function () {
 
@@ -302,7 +252,6 @@ THREE.Euler.prototype = {
 
 		};
 
-
 	}(),
 
 	equals: function ( euler ) {

+ 36 - 3
test/unit/math/Euler.js

@@ -22,6 +22,19 @@ var matrixEquals4 = function( a, b, tolerance ) {
 	return true;
 };
 
+var eulerEquals = function( a, b, tolerance ) {
+	tolerance = tolerance || 0.0001;
+	var diff = Math.abs( a.x - b.x ) + Math.abs( a.y - b.y ) + Math.abs( a.z - b.z );	
+	return ( diff < tolerance );
+};
+
+
+var quatEquals = function( a, b, tolerance ) {
+	tolerance = tolerance || 0.0001;
+	var diff = Math.abs( a.x - b.x ) + Math.abs( a.y - b.y ) + Math.abs( a.z - b.z ) + Math.abs( a.w - b.w );	
+	return ( diff < tolerance );
+};
+
 test( "constructor/equals", function() {
 	var a = new THREE.Euler();
 	ok( a.equals( eulerZero ), "Passed!" );
@@ -59,7 +72,7 @@ test( "Quaternion.setFromEuler/Euler.fromQuaternion", function() {
 
 		var v2 = new THREE.Euler().setFromQuaternion( q, v.order );
 		var q2 = new THREE.Quaternion().setFromEuler( v2 );
-		ok( q.equals( q2 ), "Passed!" );	
+		ok( eulerEquals( q, q2 ), "Passed!" );	
 	}
 });
 
@@ -84,10 +97,30 @@ test( "reorder", function() {
 
 		v.reorder( 'YZX' );		
 		var q2 = new THREE.Quaternion().setFromEuler( v );
-		ok( q.equals( q2 ), "Passed!" );	
+		ok( quatEquals( q, q2 ), "Passed!" );	
 
 		v.reorder( 'ZXY' );
 		var q3 = new THREE.Quaternion().setFromEuler( v );
-		ok( q.equals( q3 ), "Passed!" );	
+		ok( quatEquals( q, q3 ), "Passed!" );	
 	}
 });
+
+
+test( "gimbalLocalQuat", function() {
+	// known problematic quaternions
+	var q1 = new THREE.Quaternion( 0.5207769385244341, -0.4783214164122354, 0.520776938524434, 0.47832141641223547 );
+	var q2 = new THREE.Quaternion( 0.11284905712620674, 0.6980437630368944, -0.11284905712620674, 0.6980437630368944 );
+
+	var eulerOrder = "ZYX";
+
+	// create Euler directly from a Quaternion
+	var eViaQ1 = new THREE.Euler().setFromQuaternion( q1, eulerOrder ); // there is likely a bug here
+
+	// create Euler from Quaternion via an intermediate Matrix4
+	var mViaQ1 = new THREE.Matrix4().makeRotationFromQuaternion( q1 );
+	var eViaMViaQ1 = new THREE.Euler().setFromRotationMatrix( mViaQ1, eulerOrder );
+
+	// the results here are different
+	ok( eulerEquals( eViaQ1, eViaMViaQ1 ), "Passed!" );  // this result is correct
+
+});