@haiyue/extensions 0.1.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 (293) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/dist/animation/Animation2DComponent.d.ts +46 -0
  4. package/dist/animation/Animation2DExtensionRegistry.d.ts +27 -0
  5. package/dist/animation/Animation2DRenderSystem.d.ts +89 -0
  6. package/dist/animation/Animation2DRuntime.d.ts +99 -0
  7. package/dist/animation/Animation2DStateMachine.d.ts +13 -0
  8. package/dist/animation/Animation2DStateMachineComponent.d.ts +54 -0
  9. package/dist/animation/Animation2DStateMachineRuntime.d.ts +30 -0
  10. package/dist/animation/Animation2DStateMachineSystem.d.ts +14 -0
  11. package/dist/animation/Animation2DStateMachineVisualRuntime.d.ts +27 -0
  12. package/dist/animation/Animation2DSystem.d.ts +14 -0
  13. package/dist/animation/AnimationAssetLoader.d.ts +9 -0
  14. package/dist/animation/AnimationAudioClip.d.ts +26 -0
  15. package/dist/animation/AnimationPathTessellator.d.ts +52 -0
  16. package/dist/animation/AnimationTextRasterizer.d.ts +21 -0
  17. package/dist/animation/AnimationVisual2D.d.ts +68 -0
  18. package/dist/animation/HyaAnimation2DClipAdapter.d.ts +16 -0
  19. package/dist/animation/Particle2DDescriptorAdapter.d.ts +5 -0
  20. package/dist/animation/runtime/mixer/Action.d.ts +150 -0
  21. package/dist/animation/runtime/mixer/Mixer.d.ts +54 -0
  22. package/dist/animation/runtime/mixer/PoseBuffer.d.ts +23 -0
  23. package/dist/animation/runtime/mixer/Sampler.d.ts +17 -0
  24. package/dist/animation/runtime/mixer/Types.d.ts +101 -0
  25. package/dist/animation/runtime/mixer/index.d.ts +6 -0
  26. package/dist/animation-state-machine/AnimationStateMachine.d.ts +111 -0
  27. package/dist/animation-state-machine/AnimationStateMachineChannels.d.ts +23 -0
  28. package/dist/animation-state-machine/runtime/Animation2DStateMachineMixerAdapter.d.ts +51 -0
  29. package/dist/animation-state-machine/runtime/AnimationStateMachineBlendTreeWeights.d.ts +14 -0
  30. package/dist/animation-state-machine/runtime/AnimationStateMachineCompiler.d.ts +109 -0
  31. package/dist/animation-state-machine/runtime/AnimationStateMachineController.d.ts +103 -0
  32. package/dist/animation-state-machine/runtime/AnimationStateMachineMixerPort.d.ts +35 -0
  33. package/dist/animation-state-machine/runtime/index.d.ts +7 -0
  34. package/dist/animation.d.ts +15 -0
  35. package/dist/animation.js +1171 -0
  36. package/dist/animation3d/Animation3DAction.d.ts +46 -0
  37. package/dist/animation3d/Animation3DBinding.d.ts +85 -0
  38. package/dist/animation3d/Animation3DClip.d.ts +20 -0
  39. package/dist/animation3d/Animation3DError.d.ts +13 -0
  40. package/dist/animation3d/Animation3DMixer.d.ts +27 -0
  41. package/dist/animation3d/Animation3DPose.d.ts +40 -0
  42. package/dist/animation3d/Animation3DPoseBuffer.d.ts +24 -0
  43. package/dist/animation3d/Animation3DResource.d.ts +28 -0
  44. package/dist/animation3d/Animation3DStateMachine.d.ts +95 -0
  45. package/dist/animation3d/Animation3DStateMachineRuntime.d.ts +70 -0
  46. package/dist/animation3d/Animation3DTrack.d.ts +19 -0
  47. package/dist/animation3d/hya/HyaAnimation3DRuntime.d.ts +73 -0
  48. package/dist/animation3d/hya/HyaAnimation3DStateMachineRuntime.d.ts +65 -0
  49. package/dist/animation3d/hya/HyaAnimation3DTypes.d.ts +170 -0
  50. package/dist/animation3d/hya/index.d.ts +4 -0
  51. package/dist/animation3d/index.d.ts +14 -0
  52. package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.d.ts +81 -0
  53. package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.d.ts +39 -0
  54. package/dist/animation3d/runtime/integration/index.d.ts +2 -0
  55. package/dist/animation3d/runtime/mixer/Action.d.ts +150 -0
  56. package/dist/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.d.ts +6 -0
  57. package/dist/animation3d/runtime/mixer/Mixer.d.ts +59 -0
  58. package/dist/animation3d/runtime/mixer/PoseBuffer.d.ts +1 -0
  59. package/dist/animation3d/runtime/mixer/TrackSampler.d.ts +24 -0
  60. package/dist/animation3d/runtime/mixer/index.d.ts +4 -0
  61. package/dist/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.d.ts +1 -0
  62. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.d.ts +1 -0
  63. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineController.d.ts +1 -0
  64. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.d.ts +1 -0
  65. package/dist/animation3d/runtime/state-machine/index.d.ts +6 -0
  66. package/dist/animation3d.d.ts +5 -0
  67. package/dist/animation3d.js +1708 -0
  68. package/dist/benchmark.d.ts +30 -0
  69. package/dist/benchmark.js +119 -0
  70. package/dist/canvas-text/CanvasText2DRenderSystem.d.ts +24 -0
  71. package/dist/canvas-text/CanvasTextComponent.d.ts +18 -0
  72. package/dist/canvas-text.d.ts +4 -0
  73. package/dist/canvas-text.js +276 -0
  74. package/dist/chunks/2d-ui-animation-2d.generated-D4aHzFU2.js +4 -0
  75. package/dist/chunks/2d-ui-canvas-text-2d.generated-C0Eaej2m.js +4 -0
  76. package/dist/chunks/2d-ui-spine2d.generated-CUUi8xjl.js +4 -0
  77. package/dist/chunks/2d-ui-tilemap2d.generated-gebw4mX7.js +4 -0
  78. package/dist/chunks/Animation2DStateMachine-9IonguNa.js +3245 -0
  79. package/dist/chunks/AnimationStateMachineController-BIcAh-sT.js +1438 -0
  80. package/dist/chunks/GltfAnimation3DAdapter-Cu3rQQsW.js +2240 -0
  81. package/dist/chunks/GltfAnimationRuntime-CRHsLycN.js +631 -0
  82. package/dist/chunks/RenderSystem2DBase-DDT61dAu.js +227 -0
  83. package/dist/chunks/Spine2DRuntime-D9M9LAQR.js +3056 -0
  84. package/dist/chunks/SpineAssetParser-BSDmPge1.js +378 -0
  85. package/dist/chunks/gltfLoader-Dm22jLYB.js +2085 -0
  86. package/dist/chunks/pluginUtils-CvwroRMd.js +33 -0
  87. package/dist/experimental-gltf-worker.d.ts +4 -0
  88. package/dist/experimental-gltf-worker.js +138 -0
  89. package/dist/experimental-spine-worker.d.ts +4 -0
  90. package/dist/experimental-spine-worker.js +110 -0
  91. package/dist/gltf/GltfAccessorReader.d.ts +7 -0
  92. package/dist/gltf/GltfAnimation3DAdapter.d.ts +47 -0
  93. package/dist/gltf/GltfAnimationRuntime.d.ts +10 -0
  94. package/dist/gltf/GltfAssetWorkerClient.d.ts +20 -0
  95. package/dist/gltf/GltfCompatibilityReport.d.ts +70 -0
  96. package/dist/gltf/GltfConservativeBounds.d.ts +18 -0
  97. package/dist/gltf/GltfDracoDecoder.d.ts +4 -0
  98. package/dist/gltf/GltfExtensionAdapter.d.ts +54 -0
  99. package/dist/gltf/GltfLoaderContract.d.ts +249 -0
  100. package/dist/gltf/GltfLoaderErrors.d.ts +4 -0
  101. package/dist/gltf/GltfMaterialDescriptor.d.ts +23 -0
  102. package/dist/gltf/GltfMaterialEncoder.d.ts +20 -0
  103. package/dist/gltf/GltfMaterialLoader.d.ts +6 -0
  104. package/dist/gltf/GltfModelComponent.d.ts +72 -0
  105. package/dist/gltf/GltfModelSystem.d.ts +37 -0
  106. package/dist/gltf/GltfPlugin.d.ts +6 -0
  107. package/dist/gltf/GltfSchema.d.ts +225 -0
  108. package/dist/gltf/GltfUvSemanticPlanner.d.ts +33 -0
  109. package/dist/gltf/gltfLoader.d.ts +9 -0
  110. package/dist/gltf-animation3d.d.ts +3 -0
  111. package/dist/gltf-animation3d.js +8 -0
  112. package/dist/gltf-worker-runtime.d.ts +7 -0
  113. package/dist/gltf-worker-runtime.js +6981 -0
  114. package/dist/gltf.d.ts +12 -0
  115. package/dist/gltf.js +517 -0
  116. package/dist/grid/Grid2DComponent.d.ts +25 -0
  117. package/dist/grid.d.ts +2 -0
  118. package/dist/grid.js +56 -0
  119. package/dist/hya-state-machine.d.ts +7 -0
  120. package/dist/hya-state-machine.js +2934 -0
  121. package/dist/index.d.ts +2 -0
  122. package/dist/index.js +7 -0
  123. package/dist/internal/2d-ui-shader-artifact.js +870 -0
  124. package/dist/plugins/pluginUtils.d.ts +20 -0
  125. package/dist/plugins.d.ts +6 -0
  126. package/dist/shaders/generated/2d-ui-artifact.generated.d.ts +658 -0
  127. package/dist/spine/Spine2DComponent.d.ts +116 -0
  128. package/dist/spine/Spine2DGpuRenderer.d.ts +65 -0
  129. package/dist/spine/Spine2DRenderSystem.d.ts +27 -0
  130. package/dist/spine/Spine2DRuntime.d.ts +103 -0
  131. package/dist/spine/SpineAnimationBoneTimelines.d.ts +8 -0
  132. package/dist/spine/SpineAssetParser.d.ts +18 -0
  133. package/dist/spine/SpineAssetWorkerClient.d.ts +19 -0
  134. package/dist/spine/SpineAssetWorkerContract.d.ts +6 -0
  135. package/dist/spine/SpineAtlasParser.d.ts +21 -0
  136. package/dist/spine/SpineFloatBuilder.d.ts +15 -0
  137. package/dist/spine/SpinePathConstraintSolver.d.ts +32 -0
  138. package/dist/spine/SpinePlugin.d.ts +8 -0
  139. package/dist/spine/SpineSkeletonRuntime.d.ts +185 -0
  140. package/dist/spine/SpineSlotAnimation.d.ts +17 -0
  141. package/dist/spine/SpineTimelineRuntime.d.ts +25 -0
  142. package/dist/spine/SpineVertexBuilder.d.ts +61 -0
  143. package/dist/spine.d.ts +7 -0
  144. package/dist/spine.js +578 -0
  145. package/dist/tilemap/Tilemap2DComponent.d.ts +110 -0
  146. package/dist/tilemap/Tilemap2DRenderSystem.d.ts +11 -0
  147. package/dist/tilemap/Tilemap2DRenderer.d.ts +24 -0
  148. package/dist/tilemap/TilemapPlugin.d.ts +8 -0
  149. package/dist/tilemap.d.ts +6 -0
  150. package/dist/tilemap.js +535 -0
  151. package/dist/tween/Tween2DComponent.d.ts +70 -0
  152. package/dist/tween/Tween2DSystem.d.ts +12 -0
  153. package/dist/tween.d.ts +4 -0
  154. package/dist/tween.js +138 -0
  155. package/dist/utils/RenderSystem2DBase.d.ts +42 -0
  156. package/dist/utils/arrayAccess.d.ts +4 -0
  157. package/dist/utils/camera2d.d.ts +12 -0
  158. package/dist/utils/render2dGpu.d.ts +32 -0
  159. package/package.json +124 -0
  160. package/src/animation/Animation2DComponent.ts +135 -0
  161. package/src/animation/Animation2DExtensionRegistry.ts +52 -0
  162. package/src/animation/Animation2DRenderSystem.ts +1026 -0
  163. package/src/animation/Animation2DRuntime.ts +1326 -0
  164. package/src/animation/Animation2DStateMachine.ts +39 -0
  165. package/src/animation/Animation2DStateMachineComponent.ts +228 -0
  166. package/src/animation/Animation2DStateMachineRuntime.ts +141 -0
  167. package/src/animation/Animation2DStateMachineSystem.ts +60 -0
  168. package/src/animation/Animation2DStateMachineVisualRuntime.ts +282 -0
  169. package/src/animation/Animation2DSystem.ts +72 -0
  170. package/src/animation/AnimationAssetLoader.ts +84 -0
  171. package/src/animation/AnimationAudioClip.ts +140 -0
  172. package/src/animation/AnimationPathTessellator.ts +691 -0
  173. package/src/animation/AnimationTextRasterizer.ts +340 -0
  174. package/src/animation/AnimationVisual2D.ts +153 -0
  175. package/src/animation/HyaAnimation2DClipAdapter.ts +845 -0
  176. package/src/animation/Particle2DDescriptorAdapter.ts +35 -0
  177. package/src/animation/runtime/mixer/Action.ts +876 -0
  178. package/src/animation/runtime/mixer/Mixer.ts +505 -0
  179. package/src/animation/runtime/mixer/PoseBuffer.ts +105 -0
  180. package/src/animation/runtime/mixer/Sampler.ts +252 -0
  181. package/src/animation/runtime/mixer/Types.ts +138 -0
  182. package/src/animation/runtime/mixer/index.ts +31 -0
  183. package/src/animation-state-machine/AnimationStateMachine.ts +146 -0
  184. package/src/animation-state-machine/AnimationStateMachineChannels.ts +116 -0
  185. package/src/animation-state-machine/runtime/Animation2DStateMachineMixerAdapter.ts +197 -0
  186. package/src/animation-state-machine/runtime/AnimationStateMachineBlendTreeWeights.ts +503 -0
  187. package/src/animation-state-machine/runtime/AnimationStateMachineCompiler.ts +763 -0
  188. package/src/animation-state-machine/runtime/AnimationStateMachineController.ts +987 -0
  189. package/src/animation-state-machine/runtime/AnimationStateMachineMixerPort.ts +54 -0
  190. package/src/animation-state-machine/runtime/index.ts +47 -0
  191. package/src/animation.ts +33 -0
  192. package/src/animation3d/Animation3DAction.ts +73 -0
  193. package/src/animation3d/Animation3DBinding.ts +116 -0
  194. package/src/animation3d/Animation3DClip.ts +22 -0
  195. package/src/animation3d/Animation3DError.ts +34 -0
  196. package/src/animation3d/Animation3DMixer.ts +82 -0
  197. package/src/animation3d/Animation3DPose.ts +100 -0
  198. package/src/animation3d/Animation3DPoseBuffer.ts +85 -0
  199. package/src/animation3d/Animation3DResource.ts +49 -0
  200. package/src/animation3d/Animation3DStateMachine.ts +126 -0
  201. package/src/animation3d/Animation3DStateMachineRuntime.ts +191 -0
  202. package/src/animation3d/Animation3DTrack.ts +29 -0
  203. package/src/animation3d/hya/HyaAnimation3DRuntime.ts +756 -0
  204. package/src/animation3d/hya/HyaAnimation3DStateMachineRuntime.ts +618 -0
  205. package/src/animation3d/hya/HyaAnimation3DTypes.ts +168 -0
  206. package/src/animation3d/hya/index.ts +10 -0
  207. package/src/animation3d/index.ts +76 -0
  208. package/src/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.ts +325 -0
  209. package/src/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.ts +158 -0
  210. package/src/animation3d/runtime/integration/index.ts +9 -0
  211. package/src/animation3d/runtime/mixer/Action.ts +833 -0
  212. package/src/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.ts +26 -0
  213. package/src/animation3d/runtime/mixer/Mixer.ts +562 -0
  214. package/src/animation3d/runtime/mixer/PoseBuffer.ts +1 -0
  215. package/src/animation3d/runtime/mixer/TrackSampler.ts +399 -0
  216. package/src/animation3d/runtime/mixer/index.ts +8 -0
  217. package/src/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.ts +4 -0
  218. package/src/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.ts +1 -0
  219. package/src/animation3d/runtime/state-machine/Animation3DStateMachineController.ts +1 -0
  220. package/src/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.ts +1 -0
  221. package/src/animation3d/runtime/state-machine/index.ts +45 -0
  222. package/src/animation3d.ts +5 -0
  223. package/src/benchmark.ts +168 -0
  224. package/src/canvas-text/CanvasText2DRenderSystem.ts +292 -0
  225. package/src/canvas-text/CanvasTextComponent.ts +47 -0
  226. package/src/canvas-text.ts +4 -0
  227. package/src/experimental-gltf-worker.ts +18 -0
  228. package/src/experimental-spine-worker.ts +9 -0
  229. package/src/gltf/GltfAccessorReader.ts +271 -0
  230. package/src/gltf/GltfAnimation3DAdapter.ts +680 -0
  231. package/src/gltf/GltfAnimationRuntime.ts +392 -0
  232. package/src/gltf/GltfAssetWorkerClient.ts +136 -0
  233. package/src/gltf/GltfCompatibilityReport.ts +214 -0
  234. package/src/gltf/GltfConservativeBounds.ts +219 -0
  235. package/src/gltf/GltfDracoDecoder.ts +251 -0
  236. package/src/gltf/GltfExtensionAdapter.ts +340 -0
  237. package/src/gltf/GltfLoaderContract.ts +333 -0
  238. package/src/gltf/GltfLoaderErrors.ts +59 -0
  239. package/src/gltf/GltfMaterialDescriptor.ts +134 -0
  240. package/src/gltf/GltfMaterialEncoder.ts +173 -0
  241. package/src/gltf/GltfMaterialLoader.ts +293 -0
  242. package/src/gltf/GltfModelComponent.ts +115 -0
  243. package/src/gltf/GltfModelSystem.ts +208 -0
  244. package/src/gltf/GltfPlugin.ts +84 -0
  245. package/src/gltf/GltfSchema.ts +229 -0
  246. package/src/gltf/GltfUvSemanticPlanner.ts +166 -0
  247. package/src/gltf/gltfLoader.ts +910 -0
  248. package/src/gltf-animation3d.ts +10 -0
  249. package/src/gltf-worker-runtime.ts +10 -0
  250. package/src/gltf.ts +56 -0
  251. package/src/grid/Grid2DComponent.ts +69 -0
  252. package/src/grid.ts +2 -0
  253. package/src/hya-state-machine.ts +19 -0
  254. package/src/index.ts +2 -0
  255. package/src/plugins/pluginUtils.ts +50 -0
  256. package/src/plugins.ts +6 -0
  257. package/src/shaders/generated/2d-ui-animation-2d.generated.wgsl +242 -0
  258. package/src/shaders/generated/2d-ui-artifact.generated.ts +867 -0
  259. package/src/shaders/generated/2d-ui-canvas-text-2d.generated.wgsl +38 -0
  260. package/src/shaders/generated/2d-ui-spine2d.generated.wgsl +41 -0
  261. package/src/shaders/generated/2d-ui-tilemap2d.generated.wgsl +36 -0
  262. package/src/spine/Spine2DComponent.ts +168 -0
  263. package/src/spine/Spine2DGpuRenderer.ts +449 -0
  264. package/src/spine/Spine2DRenderSystem.ts +199 -0
  265. package/src/spine/Spine2DRuntime.ts +1529 -0
  266. package/src/spine/SpineAnimationBoneTimelines.ts +30 -0
  267. package/src/spine/SpineAssetParser.ts +20 -0
  268. package/src/spine/SpineAssetWorkerClient.ts +112 -0
  269. package/src/spine/SpineAssetWorkerContract.ts +4 -0
  270. package/src/spine/SpineAtlasParser.ts +127 -0
  271. package/src/spine/SpineFloatBuilder.ts +66 -0
  272. package/src/spine/SpinePathConstraintSolver.ts +582 -0
  273. package/src/spine/SpinePlugin.ts +76 -0
  274. package/src/spine/SpineSkeletonRuntime.ts +521 -0
  275. package/src/spine/SpineSlotAnimation.ts +195 -0
  276. package/src/spine/SpineTimelineRuntime.ts +453 -0
  277. package/src/spine/SpineVertexBuilder.ts +686 -0
  278. package/src/spine.ts +7 -0
  279. package/src/tilemap/Tilemap2DComponent.ts +232 -0
  280. package/src/tilemap/Tilemap2DRenderSystem.ts +58 -0
  281. package/src/tilemap/Tilemap2DRenderer.ts +289 -0
  282. package/src/tilemap/TilemapPlugin.ts +75 -0
  283. package/src/tilemap.ts +6 -0
  284. package/src/tween/Tween2DComponent.ts +83 -0
  285. package/src/tween/Tween2DSystem.ts +78 -0
  286. package/src/tween.ts +4 -0
  287. package/src/types/earcut.d.ts +7 -0
  288. package/src/types/wgsl.d.ts +4 -0
  289. package/src/utils/RenderSystem2DBase.ts +164 -0
  290. package/src/utils/arrayAccess.ts +17 -0
  291. package/src/utils/camera2d.ts +68 -0
  292. package/src/utils/render2dGpu.ts +132 -0
  293. package/test/fixtures/gltf/animation-characterization.gltf +344 -0
