@pmndrs/klipp 0.0.0-stage → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +72 -2
  3. package/dist/_virtual/_rolldown/runtime.js +13 -0
  4. package/dist/core/CameraState.d.ts +32 -0
  5. package/dist/core/CameraState.js +97 -0
  6. package/dist/core/EventDispatcher.d.ts +18 -0
  7. package/dist/core/EventDispatcher.js +28 -0
  8. package/dist/core/Klipp.d.ts +109 -0
  9. package/dist/core/Klipp.js +253 -0
  10. package/dist/core/TargetExtent.d.ts +11 -0
  11. package/dist/core/TargetExtent.js +54 -0
  12. package/dist/core/TargetPose.d.ts +13 -0
  13. package/dist/core/TargetPose.js +21 -0
  14. package/dist/core/VirtualCamera.d.ts +102 -0
  15. package/dist/core/VirtualCamera.js +290 -0
  16. package/dist/core/aim/HardLookAtAim.d.ts +10 -0
  17. package/dist/core/aim/HardLookAtAim.js +19 -0
  18. package/dist/core/aim/PanTiltAim.d.ts +26 -0
  19. package/dist/core/aim/PanTiltAim.js +38 -0
  20. package/dist/core/aim/RotateWithFollowTargetAim.d.ts +19 -0
  21. package/dist/core/aim/RotateWithFollowTargetAim.js +23 -0
  22. package/dist/core/aim/RotationComposerAim.d.ts +27 -0
  23. package/dist/core/aim/RotationComposerAim.js +27 -0
  24. package/dist/core/aim/hardLookAt.d.ts +4 -0
  25. package/dist/core/aim/hardLookAt.js +13 -0
  26. package/dist/core/aim/index.d.ts +11 -0
  27. package/dist/core/aim/lookRotation.d.ts +6 -0
  28. package/dist/core/aim/lookRotation.js +46 -0
  29. package/dist/core/aim/panTilt.d.ts +16 -0
  30. package/dist/core/aim/panTilt.js +121 -0
  31. package/dist/core/aim/rotateWithFollowTarget.d.ts +20 -0
  32. package/dist/core/aim/rotateWithFollowTarget.js +35 -0
  33. package/dist/core/aim/rotationComposer.d.ts +58 -0
  34. package/dist/core/aim/rotationComposer.js +284 -0
  35. package/dist/core/blend/BlendCurves.d.ts +19 -0
  36. package/dist/core/blend/BlendCurves.js +22 -0
  37. package/dist/core/blend/BlendDefinition.d.ts +21 -0
  38. package/dist/core/blend/BlendDefinition.js +9 -0
  39. package/dist/core/blend/BlendDriver.d.ts +23 -0
  40. package/dist/core/blend/BlendDriver.js +42 -0
  41. package/dist/core/blend/BlendHints.d.ts +9 -0
  42. package/dist/core/blend/BlendHints.js +13 -0
  43. package/dist/core/blend/blend.d.ts +41 -0
  44. package/dist/core/blend/blend.js +110 -0
  45. package/dist/core/blend/index.d.ts +6 -0
  46. package/dist/core/blend/lerpCameraState.d.ts +4 -0
  47. package/dist/core/blend/lerpCameraState.js +143 -0
  48. package/dist/core/body/BindingModes.d.ts +9 -0
  49. package/dist/core/body/BindingModes.js +11 -0
  50. package/dist/core/body/FollowBody.d.ts +24 -0
  51. package/dist/core/body/FollowBody.js +26 -0
  52. package/dist/core/body/HardLockToTargetBody.d.ts +17 -0
  53. package/dist/core/body/HardLockToTargetBody.js +20 -0
  54. package/dist/core/body/OrbitFollowBody.d.ts +47 -0
  55. package/dist/core/body/OrbitFollowBody.js +48 -0
  56. package/dist/core/body/PositionComposerBody.d.ts +28 -0
  57. package/dist/core/body/PositionComposerBody.js +27 -0
  58. package/dist/core/body/follow.d.ts +33 -0
  59. package/dist/core/body/follow.js +83 -0
  60. package/dist/core/body/hardLockToTarget.d.ts +17 -0
  61. package/dist/core/body/hardLockToTarget.js +26 -0
  62. package/dist/core/body/index.d.ts +17 -0
  63. package/dist/core/body/orbitFollow.d.ts +76 -0
  64. package/dist/core/body/orbitFollow.js +336 -0
  65. package/dist/core/body/positionComposer.d.ts +56 -0
  66. package/dist/core/body/positionComposer.js +207 -0
  67. package/dist/core/body/safeFromToRotation.d.ts +3 -0
  68. package/dist/core/body/safeFromToRotation.js +39 -0
  69. package/dist/core/body/threeRing.d.ts +28 -0
  70. package/dist/core/body/threeRing.js +100 -0
  71. package/dist/core/body/tracker.d.ts +42 -0
  72. package/dist/core/body/tracker.js +193 -0
  73. package/dist/core/damping/Damper.d.ts +11 -0
  74. package/dist/core/damping/Damper.js +22 -0
  75. package/dist/core/damping/damping.d.ts +30 -0
  76. package/dist/core/damping/damping.js +119 -0
  77. package/dist/core/damping/index.d.ts +5 -0
  78. package/dist/core/damping/predictor.d.ts +16 -0
  79. package/dist/core/damping/predictor.js +56 -0
  80. package/dist/core/debug/debugZones.d.ts +16 -0
  81. package/dist/core/debug/debugZones.js +38 -0
  82. package/dist/core/debug/index.d.ts +2 -0
  83. package/dist/core/extension/GroupFramingExtension.d.ts +26 -0
  84. package/dist/core/extension/GroupFramingExtension.js +24 -0
  85. package/dist/core/extension/LensExtension.d.ts +20 -0
  86. package/dist/core/extension/LensExtension.js +12 -0
  87. package/dist/core/extension/groupFraming.d.ts +62 -0
  88. package/dist/core/extension/groupFraming.js +271 -0
  89. package/dist/core/extension/index.d.ts +6 -0
  90. package/dist/core/extension/lens.d.ts +35 -0
  91. package/dist/core/extension/lens.js +57 -0
  92. package/dist/core/groups/ClearShot.d.ts +32 -0
  93. package/dist/core/groups/MixingCamera.d.ts +20 -0
  94. package/dist/core/groups/Sequencer.d.ts +24 -0
  95. package/dist/core/groups/StateDrivenCamera.d.ts +26 -0
  96. package/dist/core/groups/clearShotState.d.ts +32 -0
  97. package/dist/core/groups/index.d.ts +11 -0
  98. package/dist/core/groups/mixCameraStates.d.ts +13 -0
  99. package/dist/core/groups/sequencerState.d.ts +26 -0
  100. package/dist/core/groups/stateDrivenState.d.ts +28 -0
  101. package/dist/core/impulse/ImpulseField.d.ts +15 -0
  102. package/dist/core/impulse/ImpulseField.js +22 -0
  103. package/dist/core/impulse/ImpulseListenerNoise.d.ts +19 -0
  104. package/dist/core/impulse/ImpulseListenerNoise.js +29 -0
  105. package/dist/core/impulse/impulseListener.d.ts +13 -0
  106. package/dist/core/impulse/impulseListener.js +14 -0
  107. package/dist/core/impulse/impulses.d.ts +54 -0
  108. package/dist/core/impulse/impulses.js +84 -0
  109. package/dist/core/impulse/index.d.ts +6 -0
  110. package/dist/core/index.d.ts +20 -0
  111. package/dist/core/index.js +52 -0
  112. package/dist/core/input/InputAxis.d.ts +29 -0
  113. package/dist/core/input/InputAxis.js +28 -0
  114. package/dist/core/input/InputAxisOwner.d.ts +6 -0
  115. package/dist/core/input/InputAxisOwner.js +4 -0
  116. package/dist/core/input/InputController.d.ts +15 -0
  117. package/dist/core/input/InputController.js +24 -0
  118. package/dist/core/input/axis.d.ts +52 -0
  119. package/dist/core/input/axis.js +89 -0
  120. package/dist/core/input/consumedInput.d.ts +37 -0
  121. package/dist/core/input/consumedInput.js +34 -0
  122. package/dist/core/input/index.d.ts +9 -0
  123. package/dist/core/input/inputMapping.d.ts +46 -0
  124. package/dist/core/input/inputMapping.js +55 -0
  125. package/dist/core/input/shortestWrappedDelta.d.ts +2 -0
  126. package/dist/core/input/shortestWrappedDelta.js +11 -0
  127. package/dist/core/internal.d.ts +16 -0
  128. package/dist/core/internal.js +19 -0
  129. package/dist/core/klippState.d.ts +78 -0
  130. package/dist/core/klippState.js +124 -0
  131. package/dist/core/noise/BasicMultiChannelPerlinNoise.d.ts +21 -0
  132. package/dist/core/noise/BasicMultiChannelPerlinNoise.js +13 -0
  133. package/dist/core/noise/index.d.ts +3 -0
  134. package/dist/core/noise/perlinNoise.d.ts +36 -0
  135. package/dist/core/noise/perlinNoise.js +110 -0
  136. package/dist/core/params.d.ts +2 -0
  137. package/dist/core/params.js +12 -0
  138. package/dist/dom/DebugOverlay.d.ts +11 -0
  139. package/dist/dom/DebugOverlay.js +96 -0
  140. package/dist/dom/InputControllerDom.d.ts +15 -0
  141. package/dist/dom/InputControllerDom.js +31 -0
  142. package/dist/dom/InputSystem.d.ts +40 -0
  143. package/dist/dom/InputSystem.js +113 -0
  144. package/dist/dom/index.d.ts +3 -0
  145. package/dist/dom/index.js +5 -0
  146. package/dist/input-sources/buttonInput.d.ts +15 -0
  147. package/dist/input-sources/buttonInput.js +23 -0
  148. package/dist/input-sources/isInsideInteractiveArea.d.ts +9 -0
  149. package/dist/input-sources/isInsideInteractiveArea.js +11 -0
  150. package/dist/input-sources/mouse.d.ts +47 -0
  151. package/dist/input-sources/mouse.js +171 -0
  152. package/dist/input-sources/safariGesture.d.ts +32 -0
  153. package/dist/input-sources/safariGesture.js +107 -0
  154. package/dist/input-sources/suppressNativeGestures.d.ts +5 -0
  155. package/dist/input-sources/suppressNativeGestures.js +32 -0
  156. package/dist/input-sources/touch.d.ts +47 -0
  157. package/dist/input-sources/touch.js +173 -0
  158. package/dist/input-sources/wheel.d.ts +28 -0
  159. package/dist/input-sources/wheel.js +44 -0
  160. package/dist/react/CameraFrustumHelper.d.ts +13 -0
  161. package/dist/react/CameraFrustumHelper.js +32 -0
  162. package/dist/react/DebugZoneOverlay.d.ts +7 -0
  163. package/dist/react/DebugZoneOverlay.js +23 -0
  164. package/dist/react/Klipp.d.ts +20 -0
  165. package/dist/react/Klipp.js +47 -0
  166. package/dist/react/KlippContext.d.ts +5 -0
  167. package/dist/react/KlippContext.js +11 -0
  168. package/dist/react/VirtualCamera.d.ts +28 -0
  169. package/dist/react/VirtualCamera.js +81 -0
  170. package/dist/react/VirtualCameraContext.d.ts +11 -0
  171. package/dist/react/VirtualCameraContext.js +21 -0
  172. package/dist/react/aim/Aim.d.ts +11 -0
  173. package/dist/react/aim/Aim.js +14 -0
  174. package/dist/react/aim/HardLookAt.d.ts +10 -0
  175. package/dist/react/aim/HardLookAt.js +15 -0
  176. package/dist/react/aim/PanTilt.d.ts +31 -0
  177. package/dist/react/aim/PanTilt.js +38 -0
  178. package/dist/react/aim/RotateWithFollowTarget.d.ts +10 -0
  179. package/dist/react/aim/RotateWithFollowTarget.js +18 -0
  180. package/dist/react/aim/RotationComposer.d.ts +12 -0
  181. package/dist/react/aim/RotationComposer.js +34 -0
  182. package/dist/react/body/Body.d.ts +11 -0
  183. package/dist/react/body/Body.js +14 -0
  184. package/dist/react/body/CameraControls.d.ts +50 -0
  185. package/dist/react/body/CameraControls.js +106 -0
  186. package/dist/react/body/Follow.d.ts +10 -0
  187. package/dist/react/body/Follow.js +20 -0
  188. package/dist/react/body/HardLockToTarget.d.ts +10 -0
  189. package/dist/react/body/HardLockToTarget.js +18 -0
  190. package/dist/react/body/OrbitFollow.d.ts +25 -0
  191. package/dist/react/body/OrbitFollow.js +67 -0
  192. package/dist/react/body/PositionComposer.d.ts +12 -0
  193. package/dist/react/body/PositionComposer.js +32 -0
  194. package/dist/react/body/camera-controls.d.ts +2 -0
  195. package/dist/react/body/camera-controls.js +3 -0
  196. package/dist/react/extension/Extension.d.ts +7 -0
  197. package/dist/react/extension/Extension.js +10 -0
  198. package/dist/react/extension/GroupFraming.d.ts +14 -0
  199. package/dist/react/extension/GroupFraming.js +52 -0
  200. package/dist/react/extension/Lens.d.ts +7 -0
  201. package/dist/react/extension/Lens.js +31 -0
  202. package/dist/react/impulse/ImpulseListener.d.ts +12 -0
  203. package/dist/react/impulse/ImpulseListener.js +33 -0
  204. package/dist/react/index.d.ts +24 -0
  205. package/dist/react/index.js +24 -0
  206. package/dist/react/input/InputAxisOwnerContext.d.ts +3 -0
  207. package/dist/react/input/InputAxisOwnerContext.js +6 -0
  208. package/dist/react/input/InputController.d.ts +53 -0
  209. package/dist/react/input/InputController.js +115 -0
  210. package/dist/react/noise/BasicMultiChannelPerlin.d.ts +7 -0
  211. package/dist/react/noise/BasicMultiChannelPerlin.js +14 -0
  212. package/dist/react/noise/Noise.d.ts +5 -0
  213. package/dist/react/noise/Noise.js +6 -0
  214. package/dist/react/noise/useBasicMultiChannelPerlinNoise.d.ts +4 -0
  215. package/dist/react/noise/useBasicMultiChannelPerlinNoise.js +21 -0
  216. package/dist/react/useCameraTransitionEvent.d.ts +12 -0
  217. package/dist/react/useCameraTransitionEvent.js +14 -0
  218. package/dist/three/CameraFrustumHelperThree.d.ts +11 -0
  219. package/dist/three/CameraFrustumHelperThree.js +44 -0
  220. package/dist/three/KlippThree.d.ts +18 -0
  221. package/dist/three/KlippThree.js +50 -0
  222. package/dist/three/VirtualCameraThree.d.ts +35 -0
  223. package/dist/three/VirtualCameraThree.js +108 -0
  224. package/dist/three/aim/HardLookAtAimThree.d.ts +10 -0
  225. package/dist/three/aim/HardLookAtAimThree.js +14 -0
  226. package/dist/three/aim/PanTiltAimThree.d.ts +10 -0
  227. package/dist/three/aim/PanTiltAimThree.js +15 -0
  228. package/dist/three/aim/RotateWithFollowTargetAimThree.d.ts +11 -0
  229. package/dist/three/aim/RotateWithFollowTargetAimThree.js +15 -0
  230. package/dist/three/aim/RotationComposerAimThree.d.ts +26 -0
  231. package/dist/three/aim/RotationComposerAimThree.js +35 -0
  232. package/dist/three/body/CameraControlsBodyThree.d.ts +30 -0
  233. package/dist/three/body/CameraControlsBodyThree.js +54 -0
  234. package/dist/three/body/FollowBodyThree.d.ts +16 -0
  235. package/dist/three/body/FollowBodyThree.js +23 -0
  236. package/dist/three/body/HardLockToTargetBodyThree.d.ts +11 -0
  237. package/dist/three/body/HardLockToTargetBodyThree.js +14 -0
  238. package/dist/three/body/OrbitFollowBodyThree.d.ts +16 -0
  239. package/dist/three/body/OrbitFollowBodyThree.js +23 -0
  240. package/dist/three/body/OrbitFollowHelperThree.d.ts +23 -0
  241. package/dist/three/body/OrbitFollowHelperThree.js +85 -0
  242. package/dist/three/body/PositionComposerBodyThree.d.ts +24 -0
  243. package/dist/three/body/PositionComposerBodyThree.js +31 -0
  244. package/dist/three/camera.d.ts +10 -0
  245. package/dist/three/camera.js +39 -0
  246. package/dist/three/damping/Predictor.d.ts +12 -0
  247. package/dist/three/damping/Predictor.js +30 -0
  248. package/dist/three/damping/QuaternionDamper.d.ts +10 -0
  249. package/dist/three/damping/QuaternionDamper.js +28 -0
  250. package/dist/three/damping/Vector3Damper.d.ts +10 -0
  251. package/dist/three/damping/Vector3Damper.js +26 -0
  252. package/dist/three/extension/GroupFramingExtensionThree.d.ts +13 -0
  253. package/dist/three/extension/GroupFramingExtensionThree.js +23 -0
  254. package/dist/three/extension/TargetGroup.d.ts +32 -0
  255. package/dist/three/extension/TargetGroup.js +53 -0
  256. package/dist/three/index.d.ts +24 -0
  257. package/dist/three/index.js +25 -0
  258. package/dist/three/noise/BasicMultiChannelPerlinNoiseThree.d.ts +16 -0
  259. package/dist/three/noise/BasicMultiChannelPerlinNoiseThree.js +17 -0
  260. package/dist/three/readTargetExtent.d.ts +9 -0
  261. package/dist/three/readTargetExtent.js +25 -0
  262. package/dist/three/readTargetPose.d.ts +8 -0
  263. package/dist/three/readTargetPose.js +21 -0
  264. package/dist/three/resolve/Target.d.ts +17 -0
  265. package/dist/three/resolve/Target.js +62 -0
  266. package/dist/three/resolve/TargetRegistry.d.ts +27 -0
  267. package/dist/three/resolve/TargetRegistry.js +78 -0
  268. package/dist/three/resolve/resolveVector3.d.ts +12 -0
  269. package/dist/three/resolve/resolveVector3.js +29 -0
  270. package/package.json +120 -4
