From 835d6b13f48b31512999d38af8b13298d1b80863 Mon Sep 17 00:00:00 2001 From: Ben Houston Date: Wed, 19 Dec 2012 14:53:31 -0500 Subject: [PATCH] initial quaternion unit tests, add Quaterion.toEuler (untested for now.) --- src/math/Quaternion.js | 131 +++++++++++++++++------------- test/math/Quaternion.js | 171 ++++++++++++++++++++++++++++++++++++++++ test/sources.html | 4 +- test/three-math.html | 3 +- test/three.html | 1 + test/three.min.html | 3 +- 6 files changed, 257 insertions(+), 56 deletions(-) create mode 100644 test/math/Quaternion.js diff --git a/src/math/Quaternion.js b/src/math/Quaternion.js index 92e1338486..06cff22886 100644 --- a/src/math/Quaternion.js +++ b/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 () { 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 ) { 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 () { 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 ) { - // 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 ); } diff --git a/test/math/Quaternion.js b/test/math/Quaternion.js new file mode 100644 index 0000000000..808427dd0a --- /dev/null +++ b/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!" ); +}); diff --git a/test/sources.html b/test/sources.html index 7f1a510edd..f0205aafb2 100644 --- a/test/sources.html +++ b/test/sources.html @@ -23,7 +23,8 @@ - + + @@ -36,6 +37,7 @@ + \ No newline at end of file diff --git a/test/three-math.html b/test/three-math.html index dd2f1c64d2..8f7003f424 100644 --- a/test/three-math.html +++ b/test/three-math.html @@ -25,6 +25,7 @@ - + + diff --git a/test/three.html b/test/three.html index 39ed785f49..942bda174e 100644 --- a/test/three.html +++ b/test/three.html @@ -25,6 +25,7 @@ + diff --git a/test/three.min.html b/test/three.min.html index 0f2a46cadf..f11a53d7e2 100644 --- a/test/three.min.html +++ b/test/three.min.html @@ -25,6 +25,7 @@ - + + -- GitLab