arcRotateCameraPointersInput.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import { Nullable } from "../../types";
  2. import { serialize } from "../../Misc/decorators";
  3. import { ArcRotateCamera } from "../../Cameras/arcRotateCamera";
  4. import { CameraInputTypes } from "../../Cameras/cameraInputsManager";
  5. import { BaseCameraPointersInput } from "../../Cameras/Inputs/BaseCameraPointersInput";
  6. import { PointerTouch } from "../../Events/pointerEvents";
  7. import { IPointerEvent } from "../../Events/deviceInputEvents";
  8. /**
  9. * Manage the pointers inputs to control an arc rotate camera.
  10. * @see https://doc.babylonjs.com/how_to/customizing_camera_inputs
  11. */
  12. export class ArcRotateCameraPointersInput extends BaseCameraPointersInput {
  13. /**
  14. * Defines the camera the input is attached to.
  15. */
  16. public camera: ArcRotateCamera;
  17. /**
  18. * Gets the class name of the current input.
  19. * @returns the class name
  20. */
  21. public getClassName(): string {
  22. return "ArcRotateCameraPointersInput";
  23. }
  24. /**
  25. * Defines the buttons associated with the input to handle camera move.
  26. */
  27. @serialize()
  28. public buttons = [0, 1, 2];
  29. /**
  30. * Defines the pointer angular sensibility along the X axis or how fast is
  31. * the camera rotating.
  32. */
  33. @serialize()
  34. public angularSensibilityX = 1000.0;
  35. /**
  36. * Defines the pointer angular sensibility along the Y axis or how fast is
  37. * the camera rotating.
  38. */
  39. @serialize()
  40. public angularSensibilityY = 1000.0;
  41. /**
  42. * Defines the pointer pinch precision or how fast is the camera zooming.
  43. */
  44. @serialize()
  45. public pinchPrecision = 12.0;
  46. /**
  47. * pinchDeltaPercentage will be used instead of pinchPrecision if different
  48. * from 0.
  49. * It defines the percentage of current camera.radius to use as delta when
  50. * pinch zoom is used.
  51. */
  52. @serialize()
  53. public pinchDeltaPercentage = 0;
  54. /**
  55. * When useNaturalPinchZoom is true, multi touch zoom will zoom in such
  56. * that any object in the plane at the camera's target point will scale
  57. * perfectly with finger motion.
  58. * Overrides pinchDeltaPercentage and pinchPrecision.
  59. */
  60. @serialize()
  61. public useNaturalPinchZoom: boolean = false;
  62. /**
  63. * Defines whether zoom (2 fingers pinch) is enabled through multitouch
  64. */
  65. @serialize()
  66. public pinchZoom: boolean = true;
  67. /**
  68. * Defines the pointer panning sensibility or how fast is the camera moving.
  69. */
  70. @serialize()
  71. public panningSensibility: number = 1000.0;
  72. /**
  73. * Defines whether panning (2 fingers swipe) is enabled through multitouch.
  74. */
  75. @serialize()
  76. public multiTouchPanning: boolean = true;
  77. /**
  78. * Defines whether panning is enabled for both pan (2 fingers swipe) and
  79. * zoom (pinch) through multitouch.
  80. */
  81. @serialize()
  82. public multiTouchPanAndZoom: boolean = true;
  83. /**
  84. * Revers pinch action direction.
  85. */
  86. public pinchInwards = true;
  87. private _isPanClick: boolean = false;
  88. private _twoFingerActivityCount: number = 0;
  89. private _isPinching: boolean = false;
  90. /**
  91. * Move camera from multi touch panning positions.
  92. */
  93. private _computeMultiTouchPanning(
  94. previousMultiTouchPanPosition: Nullable<PointerTouch>,
  95. multiTouchPanPosition: Nullable<PointerTouch>
  96. ): void {
  97. if (this.panningSensibility !== 0 && previousMultiTouchPanPosition
  98. && multiTouchPanPosition) {
  99. var moveDeltaX = multiTouchPanPosition.x - previousMultiTouchPanPosition.x;
  100. var moveDeltaY = multiTouchPanPosition.y - previousMultiTouchPanPosition.y;
  101. this.camera.inertialPanningX += -moveDeltaX / this.panningSensibility;
  102. this.camera.inertialPanningY += moveDeltaY / this.panningSensibility;
  103. }
  104. }
  105. /**
  106. * Move camera from pinch zoom distances.
  107. */
  108. private _computePinchZoom(
  109. previousPinchSquaredDistance: number,
  110. pinchSquaredDistance: number
  111. ): void {
  112. if (this.useNaturalPinchZoom) {
  113. this.camera.radius = this.camera.radius *
  114. Math.sqrt(previousPinchSquaredDistance) / Math.sqrt(pinchSquaredDistance);
  115. } else if (this.pinchDeltaPercentage) {
  116. this.camera.inertialRadiusOffset +=
  117. (pinchSquaredDistance - previousPinchSquaredDistance) * 0.001 *
  118. this.camera.radius * this.pinchDeltaPercentage;
  119. }
  120. else {
  121. this.camera.inertialRadiusOffset +=
  122. (pinchSquaredDistance - previousPinchSquaredDistance) /
  123. (this.pinchPrecision * (this.pinchInwards ? 1 : -1) *
  124. (this.angularSensibilityX + this.angularSensibilityY) / 2);
  125. }
  126. }
  127. /**
  128. * Called on pointer POINTERMOVE event if only a single touch is active.
  129. */
  130. protected onTouch(point: Nullable<PointerTouch>,
  131. offsetX: number,
  132. offsetY: number): void {
  133. if (this.panningSensibility !== 0 &&
  134. ((this._ctrlKey && this.camera._useCtrlForPanning) || this._isPanClick)) {
  135. this.camera.inertialPanningX += -offsetX / this.panningSensibility;
  136. this.camera.inertialPanningY += offsetY / this.panningSensibility;
  137. } else {
  138. this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX;
  139. this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY;
  140. }
  141. }
  142. /**
  143. * Called on pointer POINTERDOUBLETAP event.
  144. */
  145. protected onDoubleTap(type: string) {
  146. if (this.camera.useInputToRestoreState) {
  147. this.camera.restoreState();
  148. }
  149. }
  150. /**
  151. * Called on pointer POINTERMOVE event if multiple touches are active.
  152. */
  153. protected onMultiTouch(pointA: Nullable<PointerTouch>,
  154. pointB: Nullable<PointerTouch>,
  155. previousPinchSquaredDistance: number,
  156. pinchSquaredDistance: number,
  157. previousMultiTouchPanPosition: Nullable<PointerTouch>,
  158. multiTouchPanPosition: Nullable<PointerTouch>): void
  159. {
  160. if (previousPinchSquaredDistance === 0 && previousMultiTouchPanPosition === null) {
  161. // First time this method is called for new pinch.
  162. // Next time this is called there will be a
  163. // previousPinchSquaredDistance and pinchSquaredDistance to compare.
  164. return;
  165. }
  166. if (pinchSquaredDistance === 0 && multiTouchPanPosition === null) {
  167. // Last time this method is called at the end of a pinch.
  168. return;
  169. }
  170. // Zoom and panning enabled together
  171. if (this.multiTouchPanAndZoom) {
  172. this._computePinchZoom(previousPinchSquaredDistance, pinchSquaredDistance);
  173. this._computeMultiTouchPanning(previousMultiTouchPanPosition, multiTouchPanPosition);
  174. // Zoom and panning enabled but only one at a time
  175. } else if (this.multiTouchPanning && this.pinchZoom) {
  176. this._twoFingerActivityCount++;
  177. if (this._isPinching || (this._twoFingerActivityCount < 20
  178. && Math.abs(Math.sqrt(pinchSquaredDistance) - Math.sqrt(previousPinchSquaredDistance)) >
  179. this.camera.pinchToPanMaxDistance)) {
  180. // Since pinch has not been active long, assume we intend to zoom.
  181. this._computePinchZoom(previousPinchSquaredDistance, pinchSquaredDistance);
  182. // Since we are pinching, remain pinching on next iteration.
  183. this._isPinching = true;
  184. } else {
  185. // Pause between pinch starting and moving implies not a zoom event. Pan instead.
  186. this._computeMultiTouchPanning(previousMultiTouchPanPosition, multiTouchPanPosition);
  187. }
  188. // Panning enabled, zoom disabled
  189. } else if (this.multiTouchPanning) {
  190. this._computeMultiTouchPanning(previousMultiTouchPanPosition, multiTouchPanPosition);
  191. // Zoom enabled, panning disabled
  192. } else if (this.pinchZoom) {
  193. this._computePinchZoom(previousPinchSquaredDistance, pinchSquaredDistance);
  194. }
  195. }
  196. /**
  197. * Called each time a new POINTERDOWN event occurs. Ie, for each button
  198. * press.
  199. */
  200. protected onButtonDown(evt: IPointerEvent): void {
  201. this._isPanClick = evt.button === this.camera._panningMouseButton;
  202. }
  203. /**
  204. * Called each time a new POINTERUP event occurs. Ie, for each button
  205. * release.
  206. */
  207. protected onButtonUp(evt: IPointerEvent): void {
  208. this._twoFingerActivityCount = 0;
  209. this._isPinching = false;
  210. }
  211. /**
  212. * Called when window becomes inactive.
  213. */
  214. protected onLostFocus(): void {
  215. this._isPanClick = false;
  216. this._twoFingerActivityCount = 0;
  217. this._isPinching = false;
  218. }
  219. }
  220. (<any>CameraInputTypes)["ArcRotateCameraPointersInput"] =
  221. ArcRotateCameraPointersInput;