@@ -0,0 +1,47 @@
1
+ import { type Vec2 } from 'math';
2
+ import { type InteractiveArea } from './isInsideInteractiveArea.js';
3
+ export type FingerCount = 'one' | 'two' | 'three';
4
+ /** Up to three fingers: their movement per finger count, and two-finger pinch and twist, summed per frame. */
5
+ export type TouchState = {
6
+ /** Fingers down, up to three. */
7
+ fingers: number;
8
+ /** Movement this frame of the fingers' center, by how many were down at the time, in pixels. */
9
+ readonly drag: Record<FingerCount, Vec2>;
10
+ /** Two-finger pinch this frame, as the change in `ln(scale)`. Spreading is positive. */
11
+ pinchDelta: number;
12
+ /** Two-finger rotation this frame, in radians. */
13
+ twistDelta: number;
14
+ /** Only touches that start inside this normalized region count. */
15
+ interactiveArea: InteractiveArea | null;
16
+ /** Each two-finger gesture keeps only the stronger of pinch and twist. */
17
+ lockTouchAxis: boolean;
18
+ /** Internal. */
19
+ pending: TouchPending;
20
+ };
21
+ type Finger = {
22
+ id: number;
23
+ x: number;
24
+ y: number;
25
+ };
26
+ type TouchPending = {
27
+ drag: Record<FingerCount, Vec2>;
28
+ pinchDelta: number;
29
+ twistDelta: number;
30
+ /** The first `count` are down, in the order they touched. */
31
+ slots: [Finger, Finger, Finger];
32
+ count: number;
33
+ /** Two-finger gesture, measured from when the current pair formed. */
34
+ distance: number;
35
+ angle: number;
36
+ startDistance: number;
37
+ twistTotal: number;
38
+ axisLock: 'pinch' | 'twist' | null;
39
+ };
40
+ /** Which of pinch and twist a two-finger gesture mostly is so far, or `null` while it is even. */
41
+ export declare function dominantGesture(scaleFraction: number, twistDegrees: number): 'pinch' | 'twist' | null;
42
+ export declare const create: () => TouchState;
43
+ /** Listens to `element`'s touches. Returns a function that stops. */
44
+ export declare function connect(state: TouchState, element: HTMLElement, onInput?: () => void): () => void;
45
+ /** Starts a new frame: the input gathered since the last call becomes this frame's. */
46
+ export declare function update(state: TouchState): void;
47
+ export {};
@@ -0,0 +1,173 @@
1
+ import { isInsideInteractiveArea } from "./isInsideInteractiveArea.js";
2
+ import { suppressNativeGestures } from "./suppressNativeGestures.js";
3
+ import { deltaAngle, radiansToDegrees } from "math";
4
+ //#region src/input-sources/touch.ts
5
+ /** How many degrees of twist weigh as much as doubling the pinch distance, when choosing one. */
6
+ var SCALE_ANGLE_RATIO_INTENT_DEG = 30;
7
+ /** Which of pinch and twist a two-finger gesture mostly is so far, or `null` while it is even. */
8
+ function dominantGesture(scaleFraction, twistDegrees) {
9
+ const intent = Math.abs(scaleFraction) * SCALE_ANGLE_RATIO_INTENT_DEG - Math.abs(twistDegrees);
10
+ return intent < 0 ? "twist" : intent > 0 ? "pinch" : null;
11
+ }
12
+ var create = () => ({
13
+ fingers: 0,
14
+ drag: {
15
+ one: [0, 0],
16
+ two: [0, 0],
17
+ three: [0, 0]
18
+ },
19
+ pinchDelta: 0,
20
+ twistDelta: 0,
21
+ interactiveArea: null,
22
+ lockTouchAxis: false,
23
+ pending: {
24
+ drag: {
25
+ one: [0, 0],
26
+ two: [0, 0],
27
+ three: [0, 0]
28
+ },
29
+ pinchDelta: 0,
30
+ twistDelta: 0,
31
+ slots: [
32
+ {
33
+ id: 0,
34
+ x: 0,
35
+ y: 0
36
+ },
37
+ {
38
+ id: 0,
39
+ x: 0,
40
+ y: 0
41
+ },
42
+ {
43
+ id: 0,
44
+ x: 0,
45
+ y: 0
46
+ }
47
+ ],
48
+ count: 0,
49
+ distance: 0,
50
+ angle: 0,
51
+ startDistance: 0,
52
+ twistTotal: 0,
53
+ axisLock: null
54
+ }
55
+ });
56
+ function indexOf(pending, id) {
57
+ for (let i = 0; i < pending.count; i++) if (pending.slots[i].id === id) return i;
58
+ return -1;
59
+ }
60
+ /** Starts a fresh two-finger gesture from where the first two fingers are now. */
61
+ function resetPair(pending) {
62
+ if (pending.count < 2) return;
63
+ const [a, b] = pending.slots;
64
+ pending.distance = Math.hypot(b.x - a.x, b.y - a.y);
65
+ pending.angle = Math.atan2(b.y - a.y, b.x - a.x);
66
+ pending.startDistance = pending.distance;
67
+ pending.twistTotal = 0;
68
+ pending.axisLock = null;
69
+ }
70
+ function moveCenter(drag, count, moving, x, y) {
71
+ drag[0] += (x - moving.x) / count;
72
+ drag[1] += (y - moving.y) / count;
73
+ moving.x = x;
74
+ moving.y = y;
75
+ }
76
+ function movePair(state, moving, x, y) {
77
+ const pending = state.pending;
78
+ moveCenter(pending.drag.two, 2, moving, x, y);
79
+ const [a, b] = pending.slots;
80
+ const distance = Math.hypot(b.x - a.x, b.y - a.y);
81
+ const pinchStep = distance > 0 && pending.distance > 0 ? Math.log(distance / pending.distance) : 0;
82
+ pending.distance = distance;
83
+ const angle = Math.atan2(b.y - a.y, b.x - a.x);
84
+ const twistStep = deltaAngle(pending.angle, angle);
85
+ pending.angle = angle;
86
+ pending.twistTotal += twistStep;
87
+ if (state.lockTouchAxis && !pending.axisLock) pending.axisLock = dominantGesture(distance / pending.startDistance - 1, radiansToDegrees(pending.twistTotal));
88
+ if (pending.axisLock !== "twist") pending.pinchDelta += pinchStep;
89
+ if (pending.axisLock !== "pinch") pending.twistDelta += twistStep;
90
+ }
91
+ /** Listens to `element`'s touches. Returns a function that stops. */
92
+ function connect(state, element, onInput) {
93
+ const pending = state.pending;
94
+ const onPointerDown = (event) => {
95
+ if (event.pointerType !== "touch") return;
96
+ if (!isInsideInteractiveArea(element, state.interactiveArea, event.clientX, event.clientY)) return;
97
+ if (pending.count === 3 || indexOf(pending, event.pointerId) !== -1) return;
98
+ const finger = pending.slots[pending.count++];
99
+ finger.id = event.pointerId;
100
+ finger.x = event.clientX;
101
+ finger.y = event.clientY;
102
+ if (pending.count === 2) resetPair(pending);
103
+ try {
104
+ element.setPointerCapture(event.pointerId);
105
+ } catch {}
106
+ onInput?.();
107
+ };
108
+ const onPointerMove = (event) => {
109
+ if (event.pointerType !== "touch") return;
110
+ const index = indexOf(pending, event.pointerId);
111
+ if (index === -1) return;
112
+ const moving = pending.slots[index];
113
+ if (pending.count === 3) moveCenter(pending.drag.three, 3, moving, event.clientX, event.clientY);
114
+ else if (pending.count === 2) movePair(state, moving, event.clientX, event.clientY);
115
+ else moveCenter(pending.drag.one, 1, moving, event.clientX, event.clientY);
116
+ onInput?.();
117
+ };
118
+ const onPointerUp = (event) => {
119
+ if (event.pointerType !== "touch") return;
120
+ const index = indexOf(pending, event.pointerId);
121
+ if (index === -1) return;
122
+ const lifted = pending.slots[index];
123
+ for (let i = index; i < pending.count - 1; i++) pending.slots[i] = pending.slots[i + 1];
124
+ pending.slots[--pending.count] = lifted;
125
+ resetPair(pending);
126
+ onInput?.();
127
+ };
128
+ const liftAll = () => {
129
+ pending.count = 0;
130
+ };
131
+ const onVisibilityChange = () => {
132
+ if (document.hidden) liftAll();
133
+ };
134
+ const restoreStyles = suppressNativeGestures(element);
135
+ element.addEventListener("pointerdown", onPointerDown);
136
+ element.addEventListener("pointermove", onPointerMove);
137
+ element.addEventListener("pointerup", onPointerUp);
138
+ element.addEventListener("pointercancel", onPointerUp);
139
+ element.addEventListener("lostpointercapture", onPointerUp);
140
+ document.addEventListener("visibilitychange", onVisibilityChange);
141
+ window.addEventListener("blur", liftAll);
142
+ return () => {
143
+ restoreStyles();
144
+ element.removeEventListener("pointerdown", onPointerDown);
145
+ element.removeEventListener("pointermove", onPointerMove);
146
+ element.removeEventListener("pointerup", onPointerUp);
147
+ element.removeEventListener("pointercancel", onPointerUp);
148
+ element.removeEventListener("lostpointercapture", onPointerUp);
149
+ document.removeEventListener("visibilitychange", onVisibilityChange);
150
+ window.removeEventListener("blur", liftAll);
151
+ liftAll();
152
+ };
153
+ }
154
+ var copyAndZero = (out, source) => {
155
+ out[0] = source[0];
156
+ out[1] = source[1];
157
+ source[0] = 0;
158
+ source[1] = 0;
159
+ };
160
+ /** Starts a new frame: the input gathered since the last call becomes this frame's. */
161
+ function update(state) {
162
+ const pending = state.pending;
163
+ state.fingers = pending.count;
164
+ copyAndZero(state.drag.one, pending.drag.one);
165
+ copyAndZero(state.drag.two, pending.drag.two);
166
+ copyAndZero(state.drag.three, pending.drag.three);
167
+ state.pinchDelta = pending.pinchDelta;
168
+ state.twistDelta = pending.twistDelta;
169
+ pending.pinchDelta = 0;
170
+ pending.twistDelta = 0;
171
+ }
172
+ //#endregion
173
+ export { connect, create, dominantGesture, update };
@@ -0,0 +1,28 @@
1
+ import { type InteractiveArea } from './isInsideInteractiveArea.js';
2
+ /** Wheel and trackpad scrolling, in pixels, summed per frame. */
3
+ export type WheelState = {
4
+ /** Scroll since the last `update`. */
5
+ deltaX: number;
6
+ deltaY: number;
7
+ /** Trackpad pinch since the last `update`, as the change in `ln(scale)`. Spreading is positive. */
8
+ pinchDelta: number;
9
+ /** Only scrolls that start inside this normalized region count. */
10
+ interactiveArea: InteractiveArea | null;
11
+ /** Keep the page from scrolling under the element. */
12
+ preventPageScroll: boolean;
13
+ /** Keep a trackpad pinch from zooming the page. */
14
+ preventPageZoom: boolean;
15
+ /** Pixels per line, for wheels that report lines. */
16
+ pixelsPerLine: number;
17
+ /** Pixels per page, for wheels that report pages. */
18
+ pixelsPerPage: number;
19
+ /** Internal. */
20
+ pendingX: number;
21
+ pendingY: number;
22
+ pendingPinch: number;
23
+ };
24
+ export declare const create: () => WheelState;
25
+ /** Listens to `element`'s wheel. Returns a function that stops. */
26
+ export declare function connect(state: WheelState, element: HTMLElement, onInput?: () => void): () => void;
27
+ /** Starts a new frame: the scroll gathered since the last call becomes this frame's. */
28
+ export declare function update(state: WheelState): void;
@@ -0,0 +1,44 @@
1
+ import { isInsideInteractiveArea } from "./isInsideInteractiveArea.js";
2
+ //#region src/input-sources/wheel.ts
3
+ var create = () => ({
4
+ deltaX: 0,
5
+ deltaY: 0,
6
+ pinchDelta: 0,
7
+ interactiveArea: null,
8
+ preventPageScroll: true,
9
+ preventPageZoom: true,
10
+ pixelsPerLine: 33,
11
+ pixelsPerPage: 800,
12
+ pendingX: 0,
13
+ pendingY: 0,
14
+ pendingPinch: 0
15
+ });
16
+ var pixelsPerUnit = (state, deltaMode) => deltaMode === WheelEvent.DOM_DELTA_LINE ? state.pixelsPerLine : deltaMode === WheelEvent.DOM_DELTA_PAGE ? state.pixelsPerPage : 1;
17
+ /** Listens to `element`'s wheel. Returns a function that stops. */
18
+ function connect(state, element, onInput) {
19
+ const onWheel = (event) => {
20
+ if (!isInsideInteractiveArea(element, state.interactiveArea, event.clientX, event.clientY)) return;
21
+ if (event.ctrlKey ? state.preventPageZoom : state.preventPageScroll) event.preventDefault();
22
+ const scale = pixelsPerUnit(state, event.deltaMode);
23
+ if (event.ctrlKey) state.pendingPinch -= event.deltaY * scale / 100;
24
+ else if (event.shiftKey && event.deltaX === 0) state.pendingX += event.deltaY * scale;
25
+ else {
26
+ state.pendingX += event.deltaX * scale;
27
+ state.pendingY += event.deltaY * scale;
28
+ }
29
+ onInput?.();
30
+ };
31
+ element.addEventListener("wheel", onWheel, { passive: false });
32
+ return () => element.removeEventListener("wheel", onWheel);
33
+ }
34
+ /** Starts a new frame: the scroll gathered since the last call becomes this frame's. */
35
+ function update(state) {
36
+ state.deltaX = state.pendingX;
37
+ state.deltaY = state.pendingY;
38
+ state.pinchDelta = state.pendingPinch;
39
+ state.pendingX = 0;
40
+ state.pendingY = 0;
41
+ state.pendingPinch = 0;
42
+ }
43
+ //#endregion
44
+ export { connect, create, update };
@@ -0,0 +1,13 @@
1
+ import { type Ref } from 'react';
2
+ import { type CameraHelper, type ColorRepresentation } from 'three';
3
+ export type CameraFrustumHelperProps = {
4
+ /** Single color for the whole helper. */
5
+ color?: ColorRepresentation;
6
+ /** Maximum distance drawn for the frustum. */
7
+ maxDistance?: number;
8
+ /** Whether to hide the helper while its camera is live. */
9
+ hideWhenLive?: boolean;
10
+ ref?: Ref<CameraHelper>;
11
+ };
12
+ /** Debug helper that draws the current virtual camera frustum. */
13
+ export declare function CameraFrustumHelper({ color, maxDistance, hideWhenLive, ref, }?: CameraFrustumHelperProps): import("react").JSX.Element | null;
@@ -0,0 +1,32 @@
1
+ import { CameraFrustumHelperThree } from "../three/CameraFrustumHelperThree.js";
2
+ import { useIsLiveVirtualCamera, useVirtualCamera } from "./VirtualCameraContext.js";
3
+ import { Color } from "three";
4
+ import { useFrame, useThree } from "@react-three/fiber";
5
+ import { useEffect, useImperativeHandle, useState } from "react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ //#region src/react/CameraFrustumHelper.tsx
8
+ /** Debug helper that draws the current virtual camera frustum. */
9
+ function CameraFrustumHelper({ color, maxDistance = 1, hideWhenLive = true, ref } = {}) {
10
+ const { state } = useVirtualCamera();
11
+ const isLive = useIsLiveVirtualCamera();
12
+ const size = useThree((s) => s.size);
13
+ const [helper] = useState(() => new CameraFrustumHelperThree(maxDistance));
14
+ const [scratchColor] = useState(() => new Color());
15
+ helper.maxDistance = maxDistance;
16
+ useImperativeHandle(ref, () => helper, [helper]);
17
+ useEffect(() => () => helper.dispose(), [helper]);
18
+ useEffect(() => {
19
+ if (color === void 0) return;
20
+ scratchColor.set(color);
21
+ helper.setColors(scratchColor, scratchColor, scratchColor, scratchColor, scratchColor);
22
+ }, [
23
+ helper,
24
+ scratchColor,
25
+ color
26
+ ]);
27
+ useFrame(() => helper.sync(state, size.width / size.height));
28
+ if (hideWhenLive && isLive) return null;
29
+ return /* @__PURE__ */ jsx("primitive", { object: helper });
30
+ }
31
+ //#endregion
32
+ export { CameraFrustumHelper };
@@ -0,0 +1,7 @@
1
+ import { type DebugZone } from '../dom/DebugOverlay.js';
2
+ export type { DebugZone };
3
+ /** Renders debug zones and an optional crosshair while this camera is the active one. */
4
+ export declare function DebugZoneOverlay({ zones, crosshair }: {
5
+ zones: DebugZone[];
6
+ crosshair?: [number, number];
7
+ }): null;
@@ -0,0 +1,23 @@
1
+ import { useIsActiveVirtualCamera } from "./VirtualCameraContext.js";
2
+ import { DebugOverlay } from "../dom/DebugOverlay.js";
3
+ import { useThree } from "@react-three/fiber";
4
+ import { useEffect, useState } from "react";
5
+ //#region src/react/DebugZoneOverlay.tsx
6
+ /** Renders debug zones and an optional crosshair while this camera is the active one. */
7
+ function DebugZoneOverlay({ zones, crosshair }) {
8
+ const isActive = useIsActiveVirtualCamera();
9
+ const container = useThree((state) => state.gl.domElement.parentElement);
10
+ const [overlay] = useState(() => new DebugOverlay());
11
+ useEffect(() => {
12
+ if (!container) return;
13
+ overlay.connect(container);
14
+ return overlay.disconnect;
15
+ }, [container, overlay]);
16
+ useEffect(() => {
17
+ if (isActive) overlay.render(zones, crosshair);
18
+ else overlay.render([]);
19
+ });
20
+ return null;
21
+ }
22
+ //#endregion
23
+ export { DebugZoneOverlay };
@@ -0,0 +1,20 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { Camera } from 'three';
3
+ import type { KlippOptions } from '../core/Klipp.js';
4
+ import { type KlippMode } from '../three/KlippThree.js';
5
+ import { type CameraTransitionEventProps } from './useCameraTransitionEvent.js';
6
+ export type { KlippMode };
7
+ export type KlippProps = Pick<KlippOptions, 'defaultBlend' | 'customBlends'> & {
8
+ children?: ReactNode;
9
+ camera?: Camera;
10
+ /** See `KlippMode`. */
11
+ mode?: KlippMode;
12
+ };
13
+ /** Provides the camera driver and writes its output to the active camera. */
14
+ export declare function Klipp({ children, defaultBlend, customBlends, camera: cameraProp, mode }: KlippProps): import("react").JSX.Element;
15
+ export declare namespace Klipp {
16
+ export { KlippEvents as Events };
17
+ }
18
+ export type KlippEventsProps = CameraTransitionEventProps;
19
+ /** Listen to transitions from every camera in the nearest `<Klipp>`. */
20
+ export declare function KlippEvents({ onActivated, onDeactivated, onBlendCreated, onBlendFinished, onCut }: KlippEventsProps): null;
@@ -0,0 +1,47 @@
1
+ import { KlippThree } from "../three/KlippThree.js";
2
+ import { KlippContext, useKlipp } from "./KlippContext.js";
3
+ import { useCameraTransitionEvent } from "./useCameraTransitionEvent.js";
4
+ import { useFrame, useThree } from "@react-three/fiber";
5
+ import { useEffect, useState } from "react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ //#region src/react/Klipp.tsx
8
+ /** Bound `dt` under `frameloop="demand"` so an idle gap does not jump the blend forward in one frame. */
9
+ var DEMAND_MODE_MAX_DELTA = 1 / 30;
10
+ /** Provides the camera driver and writes its output to the active camera. */
11
+ function Klipp({ children, defaultBlend, customBlends, camera: cameraProp, mode = "enabled" }) {
12
+ const defaultCamera = useThree((state) => state.camera);
13
+ const size = useThree((state) => state.size);
14
+ const invalidate = useThree((state) => state.invalidate);
15
+ const camera = cameraProp ?? defaultCamera;
16
+ const [klipp] = useState(() => new KlippThree(camera, {
17
+ defaultBlend,
18
+ customBlends
19
+ }));
20
+ klipp.camera = camera;
21
+ klipp.mode = mode;
22
+ klipp.setSize(size.width, size.height);
23
+ useEffect(() => klipp.setDefaultBlend(defaultBlend), [klipp, defaultBlend]);
24
+ useEffect(() => klipp.setCustomBlends(customBlends), [klipp, customBlends]);
25
+ useEffect(() => invalidate(), [mode, invalidate]);
26
+ useFrame((state, rawDelta) => {
27
+ const delta = state.frameloop === "demand" ? Math.min(rawDelta, DEMAND_MODE_MAX_DELTA) : rawDelta;
28
+ if (klipp.update(delta)) state.invalidate();
29
+ });
30
+ return /* @__PURE__ */ jsx(KlippContext.Provider, {
31
+ value: klipp,
32
+ children
33
+ });
34
+ }
35
+ /** Listen to transitions from every camera in the nearest `<Klipp>`. */
36
+ function KlippEvents({ onActivated, onDeactivated, onBlendCreated, onBlendFinished, onCut }) {
37
+ const klipp = useKlipp();
38
+ useCameraTransitionEvent(klipp, "activated", onActivated);
39
+ useCameraTransitionEvent(klipp, "deactivated", onDeactivated);
40
+ useCameraTransitionEvent(klipp, "blendCreated", onBlendCreated);
41
+ useCameraTransitionEvent(klipp, "blendFinished", onBlendFinished);
42
+ useCameraTransitionEvent(klipp, "cut", onCut);
43
+ return null;
44
+ }
45
+ Klipp.Events = KlippEvents;
46
+ //#endregion
47
+ export { Klipp, KlippEvents };
@@ -0,0 +1,5 @@
1
+ import type { KlippThree } from '../three/KlippThree.js';
2
+ export type { FrameUpdate } from '../three/KlippThree.js';
3
+ export declare const KlippContext: import("react").Context<KlippThree | null>;
4
+ /** The nearest `<Klipp>`'s `KlippThree`, which picks the camera and knows about every one. */
5
+ export declare function useKlipp(): KlippThree;
@@ -0,0 +1,11 @@
1
+ import { createContext, use } from "react";
2
+ //#region src/react/KlippContext.ts
3
+ var KlippContext = createContext(null);
4
+ /** The nearest `<Klipp>`'s `KlippThree`, which picks the camera and knows about every one. */
5
+ function useKlipp() {
6
+ const value = use(KlippContext);
7
+ if (!value) throw new Error("useKlipp must be used within a <Klipp> provider.");
8
+ return value;
9
+ }
10
+ //#endregion
11
+ export { KlippContext, useKlipp };
@@ -0,0 +1,28 @@
1
+ import { type ReactNode, type Ref } from 'react';
2
+ import { BlendHints } from '../core/blend/BlendHints.js';
3
+ import type { StandbyUpdate } from '../core/VirtualCamera.js';
4
+ import { VirtualCameraThree } from '../three/VirtualCameraThree.js';
5
+ import { type CameraTransitionEventProps } from './useCameraTransitionEvent.js';
6
+ import { type InitialCameraState } from './VirtualCameraContext.js';
7
+ export type VirtualCameraProps = {
8
+ name: string;
9
+ priority: number;
10
+ /** Whether this camera participates in arbitration and updates. */
11
+ active?: boolean;
12
+ /** Blend hints for transitions involving this camera. */
13
+ hints?: BlendHints;
14
+ /** Initial pose applied once when the camera mounts. */
15
+ initialState?: InitialCameraState;
16
+ /** How this camera updates while another one is on screen. */
17
+ standbyUpdate?: StandbyUpdate;
18
+ children?: ReactNode;
19
+ ref?: Ref<VirtualCameraThree>;
20
+ };
21
+ /** Registers a virtual camera with the nearest `<Klipp>`. */
22
+ export declare function VirtualCamera({ name, priority, active, hints, initialState, standbyUpdate, children, ref, }: VirtualCameraProps): import("react").JSX.Element;
23
+ export declare namespace VirtualCamera {
24
+ export { VirtualCameraEvents as Events };
25
+ }
26
+ export type VirtualCameraEventsProps = CameraTransitionEventProps;
27
+ /** Listen to transition events from the nearest virtual camera. */
28
+ export declare function VirtualCameraEvents({ onActivated, onDeactivated, onBlendCreated, onBlendFinished, onCut, }: VirtualCameraEventsProps): null;
@@ -0,0 +1,81 @@
1
+ import { BlendHints } from "../core/blend/BlendHints.js";
2
+ import { VirtualCameraThree } from "../three/VirtualCameraThree.js";
3
+ import { VirtualCameraActiveContext, VirtualCameraContext, VirtualCameraLiveContext, useVirtualCamera } from "./VirtualCameraContext.js";
4
+ import { useKlipp } from "./KlippContext.js";
5
+ import { useCameraTransitionEvent } from "./useCameraTransitionEvent.js";
6
+ import { useThree } from "@react-three/fiber";
7
+ import { useEffect, useImperativeHandle, useState, useSyncExternalStore } from "react";
8
+ import { jsx } from "react/jsx-runtime";
9
+ //#region src/react/VirtualCamera.tsx
10
+ /** Registers a virtual camera with the nearest `<Klipp>`. */
11
+ function VirtualCamera({ name, priority, active = true, hints = BlendHints.none, initialState, standbyUpdate = "roundRobin", children, ref }) {
12
+ const klipp = useKlipp();
13
+ const invalidate = useThree((state) => state.invalidate);
14
+ const [camera] = useState(() => new VirtualCameraThree(name, {
15
+ priority,
16
+ active,
17
+ hints,
18
+ initialState
19
+ }));
20
+ camera.standbyUpdate = standbyUpdate;
21
+ useImperativeHandle(ref, () => camera, [camera]);
22
+ useEffect(() => {
23
+ camera.name = name;
24
+ }, [camera, name]);
25
+ useEffect(() => {
26
+ camera.priority = priority;
27
+ if (camera.active) invalidate();
28
+ }, [
29
+ camera,
30
+ priority,
31
+ invalidate
32
+ ]);
33
+ useEffect(() => {
34
+ camera.hints = hints;
35
+ }, [camera, hints]);
36
+ useEffect(() => {
37
+ camera.active = active;
38
+ invalidate();
39
+ }, [
40
+ camera,
41
+ active,
42
+ invalidate
43
+ ]);
44
+ useEffect(() => {
45
+ const remove = klipp.add(camera);
46
+ invalidate();
47
+ return () => {
48
+ remove();
49
+ invalidate();
50
+ };
51
+ }, [
52
+ klipp,
53
+ camera,
54
+ invalidate
55
+ ]);
56
+ const isActive = useSyncExternalStore(klipp.subscribeActiveId, () => active && klipp.isActive(name));
57
+ const isLive = useSyncExternalStore(klipp.subscribeLiveId, () => active && klipp.isLive(name));
58
+ return /* @__PURE__ */ jsx(VirtualCameraContext.Provider, {
59
+ value: camera,
60
+ children: /* @__PURE__ */ jsx(VirtualCameraActiveContext.Provider, {
61
+ value: isActive,
62
+ children: /* @__PURE__ */ jsx(VirtualCameraLiveContext.Provider, {
63
+ value: isLive,
64
+ children
65
+ })
66
+ })
67
+ });
68
+ }
69
+ /** Listen to transition events from the nearest virtual camera. */
70
+ function VirtualCameraEvents({ onActivated, onDeactivated, onBlendCreated, onBlendFinished, onCut }) {
71
+ const camera = useVirtualCamera();
72
+ useCameraTransitionEvent(camera, "activated", onActivated);
73
+ useCameraTransitionEvent(camera, "deactivated", onDeactivated);
74
+ useCameraTransitionEvent(camera, "blendCreated", onBlendCreated);
75
+ useCameraTransitionEvent(camera, "blendFinished", onBlendFinished);
76
+ useCameraTransitionEvent(camera, "cut", onCut);
77
+ return null;
78
+ }
79
+ VirtualCamera.Events = VirtualCameraEvents;
80
+ //#endregion
81
+ export { VirtualCamera, VirtualCameraEvents };
@@ -0,0 +1,11 @@
1
+ import type { VirtualCameraThree } from '../three/VirtualCameraThree.js';
2
+ export type { InitialCameraState } from '../three/VirtualCameraThree.js';
3
+ export declare const VirtualCameraContext: import("react").Context<VirtualCameraThree | null>;
4
+ export declare const VirtualCameraActiveContext: import("react").Context<boolean>;
5
+ export declare const VirtualCameraLiveContext: import("react").Context<boolean>;
6
+ /** The nearest `<VirtualCamera>`'s `VirtualCameraThree`, with its `state` and pieces. */
7
+ export declare function useVirtualCamera(): VirtualCameraThree;
8
+ /** Whether the nearest virtual camera currently wins priority. */
9
+ export declare function useIsActiveVirtualCamera(): boolean;
10
+ /** Whether the nearest virtual camera currently contributes to output. */
11
+ export declare function useIsLiveVirtualCamera(): boolean;
@@ -0,0 +1,21 @@
1
+ import { createContext, use } from "react";
2
+ //#region src/react/VirtualCameraContext.ts
3
+ var VirtualCameraContext = createContext(null);
4
+ var VirtualCameraActiveContext = createContext(false);
5
+ var VirtualCameraLiveContext = createContext(false);
6
+ /** The nearest `<VirtualCamera>`'s `VirtualCameraThree`, with its `state` and pieces. */
7
+ function useVirtualCamera() {
8
+ const value = use(VirtualCameraContext);
9
+ if (!value) throw new Error("useVirtualCamera must be used within a <VirtualCamera>.");
10
+ return value;
11
+ }
12
+ /** Whether the nearest virtual camera currently wins priority. */
13
+ function useIsActiveVirtualCamera() {
14
+ return use(VirtualCameraActiveContext);
15
+ }
16
+ /** Whether the nearest virtual camera currently contributes to output. */
17
+ function useIsLiveVirtualCamera() {
18
+ return use(VirtualCameraLiveContext);
19
+ }
20
+ //#endregion
21
+ export { VirtualCameraActiveContext, VirtualCameraContext, VirtualCameraLiveContext, useIsActiveVirtualCamera, useIsLiveVirtualCamera, useVirtualCamera };
@@ -0,0 +1,11 @@
1
+ import { HardLookAt } from './HardLookAt.js';
2
+ import { PanTilt } from './PanTilt.js';
3
+ import { RotateWithFollowTarget } from './RotateWithFollowTarget.js';
4
+ import { RotationComposer } from './RotationComposer.js';
5
+ /** Aim components control a `<VirtualCamera>`'s rotation. Use at most one per camera. */
6
+ export declare const Aim: {
7
+ HardLookAt: typeof HardLookAt;
8
+ RotateWithFollowTarget: typeof RotateWithFollowTarget;
9
+ RotationComposer: typeof RotationComposer;
10
+ PanTilt: typeof PanTilt;
11
+ };
@@ -0,0 +1,14 @@
1
+ import { HardLookAt } from "./HardLookAt.js";
2
+ import { PanTilt } from "./PanTilt.js";
3
+ import { RotateWithFollowTarget } from "./RotateWithFollowTarget.js";
4
+ import { RotationComposer } from "./RotationComposer.js";
5
+ //#region src/react/aim/Aim.ts
6
+ /** Aim components control a `<VirtualCamera>`'s rotation. Use at most one per camera. */
7
+ var Aim = {
8
+ HardLookAt,
9
+ RotateWithFollowTarget,
10
+ RotationComposer,
11
+ PanTilt
12
+ };
13
+ //#endregion
14
+ export { Aim };
@@ -0,0 +1,10 @@
1
+ import { type Ref } from 'react';
2
+ import { HardLookAtAimThree } from '../../three/aim/HardLookAtAimThree.js';
3
+ import type { Target } from '../../three/resolve/Target.js';
4
+ export type HardLookAtProps = {
5
+ /** Target to look at. Unresolved targets are ignored. */
6
+ target?: Target;
7
+ ref?: Ref<HardLookAtAimThree>;
8
+ };
9
+ /** Thin wrapper around `HardLookAtAimThree`. */
10
+ export declare function HardLookAt({ target, ref }: HardLookAtProps): null;
@@ -0,0 +1,15 @@
1
+ import { HardLookAtAimThree } from "../../three/aim/HardLookAtAimThree.js";
2
+ import { useVirtualCamera } from "../VirtualCameraContext.js";
3
+ import { useEffect, useImperativeHandle, useState } from "react";
4
+ //#region src/react/aim/HardLookAt.tsx
5
+ /** Thin wrapper around `HardLookAtAimThree`. */
6
+ function HardLookAt({ target, ref }) {
7
+ const camera = useVirtualCamera();
8
+ const [aim] = useState(() => new HardLookAtAimThree(target));
9
+ aim.target = target;
10
+ useImperativeHandle(ref, () => aim, [aim]);
11
+ useEffect(() => camera.setAim(aim), [camera, aim]);
12
+ return null;
13
+ }
14
+ //#endregion
15
+ export { HardLookAt };