@@ -0,0 +1,1438 @@
1
+ const EPSILON = 1e-9;
2
+ const TWO_PI = Math.PI * 2;
3
+ /**
4
+ * Evaluates direct child weights for a compiled 1D blend tree. Values outside
5
+ * the authored threshold range clamp to the nearest endpoint.
6
+ */
7
+ function evaluateAnimation3DBlend1DWeights(motion, value, out = new Float64Array(motion.children.length)) {
8
+ requireOutputSize(out, motion.children.length);
9
+ out.fill(0);
10
+ const children = motion.children;
11
+ if (children.length === 1 || value <= children[0].threshold) {
12
+ out[0] = 1;
13
+ return out;
14
+ }
15
+ const lastIndex = children.length - 1;
16
+ if (value >= children[lastIndex].threshold) {
17
+ out[lastIndex] = 1;
18
+ return out;
19
+ }
20
+ let low = 0;
21
+ let high = lastIndex;
22
+ while (low + 1 < high) {
23
+ const middle = (low + high) >>> 1;
24
+ if (value < children[middle].threshold)
25
+ high = middle;
26
+ else
27
+ low = middle;
28
+ }
29
+ const start = children[low].threshold;
30
+ const end = children[high].threshold;
31
+ const alpha = (value - start) / (end - start);
32
+ out[low] = 1 - alpha;
33
+ out[high] = alpha;
34
+ return out;
35
+ }
36
+ /**
37
+ * Evaluates direct child weights for a compiled 2D blend tree.
38
+ *
39
+ * Cartesian trees use a deterministic containing simplex after projecting
40
+ * outside samples to the convex hull. Directional trees interpolate the two
41
+ * surrounding authored directions and then interpolate/clamp radially.
42
+ */
43
+ function evaluateAnimation3DBlend2DWeights(motion, x, y, out = new Float64Array(motion.children.length)) {
44
+ requireOutputSize(out, motion.children.length);
45
+ out.fill(0);
46
+ if (motion.children.length === 1) {
47
+ out[0] = 1;
48
+ return out;
49
+ }
50
+ return motion.algorithm === 'directional'
51
+ ? evaluateDirectional(motion, x, y, out)
52
+ : evaluateCartesian(motion, x, y, out);
53
+ }
54
+ function evaluateCartesian(motion, x, y, out) {
55
+ const children = motion.children;
56
+ const exact = findExactPoint(motion, x, y);
57
+ if (exact >= 0) {
58
+ out[exact] = 1;
59
+ return out;
60
+ }
61
+ const hull = convexHull(motion);
62
+ const point = clampToHull(motion, hull, x, y);
63
+ const clampedExact = findExactPoint(motion, point[0], point[1]);
64
+ if (clampedExact >= 0) {
65
+ out[clampedExact] = 1;
66
+ return out;
67
+ }
68
+ let bestArea = Infinity;
69
+ let bestIndices = null;
70
+ let bestWeights = null;
71
+ for (let first = 0; first < children.length - 2; first++) {
72
+ for (let second = first + 1; second < children.length - 1; second++) {
73
+ for (let third = second + 1; third < children.length; third++) {
74
+ const a = children[first].position;
75
+ const b = children[second].position;
76
+ const c = children[third].position;
77
+ const signedDoubleArea = cross(a, b, c);
78
+ const area = Math.abs(signedDoubleArea);
79
+ if (area <= EPSILON)
80
+ continue;
81
+ const weights = barycentric(point, a, b, c, signedDoubleArea);
82
+ if (weights[0] < -EPSILON
83
+ || weights[1] < -EPSILON
84
+ || weights[2] < -EPSILON)
85
+ continue;
86
+ if (area < bestArea - EPSILON
87
+ || (Math.abs(area - bestArea) <= EPSILON
88
+ && lexicographicallyBefore([first, second, third], bestIndices))) {
89
+ bestArea = area;
90
+ bestIndices = [first, second, third];
91
+ bestWeights = weights;
92
+ }
93
+ }
94
+ }
95
+ }
96
+ if (bestIndices && bestWeights) {
97
+ out[bestIndices[0]] = clamp01(bestWeights[0]);
98
+ out[bestIndices[1]] = clamp01(bestWeights[1]);
99
+ out[bestIndices[2]] = clamp01(bestWeights[2]);
100
+ normalize(out);
101
+ return out;
102
+ }
103
+ // Collinear input (or a numerically degenerate hull): clamp to the closest
104
+ // authored segment with declaration order as the final tie breaker.
105
+ let bestDistance = Infinity;
106
+ let bestLength = Infinity;
107
+ let bestFirst = 0;
108
+ let bestSecond = 1;
109
+ let bestAlpha = 0;
110
+ for (let first = 0; first < children.length - 1; first++) {
111
+ for (let second = first + 1; second < children.length; second++) {
112
+ const projection = closestPointOnSegment(point[0], point[1], children[first].position, children[second].position);
113
+ const dx = projection[0] - point[0];
114
+ const dy = projection[1] - point[1];
115
+ const distance = dx * dx + dy * dy;
116
+ const segmentDx = children[second].position[0] - children[first].position[0];
117
+ const segmentDy = children[second].position[1] - children[first].position[1];
118
+ const length = segmentDx * segmentDx + segmentDy * segmentDy;
119
+ if (distance < bestDistance - EPSILON
120
+ || (Math.abs(distance - bestDistance) <= EPSILON
121
+ && length < bestLength - EPSILON)) {
122
+ bestDistance = distance;
123
+ bestLength = length;
124
+ bestFirst = first;
125
+ bestSecond = second;
126
+ bestAlpha = projection[2];
127
+ }
128
+ }
129
+ }
130
+ out[bestFirst] = 1 - bestAlpha;
131
+ out[bestSecond] = bestAlpha;
132
+ return out;
133
+ }
134
+ function evaluateDirectional(motion, x, y, out) {
135
+ const exact = findExactPoint(motion, x, y);
136
+ if (exact >= 0) {
137
+ out[exact] = 1;
138
+ return out;
139
+ }
140
+ const radius = Math.hypot(x, y);
141
+ const originIndices = [];
142
+ const directionalEntries = [];
143
+ motion.children.forEach((child, index) => {
144
+ const childRadius = Math.hypot(child.position[0], child.position[1]);
145
+ if (childRadius <= EPSILON) {
146
+ originIndices.push(index);
147
+ }
148
+ else {
149
+ directionalEntries.push({
150
+ angle: normalizeAngle(Math.atan2(child.position[1], child.position[0])),
151
+ radius: childRadius,
152
+ index,
153
+ });
154
+ }
155
+ });
156
+ if (radius <= EPSILON) {
157
+ out[originIndices[0] ?? directionalEntries[0].index] = 1;
158
+ return out;
159
+ }
160
+ if (directionalEntries.length === 0) {
161
+ out[originIndices[0]] = 1;
162
+ return out;
163
+ }
164
+ directionalEntries.sort((left, right) => left.angle - right.angle
165
+ || left.radius - right.radius
166
+ || left.index - right.index);
167
+ const groups = [];
168
+ directionalEntries.forEach(entry => {
169
+ const previous = groups[groups.length - 1];
170
+ if (previous && Math.abs(previous.angle - entry.angle) <= EPSILON) {
171
+ groups[groups.length - 1] = {
172
+ angle: previous.angle,
173
+ entries: Object.freeze([...previous.entries, {
174
+ index: entry.index,
175
+ radius: entry.radius,
176
+ }]),
177
+ };
178
+ }
179
+ else {
180
+ groups.push({
181
+ angle: entry.angle,
182
+ entries: Object.freeze([{
183
+ index: entry.index,
184
+ radius: entry.radius,
185
+ }]),
186
+ });
187
+ }
188
+ });
189
+ const queryAngle = normalizeAngle(Math.atan2(y, x));
190
+ let leftGroupIndex = groups.length - 1;
191
+ let rightGroupIndex = 0;
192
+ let angularAlpha = 0;
193
+ for (let index = 0; index < groups.length; index++) {
194
+ const left = groups[index];
195
+ const rightIndex = (index + 1) % groups.length;
196
+ const right = groups[rightIndex];
197
+ const leftAngle = left.angle;
198
+ const rightAngle = rightIndex === 0 ? right.angle + TWO_PI : right.angle;
199
+ const adjustedQuery = queryAngle < leftAngle ? queryAngle + TWO_PI : queryAngle;
200
+ if (adjustedQuery >= leftAngle - EPSILON && adjustedQuery <= rightAngle + EPSILON) {
201
+ leftGroupIndex = index;
202
+ rightGroupIndex = rightIndex;
203
+ angularAlpha = groups.length === 1
204
+ ? 0
205
+ : clamp01((adjustedQuery - leftAngle) / (rightAngle - leftAngle));
206
+ break;
207
+ }
208
+ }
209
+ const originIndex = originIndices[0] ?? -1;
210
+ accumulateRadial(groups[leftGroupIndex], radius, 1 - angularAlpha, originIndex, out);
211
+ if (rightGroupIndex !== leftGroupIndex) {
212
+ accumulateRadial(groups[rightGroupIndex], radius, angularAlpha, originIndex, out);
213
+ }
214
+ normalize(out);
215
+ return out;
216
+ }
217
+ function accumulateRadial(group, radius, angularWeight, originIndex, out) {
218
+ if (angularWeight <= EPSILON)
219
+ return;
220
+ const entries = group.entries;
221
+ const first = entries[0];
222
+ if (radius <= first.radius) {
223
+ if (originIndex >= 0) {
224
+ const alpha = clamp01(radius / first.radius);
225
+ out[originIndex] = (out[originIndex] ?? 0) + angularWeight * (1 - alpha);
226
+ out[first.index] = (out[first.index] ?? 0) + angularWeight * alpha;
227
+ }
228
+ else {
229
+ out[first.index] = (out[first.index] ?? 0) + angularWeight;
230
+ }
231
+ return;
232
+ }
233
+ const last = entries[entries.length - 1];
234
+ if (radius >= last.radius) {
235
+ out[last.index] = (out[last.index] ?? 0) + angularWeight;
236
+ return;
237
+ }
238
+ for (let index = 0; index < entries.length - 1; index++) {
239
+ const start = entries[index];
240
+ const end = entries[index + 1];
241
+ if (radius <= end.radius) {
242
+ const alpha = (radius - start.radius) / (end.radius - start.radius);
243
+ out[start.index] = (out[start.index] ?? 0) + angularWeight * (1 - alpha);
244
+ out[end.index] = (out[end.index] ?? 0) + angularWeight * alpha;
245
+ return;
246
+ }
247
+ }
248
+ }
249
+ function convexHull(motion) {
250
+ const sorted = motion.children.map((child, index) => ({
251
+ index,
252
+ x: child.position[0],
253
+ y: child.position[1],
254
+ })).sort((left, right) => left.x - right.x || left.y - right.y || left.index - right.index);
255
+ if (sorted.length <= 2)
256
+ return sorted.map(entry => entry.index);
257
+ const lower = [];
258
+ for (const entry of sorted) {
259
+ while (lower.length >= 2
260
+ && crossEntries(lower[lower.length - 2], lower[lower.length - 1], entry) <= EPSILON)
261
+ lower.pop();
262
+ lower.push(entry);
263
+ }
264
+ const upper = [];
265
+ for (let index = sorted.length - 1; index >= 0; index--) {
266
+ const entry = sorted[index];
267
+ while (upper.length >= 2
268
+ && crossEntries(upper[upper.length - 2], upper[upper.length - 1], entry) <= EPSILON)
269
+ upper.pop();
270
+ upper.push(entry);
271
+ }
272
+ lower.pop();
273
+ upper.pop();
274
+ const result = [...lower, ...upper].map(entry => entry.index);
275
+ return result.length === 0 ? [sorted[0].index] : result;
276
+ }
277
+ function clampToHull(motion, hull, x, y) {
278
+ if (hull.length === 1)
279
+ return motion.children[hull[0]].position;
280
+ if (hull.length === 2) {
281
+ const projection = closestPointOnSegment(x, y, motion.children[hull[0]].position, motion.children[hull[1]].position);
282
+ return [projection[0], projection[1]];
283
+ }
284
+ let inside = true;
285
+ for (let index = 0; index < hull.length; index++) {
286
+ const start = motion.children[hull[index]].position;
287
+ const end = motion.children[hull[(index + 1) % hull.length]].position;
288
+ if ((end[0] - start[0]) * (y - start[1]) - (end[1] - start[1]) * (x - start[0]) < -EPSILON) {
289
+ inside = false;
290
+ break;
291
+ }
292
+ }
293
+ if (inside)
294
+ return [x, y];
295
+ let bestX = x;
296
+ let bestY = y;
297
+ let bestDistance = Infinity;
298
+ for (let index = 0; index < hull.length; index++) {
299
+ const projection = closestPointOnSegment(x, y, motion.children[hull[index]].position, motion.children[hull[(index + 1) % hull.length]].position);
300
+ const dx = projection[0] - x;
301
+ const dy = projection[1] - y;
302
+ const distance = dx * dx + dy * dy;
303
+ if (distance < bestDistance - EPSILON) {
304
+ bestDistance = distance;
305
+ bestX = projection[0];
306
+ bestY = projection[1];
307
+ }
308
+ }
309
+ return [bestX, bestY];
310
+ }
311
+ function closestPointOnSegment(x, y, start, end) {
312
+ const dx = end[0] - start[0];
313
+ const dy = end[1] - start[1];
314
+ const denominator = dx * dx + dy * dy;
315
+ const alpha = denominator <= EPSILON
316
+ ? 0
317
+ : clamp01(((x - start[0]) * dx + (y - start[1]) * dy) / denominator);
318
+ return [start[0] + dx * alpha, start[1] + dy * alpha, alpha];
319
+ }
320
+ function barycentric(point, a, b, c, signedDoubleArea) {
321
+ const first = ((b[0] - point[0]) * (c[1] - point[1])
322
+ - (b[1] - point[1]) * (c[0] - point[0])) / signedDoubleArea;
323
+ const second = ((c[0] - point[0]) * (a[1] - point[1])
324
+ - (c[1] - point[1]) * (a[0] - point[0])) / signedDoubleArea;
325
+ return [first, second, 1 - first - second];
326
+ }
327
+ function findExactPoint(motion, x, y) {
328
+ for (let index = 0; index < motion.children.length; index++) {
329
+ const position = motion.children[index].position;
330
+ if (Math.abs(position[0] - x) <= EPSILON && Math.abs(position[1] - y) <= EPSILON) {
331
+ return index;
332
+ }
333
+ }
334
+ return -1;
335
+ }
336
+ function cross(a, b, c) {
337
+ return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
338
+ }
339
+ function crossEntries(a, b, c) {
340
+ return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
341
+ }
342
+ function lexicographicallyBefore(candidate, current) {
343
+ if (!current)
344
+ return true;
345
+ return candidate[0] < current[0]
346
+ || (candidate[0] === current[0] && candidate[1] < current[1])
347
+ || (candidate[0] === current[0]
348
+ && candidate[1] === current[1]
349
+ && candidate[2] < current[2]);
350
+ }
351
+ function normalize(out) {
352
+ let total = 0;
353
+ for (const value of out)
354
+ total += value;
355
+ if (total <= EPSILON)
356
+ return;
357
+ for (let index = 0; index < out.length; index++)
358
+ out[index] = out[index] / total;
359
+ }
360
+ function normalizeAngle(angle) {
361
+ return angle < 0 ? angle + TWO_PI : angle;
362
+ }
363
+ function clamp01(value) {
364
+ return Math.max(0, Math.min(1, value));
365
+ }
366
+ function requireOutputSize(out, size) {
367
+ if (out.length < size) {
368
+ throw new RangeError(`Blend weight output requires ${size} entries; received ${out.length}.`);
369
+ }
370
+ }
371
+
372
+ class AnimationStateMachineValidationError extends Error {
373
+ issues;
374
+ constructor(issues) {
375
+ super(`Invalid animation state-machine definition (${issues.length} ${issues.length === 1 ? 'issue' : 'issues'}).`);
376
+ this.name = 'AnimationStateMachineValidationError';
377
+ this.issues = Object.freeze([...issues]);
378
+ }
379
+ }
380
+ const INTERRUPTION_VALUES = [
381
+ 'none',
382
+ 'source',
383
+ 'destination',
384
+ 'source-then-destination',
385
+ 'destination-then-source',
386
+ ];
387
+ function validateAnimationStateMachineDefinition(definition) {
388
+ const issues = [];
389
+ const parameterByName = new Map();
390
+ const context = {
391
+ issues,
392
+ parameterByName,
393
+ motionStack: new Set(),
394
+ };
395
+ if (definition.format !== 'haiyue-animation-state-machine@1'
396
+ && definition.format !== 'haiyue-animation3d-state-machine@1') {
397
+ issue(context, 'invalid-value', 'format', 'Unsupported state-machine format.');
398
+ }
399
+ validateNonEmptyString(context, definition.id, 'id');
400
+ validateNonEmptyString(context, definition.name, 'name');
401
+ definition.parameters.forEach((parameter, parameterIndex) => {
402
+ const path = `parameters[${parameterIndex}]`;
403
+ validateNonEmptyString(context, parameter.name, `${path}.name`);
404
+ if (parameterByName.has(parameter.name)) {
405
+ issue(context, 'duplicate-id', `${path}.name`, `Duplicate parameter name "${parameter.name}".`);
406
+ }
407
+ else {
408
+ parameterByName.set(parameter.name, parameter);
409
+ }
410
+ if (parameter.type === 'float') {
411
+ validateFinite(context, parameter.defaultValue, `${path}.defaultValue`);
412
+ }
413
+ else if (parameter.type === 'integer') {
414
+ if (!Number.isSafeInteger(parameter.defaultValue)) {
415
+ issue(context, 'invalid-value', `${path}.defaultValue`, 'Integer parameter defaults must be safe integers.');
416
+ }
417
+ }
418
+ else if (parameter.type === 'boolean') {
419
+ if (typeof parameter.defaultValue !== 'boolean') {
420
+ issue(context, 'invalid-value', `${path}.defaultValue`, 'Boolean parameter defaults must be boolean.');
421
+ }
422
+ }
423
+ else if (parameter.type === 'trigger') ;
424
+ else {
425
+ issue(context, 'invalid-value', `${path}.type`, 'Unknown parameter type.');
426
+ }
427
+ });
428
+ const layerIds = new Set();
429
+ definition.layers.forEach((layer, layerIndex) => {
430
+ const layerPath = `layers[${layerIndex}]`;
431
+ validateNonEmptyString(context, layer.id, `${layerPath}.id`);
432
+ validateNonEmptyString(context, layer.name, `${layerPath}.name`);
433
+ if (layerIds.has(layer.id)) {
434
+ issue(context, 'duplicate-id', `${layerPath}.id`, `Duplicate layer id "${layer.id}".`);
435
+ }
436
+ layerIds.add(layer.id);
437
+ validateUnitWeight(context, layer.weight ?? 1, `${layerPath}.weight`);
438
+ if (layer.blendMode !== undefined
439
+ && layer.blendMode !== 'override'
440
+ && layer.blendMode !== 'additive') {
441
+ issue(context, 'invalid-value', `${layerPath}.blendMode`, 'Unknown layer blend mode.');
442
+ }
443
+ const stateIds = new Set();
444
+ layer.states.forEach((state, stateIndex) => {
445
+ const statePath = `${layerPath}.states[${stateIndex}]`;
446
+ validateNonEmptyString(context, state.id, `${statePath}.id`);
447
+ validateNonEmptyString(context, state.name, `${statePath}.name`);
448
+ if (stateIds.has(state.id)) {
449
+ issue(context, 'duplicate-id', `${statePath}.id`, `Duplicate state id "${state.id}".`);
450
+ }
451
+ stateIds.add(state.id);
452
+ validateFinite(context, state.speed ?? 1, `${statePath}.speed`);
453
+ if (state.loop !== undefined
454
+ && state.loop !== 'once'
455
+ && state.loop !== 'repeat'
456
+ && state.loop !== 'ping-pong') {
457
+ issue(context, 'invalid-value', `${statePath}.loop`, 'Unknown state loop mode.');
458
+ }
459
+ if (state.speedParameter !== undefined) {
460
+ validateNumericParameter(context, state.speedParameter, `${statePath}.speedParameter`);
461
+ }
462
+ validateMotion(context, state.motion, `${statePath}.motion`);
463
+ });
464
+ if (!stateIds.has(layer.initialStateId)) {
465
+ issue(context, 'missing-reference', `${layerPath}.initialStateId`, `Initial state "${layer.initialStateId}" does not exist in this layer.`);
466
+ }
467
+ const transitionIds = new Set();
468
+ layer.transitions.forEach((transition, transitionIndex) => {
469
+ const transitionPath = `${layerPath}.transitions[${transitionIndex}]`;
470
+ validateNonEmptyString(context, transition.id, `${transitionPath}.id`);
471
+ if (transitionIds.has(transition.id)) {
472
+ issue(context, 'duplicate-id', `${transitionPath}.id`, `Duplicate transition id "${transition.id}".`);
473
+ }
474
+ transitionIds.add(transition.id);
475
+ if (transition.from !== '*' && !stateIds.has(transition.from)) {
476
+ issue(context, 'missing-reference', `${transitionPath}.from`, `Source state "${transition.from}" does not exist in this layer.`);
477
+ }
478
+ if (!stateIds.has(transition.to)) {
479
+ issue(context, 'missing-reference', `${transitionPath}.to`, `Destination state "${transition.to}" does not exist in this layer.`);
480
+ }
481
+ validateNonNegativeFinite(context, transition.duration, `${transitionPath}.duration`);
482
+ if (transition.hasExitTime !== undefined
483
+ && typeof transition.hasExitTime !== 'boolean') {
484
+ issue(context, 'invalid-value', `${transitionPath}.hasExitTime`, 'hasExitTime must be boolean.');
485
+ }
486
+ if (transition.hasExitTime === true && transition.exitTime === undefined) {
487
+ issue(context, 'invalid-value', `${transitionPath}.exitTime`, 'exitTime is required when hasExitTime is true.');
488
+ }
489
+ if (transition.exitTime !== undefined) {
490
+ validateNonNegativeFinite(context, transition.exitTime, `${transitionPath}.exitTime`);
491
+ }
492
+ if (transition.destinationOffset !== undefined) {
493
+ validateNonNegativeFinite(context, transition.destinationOffset, `${transitionPath}.destinationOffset`);
494
+ }
495
+ if (transition.interruption !== undefined
496
+ && !INTERRUPTION_VALUES.includes(transition.interruption)) {
497
+ issue(context, 'invalid-value', `${transitionPath}.interruption`, 'Unknown transition interruption strategy.');
498
+ }
499
+ transition.conditions.forEach((condition, conditionIndex) => {
500
+ validateCondition(context, condition, `${transitionPath}.conditions[${conditionIndex}]`);
501
+ });
502
+ });
503
+ });
504
+ return Object.freeze(issues);
505
+ }
506
+ function compileAnimationStateMachineDefinition(definition) {
507
+ const issues = validateAnimationStateMachineDefinition(definition);
508
+ if (issues.length > 0)
509
+ throw new AnimationStateMachineValidationError(issues);
510
+ const parameterIndexByName = new Map();
511
+ const parameters = definition.parameters.map((parameter, index) => {
512
+ parameterIndexByName.set(parameter.name, index);
513
+ return Object.freeze({
514
+ index,
515
+ name: parameter.name,
516
+ type: parameter.type,
517
+ defaultValue: parameter.type === 'trigger'
518
+ ? 0
519
+ : parameter.type === 'boolean'
520
+ ? (parameter.defaultValue ? 1 : 0)
521
+ : parameter.defaultValue,
522
+ });
523
+ });
524
+ const layerIndexById = new Map();
525
+ const layers = definition.layers.map((layer, layerIndex) => {
526
+ layerIndexById.set(layer.id, layerIndex);
527
+ const stateIndexById = new Map();
528
+ layer.states.forEach((state, stateIndex) => stateIndexById.set(state.id, stateIndex));
529
+ const states = layer.states.map((state, stateIndex) => Object.freeze({
530
+ index: stateIndex,
531
+ id: state.id,
532
+ name: state.name,
533
+ motion: compileMotion(state.motion, parameterIndexByName),
534
+ speed: state.speed ?? 1,
535
+ speedParameterIndex: state.speedParameter === undefined
536
+ ? -1
537
+ : parameterIndexByName.get(state.speedParameter),
538
+ loop: state.loop ?? 'repeat',
539
+ }));
540
+ const transitions = layer.transitions.map((transition, transitionIndex) => {
541
+ const conditions = transition.conditions.map(condition => compileCondition(condition, parameterIndexByName));
542
+ const triggerParameterIndices = conditions
543
+ .filter(condition => condition.operator === 'triggered')
544
+ .map(condition => condition.parameterIndex);
545
+ return Object.freeze({
546
+ index: transitionIndex,
547
+ id: transition.id,
548
+ fromStateIndex: transition.from === '*'
549
+ ? -1
550
+ : stateIndexById.get(transition.from),
551
+ toStateIndex: stateIndexById.get(transition.to),
552
+ conditions: Object.freeze(conditions),
553
+ triggerParameterIndices: Object.freeze(triggerParameterIndices),
554
+ duration: transition.duration,
555
+ hasExitTime: transition.hasExitTime ?? false,
556
+ exitTime: transition.exitTime ?? 0,
557
+ destinationOffset: transition.destinationOffset ?? 0,
558
+ interruption: transition.interruption ?? 'none',
559
+ });
560
+ });
561
+ return Object.freeze({
562
+ index: layerIndex,
563
+ id: layer.id,
564
+ name: layer.name,
565
+ initialStateIndex: stateIndexById.get(layer.initialStateId),
566
+ states: Object.freeze(states),
567
+ transitions: Object.freeze(transitions),
568
+ blendMode: layer.blendMode ?? 'override',
569
+ weight: layer.weight ?? 1,
570
+ mask: compileMask(layer.mask),
571
+ stateIndexById,
572
+ });
573
+ });
574
+ return Object.freeze({
575
+ format: 'haiyue-animation-state-machine-compiled@1',
576
+ id: definition.id,
577
+ name: definition.name,
578
+ parameters: Object.freeze(parameters),
579
+ layers: Object.freeze(layers),
580
+ parameterIndexByName,
581
+ layerIndexById,
582
+ });
583
+ }
584
+ const validateAnimation3DStateMachineDefinition = validateAnimationStateMachineDefinition;
585
+ const compileAnimation3DStateMachineDefinition = compileAnimationStateMachineDefinition;
586
+ function validateMotion(context, motion, path) {
587
+ if (typeof motion !== 'object' || motion === null) {
588
+ issue(context, 'invalid-value', path, 'Motion must be an object.');
589
+ return;
590
+ }
591
+ if (context.motionStack.has(motion)) {
592
+ issue(context, 'recursive-motion', path, 'Recursive motion cycle detected.');
593
+ return;
594
+ }
595
+ context.motionStack.add(motion);
596
+ if (motion.kind === 'clip') {
597
+ validateNonEmptyString(context, motion.clipId, `${path}.clipId`);
598
+ }
599
+ else if (motion.kind === 'blend-1d') {
600
+ validateNumericParameter(context, motion.parameter, `${path}.parameter`);
601
+ if (motion.children.length === 0) {
602
+ issue(context, 'empty-blend-tree', `${path}.children`, '1D blend tree cannot be empty.');
603
+ }
604
+ let previousThreshold = -Infinity;
605
+ motion.children.forEach((child, childIndex) => {
606
+ const childPath = `${path}.children[${childIndex}]`;
607
+ validateFinite(context, child.threshold, `${childPath}.threshold`);
608
+ if (childIndex > 0 && child.threshold <= previousThreshold) {
609
+ issue(context, 'invalid-thresholds', `${childPath}.threshold`, child.threshold === previousThreshold
610
+ ? '1D blend thresholds must be unique.'
611
+ : '1D blend thresholds must be strictly increasing.');
612
+ }
613
+ previousThreshold = child.threshold;
614
+ validateMotion(context, child.motion, `${childPath}.motion`);
615
+ });
616
+ }
617
+ else if (motion.kind === 'blend-2d') {
618
+ validateNumericParameter(context, motion.parameterX, `${path}.parameterX`);
619
+ validateNumericParameter(context, motion.parameterY, `${path}.parameterY`);
620
+ if (motion.algorithm !== 'cartesian' && motion.algorithm !== 'directional') {
621
+ issue(context, 'invalid-value', `${path}.algorithm`, 'Unknown 2D blend algorithm.');
622
+ }
623
+ if (motion.children.length === 0) {
624
+ issue(context, 'empty-blend-tree', `${path}.children`, '2D blend tree cannot be empty.');
625
+ }
626
+ const positions = new Set();
627
+ motion.children.forEach((child, childIndex) => {
628
+ const childPath = `${path}.children[${childIndex}]`;
629
+ const x = child.position[0];
630
+ const y = child.position[1];
631
+ validateFinite(context, x, `${childPath}.position[0]`);
632
+ validateFinite(context, y, `${childPath}.position[1]`);
633
+ const key = `${x}\u0000${y}`;
634
+ if (positions.has(key)) {
635
+ issue(context, 'invalid-value', `${childPath}.position`, '2D blend child positions must be unique.');
636
+ }
637
+ positions.add(key);
638
+ validateMotion(context, child.motion, `${childPath}.motion`);
639
+ });
640
+ }
641
+ else {
642
+ issue(context, 'invalid-value', `${path}.kind`, 'Unknown motion kind.');
643
+ }
644
+ context.motionStack.delete(motion);
645
+ }
646
+ function validateCondition(context, condition, path) {
647
+ const parameter = context.parameterByName.get(condition.parameter);
648
+ if (!parameter) {
649
+ issue(context, 'missing-reference', `${path}.parameter`, `Parameter "${condition.parameter}" does not exist.`);
650
+ return;
651
+ }
652
+ const operator = condition.operator;
653
+ if (parameter.type === 'float' || parameter.type === 'integer') {
654
+ const valid = operator === 'greater'
655
+ || operator === 'greater-or-equal'
656
+ || operator === 'less'
657
+ || operator === 'less-or-equal'
658
+ || operator === 'equal'
659
+ || operator === 'not-equal';
660
+ if (!valid) {
661
+ issue(context, 'invalid-operator', `${path}.operator`, `Operator "${operator}" is invalid for ${parameter.type} parameters.`);
662
+ return;
663
+ }
664
+ const value = 'value' in condition ? condition.value : undefined;
665
+ if (typeof value !== 'number'
666
+ || !Number.isFinite(value)
667
+ || (parameter.type === 'integer' && !Number.isSafeInteger(value))) {
668
+ issue(context, 'parameter-type-mismatch', `${path}.value`, parameter.type === 'integer'
669
+ ? 'Integer conditions require a safe integer value.'
670
+ : 'Float conditions require a finite numeric value.');
671
+ }
672
+ return;
673
+ }
674
+ if (parameter.type === 'boolean') {
675
+ if (operator !== 'equal'
676
+ && operator !== 'not-equal'
677
+ && operator !== 'is-true'
678
+ && operator !== 'is-false') {
679
+ issue(context, 'invalid-operator', `${path}.operator`, `Operator "${operator}" is invalid for boolean parameters.`);
680
+ return;
681
+ }
682
+ if ((operator === 'equal' || operator === 'not-equal')
683
+ && (!('value' in condition) || typeof condition.value !== 'boolean')) {
684
+ issue(context, 'parameter-type-mismatch', `${path}.value`, 'Boolean equality conditions require a boolean value.');
685
+ }
686
+ return;
687
+ }
688
+ if (operator !== 'triggered') {
689
+ issue(context, 'invalid-operator', `${path}.operator`, `Operator "${operator}" is invalid for trigger parameters.`);
690
+ }
691
+ }
692
+ function validateNumericParameter(context, parameterName, path) {
693
+ const parameter = context.parameterByName.get(parameterName);
694
+ if (!parameter) {
695
+ issue(context, 'missing-reference', path, `Parameter "${parameterName}" does not exist.`);
696
+ }
697
+ else if (parameter.type !== 'float' && parameter.type !== 'integer') {
698
+ issue(context, 'parameter-type-mismatch', path, `Parameter "${parameterName}" must be float or integer.`);
699
+ }
700
+ }
701
+ function compileMotion(motion, parameterIndexByName) {
702
+ if (motion.kind === 'clip') {
703
+ return Object.freeze({ kind: 'clip', clipId: motion.clipId });
704
+ }
705
+ if (motion.kind === 'blend-1d') {
706
+ return Object.freeze({
707
+ kind: 'blend-1d',
708
+ parameterIndex: parameterIndexByName.get(motion.parameter),
709
+ children: Object.freeze(motion.children.map(child => Object.freeze({
710
+ threshold: child.threshold,
711
+ motion: compileMotion(child.motion, parameterIndexByName),
712
+ }))),
713
+ });
714
+ }
715
+ return Object.freeze({
716
+ kind: 'blend-2d',
717
+ algorithm: motion.algorithm,
718
+ parameterXIndex: parameterIndexByName.get(motion.parameterX),
719
+ parameterYIndex: parameterIndexByName.get(motion.parameterY),
720
+ children: Object.freeze(motion.children.map((child, declarationIndex) => Object.freeze({
721
+ position: Object.freeze([child.position[0], child.position[1]]),
722
+ motion: compileMotion(child.motion, parameterIndexByName),
723
+ declarationIndex,
724
+ }))),
725
+ });
726
+ }
727
+ function compileCondition(condition, parameterIndexByName) {
728
+ const rawValue = 'value' in condition ? condition.value : 0;
729
+ return Object.freeze({
730
+ parameterIndex: parameterIndexByName.get(condition.parameter),
731
+ operator: condition.operator,
732
+ value: typeof rawValue === 'boolean' ? (rawValue ? 1 : 0) : rawValue,
733
+ });
734
+ }
735
+ function compileMask(mask) {
736
+ if (!mask)
737
+ return null;
738
+ return Object.freeze({
739
+ ...(mask.include === undefined
740
+ ? {}
741
+ : { include: Object.freeze([...mask.include]) }),
742
+ ...(mask.exclude === undefined
743
+ ? {}
744
+ : { exclude: Object.freeze([...mask.exclude]) }),
745
+ });
746
+ }
747
+ function validateNonEmptyString(context, value, path) {
748
+ if (typeof value !== 'string' || value.length === 0) {
749
+ issue(context, 'invalid-value', path, 'Expected a non-empty string.');
750
+ }
751
+ }
752
+ function validateFinite(context, value, path) {
753
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
754
+ issue(context, 'invalid-value', path, 'Expected a finite number.');
755
+ }
756
+ }
757
+ function validateNonNegativeFinite(context, value, path) {
758
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
759
+ issue(context, 'invalid-value', path, 'Expected a finite number greater than or equal to zero.');
760
+ }
761
+ }
762
+ function validateUnitWeight(context, value, path) {
763
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 1) {
764
+ issue(context, 'invalid-value', path, 'Layer weight must be a finite number in [0, 1].');
765
+ }
766
+ }
767
+ function issue(context, code, path, message) {
768
+ context.issues.push(Object.freeze({ code, path, message }));
769
+ }
770
+
771
+ const TIME_EPSILON = 1e-9;
772
+ const DEFAULT_MAX_TRANSITIONS_PER_UPDATE = 128;
773
+ const CONTROLLER_UPDATE_TRANSACTION = Symbol('AnimationStateMachineController.updateTransaction');
774
+ /**
775
+ * Deterministic, mixer-agnostic runtime controller for a compiled definition.
776
+ * All string resolution happens in the compiler or at setter/query boundaries.
777
+ */
778
+ class Animation3DStateMachineController {
779
+ compiled;
780
+ port;
781
+ maxTransitionsPerUpdate;
782
+ lastUpdateTransitionCount = 0;
783
+ transitionLimitReached = false;
784
+ _parameterValues;
785
+ _transactionParameterValues;
786
+ _layers = [];
787
+ _layerTransactionCheckpoints = [];
788
+ _transactionLastUpdateTransitionCount = 0;
789
+ _transactionLimitReached = false;
790
+ _transactionActive = false;
791
+ _status = 'active';
792
+ constructor(compiled, port, options = {}) {
793
+ const maxTransitions = options.maxTransitionsPerUpdate ?? DEFAULT_MAX_TRANSITIONS_PER_UPDATE;
794
+ if (!Number.isSafeInteger(maxTransitions) || maxTransitions <= 0) {
795
+ throw new RangeError('maxTransitionsPerUpdate must be a positive safe integer.');
796
+ }
797
+ this.compiled = compiled;
798
+ this.port = port;
799
+ this.maxTransitionsPerUpdate = maxTransitions;
800
+ this._parameterValues = new Float64Array(compiled.parameters.length);
801
+ this._transactionParameterValues = new Float64Array(compiled.parameters.length);
802
+ this._restoreParameterDefaults();
803
+ try {
804
+ for (const layer of compiled.layers) {
805
+ const current = this._createPlayback(layer, layer.states[layer.initialStateIndex], 0);
806
+ this._layers.push({ definition: layer, current, transition: null });
807
+ }
808
+ this._synchronizeAllLayers();
809
+ }
810
+ catch (error) {
811
+ this._destroyAllLayers();
812
+ this._status = 'destroyed';
813
+ throw error;
814
+ }
815
+ }
816
+ get status() {
817
+ return this._status;
818
+ }
819
+ get layerSnapshots() {
820
+ return Object.freeze(this._layers.map(layer => this._snapshotLayer(layer)));
821
+ }
822
+ getLayerSnapshot(layerId) {
823
+ const layerIndex = this.compiled.layerIndexById.get(layerId);
824
+ if (layerIndex === undefined)
825
+ throw new ReferenceError(`Unknown layer "${layerId}".`);
826
+ const runtime = this._layers[layerIndex];
827
+ if (!runtime)
828
+ throw new Error('State-machine controller is destroyed.');
829
+ return this._snapshotLayer(runtime);
830
+ }
831
+ setFloat(name, value) {
832
+ if (!Number.isFinite(value))
833
+ throw new RangeError('Float parameters must be finite.');
834
+ this._setTypedParameter(name, 'float', value);
835
+ return this;
836
+ }
837
+ setInteger(name, value) {
838
+ if (!Number.isSafeInteger(value)) {
839
+ throw new RangeError('Integer parameters must be safe integers.');
840
+ }
841
+ this._setTypedParameter(name, 'integer', value);
842
+ return this;
843
+ }
844
+ setBoolean(name, value) {
845
+ if (typeof value !== 'boolean')
846
+ throw new TypeError('Boolean parameters require a boolean.');
847
+ this._setTypedParameter(name, 'boolean', value ? 1 : 0);
848
+ return this;
849
+ }
850
+ setTrigger(name) {
851
+ this._setTypedParameter(name, 'trigger', 1);
852
+ return this;
853
+ }
854
+ resetTrigger(name) {
855
+ this._setTypedParameter(name, 'trigger', 0);
856
+ return this;
857
+ }
858
+ getParameter(name) {
859
+ const parameter = this._resolveParameter(name);
860
+ const value = this._parameterValues[parameter.index];
861
+ return parameter.type === 'boolean' || parameter.type === 'trigger'
862
+ ? value !== 0
863
+ : value;
864
+ }
865
+ update(deltaSeconds) {
866
+ this[CONTROLLER_UPDATE_TRANSACTION](deltaSeconds);
867
+ return this;
868
+ }
869
+ /** @internal Keeps controller and transactional mixer ports atomic through final pose evaluation. */
870
+ [CONTROLLER_UPDATE_TRANSACTION](deltaSeconds, beforeUpdate, complete) {
871
+ this._requireActive();
872
+ if (!Number.isFinite(deltaSeconds) || deltaSeconds < 0) {
873
+ throw new RangeError('State-machine deltaSeconds must be finite and non-negative.');
874
+ }
875
+ if (this._transactionActive) {
876
+ throw new Error('State-machine controller update transaction is already active.');
877
+ }
878
+ const transactionalPort = resolveTransactionalPort(this.port);
879
+ this._captureUpdateTransaction();
880
+ this._transactionActive = true;
881
+ let portTransactionActive = false;
882
+ try {
883
+ if (transactionalPort) {
884
+ transactionalPort.beginControllerTransaction();
885
+ portTransactionActive = true;
886
+ }
887
+ beforeUpdate?.();
888
+ this.lastUpdateTransitionCount = 0;
889
+ this.transitionLimitReached = false;
890
+ for (const layer of this._layers)
891
+ this._updateLayer(layer, deltaSeconds);
892
+ const result = complete?.();
893
+ transactionalPort?.commitControllerTransaction();
894
+ portTransactionActive = false;
895
+ this._transactionActive = false;
896
+ return result;
897
+ }
898
+ catch (error) {
899
+ this._restoreUpdateTransaction();
900
+ this._transactionActive = false;
901
+ if (portTransactionActive) {
902
+ try {
903
+ transactionalPort.rollbackControllerTransaction();
904
+ }
905
+ catch (rollbackError) {
906
+ throw new AggregateError([error, rollbackError], 'State-machine update and rollback both failed.');
907
+ }
908
+ }
909
+ throw error;
910
+ }
911
+ }
912
+ /**
913
+ * Restores defaults and initial states. Repeated reset calls are safe and
914
+ * leave the same observable controller state.
915
+ */
916
+ reset() {
917
+ if (this._status === 'destroyed')
918
+ return this;
919
+ this._destroyAllLayers();
920
+ this._restoreParameterDefaults();
921
+ this.lastUpdateTransitionCount = 0;
922
+ this.transitionLimitReached = false;
923
+ for (const layer of this.compiled.layers) {
924
+ const current = this._createPlayback(layer, layer.states[layer.initialStateIndex], 0);
925
+ this._layers.push({ definition: layer, current, transition: null });
926
+ }
927
+ this._synchronizeAllLayers();
928
+ return this;
929
+ }
930
+ /** Stops and releases all port actions. Idempotent. */
931
+ destroy() {
932
+ if (this._status === 'destroyed')
933
+ return;
934
+ this._destroyAllLayers();
935
+ this._status = 'destroyed';
936
+ }
937
+ _updateLayer(layer, deltaSeconds) {
938
+ let remaining = deltaSeconds;
939
+ let layerTransitionCount = 0;
940
+ let inspectImmediateTransitions = true;
941
+ while (remaining > TIME_EPSILON || inspectImmediateTransitions) {
942
+ inspectImmediateTransitions = false;
943
+ if (layerTransitionCount >= this.maxTransitionsPerUpdate) {
944
+ this.transitionLimitReached = true;
945
+ this._advanceWithoutTransitions(layer, remaining);
946
+ remaining = 0;
947
+ break;
948
+ }
949
+ if (layer.transition) {
950
+ const interruption = this._findInterruption(layer);
951
+ if (interruption) {
952
+ this._interruptTransition(layer, interruption);
953
+ layerTransitionCount++;
954
+ this.lastUpdateTransitionCount++;
955
+ inspectImmediateTransitions = true;
956
+ continue;
957
+ }
958
+ const active = layer.transition;
959
+ const timeToCompletion = Math.max(0, active.definition.duration - active.elapsed);
960
+ if (timeToCompletion <= TIME_EPSILON) {
961
+ this._completeTransition(layer);
962
+ inspectImmediateTransitions = true;
963
+ continue;
964
+ }
965
+ if (remaining <= TIME_EPSILON)
966
+ break;
967
+ const step = Math.min(remaining, timeToCompletion);
968
+ this._advancePlayback(active.source, step);
969
+ this._advancePlayback(active.destination, step);
970
+ active.elapsed = Math.min(active.definition.duration, active.elapsed + step);
971
+ remaining = Math.max(0, remaining - step);
972
+ this._synchronizeLayer(layer);
973
+ if (active.elapsed + TIME_EPSILON >= active.definition.duration
974
+ && layer.transition === active) {
975
+ this._completeTransition(layer);
976
+ inspectImmediateTransitions = true;
977
+ }
978
+ continue;
979
+ }
980
+ const candidate = this._findEligibleTransition(layer.definition, layer.current, -1);
981
+ if (candidate) {
982
+ this._startTransition(layer, layer.current, candidate);
983
+ layerTransitionCount++;
984
+ this.lastUpdateTransitionCount++;
985
+ inspectImmediateTransitions = true;
986
+ continue;
987
+ }
988
+ if (remaining <= TIME_EPSILON)
989
+ break;
990
+ const wait = this._timeUntilNextExitTransition(layer);
991
+ const step = Number.isFinite(wait)
992
+ ? Math.min(remaining, Math.max(wait, TIME_EPSILON))
993
+ : remaining;
994
+ this._advancePlayback(layer.current, step);
995
+ remaining = Math.max(0, remaining - step);
996
+ this._synchronizeLayer(layer);
997
+ if (Number.isFinite(wait) && step + TIME_EPSILON >= wait) {
998
+ inspectImmediateTransitions = true;
999
+ }
1000
+ }
1001
+ this._synchronizeLayer(layer);
1002
+ }
1003
+ _captureUpdateTransaction() {
1004
+ this._transactionParameterValues.set(this._parameterValues);
1005
+ this._transactionLastUpdateTransitionCount = this.lastUpdateTransitionCount;
1006
+ this._transactionLimitReached = this.transitionLimitReached;
1007
+ for (let index = 0; index < this._layers.length; index++) {
1008
+ const layer = this._layers[index];
1009
+ const transition = layer.transition;
1010
+ let checkpoint = this._layerTransactionCheckpoints[index];
1011
+ if (!checkpoint) {
1012
+ checkpoint = {
1013
+ current: layer.current,
1014
+ transition,
1015
+ currentTime: 0,
1016
+ sourceTime: 0,
1017
+ destinationTime: 0,
1018
+ transitionElapsed: 0,
1019
+ };
1020
+ this._layerTransactionCheckpoints.push(checkpoint);
1021
+ }
1022
+ checkpoint.current = layer.current;
1023
+ checkpoint.transition = transition;
1024
+ checkpoint.currentTime = layer.current.time;
1025
+ checkpoint.sourceTime = transition?.source.time ?? 0;
1026
+ checkpoint.destinationTime = transition?.destination.time ?? 0;
1027
+ checkpoint.transitionElapsed = transition?.elapsed ?? 0;
1028
+ }
1029
+ }
1030
+ _restoreUpdateTransaction() {
1031
+ this._parameterValues.set(this._transactionParameterValues);
1032
+ this.lastUpdateTransitionCount = this._transactionLastUpdateTransitionCount;
1033
+ this.transitionLimitReached = this._transactionLimitReached;
1034
+ for (let index = 0; index < this._layers.length; index++) {
1035
+ const layer = this._layers[index];
1036
+ const checkpoint = this._layerTransactionCheckpoints[index];
1037
+ layer.current = checkpoint.current;
1038
+ layer.transition = checkpoint.transition;
1039
+ checkpoint.current.time = checkpoint.currentTime;
1040
+ if (checkpoint.transition) {
1041
+ checkpoint.transition.source.time = checkpoint.sourceTime;
1042
+ checkpoint.transition.destination.time = checkpoint.destinationTime;
1043
+ checkpoint.transition.elapsed = checkpoint.transitionElapsed;
1044
+ }
1045
+ }
1046
+ }
1047
+ _findEligibleTransition(layer, playback, excludedTransitionIndex) {
1048
+ for (const transition of layer.transitions) {
1049
+ if (transition.index === excludedTransitionIndex)
1050
+ continue;
1051
+ if (transition.fromStateIndex !== -1
1052
+ && transition.fromStateIndex !== playback.state.index)
1053
+ continue;
1054
+ if (!this._conditionsPass(transition.conditions))
1055
+ continue;
1056
+ if (transition.hasExitTime
1057
+ && !this._exitTimeReached(playback, transition.exitTime))
1058
+ continue;
1059
+ return transition;
1060
+ }
1061
+ return null;
1062
+ }
1063
+ _findInterruption(layer) {
1064
+ const active = layer.transition;
1065
+ const strategy = active.definition.interruption;
1066
+ if (strategy === 'none')
1067
+ return null;
1068
+ const source = () => {
1069
+ const transition = this._findEligibleTransition(layer.definition, active.source, active.definition.index);
1070
+ return transition ? { transition, anchor: active.source } : null;
1071
+ };
1072
+ const destination = () => {
1073
+ const transition = this._findEligibleTransition(layer.definition, active.destination, active.definition.index);
1074
+ return transition ? { transition, anchor: active.destination } : null;
1075
+ };
1076
+ if (strategy === 'source')
1077
+ return source();
1078
+ if (strategy === 'destination')
1079
+ return destination();
1080
+ if (strategy === 'source-then-destination')
1081
+ return source() ?? destination();
1082
+ return destination() ?? source();
1083
+ }
1084
+ _startTransition(layer, source, transition) {
1085
+ const destinationState = layer.definition.states[transition.toStateIndex];
1086
+ const destination = this._createPlayback(layer.definition, destinationState, transition.destinationOffset);
1087
+ // Consume only after destination action creation succeeds and the
1088
+ // transition can actually become active.
1089
+ this._consumeTriggers(transition);
1090
+ const active = {
1091
+ definition: transition,
1092
+ source,
1093
+ destination,
1094
+ elapsed: 0,
1095
+ };
1096
+ layer.transition = active;
1097
+ this._fadePlayback(source, 0, transition.duration);
1098
+ this._fadePlayback(destination, layer.definition.weight, transition.duration);
1099
+ this._synchronizeLayer(layer);
1100
+ if (transition.duration <= TIME_EPSILON)
1101
+ this._completeTransition(layer);
1102
+ }
1103
+ _interruptTransition(layer, candidate) {
1104
+ const active = layer.transition;
1105
+ const discarded = candidate.anchor === active.source
1106
+ ? active.destination
1107
+ : active.source;
1108
+ this._destroyPlayback(discarded);
1109
+ layer.current = candidate.anchor;
1110
+ layer.transition = null;
1111
+ this._startTransition(layer, candidate.anchor, candidate.transition);
1112
+ }
1113
+ _completeTransition(layer) {
1114
+ const active = layer.transition;
1115
+ if (!active)
1116
+ return;
1117
+ this._destroyPlayback(active.source);
1118
+ layer.current = active.destination;
1119
+ layer.transition = null;
1120
+ this._synchronizeLayer(layer);
1121
+ }
1122
+ _timeUntilNextExitTransition(layer) {
1123
+ let earliest = Infinity;
1124
+ for (const transition of layer.definition.transitions) {
1125
+ if (transition.fromStateIndex !== -1
1126
+ && transition.fromStateIndex !== layer.current.state.index)
1127
+ continue;
1128
+ if (!transition.hasExitTime || !this._conditionsPass(transition.conditions))
1129
+ continue;
1130
+ earliest = Math.min(earliest, this._timeUntilExit(layer.current, transition.exitTime));
1131
+ }
1132
+ return earliest;
1133
+ }
1134
+ _exitTimeReached(playback, exitTime) {
1135
+ const duration = this._playbackDuration(playback);
1136
+ if (duration <= TIME_EPSILON)
1137
+ return true;
1138
+ const normalizedTime = playback.time / duration;
1139
+ if (playback.state.loop === 'once' || exitTime >= 1) {
1140
+ return normalizedTime + TIME_EPSILON >= exitTime;
1141
+ }
1142
+ const cycleTime = normalizedTime - Math.floor(normalizedTime);
1143
+ return cycleTime + TIME_EPSILON >= exitTime;
1144
+ }
1145
+ _timeUntilExit(playback, exitTime) {
1146
+ if (this._exitTimeReached(playback, exitTime))
1147
+ return 0;
1148
+ const duration = this._playbackDuration(playback);
1149
+ if (duration <= TIME_EPSILON)
1150
+ return 0;
1151
+ const speed = this._playbackSpeed(playback);
1152
+ if (speed <= TIME_EPSILON)
1153
+ return Infinity;
1154
+ const normalizedTime = playback.time / duration;
1155
+ let targetNormalized;
1156
+ if (playback.state.loop === 'once' || exitTime >= 1) {
1157
+ targetNormalized = exitTime;
1158
+ }
1159
+ else {
1160
+ targetNormalized = Math.floor(normalizedTime) + exitTime;
1161
+ if (targetNormalized <= normalizedTime + TIME_EPSILON)
1162
+ targetNormalized += 1;
1163
+ }
1164
+ return Math.max(0, (targetNormalized * duration - playback.time) / speed);
1165
+ }
1166
+ _conditionsPass(conditions) {
1167
+ for (const condition of conditions) {
1168
+ const actual = this._parameterValues[condition.parameterIndex];
1169
+ const expected = condition.value;
1170
+ if (condition.operator === 'greater' && !(actual > expected))
1171
+ return false;
1172
+ if (condition.operator === 'greater-or-equal'
1173
+ && !(actual >= expected))
1174
+ return false;
1175
+ if (condition.operator === 'less' && !(actual < expected))
1176
+ return false;
1177
+ if (condition.operator === 'less-or-equal'
1178
+ && !(actual <= expected))
1179
+ return false;
1180
+ if (condition.operator === 'equal' && actual !== expected)
1181
+ return false;
1182
+ if (condition.operator === 'not-equal' && actual === expected)
1183
+ return false;
1184
+ if (condition.operator === 'is-true' && actual === 0)
1185
+ return false;
1186
+ if (condition.operator === 'is-false' && actual !== 0)
1187
+ return false;
1188
+ if (condition.operator === 'triggered' && actual === 0)
1189
+ return false;
1190
+ }
1191
+ return true;
1192
+ }
1193
+ _consumeTriggers(transition) {
1194
+ for (const parameterIndex of transition.triggerParameterIndices) {
1195
+ this._parameterValues[parameterIndex] = 0;
1196
+ }
1197
+ }
1198
+ _createPlayback(layer, state, normalizedOffset) {
1199
+ const createdActions = [];
1200
+ try {
1201
+ const motion = this._createRuntimeMotion(layer, state, state.motion, createdActions);
1202
+ const playback = { state, motion, time: 0 };
1203
+ playback.time = normalizedOffset * this._playbackDuration(playback);
1204
+ this._forEachAction(motion, action => {
1205
+ this.port.setWeight(action, 0);
1206
+ this.port.setTime(action, playback.time);
1207
+ this.port.setTimeScale(action, this._playbackSpeed(playback));
1208
+ this.port.play(action);
1209
+ });
1210
+ return playback;
1211
+ }
1212
+ catch (error) {
1213
+ for (let index = createdActions.length - 1; index >= 0; index--) {
1214
+ const action = createdActions[index];
1215
+ this.port.stop(action);
1216
+ this.port.destroyAction(action);
1217
+ }
1218
+ throw error;
1219
+ }
1220
+ }
1221
+ _createRuntimeMotion(layer, state, motion, createdActions) {
1222
+ if (motion.kind === 'clip') {
1223
+ const action = this.port.createAction(motion.clipId, {
1224
+ layerId: layer.id,
1225
+ stateId: state.id,
1226
+ loop: state.loop,
1227
+ blendMode: layer.blendMode,
1228
+ mask: layer.mask,
1229
+ });
1230
+ if (!Number.isFinite(action.duration) || action.duration < 0) {
1231
+ throw new RangeError(`Mixer port returned an invalid duration for clip "${motion.clipId}".`);
1232
+ }
1233
+ createdActions.push(action);
1234
+ return { kind: 'clip', action };
1235
+ }
1236
+ const children = motion.children.map(child => this._createRuntimeMotion(layer, state, child.motion, createdActions));
1237
+ return motion.kind === 'blend-1d'
1238
+ ? {
1239
+ kind: 'blend-1d',
1240
+ motion,
1241
+ children,
1242
+ weights: new Float64Array(children.length),
1243
+ }
1244
+ : {
1245
+ kind: 'blend-2d',
1246
+ motion,
1247
+ children,
1248
+ weights: new Float64Array(children.length),
1249
+ };
1250
+ }
1251
+ _advancePlayback(playback, deltaSeconds) {
1252
+ playback.time += deltaSeconds * this._playbackSpeed(playback);
1253
+ }
1254
+ _advanceWithoutTransitions(layer, deltaSeconds) {
1255
+ if (deltaSeconds <= TIME_EPSILON)
1256
+ return;
1257
+ const active = layer.transition;
1258
+ if (!active) {
1259
+ this._advancePlayback(layer.current, deltaSeconds);
1260
+ this._synchronizeLayer(layer);
1261
+ return;
1262
+ }
1263
+ const timeToCompletion = Math.max(0, active.definition.duration - active.elapsed);
1264
+ const transitionStep = Math.min(deltaSeconds, timeToCompletion);
1265
+ this._advancePlayback(active.source, transitionStep);
1266
+ this._advancePlayback(active.destination, transitionStep);
1267
+ active.elapsed += transitionStep;
1268
+ const afterCompletion = deltaSeconds - transitionStep;
1269
+ if (active.elapsed + TIME_EPSILON >= active.definition.duration) {
1270
+ this._completeTransition(layer);
1271
+ if (afterCompletion > TIME_EPSILON) {
1272
+ this._advancePlayback(layer.current, afterCompletion);
1273
+ }
1274
+ }
1275
+ this._synchronizeLayer(layer);
1276
+ }
1277
+ _synchronizeAllLayers() {
1278
+ for (const layer of this._layers)
1279
+ this._synchronizeLayer(layer);
1280
+ }
1281
+ _synchronizeLayer(layer) {
1282
+ const active = layer.transition;
1283
+ if (!active) {
1284
+ this._applyPlayback(layer.current, layer.definition.weight);
1285
+ return;
1286
+ }
1287
+ const progress = active.definition.duration <= TIME_EPSILON
1288
+ ? 1
1289
+ : Math.max(0, Math.min(1, active.elapsed / active.definition.duration));
1290
+ this._applyPlayback(active.source, layer.definition.weight * (1 - progress));
1291
+ this._applyPlayback(active.destination, layer.definition.weight * progress);
1292
+ }
1293
+ _applyPlayback(playback, parentWeight) {
1294
+ const speed = this._playbackSpeed(playback);
1295
+ this._applyMotion(playback.motion, parentWeight, playback.time, speed);
1296
+ }
1297
+ _applyMotion(motion, parentWeight, time, timeScale) {
1298
+ if (motion.kind === 'clip') {
1299
+ this.port.setWeight(motion.action, parentWeight);
1300
+ this.port.setTime(motion.action, time);
1301
+ this.port.setTimeScale(motion.action, timeScale);
1302
+ return;
1303
+ }
1304
+ this._evaluateDirectWeights(motion);
1305
+ for (let index = 0; index < motion.children.length; index++) {
1306
+ this._applyMotion(motion.children[index], parentWeight * motion.weights[index], time, timeScale);
1307
+ }
1308
+ }
1309
+ _playbackDuration(playback) {
1310
+ return this._motionDuration(playback.motion);
1311
+ }
1312
+ _motionDuration(motion) {
1313
+ if (motion.kind === 'clip')
1314
+ return motion.action.duration;
1315
+ this._evaluateDirectWeights(motion);
1316
+ let duration = 0;
1317
+ for (let index = 0; index < motion.children.length; index++) {
1318
+ duration += motion.weights[index] * this._motionDuration(motion.children[index]);
1319
+ }
1320
+ return duration;
1321
+ }
1322
+ _playbackSpeed(playback) {
1323
+ const parameterScale = playback.state.speedParameterIndex < 0
1324
+ ? 1
1325
+ : this._parameterValues[playback.state.speedParameterIndex];
1326
+ return playback.state.speed * parameterScale;
1327
+ }
1328
+ _evaluateDirectWeights(motion) {
1329
+ if (motion.kind === 'blend-1d') {
1330
+ evaluateAnimation3DBlend1DWeights(motion.motion, this._parameterValues[motion.motion.parameterIndex], motion.weights);
1331
+ }
1332
+ else {
1333
+ evaluateAnimation3DBlend2DWeights(motion.motion, this._parameterValues[motion.motion.parameterXIndex], this._parameterValues[motion.motion.parameterYIndex], motion.weights);
1334
+ }
1335
+ }
1336
+ _fadePlayback(playback, targetParentWeight, durationSeconds) {
1337
+ this._fadeMotion(playback.motion, targetParentWeight, durationSeconds);
1338
+ }
1339
+ _fadeMotion(motion, parentWeight, durationSeconds) {
1340
+ if (motion.kind === 'clip') {
1341
+ this.port.fade(motion.action, parentWeight, durationSeconds);
1342
+ return;
1343
+ }
1344
+ this._evaluateDirectWeights(motion);
1345
+ for (let index = 0; index < motion.children.length; index++) {
1346
+ this._fadeMotion(motion.children[index], parentWeight * motion.weights[index], durationSeconds);
1347
+ }
1348
+ }
1349
+ _destroyPlayback(playback) {
1350
+ this._forEachAction(playback.motion, action => {
1351
+ this.port.stop(action);
1352
+ this.port.destroyAction(action);
1353
+ });
1354
+ }
1355
+ _destroyAllLayers() {
1356
+ for (const layer of this._layers) {
1357
+ if (layer.transition) {
1358
+ this._destroyPlayback(layer.transition.source);
1359
+ this._destroyPlayback(layer.transition.destination);
1360
+ }
1361
+ else {
1362
+ this._destroyPlayback(layer.current);
1363
+ }
1364
+ }
1365
+ this._layers.length = 0;
1366
+ }
1367
+ _forEachAction(motion, visitor) {
1368
+ if (motion.kind === 'clip') {
1369
+ visitor(motion.action);
1370
+ return;
1371
+ }
1372
+ for (const child of motion.children)
1373
+ this._forEachAction(child, visitor);
1374
+ }
1375
+ _snapshotLayer(layer) {
1376
+ const active = layer.transition;
1377
+ return Object.freeze({
1378
+ layerId: layer.definition.id,
1379
+ currentStateId: active?.source.state.id ?? layer.current.state.id,
1380
+ currentTime: active?.source.time ?? layer.current.time,
1381
+ transitionId: active?.definition.id ?? null,
1382
+ sourceStateId: active?.source.state.id ?? null,
1383
+ destinationStateId: active?.destination.state.id ?? null,
1384
+ transitionProgress: active
1385
+ ? (active.definition.duration <= TIME_EPSILON
1386
+ ? 1
1387
+ : Math.max(0, Math.min(1, active.elapsed / active.definition.duration)))
1388
+ : 0,
1389
+ });
1390
+ }
1391
+ _setTypedParameter(name, expectedType, value) {
1392
+ this._requireActive();
1393
+ const parameter = this._resolveParameter(name);
1394
+ if (parameter.type !== expectedType) {
1395
+ throw new TypeError(`Parameter "${name}" is ${parameter.type}, not ${expectedType}.`);
1396
+ }
1397
+ this._parameterValues[parameter.index] = value;
1398
+ }
1399
+ _resolveParameter(name) {
1400
+ const parameterIndex = this.compiled.parameterIndexByName.get(name);
1401
+ if (parameterIndex === undefined)
1402
+ throw new ReferenceError(`Unknown parameter "${name}".`);
1403
+ return this.compiled.parameters[parameterIndex];
1404
+ }
1405
+ _restoreParameterDefaults() {
1406
+ for (const parameter of this.compiled.parameters) {
1407
+ this._parameterValues[parameter.index] = parameter.defaultValue;
1408
+ }
1409
+ }
1410
+ _requireActive() {
1411
+ if (this._status === 'destroyed') {
1412
+ throw new Error('Animation3D state-machine controller is destroyed.');
1413
+ }
1414
+ }
1415
+ }
1416
+ /**
1417
+ * @internal Extends one controller update transaction through caller-owned
1418
+ * pose evaluation. It is intentionally not re-exported from package entries.
1419
+ */
1420
+ function runAnimationStateMachineControllerUpdateTransaction(controller, deltaSeconds, beforeUpdate, complete) {
1421
+ return controller[CONTROLLER_UPDATE_TRANSACTION](deltaSeconds, beforeUpdate, complete);
1422
+ }
1423
+ function resolveTransactionalPort(port) {
1424
+ const candidate = port;
1425
+ const hasBegin = typeof candidate.beginControllerTransaction === 'function';
1426
+ const hasCommit = typeof candidate.commitControllerTransaction === 'function';
1427
+ const hasRollback = typeof candidate.rollbackControllerTransaction === 'function';
1428
+ const implemented = Number(hasBegin) + Number(hasCommit) + Number(hasRollback);
1429
+ if (implemented === 0)
1430
+ return null;
1431
+ if (implemented !== 3) {
1432
+ throw new TypeError('A transactional state-machine mixer port must implement begin, commit, and rollback.');
1433
+ }
1434
+ return candidate;
1435
+ }
1436
+
1437
+ export { Animation3DStateMachineController as A, AnimationStateMachineValidationError as a, compileAnimation3DStateMachineDefinition as b, compileAnimationStateMachineDefinition as c, validateAnimation3DStateMachineDefinition as d, runAnimationStateMachineControllerUpdateTransaction as r, validateAnimationStateMachineDefinition as v };
1438
+ //# sourceMappingURL=AnimationStateMachineController-BIcAh-sT.js.map