arcRotateCameraPointersInput.ts 9.0 KB

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