Simulate graph states in the browser
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

413 行
7.9KB

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