@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Łukasz Kwas AKA Kvvasu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,73 @@
1
- # Temporary Holding Version
1
+ # Klipp 📹
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![Version](https://badgen.net/npm/v/@pmndrs/klipp)](https://www.npmjs.com/package/@pmndrs/klipp)
4
+ [![Examples](https://img.shields.io/static/v1?message=Examples&style=flat&colorA=000000&colorB=000000&label=&logo=threedotjs&logoColor=ffffff)](https://pmndrs.github.io/klipp/examples/)
5
+ [![Docs](https://img.shields.io/static/v1?message=Docs&style=flat&colorA=000000&colorB=000000&label=&logo=googledocs&logoColor=ffffff)](https://pmndrs.github.io/klipp/docs/)
6
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+ A camera toolkit for the web, inspired by Unity Cinemachine. Describe the shots you want, and klipp picks the right one and blends between them.
9
+
10
+ The core runs anywhere, with no renderer or framework attached. Integrations are thin layers on top: three.js and React Three Fiber today, with more to come, like TresJS.
11
+
12
+ ```bash
13
+ npm install @pmndrs/klipp
14
+ ```
15
+
16
+ ⚠️ Early-stage, experimental - the API may change.
17
+
18
+ ## Core
19
+
20
+ ```ts
21
+ import { Klipp, FollowBody, HardLookAtAim, targetPose } from '@pmndrs/klipp';
22
+
23
+ const klipp = new Klipp();
24
+ const player = targetPose.create(); // keep player.position up to date
25
+
26
+ const follow = klipp.addCamera('follow', { priority: 10 });
27
+ follow.body = new FollowBody(player, { offset: [0, 3, 8], damping: 0.5 });
28
+ follow.aim = new HardLookAtAim(player);
29
+
30
+ // every frame
31
+ klipp.update(dt);
32
+ engineCamera.setPosition(klipp.shot.position);
33
+ engineCamera.setRotation(klipp.shot.quaternion);
34
+ ```
35
+
36
+ ## React Three Fiber
37
+
38
+ ```tsx
39
+ import { Klipp, VirtualCamera, Body, Aim } from '@pmndrs/klipp/react';
40
+
41
+ <Klipp>
42
+ <VirtualCamera name="follow" priority={10}>
43
+ <Body.Follow target={playerRef} offset={[0, 3, 8]} damping={0.5} />
44
+ <Aim.HardLookAt target={playerRef} />
45
+ </VirtualCamera>
46
+
47
+ <VirtualCamera name="cutscene" priority={20} active={isCutscene}>
48
+ <Body.HardLockToTarget target={[10, 2, 0]} />
49
+ <Aim.HardLookAt target={playerRef} />
50
+ </VirtualCamera>
51
+ </Klipp>;
52
+ ```
53
+
54
+ ## What's inside
55
+
56
+ - **Virtual cameras** with priorities, and **blending** between them.
57
+ - **Body** and **Aim**: follow, frame or look at targets, or let the user orbit and look around.
58
+ - **Extension**, **Noise** and **Impulse**: group framing, lens, and camera shake.
59
+ - **Debugging**: framing zones and camera frustums.
60
+
61
+ ## Packages
62
+
63
+ | Entry point | For |
64
+ | --------------------- | ------------------------------------------------------------ |
65
+ | `@pmndrs/klipp` | The core, built on [`math`](https://github.com/pmndrs/math). |
66
+ | `@pmndrs/klipp/three` | three.js classes, ending in `Three`. |
67
+ | `@pmndrs/klipp/react` | React Three Fiber components. |
68
+ | `@pmndrs/klipp/dom` | Mouse, touch and wheel input, and debug overlays. |
69
+
70
+ ## Links
71
+
72
+ - [Documentation](https://pmndrs.github.io/klipp/docs/)
73
+ - [Examples](https://pmndrs.github.io/klipp/examples/)
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll };
@@ -0,0 +1,32 @@
1
+ import { type Quat, type Vec3 } from 'math';
2
+ /** A reusable snapshot of camera transform, lens, targets, and view offset. */
3
+ export type CameraState = {
4
+ position: Vec3;
5
+ quaternion: Quat;
6
+ fov: number;
7
+ near: number;
8
+ far: number;
9
+ /** Normalized frustum offset; `0` is centered. */
10
+ viewOffset: [number, number];
11
+ /** Body tracking position, valid when `hasTarget` is true. */
12
+ target: Vec3;
13
+ hasTarget: boolean;
14
+ /** Aim look-at position, valid when `hasLookAtTarget` is true. */
15
+ lookAtTarget: Vec3;
16
+ hasLookAtTarget: boolean;
17
+ /** Up direction used by look-at rotation. */
18
+ referenceUp: Vec3;
19
+ /** World rotation the Body turned the camera by this frame, which damped Aims follow without damping. */
20
+ rotationDampingBypass: Quat;
21
+ };
22
+ /** Create a camera state. */
23
+ export declare function create(): CameraState;
24
+ /** Copy `source` into `out` without replacing nested objects. */
25
+ export declare function copy(out: CameraState, source: CameraState): CameraState;
26
+ /** Merge defined fields from `partial` into `out`. */
27
+ export declare function merge(out: CameraState, partial: Partial<CameraState>): CameraState;
28
+ /** Whether `a` and `b` have the same position and rotation. */
29
+ export declare const transformEquals: (a: CameraState, b: CameraState) => boolean;
30
+ /** Whether `a` and `b` have the same lens, view offset included. */
31
+ export declare const lensEquals: (a: CameraState, b: CameraState) => boolean;
32
+ export { lerp } from './blend/lerpCameraState.js';
@@ -0,0 +1,97 @@
1
+ import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
+ import { lerp as lerp$1 } from "./blend/lerpCameraState.js";
3
+ import { vec3, vec4 } from "math";
4
+ //#region src/core/CameraState.ts
5
+ var CameraState_exports = /* @__PURE__ */ __exportAll({
6
+ copy: () => copy,
7
+ create: () => create,
8
+ lensEquals: () => lensEquals,
9
+ lerp: () => lerp$1,
10
+ merge: () => merge,
11
+ transformEquals: () => transformEquals
12
+ });
13
+ /** Create a camera state. */
14
+ function create() {
15
+ return {
16
+ position: [
17
+ 0,
18
+ 0,
19
+ 0
20
+ ],
21
+ quaternion: [
22
+ 0,
23
+ 0,
24
+ 0,
25
+ 1
26
+ ],
27
+ fov: 50,
28
+ near: .1,
29
+ far: 1e3,
30
+ viewOffset: [0, 0],
31
+ target: [
32
+ 0,
33
+ 0,
34
+ 0
35
+ ],
36
+ hasTarget: false,
37
+ lookAtTarget: [
38
+ 0,
39
+ 0,
40
+ 0
41
+ ],
42
+ hasLookAtTarget: false,
43
+ referenceUp: [
44
+ 0,
45
+ 1,
46
+ 0
47
+ ],
48
+ rotationDampingBypass: [
49
+ 0,
50
+ 0,
51
+ 0,
52
+ 1
53
+ ]
54
+ };
55
+ }
56
+ /** Copy `source` into `out` without replacing nested objects. */
57
+ function copy(out, source) {
58
+ vec3.copy(out.position, source.position);
59
+ vec4.copy(out.quaternion, source.quaternion);
60
+ out.fov = source.fov;
61
+ out.near = source.near;
62
+ out.far = source.far;
63
+ out.viewOffset[0] = source.viewOffset[0];
64
+ out.viewOffset[1] = source.viewOffset[1];
65
+ vec3.copy(out.target, source.target);
66
+ out.hasTarget = source.hasTarget;
67
+ vec3.copy(out.lookAtTarget, source.lookAtTarget);
68
+ out.hasLookAtTarget = source.hasLookAtTarget;
69
+ vec3.copy(out.referenceUp, source.referenceUp);
70
+ vec4.copy(out.rotationDampingBypass, source.rotationDampingBypass);
71
+ return out;
72
+ }
73
+ /** Merge defined fields from `partial` into `out`. */
74
+ function merge(out, partial) {
75
+ if (partial.position) vec3.copy(out.position, partial.position);
76
+ if (partial.quaternion) vec4.copy(out.quaternion, partial.quaternion);
77
+ if (partial.fov !== void 0) out.fov = partial.fov;
78
+ if (partial.near !== void 0) out.near = partial.near;
79
+ if (partial.far !== void 0) out.far = partial.far;
80
+ if (partial.viewOffset) {
81
+ out.viewOffset[0] = partial.viewOffset[0];
82
+ out.viewOffset[1] = partial.viewOffset[1];
83
+ }
84
+ if (partial.target) vec3.copy(out.target, partial.target);
85
+ if (partial.hasTarget !== void 0) out.hasTarget = partial.hasTarget;
86
+ if (partial.lookAtTarget) vec3.copy(out.lookAtTarget, partial.lookAtTarget);
87
+ if (partial.hasLookAtTarget !== void 0) out.hasLookAtTarget = partial.hasLookAtTarget;
88
+ if (partial.referenceUp) vec3.copy(out.referenceUp, partial.referenceUp);
89
+ if (partial.rotationDampingBypass) vec4.copy(out.rotationDampingBypass, partial.rotationDampingBypass);
90
+ return out;
91
+ }
92
+ /** Whether `a` and `b` have the same position and rotation. */
93
+ var transformEquals = (a, b) => vec3.exactEquals(a.position, b.position) && vec4.exactEquals(a.quaternion, b.quaternion);
94
+ /** Whether `a` and `b` have the same lens, view offset included. */
95
+ var lensEquals = (a, b) => a.fov === b.fov && a.near === b.near && a.far === b.far && a.viewOffset[0] === b.viewOffset[0] && a.viewOffset[1] === b.viewOffset[1];
96
+ //#endregion
97
+ export { CameraState_exports, copy, create, lensEquals, lerp$1 as lerp, merge, transformEquals };
@@ -0,0 +1,18 @@
1
+ /** An event as listeners receive it: its data plus `type` and the dispatching `target`. */
2
+ export type DispatchedEvent<TData, TType extends string, TTarget> = TData & {
3
+ readonly type: TType;
4
+ readonly target: TTarget;
5
+ };
6
+ export type EventListener<TData, TType extends string, TTarget> = (event: DispatchedEvent<TData, TType, TTarget>) => void;
7
+ type EventType<TEventMap> = Extract<keyof TEventMap, string>;
8
+ /** Typed event dispatcher with the same semantics as three.js `EventDispatcher`. */
9
+ export declare class EventDispatcher<TEventMap extends {} = {}> {
10
+ private readonly listeners;
11
+ addEventListener<T extends EventType<TEventMap>>(type: T, listener: EventListener<TEventMap[T], T, this>): void;
12
+ hasEventListener<T extends EventType<TEventMap>>(type: T, listener: EventListener<TEventMap[T], T, this>): boolean;
13
+ removeEventListener<T extends EventType<TEventMap>>(type: T, listener: EventListener<TEventMap[T], T, this>): void;
14
+ dispatchEvent<T extends EventType<TEventMap>>(event: {
15
+ type: T;
16
+ } & TEventMap[T]): void;
17
+ }
18
+ export {};
@@ -0,0 +1,28 @@
1
+ //#region src/core/EventDispatcher.ts
2
+ /** Typed event dispatcher with the same semantics as three.js `EventDispatcher`. */
3
+ var EventDispatcher = class {
4
+ listeners = /* @__PURE__ */ new Map();
5
+ addEventListener(type, listener) {
6
+ const array = this.listeners.get(type) ?? [];
7
+ if (!array.includes(listener)) this.listeners.set(type, [...array, listener]);
8
+ }
9
+ hasEventListener(type, listener) {
10
+ return this.listeners.get(type)?.includes(listener) ?? false;
11
+ }
12
+ removeEventListener(type, listener) {
13
+ const array = this.listeners.get(type);
14
+ if (!array?.includes(listener)) return;
15
+ const remaining = array.filter((l) => l !== listener);
16
+ this.listeners.set(type, remaining);
17
+ }
18
+ dispatchEvent(event) {
19
+ const array = this.listeners.get(event.type);
20
+ if (!array) return;
21
+ const dispatched = event;
22
+ dispatched.target = this;
23
+ for (const listener of array) listener.call(this, dispatched);
24
+ dispatched.target = null;
25
+ }
26
+ };
27
+ //#endregion
28
+ export { EventDispatcher };
@@ -0,0 +1,109 @@
1
+ import * as klippState from './klippState.js';
2
+ import type { CameraState } from './CameraState.js';
3
+ import { EventDispatcher } from './EventDispatcher.js';
4
+ import { advance, checkName, register, setHints, setPriority } from './internal.js';
5
+ import type { CameraTransitionEventMap, VirtualCameraConfig } from './klippState.js';
6
+ import { VirtualCamera, type VirtualCameraOptions } from './VirtualCamera.js';
7
+ import { type BlendDefinition, type CustomBlend } from './blend/BlendDefinition.js';
8
+ import type { BlendHints } from './blend/BlendHints.js';
9
+ export type { CameraTransitionEventMap, VirtualCameraConfig };
10
+ /** `'enabled'` writes the output, `'standby'` keeps every camera updating without writing, `'disabled'` stops. */
11
+ export type KlippMode = 'enabled' | 'standby' | 'disabled';
12
+ /** Per-frame work that runs before the cameras, like input. Return `true` while it is still moving. */
13
+ export type FrameUpdate = (dt: number) => boolean | void;
14
+ export type KlippOptions = {
15
+ /** Used when no `customBlends` entry matches a from→to transition. */
16
+ defaultBlend?: BlendDefinition;
17
+ customBlends?: CustomBlend[];
18
+ mode?: KlippMode;
19
+ /** The pose and lens new virtual cameras start from. */
20
+ initialCameraState?: CameraState;
21
+ };
22
+ /** Runs virtual cameras, picks the winner by priority and blends between shots. */
23
+ export declare class Klipp extends EventDispatcher<CameraTransitionEventMap> {
24
+ readonly state: klippState.KlippState;
25
+ /** The pose and lens new virtual cameras start from. */
26
+ readonly initialCameraState: CameraState;
27
+ protected readonly cameras: Set<VirtualCamera>;
28
+ private readonly updates;
29
+ private readonly previousResult;
30
+ private settled;
31
+ private _width;
32
+ private _height;
33
+ private readonly params;
34
+ private readonly activeIdListeners;
35
+ private readonly liveIdListeners;
36
+ private draining;
37
+ private roundRobinTurn;
38
+ private _mode;
39
+ constructor(options?: KlippOptions);
40
+ get mode(): KlippMode;
41
+ /** Switching back to `'enabled'` writes the next shot even if it did not change, to take the camera back. */
42
+ set mode(mode: KlippMode);
43
+ setDefaultBlend(defaultBlend?: BlendDefinition): void;
44
+ setCustomBlends(customBlends?: CustomBlend[]): void;
45
+ get activeCameraId(): string | null;
46
+ isActive(id: string): boolean;
47
+ /** Subscribe to active camera changes. */
48
+ subscribeActiveId: (listener: () => void) => (() => void);
49
+ /** The last shot `update` wrote. Read it after `update` to put it on your camera. */
50
+ get shot(): CameraState;
51
+ /** The active camera's raw state. */
52
+ get activeState(): CameraState | null;
53
+ /** Camera currently settled in the output. */
54
+ get liveCameraId(): string | null;
55
+ isLive(id: string): boolean;
56
+ /** Subscribe to live camera changes. */
57
+ subscribeLiveId: (listener: () => void) => (() => void);
58
+ get isBlending(): boolean;
59
+ /** Destination of the active blend, or the live camera when settled. */
60
+ get blendTargetId(): string | null;
61
+ /** Whether the core has produced a real camera output. */
62
+ get hasEverActivated(): boolean;
63
+ /** Register a camera and return an unregister callback. */
64
+ [register](config: VirtualCameraConfig): () => void;
65
+ /** Update a candidate priority without restarting the current blend. */
66
+ [setPriority](id: string, priority: number): void;
67
+ /** Update candidate hints in place. */
68
+ [setHints](id: string, hints: BlendHints): void;
69
+ /** Viewport width in pixels, `0` until `setSize`. */
70
+ get width(): number;
71
+ /** Viewport height in pixels, `0` until `setSize`. */
72
+ get height(): number;
73
+ /** Set the viewport size in pixels. Pieces that frame by screen size get it whenever they update. */
74
+ setSize(width: number, height: number): void;
75
+ /** Create a virtual camera and add it. */
76
+ addCamera(name: string, options?: VirtualCameraOptions): VirtualCamera;
77
+ /** Add a virtual camera, moving it from any other `Klipp`. Returns a function that removes it. */
78
+ add(camera: VirtualCamera): () => void;
79
+ /** Remove a virtual camera. It leaves the arbitration, and the shot blends to the next winner. */
80
+ remove(camera: VirtualCamera): void;
81
+ [checkName](camera: VirtualCamera): void;
82
+ /**
83
+ * Call `start` while `camera` is on screen, and the function it returns once it leaves, for example to connect
84
+ * input. With `waitForBlend`, on screen begins once the blend into it has finished. Returns a function that stops.
85
+ */
86
+ whileOnScreen(camera: VirtualCamera, start: () => () => void, { waitForBlend }?: {
87
+ waitForBlend?: boolean | undefined;
88
+ }): () => void;
89
+ /** Run `update` every frame before the cameras. Returns a function that stops it. */
90
+ registerUpdate(update: FrameUpdate): () => void;
91
+ /**
92
+ * Advance one frame: run every camera, pick and blend the shot, and `write` it when it changed.
93
+ * Returns `true` while something is still moving, so on-demand rendering knows to request another frame.
94
+ */
95
+ update(dt: number): boolean;
96
+ /**
97
+ * Prepare and run the camera the shot heads to, and the others as their `standbyUpdate` says.
98
+ * Returns `true` while one moves.
99
+ */
100
+ private runCameras;
101
+ /** Runs at the start of every frame, before any camera. Layers override it to read their targets. */
102
+ protected prepareFrame(): void;
103
+ /** Called with the output whenever it changed. Layers override it to write their camera. */
104
+ protected write(_result: CameraState, _transformChanged: boolean, _lensChanged: boolean): void;
105
+ /** Advance the blend and return the reusable output state. */
106
+ [advance](dt: number): CameraState;
107
+ /** Notify subscribers and listeners of buffered events, including ones raised while notifying. */
108
+ private drainEvents;
109
+ }
@@ -0,0 +1,253 @@
1
+ import { EventDispatcher } from "./EventDispatcher.js";
2
+ import { copy, create, lensEquals, transformEquals } from "./CameraState.js";
3
+ import { targetId } from "./blend/blend.js";
4
+ import { create as create$1, register, setHints, setPriority, tick, unregister } from "./klippState.js";
5
+ import { advance, attachTo, checkName, prepare, register as register$1, run, setHints as setHints$1, setPriority as setPriority$1, skip } from "./internal.js";
6
+ import { VirtualCamera } from "./VirtualCamera.js";
7
+ import { DEFAULT_BLEND } from "./blend/BlendDefinition.js";
8
+ //#region src/core/Klipp.ts
9
+ /** Runs virtual cameras, picks the winner by priority and blends between shots. */
10
+ var Klipp = class extends EventDispatcher {
11
+ state = create$1();
12
+ /** The pose and lens new virtual cameras start from. */
13
+ initialCameraState;
14
+ cameras = /* @__PURE__ */ new Set();
15
+ updates = /* @__PURE__ */ new Set();
16
+ previousResult = create();
17
+ settled = false;
18
+ _width = 0;
19
+ _height = 0;
20
+ params;
21
+ activeIdListeners = /* @__PURE__ */ new Set();
22
+ liveIdListeners = /* @__PURE__ */ new Set();
23
+ draining = false;
24
+ roundRobinTurn = 0;
25
+ _mode = "enabled";
26
+ constructor(options = {}) {
27
+ super();
28
+ this.mode = options.mode ?? "enabled";
29
+ this.initialCameraState = options.initialCameraState ?? create();
30
+ this.params = {
31
+ defaultBlend: options.defaultBlend ?? DEFAULT_BLEND,
32
+ customBlends: options.customBlends ?? []
33
+ };
34
+ }
35
+ get mode() {
36
+ return this._mode;
37
+ }
38
+ /** Switching back to `'enabled'` writes the next shot even if it did not change, to take the camera back. */
39
+ set mode(mode) {
40
+ if (mode === "enabled" && this._mode !== "enabled") this.settled = false;
41
+ this._mode = mode;
42
+ }
43
+ setDefaultBlend(defaultBlend) {
44
+ this.params.defaultBlend = defaultBlend ?? DEFAULT_BLEND;
45
+ }
46
+ setCustomBlends(customBlends) {
47
+ this.params.customBlends = customBlends ?? [];
48
+ }
49
+ get activeCameraId() {
50
+ return this.state.activeId;
51
+ }
52
+ isActive(id) {
53
+ return this.state.activeId === id;
54
+ }
55
+ /** Subscribe to active camera changes. */
56
+ subscribeActiveId = (listener) => {
57
+ this.activeIdListeners.add(listener);
58
+ return () => this.activeIdListeners.delete(listener);
59
+ };
60
+ /** The last shot `update` wrote. Read it after `update` to put it on your camera. */
61
+ get shot() {
62
+ return this.previousResult;
63
+ }
64
+ /** The active camera's raw state. */
65
+ get activeState() {
66
+ return this.state.activeId !== null ? this.state.cameras.get(this.state.activeId).state : null;
67
+ }
68
+ /** Camera currently settled in the output. */
69
+ get liveCameraId() {
70
+ return this.state.blend.liveId;
71
+ }
72
+ isLive(id) {
73
+ return this.state.blend.liveId === id;
74
+ }
75
+ /** Subscribe to live camera changes. */
76
+ subscribeLiveId = (listener) => {
77
+ this.liveIdListeners.add(listener);
78
+ return () => this.liveIdListeners.delete(listener);
79
+ };
80
+ get isBlending() {
81
+ return this.state.blend.transition.active;
82
+ }
83
+ /** Destination of the active blend, or the live camera when settled. */
84
+ get blendTargetId() {
85
+ return targetId(this.state.blend);
86
+ }
87
+ /** Whether the core has produced a real camera output. */
88
+ get hasEverActivated() {
89
+ return this.state.blend.hasEverActivated;
90
+ }
91
+ /** Register a camera and return an unregister callback. */
92
+ [register$1](config) {
93
+ const camera = register(this.state, config);
94
+ this.drainEvents();
95
+ return () => {
96
+ unregister(this.state, camera);
97
+ this.drainEvents();
98
+ };
99
+ }
100
+ /** Update a candidate priority without restarting the current blend. */
101
+ [setPriority$1](id, priority) {
102
+ setPriority(this.state, id, priority);
103
+ this.drainEvents();
104
+ }
105
+ /** Update candidate hints in place. */
106
+ [setHints$1](id, hints) {
107
+ setHints(this.state, id, hints);
108
+ }
109
+ /** Viewport width in pixels, `0` until `setSize`. */
110
+ get width() {
111
+ return this._width;
112
+ }
113
+ /** Viewport height in pixels, `0` until `setSize`. */
114
+ get height() {
115
+ return this._height;
116
+ }
117
+ /** Set the viewport size in pixels. Pieces that frame by screen size get it whenever they update. */
118
+ setSize(width, height) {
119
+ this._width = width;
120
+ this._height = height;
121
+ }
122
+ /** Create a virtual camera and add it. */
123
+ addCamera(name, options) {
124
+ const camera = new VirtualCamera(name, options);
125
+ this.add(camera);
126
+ return camera;
127
+ }
128
+ /** Add a virtual camera, moving it from any other `Klipp`. Returns a function that removes it. */
129
+ add(camera) {
130
+ if (camera.klipp !== this) {
131
+ camera.klipp?.remove(camera);
132
+ this[checkName](camera);
133
+ this.cameras.add(camera);
134
+ camera[attachTo](this);
135
+ }
136
+ return () => this.remove(camera);
137
+ }
138
+ /** Remove a virtual camera. It leaves the arbitration, and the shot blends to the next winner. */
139
+ remove(camera) {
140
+ if (!this.cameras.delete(camera)) return;
141
+ camera[attachTo](null);
142
+ }
143
+ [checkName](camera) {
144
+ for (const other of this.cameras) if (other !== camera && other.name === camera.name) {
145
+ console.warn(`Two virtual cameras are named "${camera.name}". Names must be unique: blends and events use them.`);
146
+ return;
147
+ }
148
+ }
149
+ /**
150
+ * Call `start` while `camera` is on screen, and the function it returns once it leaves, for example to connect
151
+ * input. With `waitForBlend`, on screen begins once the blend into it has finished. Returns a function that stops.
152
+ */
153
+ whileOnScreen(camera, start, { waitForBlend = true } = {}) {
154
+ let stop = null;
155
+ const check = () => {
156
+ const onScreen = this.isActive(camera.name) && (!waitForBlend || this.isLive(camera.name));
157
+ if (onScreen && !stop) stop = start();
158
+ else if (!onScreen && stop) {
159
+ stop();
160
+ stop = null;
161
+ }
162
+ };
163
+ const offActive = this.subscribeActiveId(check);
164
+ const offLive = this.subscribeLiveId(check);
165
+ check();
166
+ return () => {
167
+ offActive();
168
+ offLive();
169
+ stop?.();
170
+ stop = null;
171
+ };
172
+ }
173
+ /** Run `update` every frame before the cameras. Returns a function that stops it. */
174
+ registerUpdate(update) {
175
+ this.updates.add(update);
176
+ return () => this.updates.delete(update);
177
+ }
178
+ /**
179
+ * Advance one frame: run every camera, pick and blend the shot, and `write` it when it changed.
180
+ * Returns `true` while something is still moving, so on-demand rendering knows to request another frame.
181
+ */
182
+ update(dt) {
183
+ if (this.mode === "disabled") return false;
184
+ this.prepareFrame();
185
+ let stillInFlight = false;
186
+ for (const update of this.updates) if (update(dt) === true) stillInFlight = true;
187
+ if (this.runCameras(dt)) stillInFlight = true;
188
+ const result = this[advance](dt);
189
+ if (this.mode === "standby") return stillInFlight || this.isBlending;
190
+ if (!this.hasEverActivated) return stillInFlight;
191
+ const previous = this.previousResult;
192
+ const transformChanged = !this.settled || !transformEquals(result, previous);
193
+ const lensChanged = !this.settled || !lensEquals(result, previous);
194
+ if (!transformChanged && !lensChanged) return stillInFlight;
195
+ copy(previous, result);
196
+ this.settled = true;
197
+ this.write(result, transformChanged, lensChanged);
198
+ return true;
199
+ }
200
+ /**
201
+ * Prepare and run the camera the shot heads to, and the others as their `standbyUpdate` says.
202
+ * Returns `true` while one moves.
203
+ */
204
+ runCameras(dt) {
205
+ const activeId = this.state.activeId;
206
+ let roundRobinCount = 0;
207
+ for (const camera of this.cameras) if (camera.active && camera.name !== activeId && camera.standbyUpdate === "roundRobin") roundRobinCount++;
208
+ const turn = roundRobinCount > 0 ? this.roundRobinTurn++ % roundRobinCount : -1;
209
+ let stillInFlight = false;
210
+ let roundRobinIndex = 0;
211
+ for (const camera of this.cameras) {
212
+ if (!camera.active) continue;
213
+ if (camera.name !== activeId && (camera.standbyUpdate === "never" || camera.standbyUpdate === "roundRobin" && roundRobinIndex++ !== turn)) {
214
+ camera[skip](dt);
215
+ continue;
216
+ }
217
+ camera[prepare](this._width, this._height);
218
+ if (camera[run](dt)) stillInFlight = true;
219
+ }
220
+ return stillInFlight;
221
+ }
222
+ /** Runs at the start of every frame, before any camera. Layers override it to read their targets. */
223
+ prepareFrame() {}
224
+ /** Called with the output whenever it changed. Layers override it to write their camera. */
225
+ write(_result, _transformChanged, _lensChanged) {}
226
+ /** Advance the blend and return the reusable output state. */
227
+ [advance](dt) {
228
+ const result = tick(this.state, this.params, dt);
229
+ this.drainEvents();
230
+ return result;
231
+ }
232
+ /** Notify subscribers and listeners of buffered events, including ones raised while notifying. */
233
+ drainEvents() {
234
+ if (this.draining) return;
235
+ this.draining = true;
236
+ const events = this.state.events;
237
+ let i = 0;
238
+ try {
239
+ for (; i < events.length; i++) {
240
+ const event = events[i];
241
+ if (event.type === "activeIdChanged") for (const listener of this.activeIdListeners) listener();
242
+ else if (event.type === "liveIdChanged") for (const listener of this.liveIdListeners) listener();
243
+ else this.dispatchEvent(event);
244
+ }
245
+ } finally {
246
+ if (i >= events.length) events.length = 0;
247
+ else events.splice(0, i + 1);
248
+ this.draining = false;
249
+ }
250
+ }
251
+ };
252
+ //#endregion
253
+ export { Klipp };
@@ -0,0 +1,11 @@
1
+ import { type Quat, type Vec3 } from 'math';
2
+ /** How far a target reaches: a sphere of `radius`, or (when `hasSize`) a box of `size` with its world `rotation`. */
3
+ export type TargetExtent = {
4
+ radius: number;
5
+ size: Vec3;
6
+ rotation: Quat;
7
+ hasSize: boolean;
8
+ };
9
+ export declare const create: () => TargetExtent;
10
+ /** Half-extents of `extent` along two world axes. */
11
+ export declare function project(out: [number, number], extent: TargetExtent, axisA: Vec3, axisB: Vec3): [number, number];