@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,46 @@
1
+ import type { InputAxisData } from './axis.js';
2
+ import type { ConsumedInput } from './consumedInput.js';
3
+ export type InputAxisPair = {
4
+ x: InputAxisData;
5
+ y: InputAxisData;
6
+ };
7
+ /** Inverts both axes or configures them independently. */
8
+ export type InputInvert = boolean | {
9
+ x?: boolean;
10
+ y?: boolean;
11
+ };
12
+ export type InputSourceMapping = {
13
+ axes: InputAxisPair;
14
+ /** Multiplies the raw delta before it reaches the axes. */
15
+ gain?: number;
16
+ /** Whether either axis is inverted. */
17
+ invert?: InputInvert;
18
+ };
19
+ /** One input feeding one axis. */
20
+ export type InputAxisMapping = {
21
+ axis: InputAxisData;
22
+ /** Multiplies the raw delta before it reaches the axis. */
23
+ gain?: number;
24
+ invert?: boolean;
25
+ };
26
+ /** Which axes each input drives. Leave a source out, or set it to `null`, to ignore it. */
27
+ export type InputControllerConfig = {
28
+ /** Dragging with each mouse button held. */
29
+ mouseButtons?: {
30
+ left?: InputSourceMapping | null;
31
+ right?: InputSourceMapping | null;
32
+ middle?: InputSourceMapping | null;
33
+ };
34
+ /** Dragging with one, two or three fingers. */
35
+ touches?: {
36
+ one?: InputSourceMapping | null;
37
+ two?: InputSourceMapping | null;
38
+ three?: InputSourceMapping | null;
39
+ };
40
+ /** Vertical wheel scrolling, in pixels. Scrolling up is positive, like spreading a pinch. */
41
+ wheel?: InputAxisMapping | null;
42
+ /** Pinch with two fingers or on a trackpad, as the change in `ln(scale)`. Spreading is positive. */
43
+ pinch?: InputAxisMapping | null;
44
+ };
45
+ /** Feeds every configured source's shaped delta and hold state into its axes. */
46
+ export declare function feedAxes(config: InputControllerConfig, input: ConsumedInput, enabled: boolean): void;
@@ -0,0 +1,55 @@
1
+ import { __exportAll } from "../../_virtual/_rolldown/runtime.js";
2
+ import { applyDelta } from "./axis.js";
3
+ //#region src/core/input/inputMapping.ts
4
+ var inputMapping_exports = /* @__PURE__ */ __exportAll({ feedAxes: () => feedAxes });
5
+ var isInverted = (invert, axis) => invert === true || typeof invert === "object" && !!invert[axis];
6
+ function applySource(mapping, dx, dy) {
7
+ if (!mapping || dx === 0 && dy === 0) return;
8
+ const gain = mapping.gain ?? 1;
9
+ applyDelta(mapping.axes.x, dx * gain * (isInverted(mapping.invert, "x") ? -1 : 1));
10
+ applyDelta(mapping.axes.y, dy * gain * (isInverted(mapping.invert, "y") ? -1 : 1));
11
+ }
12
+ function applyAxis(mapping, delta) {
13
+ if (!mapping || delta === 0) return;
14
+ applyDelta(mapping.axis, delta * (mapping.gain ?? 1) * (mapping.invert ? -1 : 1));
15
+ }
16
+ function resetHeld(mapping) {
17
+ if (!mapping) return;
18
+ mapping.axes.x.held = false;
19
+ mapping.axes.y.held = false;
20
+ }
21
+ function applyHeld(mapping, held) {
22
+ if (!mapping || !held) return;
23
+ mapping.axes.x.held = true;
24
+ mapping.axes.y.held = true;
25
+ }
26
+ /** Feeds every configured source's shaped delta and hold state into its axes. */
27
+ function feedAxes(config, input, enabled) {
28
+ const { mouseButtons, touches } = config;
29
+ resetHeld(mouseButtons?.left);
30
+ resetHeld(mouseButtons?.right);
31
+ resetHeld(mouseButtons?.middle);
32
+ resetHeld(touches?.one);
33
+ resetHeld(touches?.two);
34
+ resetHeld(touches?.three);
35
+ if (config.pinch) config.pinch.axis.held = false;
36
+ if (!enabled) return;
37
+ applySource(mouseButtons?.left, input.leftDx, input.leftDy);
38
+ applySource(mouseButtons?.right, input.rightDx, input.rightDy);
39
+ applySource(mouseButtons?.middle, input.middleDx, input.middleDy);
40
+ applySource(touches?.one, input.touchOneDx, input.touchOneDy);
41
+ applySource(touches?.two, input.touchTwoDx, input.touchTwoDy);
42
+ applySource(touches?.three, input.touchThreeDx, input.touchThreeDy);
43
+ applySource(mouseButtons?.left, input.lockedDx, input.lockedDy);
44
+ applyHeld(mouseButtons?.left, input.leftHeld);
45
+ applyHeld(mouseButtons?.right, input.rightHeld);
46
+ applyHeld(mouseButtons?.middle, input.middleHeld);
47
+ applyHeld(touches?.one, input.touchOneHeld);
48
+ applyHeld(touches?.two, input.touchTwoHeld);
49
+ applyHeld(touches?.three, input.touchThreeHeld);
50
+ applyAxis(config.wheel, -input.wheelDeltaY);
51
+ applyAxis(config.pinch, input.pinchDelta);
52
+ if (config.pinch && input.touchTwoHeld) config.pinch.axis.held = true;
53
+ }
54
+ //#endregion
55
+ export { feedAxes, inputMapping_exports };
@@ -0,0 +1,2 @@
1
+ /** Shortest signed distance from `value` to `target` within a wrapped range. */
2
+ export declare function shortestWrappedDelta(value: number, target: number, range: [number, number]): number;
@@ -0,0 +1,11 @@
1
+ //#region src/core/input/shortestWrappedDelta.ts
2
+ /** Shortest signed distance from `value` to `target` within a wrapped range. */
3
+ function shortestWrappedDelta(value, target, range) {
4
+ const span = range[1] - range[0];
5
+ let delta = (target - value) % span;
6
+ if (delta > span / 2) delta -= span;
7
+ if (delta < -span / 2) delta += span;
8
+ return delta;
9
+ }
10
+ //#endregion
11
+ export { shortestWrappedDelta };
@@ -0,0 +1,16 @@
1
+ /** Connect a camera to a `Klipp`, or disconnect it with `null`. */
2
+ export declare const attachTo: unique symbol;
3
+ /** Register a raw state in a `Klipp`'s arbitration. Returns a function that unregisters it. */
4
+ export declare const register: unique symbol;
5
+ export declare const setPriority: unique symbol;
6
+ export declare const setHints: unique symbol;
7
+ /** Advance a `Klipp`'s arbitration and blend, without running any camera. */
8
+ export declare const advance: unique symbol;
9
+ /** Warn when another camera in a `Klipp` already has this camera's name. */
10
+ export declare const checkName: unique symbol;
11
+ /** Pass the viewport size on to a camera's pieces. */
12
+ export declare const prepare: unique symbol;
13
+ /** Run a camera's pieces for one frame. */
14
+ export declare const run: unique symbol;
15
+ /** Skip a camera this frame, keeping the time for its next run. */
16
+ export declare const skip: unique symbol;
@@ -0,0 +1,19 @@
1
+ //#region src/core/internal.ts
2
+ /** Connect a camera to a `Klipp`, or disconnect it with `null`. */
3
+ var attachTo = Symbol("attachTo");
4
+ /** Register a raw state in a `Klipp`'s arbitration. Returns a function that unregisters it. */
5
+ var register = Symbol("register");
6
+ var setPriority = Symbol("setPriority");
7
+ var setHints = Symbol("setHints");
8
+ /** Advance a `Klipp`'s arbitration and blend, without running any camera. */
9
+ var advance = Symbol("advance");
10
+ /** Warn when another camera in a `Klipp` already has this camera's name. */
11
+ var checkName = Symbol("checkName");
12
+ /** Pass the viewport size on to a camera's pieces. */
13
+ var prepare = Symbol("prepare");
14
+ /** Run a camera's pieces for one frame. */
15
+ var run = Symbol("run");
16
+ /** Skip a camera this frame, keeping the time for its next run. */
17
+ var skip = Symbol("skip");
18
+ //#endregion
19
+ export { advance, attachTo, checkName, prepare, register, run, setHints, setPriority, skip };
@@ -0,0 +1,78 @@
1
+ import type { CameraState } from './CameraState.js';
2
+ import type { BlendState } from './blend/blend.js';
3
+ import type { BlendDefinition, CustomBlend } from './blend/BlendDefinition.js';
4
+ import { BlendHints } from './blend/BlendHints.js';
5
+ export type VirtualCameraConfig = {
6
+ id: string;
7
+ priority: number;
8
+ /** Mutable live state read by the core. */
9
+ state: CameraState;
10
+ /** Blend hints for transitions involving this camera. */
11
+ hints?: BlendHints;
12
+ };
13
+ export type KlippCamera = VirtualCameraConfig & {
14
+ activatedAt: number;
15
+ };
16
+ /** Transition events emitted by the core and virtual camera controllers. */
17
+ export type CameraTransitionEventMap = {
18
+ /** A camera became the active candidate. */
19
+ activated: {
20
+ incoming: string;
21
+ outgoing: string | null;
22
+ };
23
+ /** A camera stopped contributing to output. */
24
+ deactivated: {
25
+ outgoing: string;
26
+ };
27
+ /** A blend transition started. */
28
+ blendCreated: {
29
+ incoming: string;
30
+ outgoing: string | null;
31
+ };
32
+ /** A blend transition finished. */
33
+ blendFinished: {
34
+ liveId: string;
35
+ };
36
+ /** An instant transition occurred. */
37
+ cut: {
38
+ incoming: string;
39
+ outgoing: string | null;
40
+ };
41
+ };
42
+ /** Transition events plus `activeIdChanged` / `liveIdChanged`, in the order they happened. */
43
+ export type KlippEvent = {
44
+ [K in keyof CameraTransitionEventMap]: {
45
+ type: K;
46
+ } & CameraTransitionEventMap[K];
47
+ }[keyof CameraTransitionEventMap] | {
48
+ type: 'activeIdChanged';
49
+ } | {
50
+ type: 'liveIdChanged';
51
+ };
52
+ export type KlippParams = {
53
+ /** Used when no `customBlends` entry matches a from→to transition. */
54
+ defaultBlend: BlendDefinition;
55
+ customBlends: CustomBlend[];
56
+ };
57
+ export type KlippState = {
58
+ cameras: Map<string, KlippCamera>;
59
+ /** Highest-priority camera, the one the output is heading to. */
60
+ activeId: string | null;
61
+ activationCounter: number;
62
+ blend: BlendState<string>;
63
+ customBlendFromId: string | null;
64
+ customBlendFromHints: BlendHints;
65
+ /** Events since the last drain. The caller empties it. */
66
+ events: KlippEvent[];
67
+ };
68
+ export declare const create: () => KlippState;
69
+ /** Register a camera and return its record, needed to unregister it. */
70
+ export declare function register(state: KlippState, config: VirtualCameraConfig): KlippCamera;
71
+ /** Unregister `camera`, unless its id has since been taken by a newer registration. */
72
+ export declare function unregister(state: KlippState, camera: KlippCamera): void;
73
+ /** Change a camera's priority without restarting the current blend. */
74
+ export declare function setPriority(state: KlippState, id: string, priority: number): void;
75
+ /** Change a camera's blend hints in place. */
76
+ export declare function setHints(state: KlippState, id: string, hints: BlendHints): void;
77
+ /** Head for the active camera, advance the blend and return the reusable output state. */
78
+ export declare function tick(state: KlippState, params: KlippParams, dt: number): CameraState;
@@ -0,0 +1,124 @@
1
+ import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
+ import { BlendHints } from "./blend/BlendHints.js";
3
+ import { create as create$1, forget, resolveDefinition, setTarget, targetId, tick as tick$1 } from "./blend/blend.js";
4
+ //#region src/core/klippState.ts
5
+ var klippState_exports = /* @__PURE__ */ __exportAll({
6
+ create: () => create,
7
+ register: () => register,
8
+ setHints: () => setHints,
9
+ setPriority: () => setPriority,
10
+ tick: () => tick,
11
+ unregister: () => unregister
12
+ });
13
+ var create = () => ({
14
+ cameras: /* @__PURE__ */ new Map(),
15
+ activeId: null,
16
+ activationCounter: 0,
17
+ blend: create$1(),
18
+ customBlendFromId: null,
19
+ customBlendFromHints: BlendHints.none,
20
+ events: []
21
+ });
22
+ function recompute(state) {
23
+ let winner = null;
24
+ for (const camera of state.cameras.values()) if (!winner || camera.priority > winner.priority || camera.priority === winner.priority && camera.activatedAt > winner.activatedAt) winner = camera;
25
+ const newActiveId = winner?.id ?? null;
26
+ if (newActiveId === state.activeId) return;
27
+ const outgoing = state.activeId;
28
+ state.activeId = newActiveId;
29
+ state.events.push({ type: "activeIdChanged" });
30
+ if (newActiveId !== null) state.events.push({
31
+ type: "activated",
32
+ incoming: newActiveId,
33
+ outgoing
34
+ });
35
+ }
36
+ function reportLiveIdChange(state, previousLiveId) {
37
+ if (state.blend.liveId === previousLiveId) return;
38
+ state.events.push({ type: "liveIdChanged" });
39
+ if (previousLiveId !== null) state.events.push({
40
+ type: "deactivated",
41
+ outgoing: previousLiveId
42
+ });
43
+ }
44
+ /** Register a camera and return its record, needed to unregister it. */
45
+ function register(state, config) {
46
+ const camera = {
47
+ ...config,
48
+ activatedAt: ++state.activationCounter
49
+ };
50
+ state.cameras.set(config.id, camera);
51
+ recompute(state);
52
+ return camera;
53
+ }
54
+ /** Unregister `camera`, unless its id has since been taken by a newer registration. */
55
+ function unregister(state, camera) {
56
+ if (state.cameras.get(camera.id) !== camera) return;
57
+ state.cameras.delete(camera.id);
58
+ const previousLiveId = state.blend.liveId;
59
+ forget(state.blend, camera.id);
60
+ reportLiveIdChange(state, previousLiveId);
61
+ recompute(state);
62
+ }
63
+ /** Change a camera's priority without restarting the current blend. */
64
+ function setPriority(state, id, priority) {
65
+ const camera = state.cameras.get(id);
66
+ if (!camera) return;
67
+ camera.priority = priority;
68
+ recompute(state);
69
+ }
70
+ /** Change a camera's blend hints in place. */
71
+ function setHints(state, id, hints) {
72
+ const camera = state.cameras.get(id);
73
+ if (camera) camera.hints = hints;
74
+ if (id === state.customBlendFromId) state.customBlendFromHints = hints;
75
+ }
76
+ /** Start a transition to `incoming`. Returns `true` when it resolved to an instant cut. */
77
+ function retarget(state, params, incoming) {
78
+ const definition = resolveDefinition(params.customBlends, state.customBlendFromId, incoming, params.defaultBlend);
79
+ const incomingCamera = state.cameras.get(incoming);
80
+ const toHints = incomingCamera.hints ?? BlendHints.none;
81
+ const fromHints = (state.customBlendFromId !== null ? state.cameras.get(state.customBlendFromId) : void 0)?.hints ?? state.customBlendFromHints;
82
+ const outgoing = targetId(state.blend);
83
+ const isFirstEver = !state.blend.hasEverActivated;
84
+ setTarget(state.blend, incoming, incomingCamera.state, definition, fromHints | toHints);
85
+ state.customBlendFromId = incoming;
86
+ state.customBlendFromHints = toHints;
87
+ if (isFirstEver) {
88
+ state.events.push({
89
+ type: "cut",
90
+ incoming,
91
+ outgoing: null
92
+ });
93
+ return false;
94
+ }
95
+ state.events.push({
96
+ type: "blendCreated",
97
+ incoming,
98
+ outgoing
99
+ });
100
+ if ("damping" in definition || definition.time > 0) return false;
101
+ state.events.push({
102
+ type: "cut",
103
+ incoming,
104
+ outgoing
105
+ });
106
+ return true;
107
+ }
108
+ /** Head for the active camera, advance the blend and return the reusable output state. */
109
+ function tick(state, params, dt) {
110
+ const blendState = state.blend;
111
+ const previousLiveId = blendState.liveId;
112
+ const justCreatedCut = state.activeId !== null && state.activeId !== targetId(blendState) && retarget(state, params, state.activeId);
113
+ const wasBlending = blendState.transition.active;
114
+ const targetId$1 = targetId(blendState);
115
+ const result = tick$1(blendState, dt, targetId$1 !== null ? state.cameras.get(targetId$1).state : null);
116
+ if (wasBlending && !blendState.transition.active && !justCreatedCut) state.events.push({
117
+ type: "blendFinished",
118
+ liveId: blendState.liveId
119
+ });
120
+ reportLiveIdChange(state, previousLiveId);
121
+ return result;
122
+ }
123
+ //#endregion
124
+ export { create, klippState_exports, register, setHints, setPriority, tick, unregister };
@@ -0,0 +1,21 @@
1
+ import type { Vec3 } from 'math';
2
+ import type { CameraState } from '../CameraState.js';
3
+ import type { DampingConstant } from '../damping/damping.js';
4
+ import type { PerlinNoiseParams, PerlinNoiseState } from './perlinNoise.js';
5
+ export type PerlinNoiseOptions = Partial<PerlinNoiseParams> & {
6
+ /** Seed for the six independent Perlin channels. Random when omitted. */
7
+ seed?: number;
8
+ };
9
+ /** Adds Perlin position and rotation noise to a camera state. */
10
+ export declare class BasicMultiChannelPerlinNoise implements PerlinNoiseParams {
11
+ positionAmplitude: Vec3;
12
+ positionFrequency: Vec3;
13
+ rotationAmplitude: Vec3;
14
+ rotationFrequency: Vec3;
15
+ amplitudeGain: number;
16
+ frequencyGain: number;
17
+ amplitudeDamping: DampingConstant;
18
+ readonly state: PerlinNoiseState;
19
+ constructor(options?: PerlinNoiseOptions);
20
+ update: (out: CameraState, dt: number, justActivated: boolean) => void;
21
+ }
@@ -0,0 +1,13 @@
1
+ import { createParams, createState, update } from "./perlinNoise.js";
2
+ //#region src/core/noise/BasicMultiChannelPerlinNoise.ts
3
+ /** Adds Perlin position and rotation noise to a camera state. */
4
+ var BasicMultiChannelPerlinNoise = class {
5
+ state;
6
+ constructor(options) {
7
+ Object.assign(this, createParams(options));
8
+ this.state = createState(options?.seed ?? Math.random() * 1e4, this.amplitudeGain);
9
+ }
10
+ update = (out, dt, justActivated) => update(out, this.state, this, dt, justActivated);
11
+ };
12
+ //#endregion
13
+ export { BasicMultiChannelPerlinNoise };
@@ -0,0 +1,3 @@
1
+ export { BasicMultiChannelPerlinNoise, type PerlinNoiseOptions } from './BasicMultiChannelPerlinNoise.js';
2
+ export * as perlinNoise from './perlinNoise.js';
3
+ export type { PerlinNoiseParams, PerlinNoiseState } from './perlinNoise.js';
@@ -0,0 +1,36 @@
1
+ import { type Vec3 } from 'math';
2
+ import { perlin2d } from 'math/noise';
3
+ import type { CameraState } from '../CameraState.js';
4
+ import type { DamperState, DampingConstant } from '../damping/damping.js';
5
+ type Generator = ReturnType<typeof perlin2d.create>;
6
+ /** Three position and three rotation noise channels. */
7
+ type Channels = [Generator, Generator, Generator, Generator, Generator, Generator];
8
+ export type PerlinNoiseParams = {
9
+ /** Per-axis positional shake amplitude in camera-local space. */
10
+ positionAmplitude: Vec3;
11
+ /** Per-axis oscillation speed for position noise. */
12
+ positionFrequency: Vec3;
13
+ /** Per-axis rotational shake amplitude in degrees. */
14
+ rotationAmplitude: Vec3;
15
+ /** Per-axis oscillation speed for rotation noise. */
16
+ rotationFrequency: Vec3;
17
+ /** Multiplies every amplitude at once. */
18
+ amplitudeGain: number;
19
+ /** Multiplies every channel's frequency. */
20
+ frequencyGain: number;
21
+ /** Response time for changes to `amplitudeGain`. */
22
+ amplitudeDamping: DampingConstant;
23
+ };
24
+ /** Every setting from `settings`, or its default. */
25
+ export declare const createParams: (settings?: Partial<PerlinNoiseParams>) => PerlinNoiseParams;
26
+ export type PerlinNoiseState = {
27
+ channels: Channels;
28
+ amplitudeGainDamper: DamperState;
29
+ effectiveAmplitudeGain: number;
30
+ positionPhase: Vec3;
31
+ rotationPhase: Vec3;
32
+ };
33
+ export declare const createState: (seed: number, amplitudeGain?: number) => PerlinNoiseState;
34
+ /** Adds Perlin position and rotation noise to `out`, in camera-local space. */
35
+ export declare function update(out: CameraState, state: PerlinNoiseState, params: PerlinNoiseParams, dt: number, justActivated: boolean): void;
36
+ export {};
@@ -0,0 +1,110 @@
1
+ import { __exportAll } from "../../_virtual/_rolldown/runtime.js";
2
+ import { createState as createState$1, damp, reset } from "../damping/damping.js";
3
+ import { withDefaults } from "../params.js";
4
+ import { degreesToRadians, quat, vec3 } from "math";
5
+ import { perlin2d } from "math/noise";
6
+ //#region src/core/noise/perlinNoise.ts
7
+ var perlinNoise_exports = /* @__PURE__ */ __exportAll({
8
+ createParams: () => createParams,
9
+ createState: () => createState,
10
+ update: () => update
11
+ });
12
+ /** Every setting from `settings`, or its default. */
13
+ var createParams = (settings) => withDefaults({
14
+ positionAmplitude: [
15
+ 0,
16
+ 0,
17
+ 0
18
+ ],
19
+ positionFrequency: [
20
+ 1,
21
+ 1,
22
+ 1
23
+ ],
24
+ rotationAmplitude: [
25
+ 0,
26
+ 0,
27
+ 0
28
+ ],
29
+ rotationFrequency: [
30
+ 1,
31
+ 1,
32
+ 1
33
+ ],
34
+ amplitudeGain: 1,
35
+ frequencyGain: 1,
36
+ amplitudeDamping: 0
37
+ }, settings);
38
+ var createState = (seed, amplitudeGain = 1) => ({
39
+ channels: [
40
+ perlin2d.create(seed),
41
+ perlin2d.create(seed + 1),
42
+ perlin2d.create(seed + 2),
43
+ perlin2d.create(seed + 3),
44
+ perlin2d.create(seed + 4),
45
+ perlin2d.create(seed + 5)
46
+ ],
47
+ amplitudeGainDamper: createState$1(),
48
+ effectiveAmplitudeGain: amplitudeGain,
49
+ positionPhase: [
50
+ 0,
51
+ 0,
52
+ 0
53
+ ],
54
+ rotationPhase: [
55
+ 0,
56
+ 0,
57
+ 0
58
+ ]
59
+ });
60
+ var sampleY = .5;
61
+ var scratchOffset = [
62
+ 0,
63
+ 0,
64
+ 0
65
+ ];
66
+ var scratchEuler = [
67
+ 0,
68
+ 0,
69
+ 0,
70
+ "xyz"
71
+ ];
72
+ var scratchRotation = [
73
+ 0,
74
+ 0,
75
+ 0,
76
+ 1
77
+ ];
78
+ /** Adds Perlin position and rotation noise to `out`, in camera-local space. */
79
+ function update(out, state, params, dt, justActivated) {
80
+ const { channels, positionPhase, rotationPhase } = state;
81
+ const frequencyStep = dt * params.frequencyGain;
82
+ positionPhase[0] += frequencyStep * params.positionFrequency[0];
83
+ positionPhase[1] += frequencyStep * params.positionFrequency[1];
84
+ positionPhase[2] += frequencyStep * params.positionFrequency[2];
85
+ rotationPhase[0] += frequencyStep * params.rotationFrequency[0];
86
+ rotationPhase[1] += frequencyStep * params.rotationFrequency[1];
87
+ rotationPhase[2] += frequencyStep * params.rotationFrequency[2];
88
+ if (justActivated) reset(state.amplitudeGainDamper);
89
+ if (typeof params.amplitudeDamping === "number" && params.amplitudeDamping <= 0) state.effectiveAmplitudeGain = params.amplitudeGain;
90
+ else {
91
+ state.amplitudeGainDamper.value = state.effectiveAmplitudeGain;
92
+ state.effectiveAmplitudeGain = damp(state.amplitudeGainDamper, params.amplitudeGain, params.amplitudeDamping, dt).value;
93
+ }
94
+ const gain = state.effectiveAmplitudeGain;
95
+ const { positionAmplitude, rotationAmplitude } = params;
96
+ if (positionAmplitude[0] !== 0 || positionAmplitude[1] !== 0 || positionAmplitude[2] !== 0) {
97
+ vec3.set(scratchOffset, perlin2d.sample(channels[0], positionPhase[0], sampleY) * positionAmplitude[0], perlin2d.sample(channels[1], positionPhase[1], sampleY) * positionAmplitude[1], perlin2d.sample(channels[2], positionPhase[2], sampleY) * positionAmplitude[2]);
98
+ vec3.scale(scratchOffset, scratchOffset, gain);
99
+ vec3.transformQuat(scratchOffset, scratchOffset, out.quaternion);
100
+ vec3.add(out.position, out.position, scratchOffset);
101
+ }
102
+ if (rotationAmplitude[0] !== 0 || rotationAmplitude[1] !== 0 || rotationAmplitude[2] !== 0) {
103
+ scratchEuler[0] = degreesToRadians(perlin2d.sample(channels[3], rotationPhase[0], sampleY) * rotationAmplitude[0]) * gain;
104
+ scratchEuler[1] = degreesToRadians(perlin2d.sample(channels[4], rotationPhase[1], sampleY) * rotationAmplitude[1]) * gain;
105
+ scratchEuler[2] = degreesToRadians(perlin2d.sample(channels[5], rotationPhase[2], sampleY) * rotationAmplitude[2]) * gain;
106
+ quat.multiply(out.quaternion, out.quaternion, quat.fromEuler(scratchRotation, scratchEuler));
107
+ }
108
+ }
109
+ //#endregion
110
+ export { createParams, createState, perlinNoise_exports, update };
@@ -0,0 +1,2 @@
1
+ /** Fills `defaults` with every value of `settings` that isn't `undefined`, and returns it. */
2
+ export declare function withDefaults<T extends object>(defaults: T, settings: Partial<T> | undefined): T;
@@ -0,0 +1,12 @@
1
+ //#region src/core/params.ts
2
+ /** Fills `defaults` with every value of `settings` that isn't `undefined`, and returns it. */
3
+ function withDefaults(defaults, settings) {
4
+ if (!settings) return defaults;
5
+ for (const key in defaults) {
6
+ const value = settings[key];
7
+ if (value !== void 0) defaults[key] = value;
8
+ }
9
+ return defaults;
10
+ }
11
+ //#endregion
12
+ export { withDefaults };
@@ -0,0 +1,11 @@
1
+ import type { DebugZone } from '../core/debug/debugZones.js';
2
+ export type { DebugZone };
3
+ /** Draws framing zones and a crosshair as DOM elements over a canvas container. */
4
+ export declare class DebugOverlay {
5
+ private root;
6
+ /** Attaches the overlay to `container`, usually the canvas's parent. Disconnects the old one first. */
7
+ connect: (container: HTMLElement) => void;
8
+ disconnect: () => void;
9
+ /** Replaces what is drawn. Pass no zones and no crosshair to clear it. */
10
+ render: (zones: readonly DebugZone[], crosshair?: [number, number]) => void;
11
+ }
@@ -0,0 +1,96 @@
1
+ //#region src/dom/DebugOverlay.ts
2
+ var STYLESHEET_ID = "klipp-debug-zone-overlay-styles";
3
+ /** Inject default overlay styles once. */
4
+ function ensureStylesInjected() {
5
+ if (document.getElementById(STYLESHEET_ID)) return;
6
+ const style = document.createElement("style");
7
+ style.id = STYLESHEET_ID;
8
+ style.textContent = `
9
+ .klipp-debug-deadzone {
10
+ box-sizing: border-box;
11
+ border: 2px solid #33cc33;
12
+ box-shadow: 0 0 0 100vmax color-mix(in srgb, #33cc33 5%, transparent);
13
+ }
14
+ .klipp-debug-hardlimit {
15
+ box-sizing: border-box;
16
+ border: 2px solid #cc3333;
17
+ box-shadow: 0 0 0 100vmax color-mix(in srgb, #cc3333 5%, transparent);
18
+ }
19
+ .klipp-debug-groupframing {
20
+ box-sizing: border-box;
21
+ border: 2px solid #3399cc;
22
+ box-shadow: 0 0 0 100vmax color-mix(in srgb, #3399cc 5%, transparent);
23
+ }
24
+ .klipp-debug-crosshair { background: #ffcc00; }
25
+ `;
26
+ document.head.appendChild(style);
27
+ }
28
+ /** Convert normalized screen coordinates to CSS percentages. */
29
+ function ndcToPercent(ndc, invertY) {
30
+ return ((invertY ? -ndc : ndc) + 1) * 50;
31
+ }
32
+ function appendLine(root, styles) {
33
+ const line = document.createElement("div");
34
+ line.className = "klipp-debug-crosshair";
35
+ line.style.position = "absolute";
36
+ Object.assign(line.style, styles);
37
+ root.appendChild(line);
38
+ }
39
+ /** Draws framing zones and a crosshair as DOM elements over a canvas container. */
40
+ var DebugOverlay = class {
41
+ root = null;
42
+ /** Attaches the overlay to `container`, usually the canvas's parent. Disconnects the old one first. */
43
+ connect = (container) => {
44
+ this.disconnect();
45
+ ensureStylesInjected();
46
+ const root = document.createElement("div");
47
+ root.style.position = "absolute";
48
+ root.style.inset = "0";
49
+ root.style.pointerEvents = "none";
50
+ root.style.overflow = "hidden";
51
+ container.appendChild(root);
52
+ this.root = root;
53
+ };
54
+ disconnect = () => {
55
+ this.root?.remove();
56
+ this.root = null;
57
+ };
58
+ /** Replaces what is drawn. Pass no zones and no crosshair to clear it. */
59
+ render = (zones, crosshair) => {
60
+ const root = this.root;
61
+ if (!root) return;
62
+ root.replaceChildren();
63
+ for (const zone of zones) {
64
+ const halfWidth = zone.size[0] / 2;
65
+ const halfHeight = zone.size[1] / 2;
66
+ const left = ndcToPercent(zone.screenPosition[0] - halfWidth, false);
67
+ const right = ndcToPercent(zone.screenPosition[0] + halfWidth, false);
68
+ const top = ndcToPercent(zone.screenPosition[1] + halfHeight, true);
69
+ const bottom = ndcToPercent(zone.screenPosition[1] - halfHeight, true);
70
+ const box = document.createElement("div");
71
+ box.className = zone.className;
72
+ box.style.position = "absolute";
73
+ box.style.left = `${left}%`;
74
+ box.style.top = `${top}%`;
75
+ box.style.width = `${right - left}%`;
76
+ box.style.height = `${bottom - top}%`;
77
+ root.appendChild(box);
78
+ }
79
+ if (crosshair) {
80
+ appendLine(root, {
81
+ left: `${ndcToPercent(crosshair[0], false)}%`,
82
+ top: "0",
83
+ bottom: "0",
84
+ width: "1px"
85
+ });
86
+ appendLine(root, {
87
+ top: `${ndcToPercent(crosshair[1], true)}%`,
88
+ left: "0",
89
+ right: "0",
90
+ height: "1px"
91
+ });
92
+ }
93
+ };
94
+ };
95
+ //#endregion
96
+ export { DebugOverlay };