@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,3056 @@
1
+ import { Component, EngineError, EngineErrorCode } from '@haiyue/engine';
2
+ import { UniqueCheckType } from '@haiyue/engine/ecs';
3
+ import { ErrorRecovery, ErrorDomain } from '@haiyue/engine/core';
4
+ import { parseAssetWorkerFirst, createAbortError } from '@haiyue/engine/experimental/async';
5
+ import { S as SPINE_ASSET_PARSER } from './SpineAssetParser-BSDmPge1.js';
6
+
7
+ class Spine2DComponent extends Component {
8
+ static UniqueCheckType = UniqueCheckType.SYMBOL | UniqueCheckType.REPLACE;
9
+ static UniqueSymbol = Symbol.for('Spine2DComponent');
10
+ static editor = {
11
+ fields: {
12
+ jsonUrl: { type: 'text', label: 'Skeleton JSON URL' },
13
+ atlasUrl: { type: 'text', label: 'Atlas URL' },
14
+ imageUrl: { type: 'text', label: 'Image URL Override' },
15
+ skin: { type: 'text', label: 'Skin' },
16
+ animation: { type: 'text', label: 'Animation' },
17
+ loop: { type: 'boolean', label: 'Loop' },
18
+ timeScale: { type: 'number', label: 'Time Scale', step: 0.1 },
19
+ scale: { type: 'number', label: 'Scale', min: 0.001, step: 0.1 },
20
+ premultipliedAlpha: { type: 'boolean', label: 'Premultiplied Alpha' },
21
+ debugMesh: { type: 'boolean', label: 'Debug Mesh' },
22
+ debugBones: { type: 'boolean', label: 'Debug Bones' },
23
+ mixDuration: { type: 'number', label: 'Mix Duration', min: 0, step: 0.05 },
24
+ },
25
+ };
26
+ _jsonUrl = '';
27
+ _atlasUrl = '';
28
+ _imageUrl = '';
29
+ _imageUrls = {};
30
+ _imageUrlsKey = '{}';
31
+ _skin = 'default';
32
+ _sourceKey = '';
33
+ _sourceKeyDirty = true;
34
+ animation;
35
+ loop;
36
+ timeScale;
37
+ scale;
38
+ premultipliedAlpha;
39
+ debugMesh;
40
+ debugBones;
41
+ mixDuration;
42
+ status = 'idle';
43
+ error = null;
44
+ elapsed = 0;
45
+ previousAnimation = '';
46
+ previousElapsed = 0;
47
+ mixElapsed = 0;
48
+ runtimeKey = '';
49
+ loadingKey = '';
50
+ constructor(options = {}) {
51
+ super('Spine2DComponent');
52
+ this.jsonUrl = options.jsonUrl ?? '';
53
+ this.atlasUrl = options.atlasUrl ?? '';
54
+ this.imageUrl = options.imageUrl ?? '';
55
+ this.imageUrls = options.imageUrls ?? {};
56
+ this.skin = options.skin ?? 'default';
57
+ this.animation = options.animation ?? '';
58
+ this.loop = options.loop ?? true;
59
+ this.timeScale = options.timeScale ?? 1;
60
+ this.scale = options.scale ?? 1;
61
+ this.premultipliedAlpha = options.premultipliedAlpha ?? false;
62
+ this.debugMesh = options.debugMesh ?? false;
63
+ this.debugBones = options.debugBones ?? false;
64
+ this.mixDuration = options.mixDuration ?? 0;
65
+ }
66
+ get jsonUrl() {
67
+ return this._jsonUrl;
68
+ }
69
+ set jsonUrl(value) {
70
+ if (this._jsonUrl === value)
71
+ return;
72
+ this._jsonUrl = value;
73
+ this.invalidateSourceKey(false);
74
+ }
75
+ get atlasUrl() {
76
+ return this._atlasUrl;
77
+ }
78
+ set atlasUrl(value) {
79
+ if (this._atlasUrl === value)
80
+ return;
81
+ this._atlasUrl = value;
82
+ this.invalidateSourceKey(false);
83
+ }
84
+ get imageUrl() {
85
+ return this._imageUrl;
86
+ }
87
+ set imageUrl(value) {
88
+ if (this._imageUrl === value)
89
+ return;
90
+ this._imageUrl = value;
91
+ this.invalidateSourceKey(false);
92
+ }
93
+ get imageUrls() {
94
+ return this._imageUrls;
95
+ }
96
+ set imageUrls(value) {
97
+ if (this._imageUrls === value)
98
+ return;
99
+ this._imageUrls = value;
100
+ this._imageUrlsKey = JSON.stringify(value);
101
+ this.invalidateSourceKey(false);
102
+ }
103
+ get skin() {
104
+ return this._skin;
105
+ }
106
+ set skin(value) {
107
+ if (this._skin === value)
108
+ return;
109
+ this._skin = value;
110
+ this.invalidateSourceKey(false);
111
+ }
112
+ get sourceKey() {
113
+ if (this._sourceKeyDirty) {
114
+ this._sourceKey = `${this._jsonUrl}|${this._atlasUrl}|${this._imageUrl}|${this._imageUrlsKey}|${this._skin}`;
115
+ this._sourceKeyDirty = false;
116
+ }
117
+ return this._sourceKey;
118
+ }
119
+ invalidateSourceKey(refreshImageUrlsKey = true) {
120
+ if (refreshImageUrlsKey)
121
+ this._imageUrlsKey = JSON.stringify(this._imageUrls);
122
+ this._sourceKeyDirty = true;
123
+ return this;
124
+ }
125
+ clone() {
126
+ return new Spine2DComponent({
127
+ jsonUrl: this.jsonUrl,
128
+ atlasUrl: this.atlasUrl,
129
+ imageUrl: this.imageUrl,
130
+ imageUrls: { ...this.imageUrls },
131
+ skin: this.skin,
132
+ animation: this.animation,
133
+ loop: this.loop,
134
+ timeScale: this.timeScale,
135
+ scale: this.scale,
136
+ premultipliedAlpha: this.premultipliedAlpha,
137
+ debugMesh: this.debugMesh,
138
+ debugBones: this.debugBones,
139
+ mixDuration: this.mixDuration,
140
+ });
141
+ }
142
+ }
143
+
144
+ const cache = new WeakMap();
145
+ const EMPTY = [];
146
+ function compileAnimationBoneTimelines(animations) {
147
+ for (const animation of Object.values(animations))
148
+ getAnimationBoneTimelines(animation);
149
+ }
150
+ function getAnimationBoneTimelines(animation) {
151
+ if (!animation || typeof animation !== 'object')
152
+ return EMPTY;
153
+ const cached = cache.get(animation);
154
+ if (cached)
155
+ return cached;
156
+ const entries = [];
157
+ for (const [boneName, timelines] of Object.entries(animation.bones ?? {})) {
158
+ entries.push({ boneName, timelines });
159
+ }
160
+ cache.set(animation, entries);
161
+ return entries;
162
+ }
163
+
164
+ class SpineFloatBuilder {
165
+ data;
166
+ length = 0;
167
+ constructor(initialCapacity = 1024) {
168
+ this.data = new Float32Array(initialCapacity);
169
+ }
170
+ clear() {
171
+ this.length = 0;
172
+ }
173
+ get byteLength() {
174
+ return this.length * 4;
175
+ }
176
+ get(index) {
177
+ return this.data[index] ?? 0;
178
+ }
179
+ push(...values) {
180
+ this.ensureCapacity(this.length + values.length);
181
+ this.data.set(values, this.length);
182
+ this.length += values.length;
183
+ }
184
+ push8(a, b, c, d, e, f, g, h) {
185
+ this.ensureCapacity(this.length + 8);
186
+ const offset = this.length;
187
+ this.data[offset] = a;
188
+ this.data[offset + 1] = b;
189
+ this.data[offset + 2] = c;
190
+ this.data[offset + 3] = d;
191
+ this.data[offset + 4] = e;
192
+ this.data[offset + 5] = f;
193
+ this.data[offset + 6] = g;
194
+ this.data[offset + 7] = h;
195
+ this.length += 8;
196
+ }
197
+ appendArray(values, length = values.length) {
198
+ this.ensureCapacity(this.length + length);
199
+ for (let i = 0; i < length; i++)
200
+ this.data[this.length + i] = values[i] ?? 0;
201
+ this.length += length;
202
+ }
203
+ truncate(length) {
204
+ this.length = Math.max(0, Math.min(this.length, length));
205
+ }
206
+ /** Grows storage if needed and exposes a reusable writable region without allocating a temporary array. */
207
+ reserveLength(length) {
208
+ const nextLength = Math.max(0, length);
209
+ this.ensureCapacity(nextLength);
210
+ this.length = nextLength;
211
+ }
212
+ ensureCapacity(required) {
213
+ if (this.data.length >= required)
214
+ return;
215
+ let nextCapacity = Math.max(1, this.data.length);
216
+ while (nextCapacity < required)
217
+ nextCapacity *= 2;
218
+ const next = new Float32Array(nextCapacity);
219
+ next.set(this.data.subarray(0, this.length));
220
+ this.data = next;
221
+ }
222
+ }
223
+
224
+ /** Reads an element whose presence is an internal runtime invariant. */
225
+ function requiredItemAt(values, index, label) {
226
+ const value = values[index];
227
+ if (value === undefined) {
228
+ throw new RangeError(`${label} is missing element ${index} (length ${values.length}).`);
229
+ }
230
+ return value;
231
+ }
232
+ /** Reads a number whose presence is an internal runtime invariant. */
233
+ function requiredNumberAt(values, index, label) {
234
+ const value = values[index];
235
+ if (value === undefined || !Number.isFinite(value)) {
236
+ throw new RangeError(`${label} requires a finite value at ${index} (length ${values.length}).`);
237
+ }
238
+ return value;
239
+ }
240
+
241
+ const CURVE_LINEAR = 0;
242
+ const CURVE_STEPPED = 1;
243
+ const CURVE_BEZIER = 2;
244
+ const compiledFrames = new WeakMap();
245
+ const animationDurations = new WeakMap();
246
+ function createSpineTimelineSamplerState() {
247
+ return { cursors: new WeakMap(), cursorMisses: 0 };
248
+ }
249
+ /** Eagerly compiles every frame array reachable from the animation tree. */
250
+ function compileSpineTimelines(animations, samplerState) {
251
+ let timelineCount = 0;
252
+ let frameCount = 0;
253
+ let numericChannelCount = 0;
254
+ for (const animation of Object.values(animations)) {
255
+ let duration = 0;
256
+ const visit = (value) => {
257
+ if (Array.isArray(value)) {
258
+ const compiled = compileFrameArray(value);
259
+ if (compiled) {
260
+ getTimelineCursor(value, samplerState);
261
+ timelineCount++;
262
+ frameCount += compiled.times.length;
263
+ numericChannelCount += compiled.numericChannels.size;
264
+ const lastTime = compiled.times[compiled.times.length - 1];
265
+ if (lastTime !== undefined && lastTime > duration)
266
+ duration = lastTime;
267
+ }
268
+ else {
269
+ for (const child of value)
270
+ visit(child);
271
+ }
272
+ return;
273
+ }
274
+ if (!value || typeof value !== 'object')
275
+ return;
276
+ for (const child of Object.values(value))
277
+ visit(child);
278
+ };
279
+ visit(animation);
280
+ if (animation && typeof animation === 'object')
281
+ animationDurations.set(animation, duration);
282
+ }
283
+ return { timelineCount, frameCount, numericChannelCount };
284
+ }
285
+ function getCompiledAnimationDuration(animation) {
286
+ if (!animation || typeof animation !== 'object')
287
+ return 0;
288
+ const cached = animationDurations.get(animation);
289
+ if (cached !== undefined)
290
+ return cached;
291
+ let duration = 0;
292
+ const visit = (value) => {
293
+ if (Array.isArray(value)) {
294
+ const compiled = getOrCompileFrames(value);
295
+ if (compiled) {
296
+ const lastTime = compiled.times[compiled.times.length - 1];
297
+ if (lastTime !== undefined && lastTime > duration)
298
+ duration = lastTime;
299
+ }
300
+ else {
301
+ for (const child of value)
302
+ visit(child);
303
+ }
304
+ return;
305
+ }
306
+ if (!value || typeof value !== 'object')
307
+ return;
308
+ for (const child of Object.values(value))
309
+ visit(child);
310
+ };
311
+ visit(animation);
312
+ animationDurations.set(animation, duration);
313
+ return duration;
314
+ }
315
+ function sampleCompiledTimeline(frames, time, key, fallback, _duration = 0, _loop = false, samplerState) {
316
+ const timeline = getOrCompileFrames(frames);
317
+ if (!timeline || timeline.times.length === 0)
318
+ return fallback;
319
+ const channel = timeline.numericChannels.get(key) ?? timeline.numericChannels.get('value');
320
+ if (!channel)
321
+ return fallback;
322
+ const lastIndex = timeline.times.length - 1;
323
+ const firstTime = timeline.times[0] ?? 0;
324
+ if (time < firstTime)
325
+ return fallback;
326
+ const lastTime = timeline.times[lastIndex] ?? 0;
327
+ if (time >= lastTime)
328
+ return finiteOrFallback(channel.values[lastIndex], fallback);
329
+ const previousIndex = findIntervalIndex(timeline, time, getTimelineCursor(frames, samplerState));
330
+ const nextIndex = Math.min(lastIndex, previousIndex + 1);
331
+ const start = finiteOrFallback(channel.values[previousIndex], fallback);
332
+ const end = finiteOrFallback(channel.values[nextIndex], fallback);
333
+ const previousTime = timeline.times[previousIndex] ?? 0;
334
+ const nextTime = timeline.times[nextIndex] ?? previousTime;
335
+ if (nextIndex === previousIndex || nextTime === previousTime)
336
+ return start;
337
+ const curveKind = channel.curveKinds[previousIndex] ?? CURVE_LINEAR;
338
+ if (curveKind === CURVE_STEPPED)
339
+ return start;
340
+ if (curveKind === CURVE_BEZIER) {
341
+ return sampleBezierChannel(time, previousTime, start, channel.curves, previousIndex * 4, nextTime, end);
342
+ }
343
+ const alpha = clampUnit((time - previousTime) / (nextTime - previousTime));
344
+ return start + (end - start) * alpha;
345
+ }
346
+ /** Returns the last frame whose time is <= sample time, or -1 before the first frame. */
347
+ function findCompiledFrameIndex(frames, time, samplerState) {
348
+ const timeline = getOrCompileFrames(frames);
349
+ if (!timeline || timeline.times.length === 0 || time < (timeline.times[0] ?? 0))
350
+ return -1;
351
+ const lastIndex = timeline.times.length - 1;
352
+ if (time >= (timeline.times[lastIndex] ?? 0)) {
353
+ const state = getTimelineCursor(frames, samplerState);
354
+ if (state) {
355
+ state.discreteCursor = lastIndex;
356
+ state.discreteTime = time;
357
+ }
358
+ return lastIndex;
359
+ }
360
+ const state = getTimelineCursor(frames, samplerState);
361
+ let cursor = state?.discreteCursor ?? 0;
362
+ if (state
363
+ && time >= state.discreteTime
364
+ && cursor >= 0
365
+ && cursor < lastIndex
366
+ && time >= (timeline.times[cursor] ?? 0)) {
367
+ while (cursor < lastIndex && (timeline.times[cursor + 1] ?? Infinity) <= time)
368
+ cursor++;
369
+ }
370
+ else {
371
+ cursor = upperBound(timeline.times, time) - 1;
372
+ }
373
+ if (state) {
374
+ state.discreteCursor = Math.max(0, cursor);
375
+ state.discreteTime = time;
376
+ }
377
+ return cursor;
378
+ }
379
+ function sampleCompiledColor(frames, time, fallback, out, samplerState) {
380
+ const timeline = getOrCompileFrames(frames);
381
+ if (!timeline?.color || timeline.times.length === 0)
382
+ return copyColor$1(fallback, out);
383
+ const channel = timeline.color;
384
+ const lastIndex = timeline.times.length - 1;
385
+ const firstTime = timeline.times[0] ?? 0;
386
+ if (time < firstTime)
387
+ return copyColor$1(fallback, out);
388
+ const lastTime = timeline.times[lastIndex] ?? 0;
389
+ if (time >= lastTime)
390
+ return copyCompiledColor(channel, lastIndex, fallback, out);
391
+ const previousIndex = findIntervalIndex(timeline, time, getTimelineCursor(frames, samplerState));
392
+ const nextIndex = Math.min(lastIndex, previousIndex + 1);
393
+ const previousTime = timeline.times[previousIndex] ?? 0;
394
+ const nextTime = timeline.times[nextIndex] ?? previousTime;
395
+ for (let colorChannel = 0; colorChannel < 4; colorChannel++) {
396
+ const start = finiteOrFallback(channel.values[previousIndex * 4 + colorChannel], fallback[colorChannel] ?? 1);
397
+ const end = finiteOrFallback(channel.values[nextIndex * 4 + colorChannel], fallback[colorChannel] ?? 1);
398
+ if (nextIndex === previousIndex || nextTime === previousTime) {
399
+ out[colorChannel] = start;
400
+ continue;
401
+ }
402
+ const curveIndex = previousIndex * 4 + colorChannel;
403
+ const curveKind = channel.curveKinds[curveIndex] ?? CURVE_LINEAR;
404
+ if (curveKind === CURVE_STEPPED)
405
+ out[colorChannel] = start;
406
+ else if (curveKind === CURVE_BEZIER) {
407
+ out[colorChannel] = sampleBezierChannel(time, previousTime, start, channel.curves, curveIndex * 4, nextTime, end);
408
+ }
409
+ else {
410
+ const alpha = clampUnit((time - previousTime) / (nextTime - previousTime));
411
+ out[colorChannel] = start + (end - start) * alpha;
412
+ }
413
+ }
414
+ return out;
415
+ }
416
+ function compileFrameArray(frames) {
417
+ if (frames.length === 0 || compiledFrames.has(frames))
418
+ return compiledFrames.get(frames) ?? null;
419
+ for (const frame of frames) {
420
+ if (!frame || typeof frame !== 'object' || Array.isArray(frame))
421
+ return null;
422
+ }
423
+ const records = frames;
424
+ const times = new Float64Array(records.length);
425
+ const numericKeys = new Set();
426
+ let hasColor = false;
427
+ for (let frameIndex = 0; frameIndex < records.length; frameIndex++) {
428
+ const frame = records[frameIndex];
429
+ if (!frame)
430
+ continue;
431
+ times[frameIndex] = finiteNumber(frame.time, 0);
432
+ for (const [key, value] of Object.entries(frame)) {
433
+ if (key === 'time' || key === 'curve')
434
+ continue;
435
+ if (key === 'color' && typeof value === 'string')
436
+ hasColor = true;
437
+ else if (typeof value === 'number')
438
+ numericKeys.add(key);
439
+ }
440
+ }
441
+ const numericChannels = new Map();
442
+ for (const key of numericKeys)
443
+ numericChannels.set(key, compileNumericChannel(records, key));
444
+ const timeline = {
445
+ times,
446
+ numericChannels,
447
+ color: hasColor ? compileColorChannel(records) : null,
448
+ };
449
+ compiledFrames.set(frames, timeline);
450
+ return timeline;
451
+ }
452
+ function compileNumericChannel(frames, key) {
453
+ const values = new Float32Array(frames.length);
454
+ const curveKinds = new Uint8Array(frames.length);
455
+ const curves = new Float32Array(frames.length * 4);
456
+ const curveOffset = getCurveOffset(key);
457
+ for (let frameIndex = 0; frameIndex < frames.length; frameIndex++) {
458
+ const frame = frames[frameIndex];
459
+ const value = frame?.[key] ?? frame?.value;
460
+ values[frameIndex] = typeof value === 'number' && Number.isFinite(value) ? value : Number.NaN;
461
+ compileCurve(frame?.curve, curveOffset, curveKinds, frameIndex, curves, frameIndex * 4);
462
+ }
463
+ return { values, curveKinds, curves };
464
+ }
465
+ function compileColorChannel(frames) {
466
+ const values = new Float32Array(frames.length * 4);
467
+ values.fill(Number.NaN);
468
+ const curveKinds = new Uint8Array(frames.length * 4);
469
+ const curves = new Float32Array(frames.length * 16);
470
+ for (let frameIndex = 0; frameIndex < frames.length; frameIndex++) {
471
+ const frame = frames[frameIndex];
472
+ writeHexColor(frame?.color, values, frameIndex * 4);
473
+ for (let channel = 0; channel < 4; channel++) {
474
+ const curveIndex = frameIndex * 4 + channel;
475
+ compileCurve(frame?.curve, channel * 4, curveKinds, curveIndex, curves, curveIndex * 4);
476
+ }
477
+ }
478
+ return { values, curveKinds, curves };
479
+ }
480
+ function compileCurve(curve, sourceOffset, kinds, kindIndex, curves, targetOffset) {
481
+ if (curve === 'stepped') {
482
+ kinds[kindIndex] = CURVE_STEPPED;
483
+ return;
484
+ }
485
+ if (!Array.isArray(curve) || curve.length < sourceOffset + 4)
486
+ return;
487
+ kinds[kindIndex] = CURVE_BEZIER;
488
+ curves[targetOffset] = finiteNumber(curve[sourceOffset], 0);
489
+ curves[targetOffset + 1] = finiteNumber(curve[sourceOffset + 1], 0);
490
+ curves[targetOffset + 2] = finiteNumber(curve[sourceOffset + 2], 0);
491
+ curves[targetOffset + 3] = finiteNumber(curve[sourceOffset + 3], 0);
492
+ }
493
+ function getOrCompileFrames(frames) {
494
+ if (!Array.isArray(frames) || frames.length === 0)
495
+ return null;
496
+ return compiledFrames.get(frames) ?? compileFrameArray(frames);
497
+ }
498
+ function findIntervalIndex(timeline, time, state) {
499
+ const times = timeline.times;
500
+ const lastInterval = Math.max(0, times.length - 2);
501
+ let cursor = Math.min(lastInterval, state?.intervalCursor ?? 0);
502
+ if (state
503
+ && time >= state.intervalTime
504
+ && time >= (times[cursor] ?? 0)
505
+ && time <= (times[cursor + 1] ?? Infinity)) {
506
+ state.intervalTime = time;
507
+ return cursor;
508
+ }
509
+ if (state && time >= state.intervalTime && time >= (times[cursor] ?? 0)) {
510
+ while (cursor < lastInterval && (times[cursor + 1] ?? Infinity) < time)
511
+ cursor++;
512
+ }
513
+ else {
514
+ cursor = Math.max(0, lowerBound(times, time) - 1);
515
+ }
516
+ if (state) {
517
+ state.intervalCursor = cursor;
518
+ state.intervalTime = time;
519
+ }
520
+ return cursor;
521
+ }
522
+ function getTimelineCursor(frames, samplerState) {
523
+ if (!frames || !samplerState)
524
+ return null;
525
+ let cursor = samplerState.cursors.get(frames);
526
+ if (!cursor) {
527
+ cursor = { intervalCursor: 0, intervalTime: -Infinity, discreteCursor: 0, discreteTime: -Infinity };
528
+ samplerState.cursors.set(frames, cursor);
529
+ samplerState.cursorMisses++;
530
+ }
531
+ return cursor;
532
+ }
533
+ function lowerBound(values, target) {
534
+ let low = 0;
535
+ let high = values.length;
536
+ while (low < high) {
537
+ const middle = (low + high) >>> 1;
538
+ if ((values[middle] ?? Infinity) < target)
539
+ low = middle + 1;
540
+ else
541
+ high = middle;
542
+ }
543
+ return low;
544
+ }
545
+ function upperBound(values, target) {
546
+ let low = 0;
547
+ let high = values.length;
548
+ while (low < high) {
549
+ const middle = (low + high) >>> 1;
550
+ if ((values[middle] ?? Infinity) <= target)
551
+ low = middle + 1;
552
+ else
553
+ high = middle;
554
+ }
555
+ return low;
556
+ }
557
+ function sampleBezierChannel(time, time1, value1, curves, curveOffset, time2, value2) {
558
+ const cx1 = curves[curveOffset] ?? time1;
559
+ const cy1 = curves[curveOffset + 1] ?? value1;
560
+ const cx2 = curves[curveOffset + 2] ?? time2;
561
+ const cy2 = curves[curveOffset + 3] ?? value2;
562
+ let low = 0;
563
+ let high = 1;
564
+ let parameter = 0;
565
+ for (let iteration = 0; iteration < 12; iteration++) {
566
+ parameter = (low + high) * 0.5;
567
+ if (cubicBezier(time1, cx1, cx2, time2, parameter) < time)
568
+ low = parameter;
569
+ else
570
+ high = parameter;
571
+ }
572
+ parameter = (low + high) * 0.5;
573
+ return cubicBezier(value1, cy1, cy2, value2, parameter);
574
+ }
575
+ function cubicBezier(p0, p1, p2, p3, time) {
576
+ const inverse = 1 - time;
577
+ return inverse * inverse * inverse * p0
578
+ + 3 * inverse * inverse * time * p1
579
+ + 3 * inverse * time * time * p2
580
+ + time * time * time * p3;
581
+ }
582
+ function copyCompiledColor(channel, frameIndex, fallback, out) {
583
+ for (let colorChannel = 0; colorChannel < 4; colorChannel++) {
584
+ out[colorChannel] = finiteOrFallback(channel.values[frameIndex * 4 + colorChannel], fallback[colorChannel] ?? 1);
585
+ }
586
+ return out;
587
+ }
588
+ function copyColor$1(source, out) {
589
+ out[0] = source[0] ?? 1;
590
+ out[1] = source[1] ?? 1;
591
+ out[2] = source[2] ?? 1;
592
+ out[3] = source[3] ?? 1;
593
+ return out;
594
+ }
595
+ function writeHexColor(value, out, offset) {
596
+ if (typeof value !== 'string' || value.length < 8)
597
+ return;
598
+ out[offset] = parseInt(value.slice(0, 2), 16) / 255;
599
+ out[offset + 1] = parseInt(value.slice(2, 4), 16) / 255;
600
+ out[offset + 2] = parseInt(value.slice(4, 6), 16) / 255;
601
+ out[offset + 3] = parseInt(value.slice(6, 8), 16) / 255;
602
+ }
603
+ function getCurveOffset(key) {
604
+ return key === 'y' || key === 'scaleY' || key === 'softness' ? 4 : 0;
605
+ }
606
+ function finiteNumber(value, fallback) {
607
+ return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
608
+ }
609
+ function finiteOrFallback(value, fallback) {
610
+ return value !== undefined && Number.isFinite(value) ? value : fallback;
611
+ }
612
+ function clampUnit(value) {
613
+ return Math.max(0, Math.min(1, value));
614
+ }
615
+
616
+ function createSpinePathConstraintScratch() {
617
+ return {
618
+ pathBonesScratch: [],
619
+ pathConstrainedBonesScratch: new Set(),
620
+ pathSpacesScratch: [],
621
+ pathLengthsScratch: [],
622
+ pathWorldScratch: [],
623
+ pathOutScratch: [],
624
+ pathCurveWorldScratch: [],
625
+ pathCurvesScratch: [],
626
+ pathSegmentsScratch: [],
627
+ pathCurvePointScratch: [],
628
+ pathStateScratch: { position: 0, spacing: 0, mixRotate: 0, mixX: 0, mixY: 0 },
629
+ };
630
+ }
631
+ function applyPathConstraint(runtime, component, constraint, time, duration, loop, poses, children, deps) {
632
+ const data = runtime.data;
633
+ const slot = data.slots.find(item => item.name === constraint.slot);
634
+ if (!slot)
635
+ return;
636
+ const slotBone = poses.get(slot.bone);
637
+ if (!slotBone)
638
+ return;
639
+ const skin = data.skins[component.skin] ?? {};
640
+ const slotSkin = skin[slot.name] ?? data.skins.default?.[slot.name] ?? {};
641
+ const attachmentName = slot.attachment ?? Object.keys(slotSkin)[0];
642
+ const attachment = attachmentName ? slotSkin[attachmentName] : null;
643
+ if (!attachment || attachment.type !== 'path')
644
+ return;
645
+ const state = samplePathConstraint(runtime, constraint, data.animations[component.animation]?.path?.[constraint.name], time, duration, loop, runtime.pathStateScratch);
646
+ if (state.mixRotate === 0 && state.mixX === 0 && state.mixY === 0)
647
+ return;
648
+ const bones = runtime.pathBonesScratch;
649
+ const constrainedBones = runtime.pathConstrainedBonesScratch;
650
+ bones.length = 0;
651
+ constrainedBones.clear();
652
+ for (const name of constraint.bones) {
653
+ constrainedBones.add(name);
654
+ const bone = poses.get(name);
655
+ if (bone)
656
+ bones.push(bone);
657
+ }
658
+ const boneCount = bones.length;
659
+ if (boneCount === 0)
660
+ return;
661
+ const tangents = constraint.rotateMode === 'tangent';
662
+ const chainScale = constraint.rotateMode === 'chainScale';
663
+ const spacesCount = tangents ? boneCount : boneCount + 1;
664
+ const spaces = runtime.pathSpacesScratch;
665
+ const lengths = runtime.pathLengthsScratch;
666
+ spaces.length = spacesCount;
667
+ lengths.length = boneCount;
668
+ for (let i = 0; i < spacesCount; i++)
669
+ spaces[i] = 0;
670
+ for (let i = 0; i < boneCount; i++)
671
+ lengths[i] = 0;
672
+ switch (constraint.spacingMode) {
673
+ case 'percent':
674
+ if (chainScale) {
675
+ for (let i = 0; i < spacesCount - 1; i++) {
676
+ lengths[i] = getBoneWorldLength(requiredItemAt(bones, i, 'path constrained bones'));
677
+ }
678
+ }
679
+ for (let i = 1; i < spacesCount; i++)
680
+ spaces[i] = state.spacing;
681
+ break;
682
+ case 'proportional': {
683
+ let sum = 0;
684
+ for (let i = 0; i < spacesCount - 1;) {
685
+ const length = getBoneWorldLength(requiredItemAt(bones, i, 'path constrained bones'));
686
+ if (chainScale)
687
+ lengths[i] = length;
688
+ spaces[++i] = length < 0.000001 ? state.spacing : length;
689
+ sum += length < 0.000001 ? 0 : length;
690
+ }
691
+ if (sum > 0) {
692
+ const scale = spacesCount / sum * state.spacing;
693
+ for (let i = 1; i < spacesCount; i++)
694
+ spaces[i] = requiredNumberAt(spaces, i, 'path spaces') * scale;
695
+ }
696
+ break;
697
+ }
698
+ default:
699
+ for (let i = 0; i < spacesCount - 1;) {
700
+ const bone = requiredItemAt(bones, i, 'path constrained bones');
701
+ const worldLength = getBoneWorldLength(bone);
702
+ if (chainScale)
703
+ lengths[i] = worldLength;
704
+ spaces[++i] = constraint.spacingMode === 'length' && bone.data.length > 0
705
+ ? Math.max(0, bone.data.length + state.spacing) * worldLength / bone.data.length
706
+ : state.spacing;
707
+ }
708
+ break;
709
+ }
710
+ const positions = computePathWorldPositions(runtime, poses, slotBone, attachment, constraint, state.position, spaces, tangents);
711
+ let boneX = requiredNumberAt(positions, 0, 'path positions');
712
+ let boneY = requiredNumberAt(positions, 1, 'path positions');
713
+ let offsetRotation = constraint.offsetRotation * Math.PI / 180;
714
+ if (offsetRotation !== 0 && slotBone.a * slotBone.d - slotBone.b * slotBone.c < 0)
715
+ offsetRotation = -offsetRotation;
716
+ const tip = offsetRotation === 0 && constraint.rotateMode === 'chain';
717
+ for (let i = 0, positionIndex = 3; i < boneCount; i++, positionIndex += 3) {
718
+ const bone = requiredItemAt(bones, i, 'path constrained bones');
719
+ bone.worldX += (boneX - bone.worldX) * state.mixX;
720
+ bone.worldY += (boneY - bone.worldY) * state.mixY;
721
+ const x = requiredNumberAt(positions, positionIndex, 'path positions');
722
+ const y = requiredNumberAt(positions, positionIndex + 1, 'path positions');
723
+ const dx = x - boneX;
724
+ const dy = y - boneY;
725
+ if (chainScale) {
726
+ const length = requiredNumberAt(lengths, i, 'path lengths');
727
+ if (length !== 0) {
728
+ const scale = (Math.hypot(dx, dy) / length - 1) * state.mixRotate + 1;
729
+ bone.a *= scale;
730
+ bone.c *= scale;
731
+ }
732
+ }
733
+ boneX = x;
734
+ boneY = y;
735
+ if (state.mixRotate > 0) {
736
+ const a = bone.a;
737
+ const b = bone.b;
738
+ const c = bone.c;
739
+ const d = bone.d;
740
+ let rotation = tangents
741
+ ? requiredNumberAt(positions, positionIndex - 1, 'path positions')
742
+ : requiredNumberAt(spaces, i + 1, 'path spaces') === 0
743
+ ? requiredNumberAt(positions, positionIndex + 2, 'path positions')
744
+ : Math.atan2(dy, dx);
745
+ rotation -= Math.atan2(c, a);
746
+ if (tip) {
747
+ const cos = Math.cos(rotation);
748
+ const sin = Math.sin(rotation);
749
+ const length = bone.data.length;
750
+ boneX += (length * (cos * a - sin * c) - dx) * state.mixRotate;
751
+ boneY += (length * (sin * a + cos * c) - dy) * state.mixRotate;
752
+ }
753
+ else {
754
+ rotation += offsetRotation;
755
+ }
756
+ rotation = deps.normalizeRadians(rotation) * state.mixRotate;
757
+ const cos = Math.cos(rotation);
758
+ const sin = Math.sin(rotation);
759
+ bone.a = cos * a - sin * c;
760
+ bone.b = cos * b - sin * d;
761
+ bone.c = sin * a + cos * c;
762
+ bone.d = sin * b + cos * d;
763
+ }
764
+ }
765
+ for (const boneName of constraint.bones)
766
+ deps.updateDescendantBoneTrees(boneName, poses, children, constrainedBones);
767
+ }
768
+ function samplePathConstraint(runtime, constraint, frames, time, duration, loop, out) {
769
+ const sampler = runtime.timelineSamplerState;
770
+ out.position = sampleCompiledTimeline(frames ?? [], time, 'position', constraint.position, duration, loop, sampler);
771
+ out.spacing = sampleCompiledTimeline(frames ?? [], time, 'spacing', constraint.spacing, duration, loop, sampler);
772
+ out.mixRotate = sampleCompiledTimeline(frames ?? [], time, 'mixRotate', constraint.mixRotate, duration, loop, sampler);
773
+ out.mixX = sampleCompiledTimeline(frames ?? [], time, 'mixX', constraint.mixX, duration, loop, sampler);
774
+ out.mixY = sampleCompiledTimeline(frames ?? [], time, 'mixY', constraint.mixY, duration, loop, sampler);
775
+ return out;
776
+ }
777
+ function getBoneWorldLength(bone) {
778
+ return Math.hypot(bone.data.length * bone.a, bone.data.length * bone.c);
779
+ }
780
+ function computePathPoints(runtime, poses, slotBone, attachment, out) {
781
+ const data = runtime.data;
782
+ const vertices = attachment.vertices ?? [];
783
+ const vertexCount = attachment.vertices ? attachment.vertexCount ?? inferWeightedVertexCount(vertices) : 0;
784
+ out.length = 0;
785
+ if (vertexCount > 0 && vertices.length !== vertexCount * 2) {
786
+ let offset = 0;
787
+ for (let vertex = 0; vertex < vertexCount && offset < vertices.length; vertex++) {
788
+ const boneCount = vertices[offset++] ?? 0;
789
+ let x = 0;
790
+ let y = 0;
791
+ for (let influence = 0; influence < boneCount; influence++) {
792
+ const boneIndex = vertices[offset++] ?? -1;
793
+ const vx = vertices[offset++] ?? 0;
794
+ const vy = vertices[offset++] ?? 0;
795
+ const weight = vertices[offset++] ?? 0;
796
+ const boneName = data.bones[boneIndex]?.name;
797
+ const bone = boneName ? poses.get(boneName) : null;
798
+ if (!bone)
799
+ continue;
800
+ x += (bone.a * vx + bone.b * vy + bone.worldX) * weight;
801
+ y += (bone.c * vx + bone.d * vy + bone.worldY) * weight;
802
+ }
803
+ out.push(x, y);
804
+ }
805
+ return out;
806
+ }
807
+ for (let i = 0; i < vertices.length - 1; i += 2) {
808
+ out.push(slotBone.a * requiredNumberAt(vertices, i, 'path vertices')
809
+ + slotBone.b * requiredNumberAt(vertices, i + 1, 'path vertices') + slotBone.worldX, slotBone.c * requiredNumberAt(vertices, i, 'path vertices')
810
+ + slotBone.d * requiredNumberAt(vertices, i + 1, 'path vertices') + slotBone.worldY);
811
+ }
812
+ return out;
813
+ }
814
+ function computePathWorldPositions(runtime, poses, slotBone, attachment, constraint, position, spaces, tangents) {
815
+ const vertexCount = attachment.vertexCount ?? inferWeightedVertexCount(attachment.vertices ?? []);
816
+ const world = computePathPoints(runtime, poses, slotBone, attachment, runtime.pathWorldScratch);
817
+ const spacesCount = spaces.length;
818
+ const out = runtime.pathOutScratch;
819
+ out.length = spacesCount * 3 + 2;
820
+ for (let i = 0; i < out.length; i++)
821
+ out[i] = 0;
822
+ if (world.length < 8)
823
+ return out;
824
+ const closed = attachment.closed ?? false;
825
+ let verticesLength = vertexCount * 2;
826
+ let curveCount = verticesLength / 6;
827
+ if (!attachment.constantSpeed) {
828
+ const lengths = attachment.lengths ?? [];
829
+ curveCount -= closed ? 1 : 2;
830
+ const pathLength = lengths[curveCount] ?? lengths[lengths.length - 1] ?? 0;
831
+ if (constraint.positionMode === 'percent')
832
+ position *= pathLength;
833
+ const multiplier = constraint.spacingMode === 'percent'
834
+ ? pathLength
835
+ : constraint.spacingMode === 'proportional'
836
+ ? pathLength / spacesCount
837
+ : 1;
838
+ let curve = 0;
839
+ let previousCurve = -4;
840
+ const curveWorld = runtime.pathCurveWorldScratch;
841
+ for (let i = 0, o = 0; i < spacesCount; i++, o += 3) {
842
+ position += requiredNumberAt(spaces, i, 'path spaces') * multiplier;
843
+ let p = position;
844
+ if (closed) {
845
+ p %= pathLength;
846
+ if (p < 0)
847
+ p += pathLength;
848
+ curve = 0;
849
+ }
850
+ else if (p < 0) {
851
+ addBeforePathPosition(p, world, 2, out, o);
852
+ continue;
853
+ }
854
+ else if (p > pathLength) {
855
+ addAfterPathPosition(p - pathLength, world, verticesLength - 6, out, o);
856
+ continue;
857
+ }
858
+ for (;; curve++) {
859
+ const length = lengths[curve] ?? pathLength;
860
+ if (p > length)
861
+ continue;
862
+ const previousLength = curve === 0 ? 0 : requiredNumberAt(lengths, curve - 1, 'path attachment lengths');
863
+ p = curve === 0 ? p / length : (p - previousLength) / (length - previousLength);
864
+ break;
865
+ }
866
+ if (curve !== previousCurve) {
867
+ previousCurve = curve;
868
+ curveWorld.length = 0;
869
+ if (closed && curve === curveCount) {
870
+ appendArrayRange(curveWorld, world, verticesLength - 4, verticesLength);
871
+ appendArrayRange(curveWorld, world, 0, 4);
872
+ }
873
+ else {
874
+ appendArrayRange(curveWorld, world, curve * 6 + 2, curve * 6 + 10);
875
+ }
876
+ }
877
+ addCurvePathPosition(p, curveWorld, out, o, tangents || (i > 0 && requiredNumberAt(spaces, i, 'path spaces') === 0));
878
+ }
879
+ return out;
880
+ }
881
+ const pathWorld = runtime.pathCurveWorldScratch;
882
+ pathWorld.length = 0;
883
+ if (closed) {
884
+ verticesLength += 2;
885
+ appendArrayRange(pathWorld, world, 2, verticesLength - 2);
886
+ appendArrayRange(pathWorld, world, 0, 2);
887
+ pathWorld.push(requiredNumberAt(pathWorld, 0, 'closed path world points'), requiredNumberAt(pathWorld, 1, 'closed path world points'));
888
+ }
889
+ else {
890
+ curveCount--;
891
+ verticesLength -= 4;
892
+ appendArrayRange(pathWorld, world, 2, 2 + verticesLength);
893
+ }
894
+ const curves = runtime.pathCurvesScratch;
895
+ curves.length = curveCount;
896
+ let pathLength = 0;
897
+ let x1 = requiredNumberAt(pathWorld, 0, 'path world points');
898
+ let y1 = requiredNumberAt(pathWorld, 1, 'path world points');
899
+ let cx1 = 0;
900
+ let cy1 = 0;
901
+ let cx2 = 0;
902
+ let cy2 = 0;
903
+ let x2 = 0;
904
+ let y2 = 0;
905
+ let tmpx = 0;
906
+ let tmpy = 0;
907
+ let dddfx = 0;
908
+ let dddfy = 0;
909
+ let ddfx = 0;
910
+ let ddfy = 0;
911
+ let dfx = 0;
912
+ let dfy = 0;
913
+ for (let i = 0, w = 2; i < curveCount; i++, w += 6) {
914
+ cx1 = requiredNumberAt(pathWorld, w, 'path world points');
915
+ cy1 = requiredNumberAt(pathWorld, w + 1, 'path world points');
916
+ cx2 = requiredNumberAt(pathWorld, w + 2, 'path world points');
917
+ cy2 = requiredNumberAt(pathWorld, w + 3, 'path world points');
918
+ x2 = requiredNumberAt(pathWorld, w + 4, 'path world points');
919
+ y2 = requiredNumberAt(pathWorld, w + 5, 'path world points');
920
+ tmpx = (x1 - cx1 * 2 + cx2) * 0.1875;
921
+ tmpy = (y1 - cy1 * 2 + cy2) * 0.1875;
922
+ dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375;
923
+ dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375;
924
+ ddfx = tmpx * 2 + dddfx;
925
+ ddfy = tmpy * 2 + dddfy;
926
+ dfx = (cx1 - x1) * 0.75 + tmpx + dddfx * 0.16666667;
927
+ dfy = (cy1 - y1) * 0.75 + tmpy + dddfy * 0.16666667;
928
+ pathLength += Math.hypot(dfx, dfy);
929
+ dfx += ddfx;
930
+ dfy += ddfy;
931
+ ddfx += dddfx;
932
+ ddfy += dddfy;
933
+ pathLength += Math.hypot(dfx, dfy);
934
+ dfx += ddfx;
935
+ dfy += ddfy;
936
+ pathLength += Math.hypot(dfx, dfy);
937
+ dfx += ddfx + dddfx;
938
+ dfy += ddfy + dddfy;
939
+ pathLength += Math.hypot(dfx, dfy);
940
+ curves[i] = pathLength;
941
+ x1 = x2;
942
+ y1 = y2;
943
+ }
944
+ if (constraint.positionMode === 'percent')
945
+ position *= pathLength;
946
+ const multiplier = constraint.spacingMode === 'percent'
947
+ ? pathLength
948
+ : constraint.spacingMode === 'proportional'
949
+ ? pathLength / spacesCount
950
+ : 1;
951
+ const segments = runtime.pathSegmentsScratch;
952
+ segments.length = 10;
953
+ let curveLength = 0;
954
+ let previousCurve = -1;
955
+ let segment = 0;
956
+ for (let i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
957
+ const space = requiredNumberAt(spaces, i, 'path spaces') * multiplier;
958
+ position += space;
959
+ let p = position;
960
+ if (closed) {
961
+ p %= pathLength;
962
+ if (p < 0)
963
+ p += pathLength;
964
+ curve = 0;
965
+ segment = 0;
966
+ }
967
+ else if (p < 0) {
968
+ addBeforePathPosition(p, pathWorld, 0, out, o);
969
+ continue;
970
+ }
971
+ else if (p > pathLength) {
972
+ addAfterPathPosition(p - pathLength, pathWorld, pathWorld.length - 4, out, o);
973
+ continue;
974
+ }
975
+ for (;; curve++) {
976
+ const length = requiredNumberAt(curves, curve, 'path curve lengths');
977
+ if (p > length)
978
+ continue;
979
+ const previousLength = curve === 0 ? 0 : requiredNumberAt(curves, curve - 1, 'path curve lengths');
980
+ p = curve === 0 ? p / length : (p - previousLength) / (length - previousLength);
981
+ break;
982
+ }
983
+ if (curve !== previousCurve) {
984
+ previousCurve = curve;
985
+ const w = curve * 6;
986
+ x1 = requiredNumberAt(pathWorld, w, 'path world points');
987
+ y1 = requiredNumberAt(pathWorld, w + 1, 'path world points');
988
+ cx1 = requiredNumberAt(pathWorld, w + 2, 'path world points');
989
+ cy1 = requiredNumberAt(pathWorld, w + 3, 'path world points');
990
+ cx2 = requiredNumberAt(pathWorld, w + 4, 'path world points');
991
+ cy2 = requiredNumberAt(pathWorld, w + 5, 'path world points');
992
+ x2 = requiredNumberAt(pathWorld, w + 6, 'path world points');
993
+ y2 = requiredNumberAt(pathWorld, w + 7, 'path world points');
994
+ tmpx = (x1 - cx1 * 2 + cx2) * 0.03;
995
+ tmpy = (y1 - cy1 * 2 + cy2) * 0.03;
996
+ dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006;
997
+ dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006;
998
+ ddfx = tmpx * 2 + dddfx;
999
+ ddfy = tmpy * 2 + dddfy;
1000
+ dfx = (cx1 - x1) * 0.3 + tmpx + dddfx * 0.16666667;
1001
+ dfy = (cy1 - y1) * 0.3 + tmpy + dddfy * 0.16666667;
1002
+ curveLength = Math.hypot(dfx, dfy);
1003
+ segments[0] = curveLength;
1004
+ for (let ii = 1; ii < 8; ii++) {
1005
+ dfx += ddfx;
1006
+ dfy += ddfy;
1007
+ ddfx += dddfx;
1008
+ ddfy += dddfy;
1009
+ curveLength += Math.hypot(dfx, dfy);
1010
+ segments[ii] = curveLength;
1011
+ }
1012
+ dfx += ddfx;
1013
+ dfy += ddfy;
1014
+ curveLength += Math.hypot(dfx, dfy);
1015
+ segments[8] = curveLength;
1016
+ dfx += ddfx + dddfx;
1017
+ dfy += ddfy + dddfy;
1018
+ curveLength += Math.hypot(dfx, dfy);
1019
+ segments[9] = curveLength;
1020
+ segment = 0;
1021
+ }
1022
+ p *= curveLength;
1023
+ for (;; segment++) {
1024
+ const length = requiredNumberAt(segments, segment, 'path segments');
1025
+ if (p > length)
1026
+ continue;
1027
+ if (segment === 0)
1028
+ p = length === 0 ? 0 : p / length;
1029
+ else {
1030
+ const previous = requiredNumberAt(segments, segment - 1, 'path segments');
1031
+ p = length === previous ? segment : segment + (p - previous) / (length - previous);
1032
+ }
1033
+ break;
1034
+ }
1035
+ const curvePoint = runtime.pathCurvePointScratch;
1036
+ curvePoint[0] = x1;
1037
+ curvePoint[1] = y1;
1038
+ curvePoint[2] = cx1;
1039
+ curvePoint[3] = cy1;
1040
+ curvePoint[4] = cx2;
1041
+ curvePoint[5] = cy2;
1042
+ curvePoint[6] = x2;
1043
+ curvePoint[7] = y2;
1044
+ addCurvePathPosition(p * 0.1, curvePoint, out, o, tangents || (i > 0 && space === 0));
1045
+ }
1046
+ return out;
1047
+ }
1048
+ function appendArrayRange(out, source, start, end) {
1049
+ for (let i = start; i < end && i < source.length; i++) {
1050
+ out.push(requiredNumberAt(source, i, 'path source range'));
1051
+ }
1052
+ }
1053
+ function inferWeightedVertexCount(vertices) {
1054
+ let count = 0;
1055
+ let offset = 0;
1056
+ while (offset < vertices.length) {
1057
+ const boneCount = vertices[offset++] ?? 0;
1058
+ offset += boneCount * 4;
1059
+ count++;
1060
+ }
1061
+ return count;
1062
+ }
1063
+ function addBeforePathPosition(p, points, index, out, outIndex) {
1064
+ const x1 = requiredNumberAt(points, index, 'path points');
1065
+ const y1 = requiredNumberAt(points, index + 1, 'path points');
1066
+ const dx = requiredNumberAt(points, index + 2, 'path points') - x1;
1067
+ const dy = requiredNumberAt(points, index + 3, 'path points') - y1;
1068
+ const r = Math.atan2(dy, dx);
1069
+ out[outIndex] = x1 + p * Math.cos(r);
1070
+ out[outIndex + 1] = y1 + p * Math.sin(r);
1071
+ out[outIndex + 2] = r;
1072
+ }
1073
+ function addAfterPathPosition(p, points, index, out, outIndex) {
1074
+ const x1 = requiredNumberAt(points, index + 2, 'path points');
1075
+ const y1 = requiredNumberAt(points, index + 3, 'path points');
1076
+ const dx = x1 - requiredNumberAt(points, index, 'path points');
1077
+ const dy = y1 - requiredNumberAt(points, index + 1, 'path points');
1078
+ const r = Math.atan2(dy, dx);
1079
+ out[outIndex] = x1 + p * Math.cos(r);
1080
+ out[outIndex + 1] = y1 + p * Math.sin(r);
1081
+ out[outIndex + 2] = r;
1082
+ }
1083
+ function addCurvePathPosition(p, points, out, outIndex, tangents) {
1084
+ const x1 = requiredNumberAt(points, 0, 'Bezier path points');
1085
+ const y1 = requiredNumberAt(points, 1, 'Bezier path points');
1086
+ const cx1 = requiredNumberAt(points, 2, 'Bezier path points');
1087
+ const cy1 = requiredNumberAt(points, 3, 'Bezier path points');
1088
+ const cx2 = requiredNumberAt(points, 4, 'Bezier path points');
1089
+ const cy2 = requiredNumberAt(points, 5, 'Bezier path points');
1090
+ const x2 = requiredNumberAt(points, 6, 'Bezier path points');
1091
+ const y2 = requiredNumberAt(points, 7, 'Bezier path points');
1092
+ if (p === 0 || Number.isNaN(p)) {
1093
+ out[outIndex] = x1;
1094
+ out[outIndex + 1] = y1;
1095
+ out[outIndex + 2] = Math.atan2(cy1 - y1, cx1 - x1);
1096
+ return;
1097
+ }
1098
+ const tt = p * p;
1099
+ const ttt = tt * p;
1100
+ const u = 1 - p;
1101
+ const uu = u * u;
1102
+ const uuu = uu * u;
1103
+ const ut = u * p;
1104
+ const ut3 = ut * 3;
1105
+ const uut3 = u * ut3;
1106
+ const utt3 = ut3 * p;
1107
+ const x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt;
1108
+ const y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
1109
+ out[outIndex] = x;
1110
+ out[outIndex + 1] = y;
1111
+ if (tangents) {
1112
+ if (p < 0.001)
1113
+ out[outIndex + 2] = Math.atan2(cy1 - y1, cx1 - x1);
1114
+ else
1115
+ out[outIndex + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
1116
+ }
1117
+ }
1118
+
1119
+ const HASH_OFFSET = 2166136261;
1120
+ const HASH_PRIME = 16777619;
1121
+ function createEmptySlotCache(key) {
1122
+ return {
1123
+ key,
1124
+ signature: 0,
1125
+ debugSignature: 0,
1126
+ vertices: new Float32Array(0),
1127
+ vertexLength: 0,
1128
+ vertexCapacity: 0,
1129
+ debugVertices: new Float32Array(0),
1130
+ debugVertexLength: 0,
1131
+ debugVertexCapacity: 0,
1132
+ batch: { blend: 'normal', page: '', vertexCount: 0 },
1133
+ vertexOffset: -1,
1134
+ debugVertexOffset: -1,
1135
+ };
1136
+ }
1137
+ function copyBuilderData(builder, target) {
1138
+ let output = target;
1139
+ if (output.length < builder.length) {
1140
+ output = new Float32Array(builder.length);
1141
+ }
1142
+ for (let index = 0; index < builder.length; index++)
1143
+ output[index] = builder.data[index] ?? 0;
1144
+ return output;
1145
+ }
1146
+ function hasInvalidOffsets(entries) {
1147
+ let vertexOffset = 0;
1148
+ let debugOffset = 0;
1149
+ for (const entry of entries) {
1150
+ if (entry.vertexOffset !== vertexOffset || entry.debugVertexOffset !== debugOffset)
1151
+ return true;
1152
+ vertexOffset += entry.vertexCapacity;
1153
+ debugOffset += entry.debugVertexLength;
1154
+ }
1155
+ return false;
1156
+ }
1157
+ function rebuildCachedGeometryLayout(runtime, entries) {
1158
+ const vertices = runtime.vertexBuilder;
1159
+ const debugVertices = runtime.debugVertexBuilder;
1160
+ vertices.clear();
1161
+ debugVertices.clear();
1162
+ for (const entry of entries) {
1163
+ entry.vertexOffset = vertices.length;
1164
+ entry.debugVertexOffset = debugVertices.length;
1165
+ if (entry.vertexLength > 0) {
1166
+ vertices.reserveLength(vertices.length + entry.vertexCapacity);
1167
+ for (let index = 0; index < entry.vertexLength; index++) {
1168
+ vertices.data[entry.vertexOffset + index] = entry.vertices[index] ?? 0;
1169
+ }
1170
+ }
1171
+ else if (entry.vertexCapacity > 0) {
1172
+ vertices.reserveLength(vertices.length + entry.vertexCapacity);
1173
+ }
1174
+ if (entry.debugVertexLength > 0) {
1175
+ debugVertices.reserveLength(debugVertices.length + entry.debugVertexLength);
1176
+ for (let index = 0; index < entry.debugVertexLength; index++) {
1177
+ debugVertices.data[entry.debugVertexOffset + index] = entry.debugVertices[index] ?? 0;
1178
+ }
1179
+ }
1180
+ }
1181
+ }
1182
+ function rebuildCachedDrawBatches(runtime, entries) {
1183
+ const batches = runtime.batches;
1184
+ batches.length = 0;
1185
+ for (const entry of entries) {
1186
+ if (entry.vertexLength <= 0 || entry.batch.vertexCount <= 0)
1187
+ continue;
1188
+ appendBatch(batches, runtime.batchPool, runtime.allocationStats, entry.batch.blend, entry.batch.page, entry.vertexOffset / 8, entry.batch.vertexCount);
1189
+ }
1190
+ }
1191
+ function getSlotOrderHash(entries) {
1192
+ let hash = HASH_OFFSET;
1193
+ for (const entry of entries) {
1194
+ hash = hashString(hash, entry.key);
1195
+ hash = hashInt(hash, entry.vertexCapacity);
1196
+ hash = hashInt(hash, entry.debugVertexLength);
1197
+ }
1198
+ return hash;
1199
+ }
1200
+ function getSlotBuildSignature(slot, attachmentName, attachment, regionName, region, bone, poses, slotColor, clip, component) {
1201
+ let hash = HASH_OFFSET;
1202
+ hash = hashString(hash, slot.name);
1203
+ hash = hashString(hash, attachmentName);
1204
+ hash = hashString(hash, attachment.type ?? '');
1205
+ hash = hashString(hash, regionName);
1206
+ hash = hashString(hash, region.page);
1207
+ hash = hashString(hash, normalizeBlend(slot.blend));
1208
+ hash = hashInt(hash, quantizeSignatureNumber(component.scale));
1209
+ hash = hashColor(hash, slotColor);
1210
+ hash = hashClip(hash, clip);
1211
+ return attachment.type === 'mesh' ? hashAllPoses(hash, poses) : hashPose(hash, bone);
1212
+ }
1213
+ function getDebugBonesSignature(scale, poses) {
1214
+ let hash = hashString(HASH_OFFSET, 'debugBones');
1215
+ hash = hashInt(hash, quantizeSignatureNumber(scale));
1216
+ return hashAllPoses(hash, poses);
1217
+ }
1218
+ function hashInt(hash, value) {
1219
+ let input = value | 0;
1220
+ for (let i = 0; i < 4; i++) {
1221
+ hash ^= input & 0xff;
1222
+ hash = Math.imul(hash, HASH_PRIME) >>> 0;
1223
+ input >>= 8;
1224
+ }
1225
+ return hash;
1226
+ }
1227
+ function normalizeBlend(blend) {
1228
+ return blend === 'additive' ? 'additive' : 'normal';
1229
+ }
1230
+ function computeClipPolygon(bone, attachment, scale) {
1231
+ const vertices = attachment.vertices ?? [];
1232
+ const polygon = [];
1233
+ for (let i = 0; i < vertices.length - 1; i += 2) {
1234
+ polygon.push(transformBonePoint(bone, requiredNumberAt(vertices, i, 'clip attachment vertices'), requiredNumberAt(vertices, i + 1, 'clip attachment vertices'), scale));
1235
+ }
1236
+ return signedPolygonArea(polygon) < 0 ? polygon.slice().reverse() : polygon;
1237
+ }
1238
+ function replaceClippedRange(floats, start, polygon, clipped) {
1239
+ if (polygon.length < 3 || start >= floats.length)
1240
+ return;
1241
+ clipped.clear();
1242
+ for (let i = start; i < floats.length; i += 24) {
1243
+ const triangle = [
1244
+ readVertex(floats, i),
1245
+ readVertex(floats, i + 8),
1246
+ readVertex(floats, i + 16),
1247
+ ];
1248
+ const clippedPolygon = clipVertexPolygon(triangle, polygon);
1249
+ for (let vertex = 1; vertex < clippedPolygon.length - 1; vertex++) {
1250
+ writeVertex(clipped, requiredItemAt(clippedPolygon, 0, 'clipped polygon'));
1251
+ writeVertex(clipped, requiredItemAt(clippedPolygon, vertex, 'clipped polygon'));
1252
+ writeVertex(clipped, requiredItemAt(clippedPolygon, vertex + 1, 'clipped polygon'));
1253
+ }
1254
+ }
1255
+ floats.truncate(start);
1256
+ floats.appendArray(clipped.data, clipped.length);
1257
+ }
1258
+ function appendRegionVertices(out, component, runtime, bone, slotColor, attachment, region) {
1259
+ const w = attachment.width || region.originalWidth || region.width;
1260
+ const h = attachment.height || region.originalHeight || region.height;
1261
+ const sx = attachment.scaleX ?? 1;
1262
+ const sy = attachment.scaleY ?? 1;
1263
+ const rad = (attachment.rotation ?? 0) * Math.PI / 180;
1264
+ const cos = Math.cos(rad);
1265
+ const sin = Math.sin(rad);
1266
+ const halfW = w * 0.5;
1267
+ const halfH = h * 0.5;
1268
+ const tx = attachment.x ?? 0;
1269
+ const ty = attachment.y ?? 0;
1270
+ const scale = component.scale;
1271
+ const x0 = -halfW * sx;
1272
+ const y0 = -halfH * sy;
1273
+ const x1 = halfW * sx;
1274
+ const y1 = halfH * sy;
1275
+ const rx0 = x0 * cos - y0 * sin + tx;
1276
+ const ry0 = x0 * sin + y0 * cos + ty;
1277
+ const rx1 = x1 * cos - y0 * sin + tx;
1278
+ const ry1 = x1 * sin + y0 * cos + ty;
1279
+ const rx2 = x1 * cos - y1 * sin + tx;
1280
+ const ry2 = x1 * sin + y1 * cos + ty;
1281
+ const rx3 = x0 * cos - y1 * sin + tx;
1282
+ const ry3 = x0 * sin + y1 * cos + ty;
1283
+ const px0 = (bone.a * rx0 + bone.b * ry0 + bone.worldX) * scale;
1284
+ const py0 = (bone.c * rx0 + bone.d * ry0 + bone.worldY) * scale;
1285
+ const px1 = (bone.a * rx1 + bone.b * ry1 + bone.worldX) * scale;
1286
+ const py1 = (bone.c * rx1 + bone.d * ry1 + bone.worldY) * scale;
1287
+ const px2 = (bone.a * rx2 + bone.b * ry2 + bone.worldX) * scale;
1288
+ const py2 = (bone.c * rx2 + bone.d * ry2 + bone.worldY) * scale;
1289
+ const px3 = (bone.a * rx3 + bone.b * ry3 + bone.worldX) * scale;
1290
+ const py3 = (bone.c * rx3 + bone.d * ry3 + bone.worldY) * scale;
1291
+ const page = runtime.pages.get(region.page);
1292
+ const pageWidth = page?.width ?? 1;
1293
+ const pageHeight = page?.height ?? 1;
1294
+ const packedWidth = region.rotate === 90 ? region.height : region.width;
1295
+ const packedHeight = region.rotate === 90 ? region.width : region.height;
1296
+ const u0 = region.x / pageWidth;
1297
+ const v0 = region.y / pageHeight;
1298
+ const u1 = (region.x + packedWidth) / pageWidth;
1299
+ const v1 = (region.y + packedHeight) / pageHeight;
1300
+ const color = multiplyColor(slotColor, attachment.color, runtime.vertexColorScratch);
1301
+ if (region.rotate === 90) {
1302
+ pushVertex(out, px0, py0, u1, v1, color);
1303
+ pushVertex(out, px1, py1, u1, v0, color);
1304
+ pushVertex(out, px2, py2, u0, v0, color);
1305
+ pushVertex(out, px0, py0, u1, v1, color);
1306
+ pushVertex(out, px2, py2, u0, v0, color);
1307
+ pushVertex(out, px3, py3, u0, v1, color);
1308
+ }
1309
+ else {
1310
+ pushVertex(out, px0, py0, u0, v1, color);
1311
+ pushVertex(out, px1, py1, u1, v1, color);
1312
+ pushVertex(out, px2, py2, u1, v0, color);
1313
+ pushVertex(out, px0, py0, u0, v1, color);
1314
+ pushVertex(out, px2, py2, u1, v0, color);
1315
+ pushVertex(out, px3, py3, u0, v0, color);
1316
+ }
1317
+ }
1318
+ function appendMeshVertices(out, component, runtime, data, poses, bone, slotColor, attachment, region) {
1319
+ const points = computeMeshPoints(data, poses, bone, attachment, component.scale, runtime.meshPointBuilder);
1320
+ const triangles = attachment.triangles ?? [];
1321
+ const color = multiplyColor(slotColor, attachment.color, runtime.vertexColorScratch);
1322
+ for (let i = 0; i < triangles.length - 2; i += 3) {
1323
+ const a = triangles[i] ?? -1;
1324
+ const b = triangles[i + 1] ?? -1;
1325
+ const c = triangles[i + 2] ?? -1;
1326
+ if (!hasMeshPoint(points, a) || !hasMeshPoint(points, b) || !hasMeshPoint(points, c))
1327
+ continue;
1328
+ appendMeshVertex(out, points, region, runtime, attachment, a, color);
1329
+ appendMeshVertex(out, points, region, runtime, attachment, b, color);
1330
+ appendMeshVertex(out, points, region, runtime, attachment, c, color);
1331
+ }
1332
+ }
1333
+ function appendMeshWireframe(out, component, runtime, data, poses, bone, attachment) {
1334
+ const points = computeMeshPoints(data, poses, bone, attachment, component.scale, runtime.meshPointBuilder);
1335
+ const triangles = attachment.triangles ?? [];
1336
+ const seen = new Set();
1337
+ for (let i = 0; i < triangles.length - 2; i += 3) {
1338
+ appendMeshDebugEdge(out, points, triangles[i] ?? -1, triangles[i + 1] ?? -1, seen, [0.3, 0.85, 1, 0.8]);
1339
+ appendMeshDebugEdge(out, points, triangles[i + 1] ?? -1, triangles[i + 2] ?? -1, seen, [0.3, 0.85, 1, 0.8]);
1340
+ appendMeshDebugEdge(out, points, triangles[i + 2] ?? -1, triangles[i] ?? -1, seen, [0.3, 0.85, 1, 0.8]);
1341
+ }
1342
+ }
1343
+ function appendRegionWireframe(out, component, bone, attachment, region) {
1344
+ const w = attachment.width || region.originalWidth || region.width;
1345
+ const h = attachment.height || region.originalHeight || region.height;
1346
+ const sx = attachment.scaleX ?? 1;
1347
+ const sy = attachment.scaleY ?? 1;
1348
+ const rad = (attachment.rotation ?? 0) * Math.PI / 180;
1349
+ const cos = Math.cos(rad);
1350
+ const sin = Math.sin(rad);
1351
+ const localCorners = [
1352
+ [-w / 2, -h / 2],
1353
+ [w / 2, -h / 2],
1354
+ [w / 2, h / 2],
1355
+ [-w / 2, h / 2],
1356
+ ];
1357
+ const points = localCorners.map(([x, y]) => {
1358
+ const lx = x * sx;
1359
+ const ly = y * sy;
1360
+ const rx = lx * cos - ly * sin + (attachment.x ?? 0);
1361
+ const ry = lx * sin + ly * cos + (attachment.y ?? 0);
1362
+ return transformBonePoint(bone, rx, ry, component.scale);
1363
+ });
1364
+ const seen = new Set();
1365
+ appendDebugEdge(out, points, 0, 1, seen, [0.3, 0.85, 1, 0.8]);
1366
+ appendDebugEdge(out, points, 1, 2, seen, [0.3, 0.85, 1, 0.8]);
1367
+ appendDebugEdge(out, points, 2, 3, seen, [0.3, 0.85, 1, 0.8]);
1368
+ appendDebugEdge(out, points, 3, 0, seen, [0.3, 0.85, 1, 0.8]);
1369
+ }
1370
+ function appendBoneDebug(out, data, poses, scale) {
1371
+ for (const boneData of data.bones) {
1372
+ const bone = poses.get(boneData.name);
1373
+ if (!bone)
1374
+ continue;
1375
+ const start = [bone.worldX * scale, bone.worldY * scale];
1376
+ const end = boneData.length > 0
1377
+ ? [(bone.worldX + bone.a * boneData.length) * scale, (bone.worldY + bone.c * boneData.length) * scale]
1378
+ : boneData.parent && poses.get(boneData.parent)
1379
+ ? [poses.get(boneData.parent).worldX * scale, poses.get(boneData.parent).worldY * scale]
1380
+ : start;
1381
+ appendDebugLine(out, start, end, [1, 0.85, 0.25, 0.95]);
1382
+ const joint = 3 / Math.max(0.001, scale);
1383
+ appendDebugLine(out, transformBonePoint(bone, -joint, 0, scale), transformBonePoint(bone, joint, 0, scale), [1, 0.35, 0.3, 0.95]);
1384
+ appendDebugLine(out, transformBonePoint(bone, 0, -joint, scale), transformBonePoint(bone, 0, joint, scale), [1, 0.35, 0.3, 0.95]);
1385
+ }
1386
+ }
1387
+ function hashPose(hash, pose) {
1388
+ hash = hashInt(hash, quantizeSignatureNumber(pose.a));
1389
+ hash = hashInt(hash, quantizeSignatureNumber(pose.b));
1390
+ hash = hashInt(hash, quantizeSignatureNumber(pose.c));
1391
+ hash = hashInt(hash, quantizeSignatureNumber(pose.d));
1392
+ hash = hashInt(hash, quantizeSignatureNumber(pose.worldX));
1393
+ hash = hashInt(hash, quantizeSignatureNumber(pose.worldY));
1394
+ return hash;
1395
+ }
1396
+ function hashAllPoses(hash, poses) {
1397
+ for (const [name, pose] of poses) {
1398
+ hash = hashString(hash, name);
1399
+ hash = hashPose(hash, pose);
1400
+ }
1401
+ return hash;
1402
+ }
1403
+ function hashColor(hash, color) {
1404
+ if (!color)
1405
+ return hashInt(hash, 0);
1406
+ hash = hashInt(hash, 1);
1407
+ hash = hashInt(hash, quantizeSignatureNumber(color[0]));
1408
+ hash = hashInt(hash, quantizeSignatureNumber(color[1]));
1409
+ hash = hashInt(hash, quantizeSignatureNumber(color[2]));
1410
+ return hashInt(hash, quantizeSignatureNumber(color[3]));
1411
+ }
1412
+ function hashClip(hash, clip) {
1413
+ if (!clip)
1414
+ return hashInt(hash, 0);
1415
+ hash = hashInt(hash, 1);
1416
+ hash = hashString(hash, clip.endSlot ?? '');
1417
+ for (const point of clip.polygon) {
1418
+ hash = hashInt(hash, quantizeSignatureNumber(point[0]));
1419
+ hash = hashInt(hash, quantizeSignatureNumber(point[1]));
1420
+ }
1421
+ return hash;
1422
+ }
1423
+ function quantizeSignatureNumber(value) {
1424
+ return Math.round(value * 10000);
1425
+ }
1426
+ function hashString(hash, value) {
1427
+ for (let i = 0; i < value.length; i++)
1428
+ hash = hashInt(hash, value.charCodeAt(i));
1429
+ return hash;
1430
+ }
1431
+ function appendBatch(batches, pool, runtimeAllocationStats, blend, page, firstVertex, vertexCount) {
1432
+ const last = batches[batches.length - 1];
1433
+ if (last && last.blend === blend && last.page === page && last.firstVertex + last.vertexCount === firstVertex) {
1434
+ last.vertexCount += vertexCount;
1435
+ return;
1436
+ }
1437
+ const index = batches.length;
1438
+ let batch = pool[index];
1439
+ if (!batch) {
1440
+ batch = { blend, page, firstVertex, vertexCount };
1441
+ pool.push(batch);
1442
+ if (runtimeAllocationStats)
1443
+ runtimeAllocationStats.batchPoolMisses++;
1444
+ }
1445
+ else {
1446
+ batch.blend = blend;
1447
+ batch.page = page;
1448
+ batch.firstVertex = firstVertex;
1449
+ batch.vertexCount = vertexCount;
1450
+ }
1451
+ batches.push(batch);
1452
+ }
1453
+ function transformBonePoint(bone, x, y, scale) {
1454
+ return [
1455
+ (bone.a * x + bone.b * y + bone.worldX) * scale,
1456
+ (bone.c * x + bone.d * y + bone.worldY) * scale,
1457
+ ];
1458
+ }
1459
+ function computeMeshPoints(data, poses, slotBone, attachment, scale, out) {
1460
+ const vertices = attachment.vertices ?? [];
1461
+ const vertexCount = Math.floor((attachment.uvs?.length ?? 0) / 2);
1462
+ out.clear();
1463
+ if (vertices.length === vertexCount * 2) {
1464
+ for (let i = 0; i < vertices.length; i += 2) {
1465
+ const x = vertices[i] ?? 0;
1466
+ const y = vertices[i + 1] ?? 0;
1467
+ out.push((slotBone.a * x + slotBone.b * y + slotBone.worldX) * scale, (slotBone.c * x + slotBone.d * y + slotBone.worldY) * scale);
1468
+ }
1469
+ return out;
1470
+ }
1471
+ let offset = 0;
1472
+ for (let vertex = 0; vertex < vertexCount && offset < vertices.length; vertex++) {
1473
+ const boneCount = vertices[offset++] ?? 0;
1474
+ let x = 0;
1475
+ let y = 0;
1476
+ for (let influence = 0; influence < boneCount; influence++) {
1477
+ const boneIndex = vertices[offset++] ?? -1;
1478
+ const vx = vertices[offset++] ?? 0;
1479
+ const vy = vertices[offset++] ?? 0;
1480
+ const weight = vertices[offset++] ?? 0;
1481
+ const boneName = data.bones[boneIndex]?.name;
1482
+ const bone = boneName ? poses.get(boneName) : null;
1483
+ if (!bone)
1484
+ continue;
1485
+ x += (bone.a * vx + bone.b * vy + bone.worldX) * weight;
1486
+ y += (bone.c * vx + bone.d * vy + bone.worldY) * weight;
1487
+ }
1488
+ out.push(x * scale, y * scale);
1489
+ }
1490
+ return out;
1491
+ }
1492
+ function hasMeshPoint(points, vertexIndex) {
1493
+ return vertexIndex >= 0 && vertexIndex * 2 + 1 < points.length;
1494
+ }
1495
+ function appendMeshVertex(out, points, region, runtime, attachment, vertexIndex, color) {
1496
+ const rawU = attachment.uvs?.[vertexIndex * 2] ?? 0;
1497
+ const rawV = attachment.uvs?.[vertexIndex * 2 + 1] ?? 0;
1498
+ const page = runtime.pages.get(region.page);
1499
+ const textureWidth = page?.width ?? 1;
1500
+ const textureHeight = page?.height ?? 1;
1501
+ let u;
1502
+ let v;
1503
+ if (region.rotate === 90) {
1504
+ const baseU = region.x / textureWidth - (region.originalHeight - region.offsetY - region.height) / textureWidth;
1505
+ const baseV = region.y / textureHeight - (region.originalWidth - region.offsetX - region.width) / textureHeight;
1506
+ u = baseU + rawV * region.originalHeight / textureWidth;
1507
+ v = baseV + (1 - rawU) * region.originalWidth / textureHeight;
1508
+ }
1509
+ else if (region.rotate === 180) {
1510
+ const baseU = region.x / textureWidth - (region.originalWidth - region.offsetX - region.width) / textureWidth;
1511
+ const baseV = region.y / textureHeight - region.offsetY / textureHeight;
1512
+ u = baseU + (1 - rawU) * region.originalWidth / textureWidth;
1513
+ v = baseV + (1 - rawV) * region.originalHeight / textureHeight;
1514
+ }
1515
+ else if (region.rotate === 270) {
1516
+ const baseU = region.x / textureWidth - region.offsetY / textureWidth;
1517
+ const baseV = region.y / textureHeight - region.offsetX / textureHeight;
1518
+ u = baseU + (1 - rawV) * region.originalHeight / textureWidth;
1519
+ v = baseV + rawU * region.originalWidth / textureHeight;
1520
+ }
1521
+ else {
1522
+ const baseU = region.x / textureWidth - region.offsetX / textureWidth;
1523
+ const baseV = region.y / textureHeight - (region.originalHeight - region.offsetY - region.height) / textureHeight;
1524
+ u = baseU + rawU * region.originalWidth / textureWidth;
1525
+ v = baseV + rawV * region.originalHeight / textureHeight;
1526
+ }
1527
+ const pointOffset = vertexIndex * 2;
1528
+ pushVertex(out, points.get(pointOffset), points.get(pointOffset + 1), u, v, color);
1529
+ }
1530
+ function appendDebugEdge(out, points, a, b, seen, color) {
1531
+ if (!points[a] || !points[b])
1532
+ return;
1533
+ const key = a < b ? `${a}:${b}` : `${b}:${a}`;
1534
+ if (seen.has(key))
1535
+ return;
1536
+ seen.add(key);
1537
+ appendDebugLine(out, points[a], points[b], color);
1538
+ }
1539
+ function appendMeshDebugEdge(out, points, a, b, seen, color) {
1540
+ if (!hasMeshPoint(points, a) || !hasMeshPoint(points, b))
1541
+ return;
1542
+ const key = a < b ? `${a}:${b}` : `${b}:${a}`;
1543
+ if (seen.has(key))
1544
+ return;
1545
+ seen.add(key);
1546
+ const aOffset = a * 2;
1547
+ const bOffset = b * 2;
1548
+ out.push(points.get(aOffset), points.get(aOffset + 1), 0, 0, ...color, points.get(bOffset), points.get(bOffset + 1), 0, 0, ...color);
1549
+ }
1550
+ function appendDebugLine(out, a, b, color) {
1551
+ out.push(a[0], a[1], 0, 0, ...color, b[0], b[1], 0, 0, ...color);
1552
+ }
1553
+ function multiplyColor(a, b, out) {
1554
+ out[0] = (a?.[0] ?? 1) * (b?.[0] ?? 1);
1555
+ out[1] = (a?.[1] ?? 1) * (b?.[1] ?? 1);
1556
+ out[2] = (a?.[2] ?? 1) * (b?.[2] ?? 1);
1557
+ out[3] = (a?.[3] ?? 1) * (b?.[3] ?? 1);
1558
+ return out;
1559
+ }
1560
+ function pushVertex(out, x, y, u, v, color) {
1561
+ out.push8(x, y, u, v, color[0], color[1], color[2], color[3]);
1562
+ }
1563
+ function signedPolygonArea(polygon) {
1564
+ let area = 0;
1565
+ for (let i = 0; i < polygon.length; i++) {
1566
+ const a = requiredItemAt(polygon, i, 'clip polygon');
1567
+ const b = requiredItemAt(polygon, (i + 1) % polygon.length, 'clip polygon');
1568
+ area += a[0] * b[1] - b[0] * a[1];
1569
+ }
1570
+ return area * 0.5;
1571
+ }
1572
+ function clipVertexPolygon(vertices, clipPolygon) {
1573
+ let output = vertices;
1574
+ for (let i = 0; i < clipPolygon.length; i++) {
1575
+ const a = requiredItemAt(clipPolygon, i, 'clip polygon');
1576
+ const b = requiredItemAt(clipPolygon, (i + 1) % clipPolygon.length, 'clip polygon');
1577
+ const input = output;
1578
+ output = [];
1579
+ if (input.length === 0)
1580
+ break;
1581
+ let previous = requiredItemAt(input, input.length - 1, 'clip input polygon');
1582
+ let previousInside = isInsideClipEdge(previous, a, b);
1583
+ for (const current of input) {
1584
+ const currentInside = isInsideClipEdge(current, a, b);
1585
+ if (currentInside !== previousInside)
1586
+ output.push(intersectClipEdge(previous, current, a, b));
1587
+ if (currentInside)
1588
+ output.push(current);
1589
+ previous = current;
1590
+ previousInside = currentInside;
1591
+ }
1592
+ }
1593
+ return output;
1594
+ }
1595
+ function isInsideClipEdge(vertex, a, b) {
1596
+ return (b[0] - a[0]) * (vertex.y - a[1]) - (b[1] - a[1]) * (vertex.x - a[0]) >= -1e-4;
1597
+ }
1598
+ function intersectClipEdge(start, end, a, b) {
1599
+ const dx = end.x - start.x;
1600
+ const dy = end.y - start.y;
1601
+ const edgeX = b[0] - a[0];
1602
+ const edgeY = b[1] - a[1];
1603
+ const denominator = edgeX * dy - edgeY * dx;
1604
+ const t = Math.abs(denominator) < 0.000001
1605
+ ? 0
1606
+ : (edgeY * (start.x - a[0]) - edgeX * (start.y - a[1])) / denominator;
1607
+ const clamped = Math.max(0, Math.min(1, t));
1608
+ return lerpVertex(start, end, clamped);
1609
+ }
1610
+ function lerpVertex(start, end, t) {
1611
+ return {
1612
+ x: start.x + (end.x - start.x) * t,
1613
+ y: start.y + (end.y - start.y) * t,
1614
+ u: start.u + (end.u - start.u) * t,
1615
+ v: start.v + (end.v - start.v) * t,
1616
+ color: [
1617
+ start.color[0] + (end.color[0] - start.color[0]) * t,
1618
+ start.color[1] + (end.color[1] - start.color[1]) * t,
1619
+ start.color[2] + (end.color[2] - start.color[2]) * t,
1620
+ start.color[3] + (end.color[3] - start.color[3]) * t,
1621
+ ],
1622
+ };
1623
+ }
1624
+ function readVertex(floats, index) {
1625
+ return {
1626
+ x: floats.get(index),
1627
+ y: floats.get(index + 1),
1628
+ u: floats.get(index + 2),
1629
+ v: floats.get(index + 3),
1630
+ color: [floats.get(index + 4), floats.get(index + 5), floats.get(index + 6), floats.get(index + 7)],
1631
+ };
1632
+ }
1633
+ function writeVertex(out, vertex) {
1634
+ out.push(vertex.x, vertex.y, vertex.u, vertex.v, ...vertex.color);
1635
+ }
1636
+
1637
+ const WHITE_COLOR = [1, 1, 1, 1];
1638
+ /** Resolves the active slot attachment timeline without owning pose evaluation. */
1639
+ function getSpineSlotAttachmentName(runtime, component, slot, time) {
1640
+ let name = slot.attachment ?? null;
1641
+ const frames = runtime.data.animations[component.animation]?.slots?.[slot.name]?.attachment;
1642
+ if (!Array.isArray(frames) || frames.length === 0)
1643
+ return name;
1644
+ const frameIndex = findCompiledFrameIndex(frames, time, runtime.timelineSamplerState);
1645
+ if (frameIndex >= 0)
1646
+ name = frames[frameIndex]?.name ?? null;
1647
+ return name;
1648
+ }
1649
+ /** Resolves sequence attachments across the base animation and slider overlays. */
1650
+ function getSpineAttachmentRegionName(runtime, component, slot, attachmentName, attachment, time, sliderStates) {
1651
+ const basePath = attachment.path || attachment.name || attachmentName;
1652
+ const sequence = attachment.sequence;
1653
+ if (!sequence?.count)
1654
+ return basePath;
1655
+ let index = sampleSequenceIndex(getSequenceFrames(runtime.data, component.animation, component.skin, slot.name, attachmentName), time, sequence, runtime.timelineSamplerState);
1656
+ for (const slider of sliderStates) {
1657
+ const frames = getSequenceFrames(runtime.data, slider.animation, component.skin, slot.name, attachmentName);
1658
+ if (Array.isArray(frames) && frames.length > 0) {
1659
+ index = sampleSequenceIndex(frames, slider.time, sequence, runtime.timelineSamplerState);
1660
+ }
1661
+ }
1662
+ const frame = String((sequence.start ?? 0) + index).padStart(sequence.digits ?? 0, '0');
1663
+ return `${basePath}${frame}`;
1664
+ }
1665
+ /** Samples slot RGBA timelines and slider blends into runtime-owned scratch storage. */
1666
+ function getSpineSlotColor(runtime, component, slot, time, sliderStates) {
1667
+ const frames = runtime.data.animations[component.animation]?.slots?.[slot.name]?.rgba;
1668
+ const fallback = slot.color ?? WHITE_COLOR;
1669
+ const color = runtime.slotColorScratch;
1670
+ if (Array.isArray(frames) && frames.length > 0) {
1671
+ sampleCompiledColor(frames, time, fallback, color, runtime.timelineSamplerState);
1672
+ }
1673
+ else {
1674
+ copyColor(fallback, color);
1675
+ }
1676
+ for (const slider of sliderStates) {
1677
+ const sliderFrames = runtime.data.animations[slider.animation]?.slots?.[slot.name]?.rgba;
1678
+ if (!Array.isArray(sliderFrames) || sliderFrames.length === 0)
1679
+ continue;
1680
+ sampleCompiledColor(sliderFrames, slider.time, color, runtime.sliderColorScratch, runtime.timelineSamplerState);
1681
+ mixColorInto(color, runtime.sliderColorScratch, slider.mix);
1682
+ }
1683
+ return color;
1684
+ }
1685
+ function getSequenceFrames(data, animationName, skinName, slotName, attachmentName) {
1686
+ const frames = data.animations[animationName]?.attachments?.[skinName]?.[slotName]?.[attachmentName]?.sequence
1687
+ ?? data.animations[animationName]?.attachments?.default?.[slotName]?.[attachmentName]?.sequence;
1688
+ return Array.isArray(frames) ? frames : undefined;
1689
+ }
1690
+ function sampleSequenceIndex(frames, time, sequence, samplerState) {
1691
+ const count = Math.max(1, sequence.count ?? 1);
1692
+ if (!Array.isArray(frames) || frames.length === 0) {
1693
+ return clampSequenceIndex(sequence.setup ?? 0, count);
1694
+ }
1695
+ const frameIndex = findCompiledFrameIndex(frames, time, samplerState);
1696
+ if (frameIndex < 0)
1697
+ return clampSequenceIndex(sequence.setup ?? 0, count);
1698
+ const frame = frames[frameIndex];
1699
+ if (!frame)
1700
+ return clampSequenceIndex(sequence.setup ?? 0, count);
1701
+ const before = frame.time ?? 0;
1702
+ const delay = frame.delay ?? 0;
1703
+ let index = frame.index ?? 0;
1704
+ const mode = frame.mode ?? 'hold';
1705
+ if (mode !== 'hold' && delay > 0) {
1706
+ index += Math.floor((time - before) / delay + 0.00001);
1707
+ switch (mode) {
1708
+ case 'once':
1709
+ index = Math.min(count - 1, index);
1710
+ break;
1711
+ case 'loop':
1712
+ index %= count;
1713
+ break;
1714
+ case 'pingpong': {
1715
+ const range = count * 2 - 2;
1716
+ index = range === 0 ? 0 : index % range;
1717
+ if (index >= count)
1718
+ index = range - index;
1719
+ break;
1720
+ }
1721
+ case 'onceReverse':
1722
+ index = Math.max(count - 1 - index, 0);
1723
+ break;
1724
+ case 'loopReverse':
1725
+ index = count - 1 - (index % count);
1726
+ break;
1727
+ case 'pingpongReverse': {
1728
+ const range = count * 2 - 2;
1729
+ index = range === 0 ? 0 : (index + count - 1) % range;
1730
+ if (index >= count)
1731
+ index = range - index;
1732
+ break;
1733
+ }
1734
+ }
1735
+ }
1736
+ return clampSequenceIndex(index, count);
1737
+ }
1738
+ function clampSequenceIndex(index, count) {
1739
+ return Math.max(0, Math.min(count - 1, Math.floor(index)));
1740
+ }
1741
+ function mixColorInto(out, to, mix) {
1742
+ const alpha = Math.max(0, Math.min(1, mix));
1743
+ out[0] += (to[0] - out[0]) * alpha;
1744
+ out[1] += (to[1] - out[1]) * alpha;
1745
+ out[2] += (to[2] - out[2]) * alpha;
1746
+ out[3] += (to[3] - out[3]) * alpha;
1747
+ }
1748
+ function copyColor(source, out) {
1749
+ out[0] = source[0];
1750
+ out[1] = source[1];
1751
+ out[2] = source[2];
1752
+ out[3] = source[3];
1753
+ }
1754
+
1755
+ class LocalPoseSnapshot {
1756
+ data;
1757
+ length = 0;
1758
+ constructor(initialCapacity = 256) {
1759
+ this.data = new Float32Array(initialCapacity);
1760
+ }
1761
+ capture(poses) {
1762
+ const required = poses.size * 7;
1763
+ if (this.data.length < required) {
1764
+ let nextCapacity = Math.max(1, this.data.length);
1765
+ while (nextCapacity < required)
1766
+ nextCapacity *= 2;
1767
+ this.data = new Float32Array(nextCapacity);
1768
+ }
1769
+ let offset = 0;
1770
+ for (const pose of poses.values()) {
1771
+ this.data[offset++] = pose.x;
1772
+ this.data[offset++] = pose.y;
1773
+ this.data[offset++] = pose.rotation;
1774
+ this.data[offset++] = pose.shearX;
1775
+ this.data[offset++] = pose.shearY;
1776
+ this.data[offset++] = pose.scaleX;
1777
+ this.data[offset++] = pose.scaleY;
1778
+ }
1779
+ this.length = offset;
1780
+ }
1781
+ }
1782
+ const sliderPoseSnapshots = new WeakMap();
1783
+ const TWO_PI = Math.PI * 2;
1784
+ const PATH_CONSTRAINT_DEPS = { normalizeRadians, updateDescendantBoneTrees };
1785
+ function getSliderPoseSnapshot(component) {
1786
+ let snapshot = sliderPoseSnapshots.get(component);
1787
+ if (!snapshot) {
1788
+ snapshot = new LocalPoseSnapshot();
1789
+ sliderPoseSnapshots.set(component, snapshot);
1790
+ }
1791
+ return snapshot;
1792
+ }
1793
+ function advanceSpineRuntime(component, delta) {
1794
+ component.elapsed += delta * component.timeScale;
1795
+ if (component.previousAnimation)
1796
+ component.mixElapsed += delta * component.timeScale;
1797
+ if (component.previousAnimation && component.mixElapsed >= component.mixDuration * 1000)
1798
+ component.previousAnimation = '';
1799
+ }
1800
+ function buildSpineVertices(component, runtime) {
1801
+ const poses = computePoses(runtime, component);
1802
+ const floats = runtime.vertexBuilder;
1803
+ const debugFloats = runtime.debugVertexBuilder;
1804
+ const skin = runtime.data.skins[component.skin] ?? runtime.data.skins.default ?? {};
1805
+ const time = getCurrentAnimationTime(runtime.data, component);
1806
+ const sliderStates = computeSliderAnimationStates(runtime, component, poses);
1807
+ const entries = runtime.slotEntries;
1808
+ const changedVertexEntries = runtime.changedVertexEntries;
1809
+ const changedDebugEntries = runtime.changedDebugEntries;
1810
+ const liveKeys = runtime.liveSlotKeys;
1811
+ entries.length = 0;
1812
+ changedVertexEntries.length = 0;
1813
+ changedDebugEntries.length = 0;
1814
+ liveKeys.clear();
1815
+ let clip = null;
1816
+ for (const slot of getDrawOrderedSlots(runtime, component, time)) {
1817
+ const slotSkin = skin[slot.name] ?? {};
1818
+ const attachmentName = getSpineSlotAttachmentName(runtime, component, slot, time);
1819
+ if (!attachmentName) {
1820
+ if (clip?.endSlot === slot.name)
1821
+ clip = null;
1822
+ continue;
1823
+ }
1824
+ const attachment = slotSkin[attachmentName];
1825
+ if (!attachment || attachment.type === 'boundingbox') {
1826
+ if (clip?.endSlot === slot.name)
1827
+ clip = null;
1828
+ continue;
1829
+ }
1830
+ const bone = poses.get(slot.bone);
1831
+ if (attachment.type === 'clipping') {
1832
+ if (bone)
1833
+ clip = {
1834
+ ...(attachment.end === undefined ? {} : { endSlot: attachment.end }),
1835
+ polygon: computeClipPolygon(bone, attachment, component.scale),
1836
+ };
1837
+ continue;
1838
+ }
1839
+ const regionName = getSpineAttachmentRegionName(runtime, component, slot, attachmentName, attachment, time, sliderStates);
1840
+ const region = runtime.atlas.get(regionName) ?? runtime.atlas.get(attachment.path || attachment.name || attachmentName) ?? runtime.atlas.get(attachmentName);
1841
+ if (!bone || !region) {
1842
+ if (clip?.endSlot === slot.name)
1843
+ clip = null;
1844
+ continue;
1845
+ }
1846
+ const slotColor = getSpineSlotColor(runtime, component, slot, time, sliderStates);
1847
+ const cacheKey = runtime.slotCacheKeyByName.get(slot.name) ?? slot.name;
1848
+ liveKeys.add(cacheKey);
1849
+ const signature = getSlotBuildSignature(slot, attachmentName, attachment, regionName, region, bone, poses, slotColor, clip, component);
1850
+ const debugSignature = component.debugMesh ? hashInt(signature, 1) : 0;
1851
+ let cache = runtime.slotGeometryCache.get(cacheKey);
1852
+ const verticesChanged = !cache || cache.signature !== signature;
1853
+ const debugChanged = !cache || cache.debugSignature !== debugSignature;
1854
+ if (!cache) {
1855
+ cache = createEmptySlotCache(cacheKey);
1856
+ runtime.slotGeometryCache.set(cacheKey, cache);
1857
+ }
1858
+ if (verticesChanged || debugChanged) {
1859
+ runtime.slotVertexBuilder.clear();
1860
+ runtime.slotDebugVertexBuilder.clear();
1861
+ if (attachment.type === 'mesh') {
1862
+ appendMeshVertices(runtime.slotVertexBuilder, component, runtime, runtime.data, poses, bone, slotColor, attachment, region);
1863
+ if (component.debugMesh)
1864
+ appendMeshWireframe(runtime.slotDebugVertexBuilder, component, runtime, runtime.data, poses, bone, attachment);
1865
+ }
1866
+ else {
1867
+ appendRegionVertices(runtime.slotVertexBuilder, component, runtime, bone, slotColor, attachment, region);
1868
+ if (component.debugMesh)
1869
+ appendRegionWireframe(runtime.slotDebugVertexBuilder, component, bone, attachment, region);
1870
+ }
1871
+ if (clip)
1872
+ replaceClippedRange(runtime.slotVertexBuilder, 0, clip.polygon, runtime.clipBuilder);
1873
+ const previousVertexCapacity = cache.vertexCapacity;
1874
+ const previousDebugCapacity = cache.debugVertexCapacity;
1875
+ const previousDebugLength = cache.debugVertexLength;
1876
+ cache.signature = signature;
1877
+ cache.debugSignature = debugSignature;
1878
+ const previousVertices = cache.vertices;
1879
+ cache.vertices = copyBuilderData(runtime.slotVertexBuilder, cache.vertices);
1880
+ if (cache.vertices !== previousVertices)
1881
+ runtime.allocationStats.slotRegionGrowths++;
1882
+ cache.vertexLength = runtime.slotVertexBuilder.length;
1883
+ cache.vertexCapacity = cache.vertices.length;
1884
+ const previousDebugVertices = cache.debugVertices;
1885
+ cache.debugVertices = copyBuilderData(runtime.slotDebugVertexBuilder, cache.debugVertices);
1886
+ if (cache.debugVertices !== previousDebugVertices)
1887
+ runtime.allocationStats.slotRegionGrowths++;
1888
+ cache.debugVertexLength = runtime.slotDebugVertexBuilder.length;
1889
+ cache.debugVertexCapacity = cache.debugVertices.length;
1890
+ cache.batch.blend = normalizeBlend(slot.blend);
1891
+ cache.batch.page = region.page;
1892
+ cache.batch.vertexCount = cache.vertexLength / 8;
1893
+ if (cache.vertexCapacity !== previousVertexCapacity)
1894
+ cache.vertexOffset = -1;
1895
+ if (cache.debugVertexCapacity !== previousDebugCapacity || cache.debugVertexLength !== previousDebugLength) {
1896
+ cache.debugVertexOffset = -1;
1897
+ }
1898
+ }
1899
+ if (verticesChanged)
1900
+ changedVertexEntries.push(cache);
1901
+ if (debugChanged)
1902
+ changedDebugEntries.push(cache);
1903
+ entries.push(cache);
1904
+ if (clip?.endSlot === slot.name)
1905
+ clip = null;
1906
+ }
1907
+ if (component.debugBones) {
1908
+ const cacheKey = '__debug_bones__';
1909
+ liveKeys.add(cacheKey);
1910
+ const signature = 0;
1911
+ const debugSignature = getDebugBonesSignature(component.scale, poses);
1912
+ let cache = runtime.slotGeometryCache.get(cacheKey);
1913
+ const debugChanged = !cache || cache.debugSignature !== debugSignature;
1914
+ if (!cache) {
1915
+ cache = createEmptySlotCache(cacheKey);
1916
+ runtime.slotGeometryCache.set(cacheKey, cache);
1917
+ }
1918
+ if (debugChanged) {
1919
+ runtime.slotDebugVertexBuilder.clear();
1920
+ appendBoneDebug(runtime.slotDebugVertexBuilder, runtime.data, poses, component.scale);
1921
+ const previousDebugCapacity = cache.debugVertexCapacity;
1922
+ const previousDebugLength = cache.debugVertexLength;
1923
+ cache.signature = signature;
1924
+ cache.debugSignature = debugSignature;
1925
+ cache.vertexLength = 0;
1926
+ const previousDebugVertices = cache.debugVertices;
1927
+ cache.debugVertices = copyBuilderData(runtime.slotDebugVertexBuilder, cache.debugVertices);
1928
+ if (cache.debugVertices !== previousDebugVertices)
1929
+ runtime.allocationStats.slotRegionGrowths++;
1930
+ cache.debugVertexLength = runtime.slotDebugVertexBuilder.length;
1931
+ cache.debugVertexCapacity = cache.debugVertices.length;
1932
+ cache.batch.vertexCount = 0;
1933
+ if (cache.debugVertexCapacity !== previousDebugCapacity || cache.debugVertexLength !== previousDebugLength) {
1934
+ cache.debugVertexOffset = -1;
1935
+ }
1936
+ }
1937
+ if (debugChanged)
1938
+ changedDebugEntries.push(cache);
1939
+ entries.push(cache);
1940
+ }
1941
+ for (const key of runtime.slotGeometryCache.keys()) {
1942
+ if (!liveKeys.has(key))
1943
+ runtime.slotGeometryCache.delete(key);
1944
+ }
1945
+ const slotOrderHash = getSlotOrderHash(entries);
1946
+ const layoutChanged = runtime.slotOrderHash !== slotOrderHash || hasInvalidOffsets(entries);
1947
+ const vertexDirtyRanges = runtime.vertexDirtyRanges;
1948
+ const debugDirtyRanges = runtime.debugDirtyRanges;
1949
+ vertexDirtyRanges.length = 0;
1950
+ debugDirtyRanges.length = 0;
1951
+ if (layoutChanged) {
1952
+ rebuildCachedGeometryLayout(runtime, entries);
1953
+ runtime.slotOrderHash = slotOrderHash;
1954
+ if (floats.byteLength > 0)
1955
+ pushDirtyRange(runtime, vertexDirtyRanges, runtime.vertexDirtyRangePool, 0, floats.byteLength);
1956
+ if (debugFloats.byteLength > 0)
1957
+ pushDirtyRange(runtime, debugDirtyRanges, runtime.debugDirtyRangePool, 0, debugFloats.byteLength);
1958
+ }
1959
+ else {
1960
+ for (const entry of changedVertexEntries) {
1961
+ if (entry.vertexLength === 0)
1962
+ continue;
1963
+ for (let index = 0; index < entry.vertexLength; index++) {
1964
+ floats.data[entry.vertexOffset + index] = entry.vertices[index] ?? 0;
1965
+ }
1966
+ pushDirtyRange(runtime, vertexDirtyRanges, runtime.vertexDirtyRangePool, entry.vertexOffset * 4, entry.vertexLength * 4);
1967
+ }
1968
+ for (const entry of changedDebugEntries) {
1969
+ if (entry.debugVertexLength === 0)
1970
+ continue;
1971
+ for (let index = 0; index < entry.debugVertexLength; index++) {
1972
+ debugFloats.data[entry.debugVertexOffset + index] = entry.debugVertices[index] ?? 0;
1973
+ }
1974
+ pushDirtyRange(runtime, debugDirtyRanges, runtime.debugDirtyRangePool, entry.debugVertexOffset * 4, entry.debugVertexLength * 4);
1975
+ }
1976
+ }
1977
+ rebuildCachedDrawBatches(runtime, entries);
1978
+ const result = runtime.vertexBuildResult;
1979
+ result.verticesChanged = vertexDirtyRanges.length > 0;
1980
+ result.debugVerticesChanged = debugDirtyRanges.length > 0;
1981
+ return result;
1982
+ }
1983
+ function pushDirtyRange(runtime, ranges, pool, byteOffset, byteLength) {
1984
+ const index = ranges.length;
1985
+ let range = pool[index];
1986
+ if (!range) {
1987
+ range = { byteOffset, byteLength };
1988
+ pool.push(range);
1989
+ runtime.allocationStats.dirtyRangePoolMisses++;
1990
+ }
1991
+ else {
1992
+ range.byteOffset = byteOffset;
1993
+ range.byteLength = byteLength;
1994
+ }
1995
+ ranges.push(range);
1996
+ }
1997
+ async function loadSpineRuntime(gpuRenderer, component, options = {}) {
1998
+ try {
1999
+ return await loadSpineRuntimeUnchecked(gpuRenderer, component, options);
2000
+ }
2001
+ catch (error) {
2002
+ if (error instanceof EngineError) {
2003
+ throw new EngineError(error.code, error.message, {
2004
+ domain: error.domain,
2005
+ recoverable: error.recoverable,
2006
+ recovery: error.recovery,
2007
+ context: {
2008
+ resourceType: 'skeleton/spine',
2009
+ jsonUrl: component.jsonUrl,
2010
+ atlasUrl: component.atlasUrl,
2011
+ ...error.context,
2012
+ },
2013
+ ...(error.path === undefined ? {} : { path: error.path }),
2014
+ cause: error,
2015
+ });
2016
+ }
2017
+ throw new EngineError(EngineErrorCode.AssetInvalidData, 'Failed to create the Spine runtime.', {
2018
+ domain: ErrorDomain.Component,
2019
+ recovery: ErrorRecovery.ReleaseResource,
2020
+ context: { resourceType: 'skeleton/spine', jsonUrl: component.jsonUrl, atlasUrl: component.atlasUrl },
2021
+ path: 'spine',
2022
+ cause: error,
2023
+ });
2024
+ }
2025
+ }
2026
+ async function loadSpineRuntimeUnchecked(gpuRenderer, component, options) {
2027
+ const parsed = await parseAssetWorkerFirst({
2028
+ parser: {
2029
+ type: 'skeleton/spine',
2030
+ parse: (_input, context) => loadAndParseSpineMain(component, context.signal),
2031
+ },
2032
+ input: Object.freeze({ jsonUrl: component.jsonUrl, atlasUrl: component.atlasUrl }),
2033
+ context: { ...(options.signal ? { signal: options.signal } : {}), source: component.jsonUrl },
2034
+ worker: options.assetWorker
2035
+ ? async (_input, context) => requireSpineParsedAsset(await options.assetWorker.loadParsedAsset(component.jsonUrl, component.atlasUrl, context.signal ? { signal: context.signal } : {}), component.jsonUrl)
2036
+ : null,
2037
+ });
2038
+ throwIfAborted(options.signal);
2039
+ const data = parsed.data;
2040
+ const atlas = new Map(parsed.regions);
2041
+ const pages = await gpuRenderer.loadAtlasPages(component, atlas);
2042
+ throwIfAborted(options.signal);
2043
+ const runtimeBuffers = gpuRenderer.createRuntimeBuffers();
2044
+ return createSpineRuntimeState(data, atlas, pages, runtimeBuffers);
2045
+ }
2046
+ function requireSpineParsedAsset(value, source) {
2047
+ if (!value || typeof value !== 'object')
2048
+ throw invalidSpineWorkerPayload(source, value);
2049
+ const candidate = value;
2050
+ const data = candidate.data;
2051
+ if (!data || typeof data !== 'object' || !Array.isArray(data.bones) || !Array.isArray(candidate.regions)) {
2052
+ throw invalidSpineWorkerPayload(source, value);
2053
+ }
2054
+ return candidate;
2055
+ }
2056
+ function invalidSpineWorkerPayload(source, cause) {
2057
+ return new EngineError(EngineErrorCode.WorkerProtocolInvalid, 'Spine worker returned an invalid parsed asset.', {
2058
+ domain: ErrorDomain.Worker,
2059
+ recovery: ErrorRecovery.TerminateRuntime,
2060
+ context: { url: source, resourceType: 'skeleton/spine' },
2061
+ path: 'spine.worker.response',
2062
+ cause,
2063
+ });
2064
+ }
2065
+ function createSpineRuntimeState(data, atlas, pages, runtimeBuffers) {
2066
+ const timelineSamplerState = createSpineTimelineSamplerState();
2067
+ const timelineCompileStats = compileSpineTimelines(data.animations, timelineSamplerState);
2068
+ compileAnimationBoneTimelines(data.animations);
2069
+ const vertexBuilder = new SpineFloatBuilder(1024);
2070
+ const debugVertexBuilder = new SpineFloatBuilder(256);
2071
+ const batches = [];
2072
+ const vertexDirtyRanges = [];
2073
+ const debugDirtyRanges = [];
2074
+ const rangePoolCapacity = Math.max(1, data.slots.length + 1);
2075
+ const vertexBuildResult = {
2076
+ vertices: vertexBuilder,
2077
+ batches,
2078
+ debugVertices: debugVertexBuilder,
2079
+ vertexDirtyRanges,
2080
+ debugDirtyRanges,
2081
+ verticesChanged: false,
2082
+ debugVerticesChanged: false,
2083
+ };
2084
+ return {
2085
+ data,
2086
+ atlas,
2087
+ pages,
2088
+ ...runtimeBuffers,
2089
+ batches,
2090
+ batchPool: createBatchPool(Math.max(1, data.slots.length)),
2091
+ vertexBuilder,
2092
+ debugVertexBuilder,
2093
+ slotVertexBuilder: new SpineFloatBuilder(256),
2094
+ slotDebugVertexBuilder: new SpineFloatBuilder(128),
2095
+ clipBuilder: new SpineFloatBuilder(256),
2096
+ meshPointBuilder: new SpineFloatBuilder(256),
2097
+ slotGeometryCache: new Map(),
2098
+ slotOrderHash: 0,
2099
+ boneChildren: buildBoneChildren(data),
2100
+ setupPoses: createSetupPoses(data),
2101
+ previousSetupPoses: createSetupPoses(data),
2102
+ slotIndexByName: buildSlotIndexByName(data),
2103
+ slotCacheKeyByName: buildSlotCacheKeys(data),
2104
+ sliderConstraintsByOrder: buildSliderConstraintsByOrder(data),
2105
+ sortedConstraintsBySkin: new Map(),
2106
+ transformConstraintBoneSets: new WeakMap(),
2107
+ transformWorldSpaceBonesScratch: [],
2108
+ drawOrderScratch: [],
2109
+ unchangedSlotScratch: [],
2110
+ orderedSlotScratch: [],
2111
+ liveSlotKeys: new Set(),
2112
+ slotEntries: [],
2113
+ changedVertexEntries: [],
2114
+ changedDebugEntries: [],
2115
+ sliderStateScratch: [],
2116
+ sliderStatePool: [],
2117
+ vertexDirtyRanges,
2118
+ debugDirtyRanges,
2119
+ mergedDirtyRanges: [],
2120
+ vertexDirtyRangePool: createDirtyRangePool(rangePoolCapacity),
2121
+ debugDirtyRangePool: createDirtyRangePool(rangePoolCapacity),
2122
+ mergedDirtyRangePool: createDirtyRangePool(rangePoolCapacity),
2123
+ vertexBuildResult,
2124
+ slotColorScratch: [1, 1, 1, 1],
2125
+ sliderColorScratch: [1, 1, 1, 1],
2126
+ vertexColorScratch: [1, 1, 1, 1],
2127
+ timelineCompileStats,
2128
+ timelineSamplerState,
2129
+ allocationStats: { slotRegionGrowths: 0, batchPoolMisses: 0, dirtyRangePoolMisses: 0 },
2130
+ ikStateScratch: { mix: 0, softness: 0, bendPositive: true },
2131
+ transformStateScratch: { mixRotate: 0, mixX: 0, mixY: 0, mixScaleX: 0, mixScaleY: 0, mixShearY: 0 },
2132
+ ...createSpinePathConstraintScratch(),
2133
+ };
2134
+ }
2135
+ function createDirtyRangePool(capacity) {
2136
+ return Array.from({ length: capacity }, () => ({ byteOffset: 0, byteLength: 0 }));
2137
+ }
2138
+ function createBatchPool(capacity) {
2139
+ return Array.from({ length: capacity }, () => ({
2140
+ blend: 'normal',
2141
+ page: '',
2142
+ firstVertex: 0,
2143
+ vertexCount: 0,
2144
+ }));
2145
+ }
2146
+ /** Monorepo benchmark adapter; not re-exported from the Spine package facade. */
2147
+ function createSpineRuntimeForBenchmark(data, atlas, pages) {
2148
+ const placeholderBuffer = {};
2149
+ return createSpineRuntimeState(data, atlas, pages, {
2150
+ vertexBuffer: placeholderBuffer,
2151
+ vertexBufferSize: 0,
2152
+ debugVertexBuffer: placeholderBuffer,
2153
+ debugVertexBufferSize: 0,
2154
+ });
2155
+ }
2156
+ /** Monorepo benchmark adapter; samples the complete animation/constraint pose path. */
2157
+ function sampleSpinePosesForBenchmark(component, runtime) {
2158
+ let checksum = 0;
2159
+ for (const pose of computePoses(runtime, component).values()) {
2160
+ checksum += pose.worldX + pose.worldY + pose.a + pose.d;
2161
+ }
2162
+ return checksum;
2163
+ }
2164
+ async function loadAndParseSpineMain(component, signal) {
2165
+ const [json, atlasText] = await Promise.all([
2166
+ fetchJson(component.jsonUrl, signal),
2167
+ component.atlasUrl ? fetchText(component.atlasUrl, signal) : Promise.resolve(''),
2168
+ ]);
2169
+ return await SPINE_ASSET_PARSER.parse({ json, atlasText }, { source: component.jsonUrl, ...(signal === undefined ? {} : { signal }) });
2170
+ }
2171
+ async function fetchJson(url, signal) {
2172
+ const response = await fetch(url, signal ? { signal } : undefined);
2173
+ if (!response.ok) {
2174
+ throw new EngineError(EngineErrorCode.AssetLoadFailed, `Failed to load Spine JSON: ${response.status} ${response.statusText}`, {
2175
+ domain: ErrorDomain.Component,
2176
+ recovery: ErrorRecovery.Retry,
2177
+ context: { url, resourceType: 'skeleton/spine-json', status: response.status },
2178
+ path: 'spine.json',
2179
+ });
2180
+ }
2181
+ try {
2182
+ return await response.json();
2183
+ }
2184
+ catch (error) {
2185
+ throw new EngineError(EngineErrorCode.AssetInvalidData, 'Spine JSON is not valid JSON.', {
2186
+ domain: ErrorDomain.Component,
2187
+ recovery: ErrorRecovery.ReleaseResource,
2188
+ context: { url, resourceType: 'skeleton/spine-json' },
2189
+ path: 'spine.json',
2190
+ cause: error,
2191
+ });
2192
+ }
2193
+ }
2194
+ async function fetchText(url, signal) {
2195
+ const response = await fetch(url, signal ? { signal } : undefined);
2196
+ if (!response.ok) {
2197
+ throw new EngineError(EngineErrorCode.AssetLoadFailed, `Failed to load Spine atlas: ${response.status} ${response.statusText}`, {
2198
+ domain: ErrorDomain.Component,
2199
+ recovery: ErrorRecovery.Retry,
2200
+ context: { url, resourceType: 'skeleton/spine-atlas', status: response.status },
2201
+ path: 'spine.atlas',
2202
+ });
2203
+ }
2204
+ return response.text();
2205
+ }
2206
+ function throwIfAborted(signal) {
2207
+ if (!signal?.aborted)
2208
+ return;
2209
+ throw createAbortError('Spine load aborted.', signal.reason);
2210
+ }
2211
+ function computePoses(runtime, component) {
2212
+ const data = runtime.data;
2213
+ const children = runtime.boneChildren;
2214
+ const local = resetSetupPoses(runtime.setupPoses);
2215
+ if (component.previousAnimation
2216
+ && component.mixDuration > 0
2217
+ && component.mixElapsed < component.mixDuration * 1000
2218
+ && data.animations[component.previousAnimation]) {
2219
+ const previous = resetSetupPoses(runtime.previousSetupPoses);
2220
+ applyAnimationAt(runtime, data, component.previousAnimation, component.previousElapsed / 1000, component.loop, previous);
2221
+ applyAnimationAt(runtime, data, component.animation, component.elapsed / 1000, component.loop, local);
2222
+ const alpha = Math.max(0, Math.min(1, component.mixElapsed / (component.mixDuration * 1000)));
2223
+ blendLocalPoses(local, previous, alpha);
2224
+ }
2225
+ else {
2226
+ applyAnimation(runtime, data, component, local);
2227
+ }
2228
+ applySliderBoneAnimations(runtime, component, local, true);
2229
+ updateWorldPoses(data, local);
2230
+ applyConstraints(runtime, component, local, children);
2231
+ if (applySliderBoneAnimations(runtime, component, local, false))
2232
+ updateWorldPoses(data, local);
2233
+ return local;
2234
+ }
2235
+ function buildBoneChildren(data) {
2236
+ const children = new Map();
2237
+ for (const bone of data.bones) {
2238
+ if (!bone.parent)
2239
+ continue;
2240
+ const list = children.get(bone.parent) ?? [];
2241
+ list.push(bone.name);
2242
+ children.set(bone.parent, list);
2243
+ }
2244
+ return children;
2245
+ }
2246
+ function buildSlotIndexByName(data) {
2247
+ const indices = new Map();
2248
+ for (let i = 0; i < data.slots.length; i++) {
2249
+ indices.set(requiredItemAt(data.slots, i, 'Spine slots').name, i);
2250
+ }
2251
+ return indices;
2252
+ }
2253
+ function buildSlotCacheKeys(data) {
2254
+ const keys = new Map();
2255
+ for (const slot of data.slots)
2256
+ keys.set(slot.name, `slot:${slot.name}`);
2257
+ return keys;
2258
+ }
2259
+ function buildSliderConstraintsByOrder(data) {
2260
+ return data.sliders.slice().sort((a, b) => a.order - b.order);
2261
+ }
2262
+ function createSetupPoses(data) {
2263
+ const local = new Map();
2264
+ for (const bone of data.bones) {
2265
+ local.set(bone.name, {
2266
+ data: bone,
2267
+ a: 1,
2268
+ b: 0,
2269
+ c: 0,
2270
+ d: 1,
2271
+ worldX: 0,
2272
+ worldY: 0,
2273
+ rotation: bone.rotation,
2274
+ shearX: bone.shearX,
2275
+ shearY: bone.shearY,
2276
+ x: bone.x,
2277
+ y: bone.y,
2278
+ scaleX: bone.scaleX,
2279
+ scaleY: bone.scaleY,
2280
+ });
2281
+ }
2282
+ return local;
2283
+ }
2284
+ function resetSetupPoses(poses) {
2285
+ for (const pose of poses.values()) {
2286
+ const bone = pose.data;
2287
+ pose.a = 1;
2288
+ pose.b = 0;
2289
+ pose.c = 0;
2290
+ pose.d = 1;
2291
+ pose.worldX = 0;
2292
+ pose.worldY = 0;
2293
+ pose.rotation = bone.rotation;
2294
+ pose.shearX = bone.shearX;
2295
+ pose.shearY = bone.shearY;
2296
+ pose.x = bone.x;
2297
+ pose.y = bone.y;
2298
+ pose.scaleX = bone.scaleX;
2299
+ pose.scaleY = bone.scaleY;
2300
+ }
2301
+ return poses;
2302
+ }
2303
+ function blendLocalPoses(current, previous, alpha) {
2304
+ for (const [name, pose] of current) {
2305
+ const from = previous.get(name);
2306
+ if (!from)
2307
+ continue;
2308
+ pose.x = lerpNumber(from.x, pose.x, alpha);
2309
+ pose.y = lerpNumber(from.y, pose.y, alpha);
2310
+ pose.rotation = from.rotation + normalizeAngle(pose.rotation - from.rotation) * alpha;
2311
+ pose.shearX = lerpNumber(from.shearX, pose.shearX, alpha);
2312
+ pose.shearY = lerpNumber(from.shearY, pose.shearY, alpha);
2313
+ pose.scaleX = lerpNumber(from.scaleX, pose.scaleX, alpha);
2314
+ pose.scaleY = lerpNumber(from.scaleY, pose.scaleY, alpha);
2315
+ }
2316
+ }
2317
+ function blendLocalPosesFromSnapshot(current, previous, alpha) {
2318
+ let offset = 0;
2319
+ for (const pose of current.values()) {
2320
+ if (offset + 6 >= previous.length)
2321
+ return;
2322
+ const fromX = requiredNumberAt(previous.data, offset++, 'previous local pose');
2323
+ const fromY = requiredNumberAt(previous.data, offset++, 'previous local pose');
2324
+ const fromRotation = requiredNumberAt(previous.data, offset++, 'previous local pose');
2325
+ const fromShearX = requiredNumberAt(previous.data, offset++, 'previous local pose');
2326
+ const fromShearY = requiredNumberAt(previous.data, offset++, 'previous local pose');
2327
+ const fromScaleX = requiredNumberAt(previous.data, offset++, 'previous local pose');
2328
+ const fromScaleY = requiredNumberAt(previous.data, offset++, 'previous local pose');
2329
+ pose.x = lerpNumber(fromX, pose.x, alpha);
2330
+ pose.y = lerpNumber(fromY, pose.y, alpha);
2331
+ pose.rotation = fromRotation + normalizeAngle(pose.rotation - fromRotation) * alpha;
2332
+ pose.shearX = lerpNumber(fromShearX, pose.shearX, alpha);
2333
+ pose.shearY = lerpNumber(fromShearY, pose.shearY, alpha);
2334
+ pose.scaleX = lerpNumber(fromScaleX, pose.scaleX, alpha);
2335
+ pose.scaleY = lerpNumber(fromScaleY, pose.scaleY, alpha);
2336
+ }
2337
+ }
2338
+ function lerpNumber(a, b, t) {
2339
+ return a + (b - a) * t;
2340
+ }
2341
+ function getDrawOrderedSlots(runtime, component, time) {
2342
+ const data = runtime.data;
2343
+ const frames = data.animations[component.animation]?.drawOrder;
2344
+ if (!Array.isArray(frames) || frames.length === 0)
2345
+ return data.slots;
2346
+ const frameIndex = findCompiledFrameIndex(frames, time, runtime.timelineSamplerState);
2347
+ const frame = frameIndex >= 0 ? frames[frameIndex] : null;
2348
+ if (!frame?.offsets?.length)
2349
+ return data.slots;
2350
+ const drawOrder = runtime.drawOrderScratch;
2351
+ const unchanged = runtime.unchangedSlotScratch;
2352
+ const ordered = runtime.orderedSlotScratch;
2353
+ drawOrder.length = data.slots.length;
2354
+ unchanged.length = 0;
2355
+ ordered.length = 0;
2356
+ for (let i = 0; i < drawOrder.length; i++)
2357
+ drawOrder[i] = -1;
2358
+ let originalIndex = 0;
2359
+ for (const offset of frame.offsets) {
2360
+ const slotIndex = runtime.slotIndexByName.get(offset.slot) ?? -1;
2361
+ if (slotIndex < 0)
2362
+ continue;
2363
+ while (originalIndex < slotIndex)
2364
+ unchanged.push(originalIndex++);
2365
+ const drawIndex = originalIndex + (offset.offset ?? 0);
2366
+ if (drawIndex >= 0 && drawIndex < drawOrder.length)
2367
+ drawOrder[drawIndex] = originalIndex;
2368
+ originalIndex++;
2369
+ }
2370
+ while (originalIndex < data.slots.length)
2371
+ unchanged.push(originalIndex++);
2372
+ let unchangedIndex = unchanged.length - 1;
2373
+ for (let i = drawOrder.length - 1; i >= 0; i--) {
2374
+ if (drawOrder[i] === -1)
2375
+ drawOrder[i] = unchanged[unchangedIndex--] ?? i;
2376
+ }
2377
+ for (let i = 0; i < drawOrder.length; i++) {
2378
+ const drawIndex = drawOrder[i];
2379
+ const slot = drawIndex === undefined ? undefined : data.slots[drawIndex];
2380
+ if (slot)
2381
+ ordered.push(slot);
2382
+ }
2383
+ return ordered;
2384
+ }
2385
+ function updateWorldPoses(data, poses) {
2386
+ for (const bone of data.bones) {
2387
+ const pose = poses.get(bone.name);
2388
+ const parent = bone.parent ? poses.get(bone.parent) ?? null : null;
2389
+ updateWorldPose(pose, parent);
2390
+ }
2391
+ }
2392
+ function updateWorldPose(pose, parent) {
2393
+ const rx = (pose.rotation + pose.shearX) * Math.PI / 180;
2394
+ const ry = (pose.rotation + 90 + pose.shearY) * Math.PI / 180;
2395
+ const la = Math.cos(rx) * pose.scaleX;
2396
+ const lb = Math.cos(ry) * pose.scaleY;
2397
+ const lc = Math.sin(rx) * pose.scaleX;
2398
+ const ld = Math.sin(ry) * pose.scaleY;
2399
+ if (!parent) {
2400
+ pose.worldX = pose.x;
2401
+ pose.worldY = pose.y;
2402
+ pose.a = la;
2403
+ pose.b = lb;
2404
+ pose.c = lc;
2405
+ pose.d = ld;
2406
+ return;
2407
+ }
2408
+ pose.worldX = parent.a * pose.x + parent.b * pose.y + parent.worldX;
2409
+ pose.worldY = parent.c * pose.x + parent.d * pose.y + parent.worldY;
2410
+ switch (pose.data.inherit) {
2411
+ case 'onlyTranslation':
2412
+ pose.a = la;
2413
+ pose.b = lb;
2414
+ pose.c = lc;
2415
+ pose.d = ld;
2416
+ return;
2417
+ case 'noRotationOrReflection':
2418
+ updateNoRotationOrReflectionWorldPose(pose, parent);
2419
+ return;
2420
+ case 'noScale':
2421
+ case 'noScaleOrReflection':
2422
+ updateNoScaleWorldPose(pose, parent);
2423
+ return;
2424
+ default:
2425
+ pose.a = parent.a * la + parent.b * lc;
2426
+ pose.b = parent.a * lb + parent.b * ld;
2427
+ pose.c = parent.c * la + parent.d * lc;
2428
+ pose.d = parent.c * lb + parent.d * ld;
2429
+ }
2430
+ }
2431
+ function updateNoRotationOrReflectionWorldPose(pose, parent) {
2432
+ let pa = parent.a;
2433
+ let pb = parent.b;
2434
+ let pc = parent.c;
2435
+ let pd = parent.d;
2436
+ let s = pa * pa + pc * pc;
2437
+ let rotation = 0;
2438
+ if (s > 0.000001) {
2439
+ s = Math.abs(pa * pd - pb * pc) / s;
2440
+ pb = pc * s;
2441
+ pd = pa * s;
2442
+ rotation = pose.rotation - Math.atan2(pc, pa) * 180 / Math.PI;
2443
+ }
2444
+ else {
2445
+ pa = 0;
2446
+ pc = 0;
2447
+ rotation = pose.rotation - 90 + Math.atan2(pd, pb) * 180 / Math.PI;
2448
+ }
2449
+ const rx = (rotation + pose.shearX) * Math.PI / 180;
2450
+ const ry = (rotation + 90 + pose.shearY) * Math.PI / 180;
2451
+ const la = Math.cos(rx) * pose.scaleX;
2452
+ const lb = Math.cos(ry) * pose.scaleY;
2453
+ const lc = Math.sin(rx) * pose.scaleX;
2454
+ const ld = Math.sin(ry) * pose.scaleY;
2455
+ pose.a = pa * la - pb * lc;
2456
+ pose.b = pa * lb - pb * ld;
2457
+ pose.c = pc * la + pd * lc;
2458
+ pose.d = pc * lb + pd * ld;
2459
+ }
2460
+ function updateNoScaleWorldPose(pose, parent) {
2461
+ const rotation = pose.rotation * Math.PI / 180;
2462
+ const cos = Math.cos(rotation);
2463
+ const sin = Math.sin(rotation);
2464
+ let za = parent.a * cos + parent.b * sin;
2465
+ let zc = parent.c * cos + parent.d * sin;
2466
+ const length = Math.sqrt(za * za + zc * zc);
2467
+ if (length > 0.000001) {
2468
+ za /= length;
2469
+ zc /= length;
2470
+ }
2471
+ let zb = -zc;
2472
+ let zd = za;
2473
+ const determinant = parent.a * parent.d - parent.b * parent.c;
2474
+ if (pose.data.inherit === 'noScale' && determinant < 0) {
2475
+ zb = -zb;
2476
+ zd = -zd;
2477
+ }
2478
+ const rx = pose.shearX * Math.PI / 180;
2479
+ const ry = (90 + pose.shearY) * Math.PI / 180;
2480
+ const la = Math.cos(rx) * pose.scaleX;
2481
+ const lb = Math.cos(ry) * pose.scaleY;
2482
+ const lc = Math.sin(rx) * pose.scaleX;
2483
+ const ld = Math.sin(ry) * pose.scaleY;
2484
+ pose.a = za * la + zb * lc;
2485
+ pose.b = za * lb + zb * ld;
2486
+ pose.c = zc * la + zd * lc;
2487
+ pose.d = zc * lb + zd * ld;
2488
+ }
2489
+ function applyAnimation(runtime, data, component, poses) {
2490
+ applyAnimationAt(runtime, data, component.animation, getCurrentAnimationTime(data, component), component.loop, poses);
2491
+ }
2492
+ function applyAnimationAt(runtime, data, animationName, rawTime, loop, poses) {
2493
+ const animation = data.animations[animationName];
2494
+ if (!animation?.bones)
2495
+ return;
2496
+ const duration = getCompiledAnimationDuration(animation);
2497
+ const time = loop && duration > 0 ? rawTime % duration : Math.min(rawTime, duration);
2498
+ for (const entry of getAnimationBoneTimelines(animation)) {
2499
+ const pose = poses.get(entry.boneName);
2500
+ if (!pose)
2501
+ continue;
2502
+ const timelines = entry.timelines;
2503
+ if (timelines.rotate)
2504
+ pose.rotation = pose.data.rotation + sampleCompiledTimeline(timelines.rotate, time, 'angle', 0, duration, loop, runtime.timelineSamplerState);
2505
+ if (timelines.translate) {
2506
+ pose.x = pose.data.x + sampleCompiledTimeline(timelines.translate, time, 'x', 0, duration, loop, runtime.timelineSamplerState);
2507
+ pose.y = pose.data.y + sampleCompiledTimeline(timelines.translate, time, 'y', 0, duration, loop, runtime.timelineSamplerState);
2508
+ }
2509
+ if (timelines.scale) {
2510
+ pose.scaleX = pose.data.scaleX * sampleCompiledTimeline(timelines.scale, time, 'x', 1, duration, loop, runtime.timelineSamplerState);
2511
+ pose.scaleY = pose.data.scaleY * sampleCompiledTimeline(timelines.scale, time, 'y', 1, duration, loop, runtime.timelineSamplerState);
2512
+ }
2513
+ if (timelines.shear) {
2514
+ pose.shearX = pose.data.shearX + sampleCompiledTimeline(timelines.shear, time, 'x', 0, duration, loop, runtime.timelineSamplerState);
2515
+ pose.shearY = pose.data.shearY + sampleCompiledTimeline(timelines.shear, time, 'y', 0, duration, loop, runtime.timelineSamplerState);
2516
+ }
2517
+ }
2518
+ }
2519
+ function computeSliderAnimationStates(runtime, component, poses, localOnly) {
2520
+ const data = runtime.data;
2521
+ const states = runtime.sliderStateScratch;
2522
+ states.length = 0;
2523
+ if (!runtime.sliderConstraintsByOrder.length)
2524
+ return states;
2525
+ const currentAnimation = data.animations[component.animation];
2526
+ const currentDuration = getCompiledAnimationDuration(currentAnimation);
2527
+ const currentTime = getCurrentAnimationTime(data, component);
2528
+ for (const slider of runtime.sliderConstraintsByOrder) {
2529
+ if (localOnly !== undefined && slider.local !== localOnly)
2530
+ continue;
2531
+ if (slider.animation === component.animation)
2532
+ continue;
2533
+ if (!slider.animation || slider.mix <= 0 || !data.animations[slider.animation])
2534
+ continue;
2535
+ if (!isConstraintActiveForSkin(data, component.skin, 'slider', slider.name, slider.skin))
2536
+ continue;
2537
+ const mix = sampleCompiledTimeline(currentAnimation?.slider?.[slider.name]?.mix ?? [], currentTime, 'mix', slider.mix, currentDuration, component.loop, runtime.timelineSamplerState);
2538
+ if (mix <= 0)
2539
+ continue;
2540
+ const duration = getCompiledAnimationDuration(data.animations[slider.animation]);
2541
+ let time = slider.offset;
2542
+ if (slider.bone) {
2543
+ const bone = poses.get(slider.bone);
2544
+ if (!bone)
2545
+ continue;
2546
+ time += getSliderBoneProperty(bone, slider.property, slider.local) * slider.scale;
2547
+ }
2548
+ if (slider.loop && duration > 0) {
2549
+ time %= duration;
2550
+ if (time < 0)
2551
+ time += duration;
2552
+ }
2553
+ else {
2554
+ time = Math.max(0, Math.min(time, duration));
2555
+ }
2556
+ const stateIndex = states.length;
2557
+ let state = runtime.sliderStatePool[stateIndex];
2558
+ if (!state) {
2559
+ state = { animation: slider.animation, time, loop: slider.loop, mix };
2560
+ runtime.sliderStatePool.push(state);
2561
+ }
2562
+ else {
2563
+ state.animation = slider.animation;
2564
+ state.time = time;
2565
+ state.loop = slider.loop;
2566
+ state.mix = mix;
2567
+ }
2568
+ states.push(state);
2569
+ }
2570
+ return states;
2571
+ }
2572
+ function applySliderBoneAnimations(runtime, component, poses, localOnly) {
2573
+ const data = runtime.data;
2574
+ let applied = false;
2575
+ let before = null;
2576
+ for (const slider of computeSliderAnimationStates(runtime, component, poses, localOnly)) {
2577
+ if (!data.animations[slider.animation]?.bones)
2578
+ continue;
2579
+ if (slider.mix < 1) {
2580
+ before ??= getSliderPoseSnapshot(component);
2581
+ before.capture(poses);
2582
+ }
2583
+ applyAnimationAt(runtime, data, slider.animation, slider.time, slider.loop, poses);
2584
+ if (slider.mix < 1 && before)
2585
+ blendLocalPosesFromSnapshot(poses, before, slider.mix);
2586
+ applied = true;
2587
+ }
2588
+ return applied;
2589
+ }
2590
+ function getSliderBoneProperty(bone, property, local) {
2591
+ if (local) {
2592
+ if (property === 'x')
2593
+ return bone.x;
2594
+ if (property === 'y')
2595
+ return bone.y;
2596
+ if (property === 'scaleX')
2597
+ return bone.scaleX;
2598
+ if (property === 'scaleY')
2599
+ return bone.scaleY;
2600
+ if (property === 'shearX')
2601
+ return bone.shearX;
2602
+ if (property === 'shearY')
2603
+ return bone.shearY;
2604
+ return bone.rotation;
2605
+ }
2606
+ if (property === 'x')
2607
+ return bone.worldX;
2608
+ if (property === 'y')
2609
+ return bone.worldY;
2610
+ if (property === 'scaleX')
2611
+ return Math.hypot(bone.a, bone.c);
2612
+ if (property === 'scaleY')
2613
+ return Math.hypot(bone.b, bone.d);
2614
+ if (property === 'shearY')
2615
+ return Math.atan2(bone.d, bone.b) * 180 / Math.PI - Math.atan2(bone.c, bone.a) * 180 / Math.PI - 90;
2616
+ return Math.atan2(bone.c, bone.a) * 180 / Math.PI;
2617
+ }
2618
+ function applyConstraints(runtime, component, poses, children) {
2619
+ const data = runtime.data;
2620
+ const animation = data.animations[component.animation];
2621
+ const duration = getCompiledAnimationDuration(animation);
2622
+ const time = getCurrentAnimationTime(data, component);
2623
+ const constraints = getSortedConstraintEntries(runtime, component.skin);
2624
+ for (const constraint of constraints) {
2625
+ if (constraint.type === 'ik') {
2626
+ applyIkConstraint(runtime, animation, constraint.constraint, time, duration, component.loop, poses, children);
2627
+ }
2628
+ else if (constraint.type === 'path') {
2629
+ applyPathConstraint(runtime, component, constraint.constraint, time, duration, component.loop, poses, children, PATH_CONSTRAINT_DEPS);
2630
+ }
2631
+ else {
2632
+ applyTransformConstraint(runtime, animation, constraint.constraint, time, duration, component.loop, poses, children);
2633
+ }
2634
+ }
2635
+ }
2636
+ function getSortedConstraintEntries(runtime, skinName) {
2637
+ const cached = runtime.sortedConstraintsBySkin.get(skinName);
2638
+ if (cached)
2639
+ return cached;
2640
+ const data = runtime.data;
2641
+ const constraints = [];
2642
+ for (const constraint of data.ik) {
2643
+ if (isConstraintActiveForSkin(data, skinName, 'ik', constraint.name, constraint.skin)) {
2644
+ constraints.push({ type: 'ik', order: constraint.order, constraint });
2645
+ }
2646
+ }
2647
+ for (const constraint of data.path) {
2648
+ if (isConstraintActiveForSkin(data, skinName, 'path', constraint.name, constraint.skin)) {
2649
+ constraints.push({ type: 'path', order: constraint.order, constraint });
2650
+ }
2651
+ }
2652
+ for (const constraint of data.transform) {
2653
+ if (isConstraintActiveForSkin(data, skinName, 'transform', constraint.name, constraint.skin)) {
2654
+ constraints.push({ type: 'transform', order: constraint.order, constraint });
2655
+ }
2656
+ }
2657
+ constraints.sort((a, b) => a.order - b.order);
2658
+ runtime.sortedConstraintsBySkin.set(skinName, constraints);
2659
+ return constraints;
2660
+ }
2661
+ function isConstraintActiveForSkin(data, skinName, type, constraintName, skinRequired) {
2662
+ if (!skinRequired)
2663
+ return true;
2664
+ return data.skinConstraints[skinName]?.[type]?.includes(constraintName) ?? false;
2665
+ }
2666
+ function applyIkConstraint(runtime, animation, constraint, time, duration, loop, poses, children) {
2667
+ const timeline = animation?.ik?.[constraint.name];
2668
+ const state = sampleIkConstraint(constraint, timeline, time, duration, loop, runtime.ikStateScratch, runtime.timelineSamplerState);
2669
+ if (state.mix <= 0)
2670
+ return;
2671
+ if (constraint.bones.length === 1) {
2672
+ const rootBone = constraint.bones[0];
2673
+ if (rootBone === undefined)
2674
+ return;
2675
+ applyOneBoneIk(constraint, state, poses);
2676
+ updateBoneTree(rootBone, poses, children);
2677
+ }
2678
+ else if (constraint.bones.length === 2) {
2679
+ const rootBone = constraint.bones[0];
2680
+ if (rootBone === undefined)
2681
+ return;
2682
+ applyTwoBoneIk(constraint, state, poses);
2683
+ updateBoneTree(rootBone, poses, children);
2684
+ }
2685
+ }
2686
+ function sampleIkConstraint(constraint, frames, time, duration, loop, out, samplerState) {
2687
+ out.mix = sampleCompiledTimeline(frames ?? [], time, 'mix', constraint.mix, duration, loop, samplerState);
2688
+ out.softness = sampleCompiledTimeline(frames ?? [], time, 'softness', constraint.softness, duration, loop, samplerState);
2689
+ let bendPositive = constraint.bendPositive;
2690
+ if (!Array.isArray(frames) || frames.length === 0) {
2691
+ out.bendPositive = bendPositive;
2692
+ return out;
2693
+ }
2694
+ const frameIndex = findCompiledFrameIndex(frames, time, samplerState);
2695
+ const frame = frameIndex >= 0 ? frames[frameIndex] : null;
2696
+ if (frame && typeof frame === 'object') {
2697
+ const value = frame.bendPositive;
2698
+ if (typeof value === 'boolean')
2699
+ bendPositive = value;
2700
+ }
2701
+ out.bendPositive = bendPositive;
2702
+ return out;
2703
+ }
2704
+ function applyTransformConstraint(runtime, animation, constraint, time, duration, loop, poses, children) {
2705
+ const state = sampleTransformConstraint(constraint, animation?.transform?.[constraint.name], time, duration, loop, runtime.transformStateScratch, runtime.timelineSamplerState);
2706
+ if (state.mixRotate === 0
2707
+ && state.mixX === 0
2708
+ && state.mixY === 0
2709
+ && state.mixScaleX === 0
2710
+ && state.mixScaleY === 0
2711
+ && state.mixShearY === 0)
2712
+ return;
2713
+ const source = poses.get(constraint.target);
2714
+ if (!source)
2715
+ return;
2716
+ let constrainedBones = runtime.transformConstraintBoneSets.get(constraint);
2717
+ if (!constrainedBones) {
2718
+ constrainedBones = new Set(constraint.bones);
2719
+ runtime.transformConstraintBoneSets.set(constraint, constrainedBones);
2720
+ }
2721
+ const worldSpaceBones = runtime.transformWorldSpaceBonesScratch;
2722
+ worldSpaceBones.length = 0;
2723
+ for (const boneName of constraint.bones) {
2724
+ const bone = poses.get(boneName);
2725
+ if (!bone)
2726
+ continue;
2727
+ if (constraint.localTarget || constraint.localSource) {
2728
+ applyLocalTransformConstraint(constraint, state, source, bone);
2729
+ updateBoneTree(boneName, poses, children);
2730
+ }
2731
+ else {
2732
+ applyWorldTransformConstraint(constraint, state, source, bone);
2733
+ worldSpaceBones.push(boneName);
2734
+ }
2735
+ }
2736
+ for (const boneName of worldSpaceBones)
2737
+ updateDescendantBoneTrees(boneName, poses, children, constrainedBones);
2738
+ }
2739
+ function sampleTransformConstraint(constraint, frames, time, duration, loop, out, samplerState) {
2740
+ out.mixRotate = sampleCompiledTimeline(frames ?? [], time, 'mixRotate', constraint.mixRotate, duration, loop, samplerState);
2741
+ out.mixX = sampleCompiledTimeline(frames ?? [], time, 'mixX', constraint.mixX, duration, loop, samplerState);
2742
+ out.mixY = sampleCompiledTimeline(frames ?? [], time, 'mixY', constraint.mixY, duration, loop, samplerState);
2743
+ out.mixScaleX = sampleCompiledTimeline(frames ?? [], time, 'mixScaleX', constraint.mixScaleX, duration, loop, samplerState);
2744
+ out.mixScaleY = sampleCompiledTimeline(frames ?? [], time, 'mixScaleY', constraint.mixScaleY, duration, loop, samplerState);
2745
+ out.mixShearY = sampleCompiledTimeline(frames ?? [], time, 'mixShearY', constraint.mixShearY, duration, loop, samplerState);
2746
+ return out;
2747
+ }
2748
+ function applyWorldTransformConstraint(constraint, state, source, bone) {
2749
+ if (state.mixRotate !== 0) {
2750
+ let value = Math.atan2(source.c, source.a) * 180 / Math.PI
2751
+ + ((source.a * source.d - source.b * source.c) > 0 ? constraint.rotation : -constraint.rotation);
2752
+ if (value < 0)
2753
+ value += 360;
2754
+ if (!constraint.additive)
2755
+ value -= Math.atan2(bone.c, bone.a) * 180 / Math.PI;
2756
+ value = normalizeAngle(value) * Math.PI / 180 * state.mixRotate;
2757
+ const cos = Math.cos(value);
2758
+ const sin = Math.sin(value);
2759
+ const a = bone.a;
2760
+ const b = bone.b;
2761
+ const c = bone.c;
2762
+ const d = bone.d;
2763
+ bone.a = cos * a - sin * c;
2764
+ bone.b = cos * b - sin * d;
2765
+ bone.c = sin * a + cos * c;
2766
+ bone.d = sin * b + cos * d;
2767
+ }
2768
+ if (state.mixX !== 0) {
2769
+ let value = constraint.x * source.a + constraint.y * source.b + source.worldX;
2770
+ if (!constraint.additive)
2771
+ value -= bone.worldX;
2772
+ bone.worldX += value * state.mixX;
2773
+ }
2774
+ if (state.mixY !== 0) {
2775
+ let value = constraint.x * source.c + constraint.y * source.d + source.worldY;
2776
+ if (!constraint.additive)
2777
+ value -= bone.worldY;
2778
+ bone.worldY += value * state.mixY;
2779
+ }
2780
+ if (state.mixScaleX !== 0) {
2781
+ const sourceScale = Math.hypot(source.a, source.c) + constraint.scaleX;
2782
+ const boneScale = Math.hypot(bone.a, bone.c);
2783
+ if (constraint.additive) {
2784
+ const scale = 1 + (sourceScale - 1) * state.mixScaleX;
2785
+ bone.a *= scale;
2786
+ bone.c *= scale;
2787
+ }
2788
+ else if (boneScale !== 0) {
2789
+ const scale = 1 + (sourceScale - boneScale) * state.mixScaleX / boneScale;
2790
+ bone.a *= scale;
2791
+ bone.c *= scale;
2792
+ }
2793
+ }
2794
+ if (state.mixScaleY !== 0) {
2795
+ const sourceScale = Math.hypot(source.b, source.d) + constraint.scaleY;
2796
+ const boneScale = Math.hypot(bone.b, bone.d);
2797
+ if (constraint.additive) {
2798
+ const scale = 1 + (sourceScale - 1) * state.mixScaleY;
2799
+ bone.b *= scale;
2800
+ bone.d *= scale;
2801
+ }
2802
+ else if (boneScale !== 0) {
2803
+ const scale = 1 + (sourceScale - boneScale) * state.mixScaleY / boneScale;
2804
+ bone.b *= scale;
2805
+ bone.d *= scale;
2806
+ }
2807
+ }
2808
+ if (state.mixShearY !== 0) {
2809
+ const by = Math.atan2(bone.d, bone.b);
2810
+ let value = (Math.atan2(source.d, source.b) - Math.atan2(source.c, source.a)) * 180 / Math.PI - 90 + constraint.shearY;
2811
+ value = (value + 90) * Math.PI / 180;
2812
+ if (constraint.additive)
2813
+ value -= Math.PI / 2;
2814
+ else
2815
+ value = by + normalizeRadians(value - by + Math.atan2(bone.c, bone.a)) * state.mixShearY;
2816
+ const scale = Math.hypot(bone.b, bone.d);
2817
+ bone.b = Math.cos(value) * scale;
2818
+ bone.d = Math.sin(value) * scale;
2819
+ }
2820
+ }
2821
+ function applyLocalTransformConstraint(constraint, state, source, bone) {
2822
+ if (state.mixRotate !== 0) {
2823
+ const value = source.rotation + constraint.rotation;
2824
+ bone.rotation += (constraint.additive ? value : value - bone.rotation) * state.mixRotate;
2825
+ }
2826
+ if (state.mixX !== 0) {
2827
+ const value = source.x + constraint.x;
2828
+ bone.x += (constraint.additive ? value : value - bone.x) * state.mixX;
2829
+ }
2830
+ if (state.mixY !== 0) {
2831
+ const value = source.y + constraint.y;
2832
+ bone.y += (constraint.additive ? value : value - bone.y) * state.mixY;
2833
+ }
2834
+ if (state.mixScaleX !== 0) {
2835
+ const value = source.scaleX + constraint.scaleX;
2836
+ if (constraint.additive)
2837
+ bone.scaleX *= 1 + (value - 1) * state.mixScaleX;
2838
+ else
2839
+ bone.scaleX += (value - bone.scaleX) * state.mixScaleX;
2840
+ }
2841
+ if (state.mixScaleY !== 0) {
2842
+ const value = source.scaleY + constraint.scaleY;
2843
+ if (constraint.additive)
2844
+ bone.scaleY *= 1 + (value - 1) * state.mixScaleY;
2845
+ else
2846
+ bone.scaleY += (value - bone.scaleY) * state.mixScaleY;
2847
+ }
2848
+ }
2849
+ function applyOneBoneIk(constraint, state, poses) {
2850
+ const boneName = constraint.bones[0];
2851
+ if (boneName === undefined)
2852
+ return;
2853
+ const bone = poses.get(boneName);
2854
+ const target = poses.get(constraint.target);
2855
+ if (!bone || !target)
2856
+ return;
2857
+ const parent = bone.data.parent ? poses.get(bone.data.parent) : null;
2858
+ const [targetX, targetY] = parent
2859
+ ? worldToLocal(parent, target.worldX, target.worldY)
2860
+ : [target.worldX, target.worldY];
2861
+ const rotation = Math.atan2(targetY - bone.y, targetX - bone.x) * 180 / Math.PI;
2862
+ bone.rotation += normalizeAngle(rotation - bone.rotation) * state.mix;
2863
+ }
2864
+ function applyTwoBoneIk(constraint, state, poses) {
2865
+ const parentName = constraint.bones[0];
2866
+ const childName = constraint.bones[1];
2867
+ if (parentName === undefined || childName === undefined)
2868
+ return;
2869
+ const parent = poses.get(parentName);
2870
+ const child = poses.get(childName);
2871
+ const target = poses.get(constraint.target);
2872
+ if (!parent || !child || !target)
2873
+ return;
2874
+ if (parent.data.inherit && parent.data.inherit !== 'normal')
2875
+ return;
2876
+ if (child.data.inherit && child.data.inherit !== 'normal')
2877
+ return;
2878
+ const grandParent = parent.data.parent ? poses.get(parent.data.parent) : null;
2879
+ if (!grandParent)
2880
+ return;
2881
+ let parentScaleX = parent.scaleX;
2882
+ let parentScaleY = parent.scaleY;
2883
+ let childScaleX = child.scaleX;
2884
+ let parentOffset = 0;
2885
+ let childOffset = 0;
2886
+ let sign = 1;
2887
+ if (parentScaleX < 0) {
2888
+ parentScaleX = -parentScaleX;
2889
+ parentOffset = 180;
2890
+ sign = -1;
2891
+ }
2892
+ if (parentScaleY < 0) {
2893
+ parentScaleY = -parentScaleY;
2894
+ sign = -sign;
2895
+ }
2896
+ if (childScaleX < 0) {
2897
+ childScaleX = -childScaleX;
2898
+ childOffset = 180;
2899
+ }
2900
+ let childWorldX = 0;
2901
+ let childWorldY = 0;
2902
+ const uniformScale = Math.abs(parentScaleX - parentScaleY) <= 0.000001;
2903
+ if (!uniformScale) {
2904
+ child.y = 0;
2905
+ childWorldX = parent.a * child.x + parent.worldX;
2906
+ childWorldY = parent.c * child.x + parent.worldY;
2907
+ }
2908
+ else {
2909
+ childWorldX = parent.a * child.x + parent.b * child.y + parent.worldX;
2910
+ childWorldY = parent.c * child.x + parent.d * child.y + parent.worldY;
2911
+ }
2912
+ let a = grandParent.a;
2913
+ let b = grandParent.b;
2914
+ let c = grandParent.c;
2915
+ let d = grandParent.d;
2916
+ let det = a * d - b * c;
2917
+ det = Math.abs(det) <= 0.000001 ? 0 : 1 / det;
2918
+ let x = childWorldX - grandParent.worldX;
2919
+ let y = childWorldY - grandParent.worldY;
2920
+ const dx = (x * d - y * b) * det - parent.x;
2921
+ const dy = (y * a - x * c) * det - parent.y;
2922
+ let length1 = Math.hypot(dx, dy);
2923
+ let length2 = child.data.length * childScaleX;
2924
+ if (length1 < 0.000001) {
2925
+ applyOneBoneIk({ ...constraint, bones: [parentName] }, state, poses);
2926
+ child.rotation = 0;
2927
+ return;
2928
+ }
2929
+ x = target.worldX - grandParent.worldX;
2930
+ y = target.worldY - grandParent.worldY;
2931
+ let targetX = (x * d - y * b) * det - parent.x;
2932
+ let targetY = (y * a - x * c) * det - parent.y;
2933
+ let distanceSq = targetX * targetX + targetY * targetY;
2934
+ if (state.softness !== 0) {
2935
+ const softness = state.softness * parentScaleX * (childScaleX + 1) * 0.5;
2936
+ const targetDistance = Math.sqrt(distanceSq);
2937
+ const softDistance = targetDistance - length1 - length2 * parentScaleX + softness;
2938
+ if (softDistance > 0 && targetDistance > 0.000001) {
2939
+ let percent = Math.min(1, softDistance / (softness * 2)) - 1;
2940
+ percent = (softDistance - softness * (1 - percent * percent)) / targetDistance;
2941
+ targetX -= percent * targetX;
2942
+ targetY -= percent * targetY;
2943
+ distanceSq = targetX * targetX + targetY * targetY;
2944
+ }
2945
+ }
2946
+ const bend = state.bendPositive ? 1 : -1;
2947
+ let angle1 = 0;
2948
+ let angle2 = 0;
2949
+ if (uniformScale) {
2950
+ length2 *= parentScaleX;
2951
+ let cos = (distanceSq - length1 * length1 - length2 * length2) / (2 * length1 * length2);
2952
+ if (cos < -1) {
2953
+ cos = -1;
2954
+ angle2 = Math.PI * bend;
2955
+ }
2956
+ else if (cos > 1) {
2957
+ cos = 1;
2958
+ angle2 = 0;
2959
+ }
2960
+ else {
2961
+ angle2 = Math.acos(cos) * bend;
2962
+ }
2963
+ a = length1 + length2 * cos;
2964
+ b = length2 * Math.sin(angle2);
2965
+ angle1 = Math.atan2(targetY * a - targetX * b, targetX * a + targetY * b);
2966
+ }
2967
+ else {
2968
+ a = parentScaleX * length2;
2969
+ b = parentScaleY * length2;
2970
+ const aa = a * a;
2971
+ const bb = b * b;
2972
+ const targetAngle = Math.atan2(targetY, targetX);
2973
+ c = bb * length1 * length1 + aa * distanceSq - aa * bb;
2974
+ const c1 = -2 * bb * length1;
2975
+ const c2 = bb - aa;
2976
+ d = c1 * c1 - 4 * c2 * c;
2977
+ if (d >= 0) {
2978
+ let q = Math.sqrt(d);
2979
+ if (c1 < 0)
2980
+ q = -q;
2981
+ q = -(c1 + q) * 0.5;
2982
+ let r0 = q / c2;
2983
+ const r1 = c / q;
2984
+ const r = Math.abs(r0) < Math.abs(r1) ? r0 : r1;
2985
+ r0 = distanceSq - r * r;
2986
+ if (r0 >= 0) {
2987
+ y = Math.sqrt(r0) * bend;
2988
+ angle1 = targetAngle - Math.atan2(y, r);
2989
+ angle2 = Math.atan2(y / parentScaleY, (r - length1) / parentScaleX);
2990
+ }
2991
+ }
2992
+ if (!Number.isFinite(angle1) || !Number.isFinite(angle2)) {
2993
+ const minX = length1 - a;
2994
+ const maxX = length1 + a;
2995
+ if (distanceSq <= (minX * minX + maxX * maxX) * 0.5) {
2996
+ angle1 = targetAngle;
2997
+ angle2 = Math.PI * bend;
2998
+ }
2999
+ else {
3000
+ angle1 = targetAngle;
3001
+ angle2 = 0;
3002
+ }
3003
+ }
3004
+ }
3005
+ const childOffsetAngle = Math.atan2(child.y, child.x) * sign;
3006
+ let parentDelta = (angle1 - childOffsetAngle) * 180 / Math.PI + parentOffset - parent.rotation;
3007
+ parent.rotation += normalizeAngle(parentDelta) * state.mix;
3008
+ let childDelta = ((angle2 + childOffsetAngle) * 180 / Math.PI) * sign + childOffset - child.rotation;
3009
+ child.rotation += normalizeAngle(childDelta) * state.mix;
3010
+ }
3011
+ function updateBoneTree(name, poses, children) {
3012
+ const pose = poses.get(name);
3013
+ if (!pose)
3014
+ return;
3015
+ const parent = pose.data.parent ? poses.get(pose.data.parent) ?? null : null;
3016
+ updateWorldPose(pose, parent);
3017
+ for (const childName of children.get(name) ?? [])
3018
+ updateBoneTree(childName, poses, children);
3019
+ }
3020
+ function updateDescendantBoneTrees(name, poses, children, blockedRoots = new Set()) {
3021
+ for (const childName of children.get(name) ?? []) {
3022
+ if (blockedRoots.has(childName))
3023
+ continue;
3024
+ updateBoneTree(childName, poses, children);
3025
+ }
3026
+ }
3027
+ function worldToLocal(bone, worldX, worldY) {
3028
+ const dx = worldX - bone.worldX;
3029
+ const dy = worldY - bone.worldY;
3030
+ const det = bone.a * bone.d - bone.b * bone.c;
3031
+ if (Math.abs(det) < 0.000001)
3032
+ return [dx, dy];
3033
+ return [
3034
+ (dx * bone.d - dy * bone.b) / det,
3035
+ (dy * bone.a - dx * bone.c) / det,
3036
+ ];
3037
+ }
3038
+ function normalizeAngle(value) {
3039
+ return ((value % 360) + 540) % 360 - 180;
3040
+ }
3041
+ function normalizeRadians(value) {
3042
+ let angle = value % TWO_PI;
3043
+ if (angle > Math.PI)
3044
+ angle -= TWO_PI;
3045
+ else if (angle < -Math.PI)
3046
+ angle += TWO_PI;
3047
+ return angle;
3048
+ }
3049
+ function getCurrentAnimationTime(data, component) {
3050
+ const animation = data.animations[component.animation];
3051
+ const duration = getCompiledAnimationDuration(animation);
3052
+ return component.loop && duration > 0 ? (component.elapsed / 1000) % duration : Math.min(component.elapsed / 1000, duration);
3053
+ }
3054
+
3055
+ export { Spine2DComponent as S, createSpineTimelineSamplerState as a, sampleCompiledTimeline as b, compileSpineTimelines as c, advanceSpineRuntime as d, sampleSpinePosesForBenchmark as e, findCompiledFrameIndex as f, buildSpineVertices as g, createSpineRuntimeForBenchmark as h, loadSpineRuntime as l, sampleCompiledColor as s };
3056
+ //# sourceMappingURL=Spine2DRuntime-D9M9LAQR.js.map