OrbitControls.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /**
  2. * @author qiao / https://github.com/qiao
  3. * @author mrdoob / http://mrdoob.com
  4. * @author alteredq / http://alteredqualia.com/
  5. * @author WestLangley / https://github.com/WestLangley
  6. */
  7. THREE.OrbitControls = function ( object, domElement ) {
  8. THREE.EventDispatcher.call( this );
  9. this.object = object;
  10. this.domElement = ( domElement !== undefined ) ? domElement : document;
  11. // API
  12. this.center = new THREE.Vector3();
  13. this.userZoom = true;
  14. this.userZoomSpeed = 1.0;
  15. this.userRotate = true;
  16. this.userRotateSpeed = 1.0;
  17. this.autoRotate = false;
  18. this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
  19. this.minPolarAngle = 0; // radians
  20. this.maxPolarAngle = Math.PI; // radians
  21. this.minDistance = 0;
  22. this.maxDistance = Infinity;
  23. // internals
  24. var scope = this;
  25. var EPS = 0.000001;
  26. var PIXELS_PER_ROUND = 1800;
  27. var rotateStart = new THREE.Vector2();
  28. var rotateEnd = new THREE.Vector2();
  29. var rotateDelta = new THREE.Vector2();
  30. var zoomStart = new THREE.Vector2();
  31. var zoomEnd = new THREE.Vector2();
  32. var zoomDelta = new THREE.Vector2();
  33. var phiDelta = 0;
  34. var thetaDelta = 0;
  35. var scale = 1;
  36. var lastPosition = new THREE.Vector3();
  37. var STATE = { NONE : -1, ROTATE : 0, ZOOM : 1 };
  38. var state = STATE.NONE;
  39. // events
  40. var changeEvent = { type: 'change' };
  41. this.rotateLeft = function ( angle ) {
  42. if ( angle === undefined ) {
  43. angle = getAutoRotationAngle();
  44. }
  45. thetaDelta -= angle;
  46. };
  47. this.rotateRight = function ( angle ) {
  48. if ( angle === undefined ) {
  49. angle = getAutoRotationAngle();
  50. }
  51. thetaDelta += angle;
  52. };
  53. this.rotateUp = function ( angle ) {
  54. if ( angle === undefined ) {
  55. angle = getAutoRotationAngle();
  56. }
  57. phiDelta -= angle;
  58. };
  59. this.rotateDown = function ( angle ) {
  60. if ( angle === undefined ) {
  61. angle = getAutoRotationAngle();
  62. }
  63. phiDelta += angle;
  64. };
  65. this.zoomIn = function ( zoomScale ) {
  66. if ( zoomScale === undefined ) {
  67. zoomScale = getZoomScale();
  68. }
  69. scale /= zoomScale;
  70. };
  71. this.zoomOut = function ( zoomScale ) {
  72. if ( zoomScale === undefined ) {
  73. zoomScale = getZoomScale();
  74. }
  75. scale *= zoomScale;
  76. };
  77. this.update = function () {
  78. var position = this.object.position;
  79. var offset = position.clone().subSelf( this.center )
  80. // angle from z-axis around y-axis
  81. var theta = Math.atan2( offset.x, offset.z );
  82. // angle from y-axis
  83. var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
  84. if ( this.autoRotate ) {
  85. this.rotateLeft( getAutoRotationAngle() );
  86. }
  87. theta += thetaDelta;
  88. phi += phiDelta;
  89. // restrict phi to be between desired limits
  90. phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
  91. // restrict phi to be betwee EPS and PI-EPS
  92. phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
  93. var radius = offset.length() * scale;
  94. // restrict radius to be between desired limits
  95. radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
  96. offset.x = radius * Math.sin( phi ) * Math.sin( theta );
  97. offset.y = radius * Math.cos( phi );
  98. offset.z = radius * Math.sin( phi ) * Math.cos( theta );
  99. position.copy( this.center ).addSelf( offset );
  100. this.object.lookAt( this.center );
  101. thetaDelta = 0;
  102. phiDelta = 0;
  103. scale = 1;
  104. if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
  105. this.dispatchEvent( changeEvent );
  106. lastPosition.copy( this.object.position );
  107. }
  108. };
  109. function getAutoRotationAngle() {
  110. return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
  111. }
  112. function getZoomScale() {
  113. return Math.pow( 0.95, scope.userZoomSpeed );
  114. }
  115. function onMouseDown( event ) {
  116. if ( !scope.userRotate ) return;
  117. event.preventDefault();
  118. if ( event.button === 0 || event.button === 2 ) {
  119. state = STATE.ROTATE;
  120. rotateStart.set( event.clientX, event.clientY );
  121. } else if ( event.button === 1 ) {
  122. state = STATE.ZOOM;
  123. zoomStart.set( event.clientX, event.clientY );
  124. }
  125. document.addEventListener( 'mousemove', onMouseMove, false );
  126. document.addEventListener( 'mouseup', onMouseUp, false );
  127. }
  128. function onMouseMove( event ) {
  129. event.preventDefault();
  130. if ( state === STATE.ROTATE ) {
  131. rotateEnd.set( event.clientX, event.clientY );
  132. rotateDelta.sub( rotateEnd, rotateStart );
  133. scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed );
  134. scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed );
  135. rotateStart.copy( rotateEnd );
  136. } else if ( state === STATE.ZOOM ) {
  137. zoomEnd.set( event.clientX, event.clientY );
  138. zoomDelta.sub( zoomEnd, zoomStart );
  139. if ( zoomDelta.y > 0 ) {
  140. scope.zoomIn();
  141. } else {
  142. scope.zoomOut();
  143. }
  144. zoomStart.copy( zoomEnd );
  145. }
  146. }
  147. function onMouseUp( event ) {
  148. if ( ! scope.userRotate ) return;
  149. document.removeEventListener( 'mousemove', onMouseMove, false );
  150. document.removeEventListener( 'mouseup', onMouseUp, false );
  151. state = STATE.NONE;
  152. }
  153. function onMouseWheel( event ) {
  154. if ( ! scope.userZoom ) return;
  155. if ( event.wheelDelta > 0 ) {
  156. scope.zoomOut();
  157. } else {
  158. scope.zoomIn();
  159. }
  160. }
  161. this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
  162. this.domElement.addEventListener( 'mousedown', onMouseDown, false );
  163. this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
  164. };