You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1036 lines
24 KiB

5 years ago
  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. * @author erich666 / http://erichaines.com
  7. */
  8. // This set of controls performs orbiting, dollying (zooming), and panning.
  9. // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
  10. //
  11. // Orbit - left mouse / touch: one-finger move
  12. // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
  13. // Pan - right mouse, or left mouse + ctrl/metaKey, or arrow keys / touch: two-finger move
  14. THREE.OrbitControls = function(object, domElement) {
  15. this.object = object
  16. this.domElement = domElement !== undefined ? domElement : document
  17. // Set to false to disable this control
  18. this.enabled = true
  19. // "target" sets the location of focus, where the object orbits around
  20. this.target = new THREE.Vector3()
  21. // How far you can dolly in and out ( PerspectiveCamera only )
  22. this.minDistance = 0
  23. this.maxDistance = Infinity
  24. // How far you can zoom in and out ( OrthographicCamera only )
  25. this.minZoom = 0
  26. this.maxZoom = Infinity
  27. // How far you can orbit vertically, upper and lower limits.
  28. // Range is 0 to Math.PI radians.
  29. this.minPolarAngle = 0 // radians
  30. this.maxPolarAngle = Math.PI // radians
  31. // How far you can orbit horizontally, upper and lower limits.
  32. // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
  33. this.minAzimuthAngle = -Infinity // radians
  34. this.maxAzimuthAngle = Infinity // radians
  35. // Set to true to enable damping (inertia)
  36. // If damping is enabled, you must call controls.update() in your animation loop
  37. this.enableDamping = false
  38. this.dampingFactor = 0.25
  39. // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
  40. // Set to false to disable zooming
  41. this.enableZoom = true
  42. this.zoomSpeed = 1.0
  43. // Set to false to disable rotating
  44. this.enableRotate = true
  45. this.rotateSpeed = 1.0
  46. // Set to false to disable panning
  47. this.enablePan = true
  48. this.panSpeed = 1.0
  49. this.screenSpacePanning = false // if true, pan in screen-space
  50. this.keyPanSpeed = 7.0 // pixels moved per arrow key push
  51. // Set to true to automatically rotate around the target
  52. // If auto-rotate is enabled, you must call controls.update() in your animation loop
  53. this.autoRotate = false
  54. this.autoRotateSpeed = 2.0 // 30 seconds per round when fps is 60
  55. // Set to false to disable use of the keys
  56. this.enableKeys = true
  57. // The four arrow keys
  58. this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }
  59. // Mouse buttons
  60. this.mouseButtons = {
  61. LEFT: THREE.MOUSE.LEFT,
  62. MIDDLE: THREE.MOUSE.MIDDLE,
  63. RIGHT: THREE.MOUSE.RIGHT,
  64. }
  65. // for reset
  66. this.target0 = this.target.clone()
  67. this.position0 = this.object.position.clone()
  68. this.zoom0 = this.object.zoom
  69. //
  70. // public methods
  71. //
  72. this.getPolarAngle = function() {
  73. return spherical.phi
  74. }
  75. this.getAzimuthalAngle = function() {
  76. return spherical.theta
  77. }
  78. this.setPolarAngle = function(angle) {
  79. spherical.phi = angle
  80. this.forceUpdate()
  81. }
  82. this.setAzimuthalAngle = function(angle) {
  83. spherical.theta = angle
  84. this.forceUpdate()
  85. }
  86. this.saveState = function() {
  87. scope.target0.copy(scope.target)
  88. scope.position0.copy(scope.object.position)
  89. scope.zoom0 = scope.object.zoom
  90. }
  91. this.reset = function() {
  92. scope.target.copy(scope.target0)
  93. scope.object.position.copy(scope.position0)
  94. scope.object.zoom = scope.zoom0
  95. scope.object.updateProjectionMatrix()
  96. scope.dispatchEvent(changeEvent)
  97. scope.update()
  98. state = STATE.NONE
  99. }
  100. this.forceUpdate = (function() {
  101. var offset = new THREE.Vector3()
  102. // so camera.up is the orbit axis
  103. var quat = new THREE.Quaternion().setFromUnitVectors(
  104. object.up,
  105. new THREE.Vector3(0, 1, 0)
  106. )
  107. var quatInverse = quat.clone().inverse()
  108. var lastPosition = new THREE.Vector3()
  109. var lastQuaternion = new THREE.Quaternion()
  110. return function() {
  111. var position = this.object.position
  112. offset.copy(position).sub(this.target)
  113. // rotate offset to "y-axis-is-up" space
  114. offset.applyQuaternion(quat)
  115. // restrict spherical.theta to be between desired limits
  116. spherical.theta = Math.max(
  117. this.minAzimuthAngle,
  118. Math.min(this.maxAzimuthAngle, spherical.theta)
  119. )
  120. // restrict spherical.phi to be between desired limits
  121. spherical.phi = Math.max(
  122. this.minPolarAngle,
  123. Math.min(this.maxPolarAngle, spherical.phi)
  124. )
  125. // restrict spherical.phi to be betwee EPS and PI-EPS
  126. spherical.phi = Math.max(EPS, Math.min(Math.PI - EPS, spherical.phi))
  127. var radius = offset.length() * scale
  128. // restrict radius to be between desired limits
  129. radius = Math.max(this.minDistance, Math.min(this.maxDistance, radius))
  130. // move target to panned location
  131. this.target.add(panOffset)
  132. offset.x = radius * Math.sin(spherical.phi) * Math.sin(spherical.theta)
  133. offset.y = radius * Math.cos(spherical.phi)
  134. offset.z = radius * Math.sin(spherical.phi) * Math.cos(spherical.theta)
  135. // rotate offset back to "camera-up-vector-is-up" space
  136. offset.applyQuaternion(quatInverse)
  137. position.copy(this.target).add(offset)
  138. this.object.lookAt(this.target)
  139. if (this.enableDamping === true) {
  140. spherical.thetaDelta *= 1 - this.dampingFactor
  141. spherical.phiDelta *= 1 - this.dampingFactor
  142. } else {
  143. spherical.thetaDelta = 0
  144. spherical.phiDelta = 0
  145. }
  146. scale = 1
  147. panOffset.set(0, 0, 0)
  148. // update condition is:
  149. // min(camera displacement, camera rotation in radians)^2 > EPS
  150. // using small-angle approximation cos(x/2) = 1 - x^2 / 8
  151. if (
  152. zoomChanged ||
  153. lastPosition.distanceToSquared(this.object.position) > EPS ||
  154. 8 * (1 - lastQuaternion.dot(this.object.quaternion)) > EPS
  155. ) {
  156. lastPosition.copy(this.object.position)
  157. lastQuaternion.copy(this.object.quaternion)
  158. zoomChanged = false
  159. return true
  160. }
  161. return false
  162. }
  163. })()
  164. // this method is exposed, but perhaps it would be better if we can make it private...
  165. this.update = (function() {
  166. var offset = new THREE.Vector3()
  167. // so camera.up is the orbit axis
  168. var quat = new THREE.Quaternion().setFromUnitVectors(
  169. object.up,
  170. new THREE.Vector3(0, 1, 0)
  171. )
  172. var quatInverse = quat.clone().inverse()
  173. var lastPosition = new THREE.Vector3()
  174. var lastQuaternion = new THREE.Quaternion()
  175. return function update() {
  176. var position = scope.object.position
  177. offset.copy(position).sub(scope.target)
  178. // rotate offset to "y-axis-is-up" space
  179. offset.applyQuaternion(quat)
  180. // angle from z-axis around y-axis
  181. spherical.setFromVector3(offset)
  182. if (scope.autoRotate && state === STATE.NONE) {
  183. rotateLeft(getAutoRotationAngle())
  184. }
  185. spherical.theta += sphericalDelta.theta
  186. spherical.phi += sphericalDelta.phi
  187. // restrict theta to be between desired limits
  188. spherical.theta = Math.max(
  189. scope.minAzimuthAngle,
  190. Math.min(scope.maxAzimuthAngle, spherical.theta)
  191. )
  192. // restrict phi to be between desired limits
  193. spherical.phi = Math.max(
  194. scope.minPolarAngle,
  195. Math.min(scope.maxPolarAngle, spherical.phi)
  196. )
  197. spherical.makeSafe()
  198. spherical.radius *= scale
  199. // restrict radius to be between desired limits
  200. spherical.radius = Math.max(
  201. scope.minDistance,
  202. Math.min(scope.maxDistance, spherical.radius)
  203. )
  204. // move target to panned location
  205. scope.target.add(panOffset)
  206. offset.setFromSpherical(spherical)
  207. // rotate offset back to "camera-up-vector-is-up" space
  208. offset.applyQuaternion(quatInverse)
  209. position.copy(scope.target).add(offset)
  210. scope.object.lookAt(scope.target)
  211. if (scope.enableDamping === true) {
  212. sphericalDelta.theta *= 1 - scope.dampingFactor
  213. sphericalDelta.phi *= 1 - scope.dampingFactor
  214. panOffset.multiplyScalar(1 - scope.dampingFactor)
  215. } else {
  216. sphericalDelta.set(0, 0, 0)
  217. panOffset.set(0, 0, 0)
  218. }
  219. scale = 1
  220. // update condition is:
  221. // min(camera displacement, camera rotation in radians)^2 > EPS
  222. // using small-angle approximation cos(x/2) = 1 - x^2 / 8
  223. if (
  224. zoomChanged ||
  225. lastPosition.distanceToSquared(scope.object.position) > EPS ||
  226. 8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS
  227. ) {
  228. scope.dispatchEvent(changeEvent)
  229. lastPosition.copy(scope.object.position)
  230. lastQuaternion.copy(scope.object.quaternion)
  231. zoomChanged = false
  232. return true
  233. }
  234. return false
  235. }
  236. })()
  237. this.dispose = function() {
  238. scope.domElement.removeEventListener('contextmenu', onContextMenu, false)
  239. scope.domElement.removeEventListener('mousedown', onMouseDown, false)
  240. scope.domElement.removeEventListener('wheel', onMouseWheel, false)
  241. scope.domElement.removeEventListener('touchstart', onTouchStart, false)
  242. scope.domElement.removeEventListener('touchend', onTouchEnd, false)
  243. scope.domElement.removeEventListener('touchmove', onTouchMove, false)
  244. document.removeEventListener('mousemove', onMouseMove, false)
  245. document.removeEventListener('mouseup', onMouseUp, false)
  246. window.removeEventListener('keydown', onKeyDown, false)
  247. //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
  248. }
  249. //
  250. // internals
  251. //
  252. var scope = this
  253. var changeEvent = { type: 'change' }
  254. var startEvent = { type: 'start' }
  255. var endEvent = { type: 'end' }
  256. var STATE = {
  257. NONE: -1,
  258. ROTATE: 0,
  259. DOLLY: 1,
  260. PAN: 2,
  261. TOUCH_ROTATE: 3,
  262. TOUCH_DOLLY_PAN: 4,
  263. }
  264. var state = STATE.NONE
  265. var EPS = 0.000001
  266. // current position in spherical coordinates
  267. var spherical = new THREE.Spherical()
  268. var sphericalDelta = new THREE.Spherical()
  269. var scale = 1
  270. var panOffset = new THREE.Vector3()
  271. var zoomChanged = false
  272. var rotateStart = new THREE.Vector2()
  273. var rotateEnd = new THREE.Vector2()
  274. var rotateDelta = new THREE.Vector2()
  275. var panStart = new THREE.Vector2()
  276. var panEnd = new THREE.Vector2()
  277. var panDelta = new THREE.Vector2()
  278. var dollyStart = new THREE.Vector2()
  279. var dollyEnd = new THREE.Vector2()
  280. var dollyDelta = new THREE.Vector2()
  281. function getAutoRotationAngle() {
  282. return ((2 * Math.PI) / 60 / 60) * scope.autoRotateSpeed
  283. }
  284. function getZoomScale() {
  285. return Math.pow(0.95, scope.zoomSpeed)
  286. }
  287. function rotateLeft(angle) {
  288. sphericalDelta.theta -= angle
  289. }
  290. function rotateUp(angle) {
  291. sphericalDelta.phi -= angle
  292. }
  293. var panLeft = (function() {
  294. var v = new THREE.Vector3()
  295. return function panLeft(distance, objectMatrix) {
  296. v.setFromMatrixColumn(objectMatrix, 0) // get X column of objectMatrix
  297. v.multiplyScalar(-distance)
  298. panOffset.add(v)
  299. }
  300. })()
  301. var panUp = (function() {
  302. var v = new THREE.Vector3()
  303. return function panUp(distance, objectMatrix) {
  304. if (scope.screenSpacePanning === true) {
  305. v.setFromMatrixColumn(objectMatrix, 1)
  306. } else {
  307. v.setFromMatrixColumn(objectMatrix, 0)
  308. v.crossVectors(scope.object.up, v)
  309. }
  310. v.multiplyScalar(distance)
  311. panOffset.add(v)
  312. }
  313. })()
  314. // deltaX and deltaY are in pixels; right and down are positive
  315. var pan = (function() {
  316. var offset = new THREE.Vector3()
  317. return function pan(deltaX, deltaY) {
  318. var element =
  319. scope.domElement === document ? scope.domElement.body : scope.domElement
  320. if (scope.object.isPerspectiveCamera) {
  321. // perspective
  322. var position = scope.object.position
  323. offset.copy(position).sub(scope.target)
  324. var targetDistance = offset.length()
  325. // half of the fov is center to top of screen
  326. targetDistance *= Math.tan(((scope.object.fov / 2) * Math.PI) / 180.0)
  327. // we use only clientHeight here so aspect ratio does not distort speed
  328. panLeft(
  329. (2 * deltaX * targetDistance) / element.clientHeight,
  330. scope.object.matrix
  331. )
  332. panUp(
  333. (2 * deltaY * targetDistance) / element.clientHeight,
  334. scope.object.matrix
  335. )
  336. } else if (scope.object.isOrthographicCamera) {
  337. // orthographic
  338. panLeft(
  339. (deltaX * (scope.object.right - scope.object.left)) /
  340. scope.object.zoom /
  341. element.clientWidth,
  342. scope.object.matrix
  343. )
  344. panUp(
  345. (deltaY * (scope.object.top - scope.object.bottom)) /
  346. scope.object.zoom /
  347. element.clientHeight,
  348. scope.object.matrix
  349. )
  350. } else {
  351. // camera neither orthographic nor perspective
  352. console.warn(
  353. 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.'
  354. )
  355. scope.enablePan = false
  356. }
  357. }
  358. })()
  359. function dollyIn(dollyScale) {
  360. if (scope.object.isPerspectiveCamera) {
  361. scale /= dollyScale
  362. } else if (scope.object.isOrthographicCamera) {
  363. scope.object.zoom = Math.max(
  364. scope.minZoom,
  365. Math.min(scope.maxZoom, scope.object.zoom * dollyScale)
  366. )
  367. scope.object.updateProjectionMatrix()
  368. zoomChanged = true
  369. } else {
  370. console.warn(
  371. 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.'
  372. )
  373. scope.enableZoom = false
  374. }
  375. }
  376. function dollyOut(dollyScale) {
  377. if (scope.object.isPerspectiveCamera) {
  378. scale *= dollyScale
  379. } else if (scope.object.isOrthographicCamera) {
  380. scope.object.zoom = Math.max(
  381. scope.minZoom,
  382. Math.min(scope.maxZoom, scope.object.zoom / dollyScale)
  383. )
  384. scope.object.updateProjectionMatrix()
  385. zoomChanged = true
  386. } else {
  387. console.warn(
  388. 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.'
  389. )
  390. scope.enableZoom = false
  391. }
  392. }
  393. //
  394. // event callbacks - update the object state
  395. //
  396. function handleMouseDownRotate(event) {
  397. //console.log( 'handleMouseDownRotate' );
  398. rotateStart.set(event.clientX, event.clientY)
  399. }
  400. function handleMouseDownDolly(event) {
  401. //console.log( 'handleMouseDownDolly' );
  402. dollyStart.set(event.clientX, event.clientY)
  403. }
  404. function handleMouseDownPan(event) {
  405. //console.log( 'handleMouseDownPan' );
  406. panStart.set(event.clientX, event.clientY)
  407. }
  408. function handleMouseMoveRotate(event) {
  409. //console.log( 'handleMouseMoveRotate' );
  410. rotateEnd.set(event.clientX, event.clientY)
  411. rotateDelta
  412. .subVectors(rotateEnd, rotateStart)
  413. .multiplyScalar(scope.rotateSpeed)
  414. var element =
  415. scope.domElement === document ? scope.domElement.body : scope.domElement
  416. rotateLeft((2 * Math.PI * rotateDelta.x) / element.clientHeight) // yes, height
  417. rotateUp((2 * Math.PI * rotateDelta.y) / element.clientHeight)
  418. rotateStart.copy(rotateEnd)
  419. scope.update()
  420. }
  421. function handleMouseMoveDolly(event) {
  422. //console.log( 'handleMouseMoveDolly' );
  423. dollyEnd.set(event.clientX, event.clientY)
  424. dollyDelta.subVectors(dollyEnd, dollyStart)
  425. if (dollyDelta.y > 0) {
  426. dollyIn(getZoomScale())
  427. } else if (dollyDelta.y < 0) {
  428. dollyOut(getZoomScale())
  429. }
  430. dollyStart.copy(dollyEnd)
  431. scope.update()
  432. }
  433. function handleMouseMovePan(event) {
  434. //console.log( 'handleMouseMovePan' );
  435. panEnd.set(event.clientX, event.clientY)
  436. panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed)
  437. pan(panDelta.x, panDelta.y)
  438. panStart.copy(panEnd)
  439. scope.update()
  440. }
  441. function handleMouseUp(event) {
  442. // console.log( 'handleMouseUp' );
  443. }
  444. function handleMouseWheel(event) {
  445. // console.log( 'handleMouseWheel' );
  446. if (event.deltaY < 0) {
  447. dollyOut(getZoomScale())
  448. } else if (event.deltaY > 0) {
  449. dollyIn(getZoomScale())
  450. }
  451. scope.update()
  452. }
  453. function handleKeyDown(event) {
  454. //console.log( 'handleKeyDown' );
  455. switch (event.keyCode) {
  456. case scope.keys.UP:
  457. pan(0, scope.keyPanSpeed)
  458. scope.update()
  459. break
  460. case scope.keys.BOTTOM:
  461. pan(0, -scope.keyPanSpeed)
  462. scope.update()
  463. break
  464. case scope.keys.LEFT:
  465. pan(scope.keyPanSpeed, 0)
  466. scope.update()
  467. break
  468. case scope.keys.RIGHT:
  469. pan(-scope.keyPanSpeed, 0)
  470. scope.update()
  471. break
  472. }
  473. }
  474. function handleTouchStartRotate(event) {
  475. //console.log( 'handleTouchStartRotate' );
  476. rotateStart.set(event.touches[0].pageX, event.touches[0].pageY)
  477. }
  478. function handleTouchStartDollyPan(event) {
  479. //console.log( 'handleTouchStartDollyPan' );
  480. if (scope.enableZoom) {
  481. var dx = event.touches[0].pageX - event.touches[1].pageX
  482. var dy = event.touches[0].pageY - event.touches[1].pageY
  483. var distance = Math.sqrt(dx * dx + dy * dy)
  484. dollyStart.set(0, distance)
  485. }
  486. if (scope.enablePan) {
  487. var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX)
  488. var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY)
  489. panStart.set(x, y)
  490. }
  491. }
  492. function handleTouchMoveRotate(event) {
  493. //console.log( 'handleTouchMoveRotate' );
  494. rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY)
  495. rotateDelta
  496. .subVectors(rotateEnd, rotateStart)
  497. .multiplyScalar(scope.rotateSpeed)
  498. var element =
  499. scope.domElement === document ? scope.domElement.body : scope.domElement
  500. rotateLeft((2 * Math.PI * rotateDelta.x) / element.clientHeight) // yes, height
  501. rotateUp((2 * Math.PI * rotateDelta.y) / element.clientHeight)
  502. rotateStart.copy(rotateEnd)
  503. scope.update()
  504. }
  505. function handleTouchMoveDollyPan(event) {
  506. //console.log( 'handleTouchMoveDollyPan' );
  507. if (scope.enableZoom) {
  508. var dx = event.touches[0].pageX - event.touches[1].pageX
  509. var dy = event.touches[0].pageY - event.touches[1].pageY
  510. var distance = Math.sqrt(dx * dx + dy * dy)
  511. dollyEnd.set(0, distance)
  512. dollyDelta.set(0, Math.pow(dollyEnd.y / dollyStart.y, scope.zoomSpeed))
  513. dollyIn(dollyDelta.y)
  514. dollyStart.copy(dollyEnd)
  515. }
  516. if (scope.enablePan) {
  517. var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX)
  518. var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY)
  519. panEnd.set(x, y)
  520. panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed)
  521. pan(panDelta.x, panDelta.y)
  522. panStart.copy(panEnd)
  523. }
  524. scope.update()
  525. }
  526. function handleTouchEnd(event) {
  527. //console.log( 'handleTouchEnd' );
  528. }
  529. //
  530. // event handlers - FSM: listen for events and reset state
  531. //
  532. function onMouseDown(event) {
  533. if (scope.enabled === false) return
  534. event.preventDefault()
  535. switch (event.button) {
  536. case scope.mouseButtons.LEFT:
  537. if (event.ctrlKey || event.metaKey) {
  538. if (scope.enablePan === false) return
  539. handleMouseDownPan(event)
  540. state = STATE.PAN
  541. } else {
  542. if (scope.enableRotate === false) return
  543. handleMouseDownRotate(event)
  544. state = STATE.ROTATE
  545. }
  546. break
  547. case scope.mouseButtons.MIDDLE:
  548. if (scope.enableZoom === false) return
  549. handleMouseDownDolly(event)
  550. state = STATE.DOLLY
  551. break
  552. case scope.mouseButtons.RIGHT:
  553. if (scope.enablePan === false) return
  554. handleMouseDownPan(event)
  555. state = STATE.PAN
  556. break
  557. }
  558. if (state !== STATE.NONE) {
  559. document.addEventListener('mousemove', onMouseMove, false)
  560. document.addEventListener('mouseup', onMouseUp, false)
  561. scope.dispatchEvent(startEvent)
  562. }
  563. }
  564. function onMouseMove(event) {
  565. if (scope.enabled === false) return
  566. event.preventDefault()
  567. switch (state) {
  568. case STATE.ROTATE:
  569. if (scope.enableRotate === false) return
  570. handleMouseMoveRotate(event)
  571. break
  572. case STATE.DOLLY:
  573. if (scope.enableZoom === false) return
  574. handleMouseMoveDolly(event)
  575. break
  576. case STATE.PAN:
  577. if (scope.enablePan === false) return
  578. handleMouseMovePan(event)
  579. break
  580. }
  581. }
  582. function onMouseUp(event) {
  583. if (scope.enabled === false) return
  584. handleMouseUp(event)
  585. document.removeEventListener('mousemove', onMouseMove, false)
  586. document.removeEventListener('mouseup', onMouseUp, false)
  587. scope.dispatchEvent(endEvent)
  588. state = STATE.NONE
  589. }
  590. function onMouseWheel(event) {
  591. if (
  592. scope.enabled === false ||
  593. scope.enableZoom === false ||
  594. (state !== STATE.NONE && state !== STATE.ROTATE)
  595. )
  596. return
  597. event.preventDefault()
  598. event.stopPropagation()
  599. scope.dispatchEvent(startEvent)
  600. handleMouseWheel(event)
  601. scope.dispatchEvent(endEvent)
  602. }
  603. function onKeyDown(event) {
  604. if (
  605. scope.enabled === false ||
  606. scope.enableKeys === false ||
  607. scope.enablePan === false
  608. )
  609. return
  610. handleKeyDown(event)
  611. }
  612. function onTouchStart(event) {
  613. if (scope.enabled === false) return
  614. event.preventDefault()
  615. switch (event.touches.length) {
  616. case 1: // one-fingered touch: rotate
  617. if (scope.enableRotate === false) return
  618. handleTouchStartRotate(event)
  619. state = STATE.TOUCH_ROTATE
  620. break
  621. case 2: // two-fingered touch: dolly-pan
  622. if (scope.enableZoom === false && scope.enablePan === false) return
  623. handleTouchStartDollyPan(event)
  624. state = STATE.TOUCH_DOLLY_PAN
  625. break
  626. default:
  627. state = STATE.NONE
  628. }
  629. if (state !== STATE.NONE) {
  630. scope.dispatchEvent(startEvent)
  631. }
  632. }
  633. function onTouchMove(event) {
  634. if (scope.enabled === false) return
  635. event.preventDefault()
  636. event.stopPropagation()
  637. switch (event.touches.length) {
  638. case 1: // one-fingered touch: rotate
  639. if (scope.enableRotate === false) return
  640. if (state !== STATE.TOUCH_ROTATE) return // is this needed?
  641. handleTouchMoveRotate(event)
  642. break
  643. case 2: // two-fingered touch: dolly-pan
  644. if (scope.enableZoom === false && scope.enablePan === false) return
  645. if (state !== STATE.TOUCH_DOLLY_PAN) return // is this needed?
  646. handleTouchMoveDollyPan(event)
  647. break
  648. default:
  649. state = STATE.NONE
  650. }
  651. }
  652. function onTouchEnd(event) {
  653. if (scope.enabled === false) return
  654. handleTouchEnd(event)
  655. scope.dispatchEvent(endEvent)
  656. state = STATE.NONE
  657. }
  658. function onContextMenu(event) {
  659. if (scope.enabled === false) return
  660. event.preventDefault()
  661. }
  662. //
  663. scope.domElement.addEventListener('contextmenu', onContextMenu, false)
  664. scope.domElement.addEventListener('mousedown', onMouseDown, false)
  665. scope.domElement.addEventListener('wheel', onMouseWheel, false)
  666. scope.domElement.addEventListener('touchstart', onTouchStart, false)
  667. scope.domElement.addEventListener('touchend', onTouchEnd, false)
  668. scope.domElement.addEventListener('touchmove', onTouchMove, false)
  669. window.addEventListener('keydown', onKeyDown, false)
  670. // force an update at start
  671. this.update()
  672. }
  673. THREE.OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype)
  674. THREE.OrbitControls.prototype.constructor = THREE.OrbitControls
  675. Object.defineProperties(THREE.OrbitControls.prototype, {
  676. center: {
  677. get: function() {
  678. console.warn('THREE.OrbitControls: .center has been renamed to .target')
  679. return this.target
  680. },
  681. },
  682. // backward compatibility
  683. noZoom: {
  684. get: function() {
  685. console.warn(
  686. 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.'
  687. )
  688. return !this.enableZoom
  689. },
  690. set: function(value) {
  691. console.warn(
  692. 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.'
  693. )
  694. this.enableZoom = !value
  695. },
  696. },
  697. noRotate: {
  698. get: function() {
  699. console.warn(
  700. 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.'
  701. )
  702. return !this.enableRotate
  703. },
  704. set: function(value) {
  705. console.warn(
  706. 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.'
  707. )
  708. this.enableRotate = !value
  709. },
  710. },
  711. noPan: {
  712. get: function() {
  713. console.warn(
  714. 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.'
  715. )
  716. return !this.enablePan
  717. },
  718. set: function(value) {
  719. console.warn(
  720. 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.'
  721. )
  722. this.enablePan = !value
  723. },
  724. },
  725. noKeys: {
  726. get: function() {
  727. console.warn(
  728. 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.'
  729. )
  730. return !this.enableKeys
  731. },
  732. set: function(value) {
  733. console.warn(
  734. 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.'
  735. )
  736. this.enableKeys = !value
  737. },
  738. },
  739. staticMoving: {
  740. get: function() {
  741. console.warn(
  742. 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.'
  743. )
  744. return !this.enableDamping
  745. },
  746. set: function(value) {
  747. console.warn(
  748. 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.'
  749. )
  750. this.enableDamping = !value
  751. },
  752. },
  753. dynamicDampingFactor: {
  754. get: function() {
  755. console.warn(
  756. 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.'
  757. )
  758. return this.dampingFactor
  759. },
  760. set: function(value) {
  761. console.warn(
  762. 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.'
  763. )
  764. this.dampingFactor = value
  765. },
  766. },
  767. })