diff --git a/docs/api/math/Quaternion.html b/docs/api/math/Quaternion.html index d4d20860e29e21..b6e0ef0166aa96 100644 --- a/docs/api/math/Quaternion.html +++ b/docs/api/math/Quaternion.html @@ -57,24 +57,31 @@

.copy( [page:Quaternion q] ) [page:Quaternion]

Copies values of *q* to this quaternion. -

.setFromEuler( [page:Vector3 vector] ) [page:Quaternion]

+

.setFromEuler( [page:Euler euler] ) [page:Quaternion]

- Sets this quaternion from rotation specified by Euler angles. + Sets this quaternion from rotation specified by Euler angle.

.setFromAxisAngle( [page:Vector3 axis], [page:Float angle] ) [page:Quaternion]

Sets this quaternion from rotation specified by axis and angle.
Adapted from [link:http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm].
- *Axis* have to be normalized, *angle* is in radians. + *Axis* is asumed to be normalized, *angle* is in radians.

.setFromRotationMatrix( [page:Matrix4 m] ) [page:Quaternion]

- Sets this quaternion from rotation component of *m*. + Sets this quaternion from rotation component of *m*.
Adapted from [link:http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm].
+

.setFromUnitVectors( [page:Vector3 vFrom], [page:Vector3 vTo] ) [page:Quaternion]

+
+ Sets this quaternion to the rotation required to rotate direction vector *vFrom* to direction vector *vTo*.
+ Adapted from [link:http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors].
+ *vFrom* and *vTo* are assumed to be normalized. +
+

.inverse() [page:Quaternion]

Inverts this quaternion. diff --git a/examples/js/controls/OrbitControls.js b/examples/js/controls/OrbitControls.js index cc82fe3cbc891d..ae14f663304a5c 100644 --- a/examples/js/controls/OrbitControls.js +++ b/examples/js/controls/OrbitControls.js @@ -108,6 +108,11 @@ THREE.OrbitControls = function ( object, domElement ) { this.target0 = this.target.clone(); this.position0 = this.object.position.clone(); + // so camera.up is the orbit axis + + var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); + var quatInverse = quat.clone().inverse(); + // events var changeEvent = { type: 'change' }; @@ -229,6 +234,9 @@ THREE.OrbitControls = function ( object, domElement ) { offset.copy( position ).sub( this.target ); + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); + // angle from z-axis around y-axis var theta = Math.atan2( offset.x, offset.z ); @@ -264,6 +272,9 @@ THREE.OrbitControls = function ( object, domElement ) { offset.y = radius * Math.cos( phi ); offset.z = radius * Math.sin( phi ) * Math.cos( theta ); + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + position.copy( this.target ).add( offset ); this.object.lookAt( this.target ); @@ -273,7 +284,7 @@ THREE.OrbitControls = function ( object, domElement ) { scale = 1; pan.set( 0, 0, 0 ); - if ( lastPosition.distanceTo( this.object.position ) > EPS ) { + if ( lastPosition.distanceToSquared( this.object.position ) > EPS ) { this.dispatchEvent( changeEvent ); @@ -620,6 +631,9 @@ THREE.OrbitControls = function ( object, domElement ) { window.addEventListener( 'keydown', onKeyDown, false ); + // force an update at start + this.update(); + }; THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); diff --git a/examples/js/controls/TrackballControls.js b/examples/js/controls/TrackballControls.js index e19d097fd2ba32..259f1ee1c4a472 100644 --- a/examples/js/controls/TrackballControls.js +++ b/examples/js/controls/TrackballControls.js @@ -38,6 +38,8 @@ THREE.TrackballControls = function ( object, domElement ) { this.target = new THREE.Vector3(); + var EPS = 0.000001; + var lastPosition = new THREE.Vector3(); var _state = STATE.NONE, @@ -315,7 +317,7 @@ THREE.TrackballControls = function ( object, domElement ) { _this.object.lookAt( _this.target ); - if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) { + if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) { _this.dispatchEvent( changeEvent ); @@ -587,6 +589,9 @@ THREE.TrackballControls = function ( object, domElement ) { this.handleResize(); + // force an update at start + this.update(); + }; THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype ); diff --git a/src/math/Quaternion.js b/src/math/Quaternion.js index fb05e674618d69..2c867ecc0f07fd 100644 --- a/src/math/Quaternion.js +++ b/src/math/Quaternion.js @@ -180,8 +180,9 @@ THREE.Quaternion.prototype = { setFromAxisAngle: function ( axis, angle ) { - // from http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - // axis have to be normalized + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + + // assumes axis is normalized var halfAngle = angle / 2, s = Math.sin( halfAngle ); @@ -255,6 +256,30 @@ THREE.Quaternion.prototype = { }, + setFromUnitVectors: function () { + + // http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors + + // assumes direction vectors vFrom and vTo are normalized + + var v1; + + return function( vFrom, vTo ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.crossVectors( vFrom, vTo ); + + this.set( v1.x, v1.y, v1.z, vFrom.dot( vTo ) + 1 ).normalize(); + + this._updateEuler(); + + return this; + + } + + }(), + inverse: function () { this.conjugate().normalize();