OrbitControls.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /**
  2. * @author qiao / https://github.com/qiao
  3. * @author mrdoob / http://mrdoob.com
  4. * @author alteredq / http://alteredqualia.com/
  5. * @author WestLangley / http://github.com/WestLangley
  6. */
  7. THREE.OrbitControls = function ( object, domElement ) {
  8. this.object = object;
  9. this.domElement = ( domElement !== undefined ) ? domElement : document;
  10. // API
  11. this.enabled = true;
  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.userPan = true;
  18. this.userPanSpeed = 2.0;
  19. this.autoRotate = false;
  20. this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
  21. this.minPolarAngle = 0; // radians
  22. this.maxPolarAngle = Math.PI; // radians
  23. this.minDistance = 0;
  24. this.maxDistance = Infinity;
  25. this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
  26. // internals
  27. var scope = this;
  28. var EPS = 0.000001;
  29. var PIXELS_PER_ROUND = 1800;
  30. var rotateStart = new THREE.Vector2();
  31. var rotateEnd = new THREE.Vector2();
  32. var rotateDelta = new THREE.Vector2();
  33. var zoomStart = new THREE.Vector2();
  34. var zoomEnd = new THREE.Vector2();
  35. var zoomDelta = new THREE.Vector2();
  36. var phiDelta = 0;
  37. var thetaDelta = 0;
  38. var scale = 1;
  39. var lastPosition = new THREE.Vector3();
  40. var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 };
  41. var state = STATE.NONE;
  42. // events
  43. var changeEvent = { type: 'change' };
  44. this.rotateLeft = function ( angle ) {
  45. if ( angle === undefined ) {
  46. angle = getAutoRotationAngle();
  47. }
  48. thetaDelta -= angle;
  49. };
  50. this.rotateRight = function ( angle ) {
  51. if ( angle === undefined ) {
  52. angle = getAutoRotationAngle();
  53. }
  54. thetaDelta += angle;
  55. };
  56. this.rotateUp = function ( angle ) {
  57. if ( angle === undefined ) {
  58. angle = getAutoRotationAngle();
  59. }
  60. phiDelta -= angle;
  61. };
  62. this.rotateDown = function ( angle ) {
  63. if ( angle === undefined ) {
  64. angle = getAutoRotationAngle();
  65. }
  66. phiDelta += angle;
  67. };
  68. this.zoomIn = function ( zoomScale ) {
  69. if ( zoomScale === undefined ) {
  70. zoomScale = getZoomScale();
  71. }
  72. scale /= zoomScale;
  73. };
  74. this.zoomOut = function ( zoomScale ) {
  75. if ( zoomScale === undefined ) {
  76. zoomScale = getZoomScale();
  77. }
  78. scale *= zoomScale;
  79. };
  80. this.pan = function ( distance ) {
  81. distance.transformDirection( this.object.matrix );
  82. distance.multiplyScalar( scope.userPanSpeed );
  83. this.object.position.add( distance );
  84. this.center.add( distance );
  85. };
  86. this.update = function () {
  87. var position = this.object.position;
  88. var offset = position.clone().sub( this.center );
  89. // angle from z-axis around y-axis
  90. var theta = Math.atan2( offset.x, offset.z );
  91. // angle from y-axis
  92. var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
  93. if ( this.autoRotate ) {
  94. this.rotateLeft( getAutoRotationAngle() );
  95. }
  96. theta += thetaDelta;
  97. phi += phiDelta;
  98. // restrict phi to be between desired limits
  99. phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
  100. // restrict phi to be betwee EPS and PI-EPS
  101. phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
  102. var radius = offset.length() * scale;
  103. // restrict radius to be between desired limits
  104. radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
  105. offset.x = radius * Math.sin( phi ) * Math.sin( theta );
  106. offset.y = radius * Math.cos( phi );
  107. offset.z = radius * Math.sin( phi ) * Math.cos( theta );
  108. position.copy( this.center ).add( offset );
  109. this.object.lookAt( this.center );
  110. thetaDelta = 0;
  111. phiDelta = 0;
  112. scale = 1;
  113. if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
  114. this.dispatchEvent( changeEvent );
  115. lastPosition.copy( this.object.position );
  116. }
  117. };
  118. function getAutoRotationAngle() {
  119. return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
  120. }
  121. function getZoomScale() {
  122. return Math.pow( 0.95, scope.userZoomSpeed );
  123. }
  124. function onMouseDown( event ) {
  125. if ( scope.enabled === false ) return;
  126. if ( scope.userRotate === false ) return;
  127. event.preventDefault();
  128. if ( event.button === 0 ) {
  129. state = STATE.ROTATE;
  130. rotateStart.set( event.clientX, event.clientY );
  131. } else if ( event.button === 1 ) {
  132. state = STATE.ZOOM;
  133. zoomStart.set( event.clientX, event.clientY );
  134. } else if ( event.button === 2 ) {
  135. state = STATE.PAN;
  136. }
  137. document.addEventListener( 'mousemove', onMouseMove, false );
  138. document.addEventListener( 'mouseup', onMouseUp, false );
  139. }
  140. function onMouseMove( event ) {
  141. if ( scope.enabled === false ) return;
  142. event.preventDefault();
  143. if ( state === STATE.ROTATE ) {
  144. rotateEnd.set( event.clientX, event.clientY );
  145. rotateDelta.subVectors( rotateEnd, rotateStart );
  146. scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed );
  147. scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed );
  148. rotateStart.copy( rotateEnd );
  149. } else if ( state === STATE.ZOOM ) {
  150. zoomEnd.set( event.clientX, event.clientY );
  151. zoomDelta.subVectors( zoomEnd, zoomStart );
  152. if ( zoomDelta.y > 0 ) {
  153. scope.zoomIn();
  154. } else {
  155. scope.zoomOut();
  156. }
  157. zoomStart.copy( zoomEnd );
  158. } else if ( state === STATE.PAN ) {
  159. var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
  160. var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
  161. scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) );
  162. }
  163. }
  164. function onMouseUp( event ) {
  165. if ( scope.enabled === false ) return;
  166. if ( scope.userRotate === false ) return;
  167. document.removeEventListener( 'mousemove', onMouseMove, false );
  168. document.removeEventListener( 'mouseup', onMouseUp, false );
  169. state = STATE.NONE;
  170. }
  171. function onMouseWheel( event ) {
  172. if ( scope.enabled === false ) return;
  173. if ( scope.userZoom === false ) return;
  174. var delta = 0;
  175. if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
  176. delta = event.wheelDelta;
  177. } else if ( event.detail ) { // Firefox
  178. delta = - event.detail;
  179. }
  180. if ( delta > 0 ) {
  181. scope.zoomOut();
  182. } else {
  183. scope.zoomIn();
  184. }
  185. }
  186. function onKeyDown( event ) {
  187. if ( scope.enabled === false ) return;
  188. if ( scope.userPan === false ) return;
  189. switch ( event.keyCode ) {
  190. case scope.keys.UP:
  191. scope.pan( new THREE.Vector3( 0, 1, 0 ) );
  192. break;
  193. case scope.keys.BOTTOM:
  194. scope.pan( new THREE.Vector3( 0, - 1, 0 ) );
  195. break;
  196. case scope.keys.LEFT:
  197. scope.pan( new THREE.Vector3( - 1, 0, 0 ) );
  198. break;
  199. case scope.keys.RIGHT:
  200. scope.pan( new THREE.Vector3( 1, 0, 0 ) );
  201. break;
  202. }
  203. }
  204. this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
  205. this.domElement.addEventListener( 'mousedown', onMouseDown, false );
  206. this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
  207. this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
  208. this.domElement.addEventListener( 'keydown', onKeyDown, false );
  209. };
  210. THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );