@haiyue/extensions 0.1.0 → 0.2.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 (269) hide show
  1. package/README.md +25 -0
  2. package/dist/animation/Animation2DComponent.d.ts +18 -0
  3. package/dist/animation/Animation2DExtensionRegistry.d.ts +7 -0
  4. package/dist/animation/Animation2DRenderSystem.d.ts +6 -0
  5. package/dist/animation/Animation2DRuntime.d.ts +6 -2
  6. package/dist/animation/AnimationTextRasterizer.d.ts +4 -0
  7. package/dist/animation/AnimationVisual2D.d.ts +58 -2
  8. package/dist/animation/interaction/InteractionRuntime.d.ts +83 -0
  9. package/dist/animation/interaction/runtime-types.d.ts +257 -0
  10. package/dist/animation.d.ts +3 -1
  11. package/dist/animation.js +551 -59
  12. package/dist/animation3d.js +1 -1
  13. package/dist/canvas-text.js +1 -1
  14. package/dist/chunks/2d-ui-animation-2d.generated-FpziAeGJ.js +4 -0
  15. package/dist/chunks/2d-ui-indexed-sprite.generated-DmKR0CC3.js +4 -0
  16. package/dist/chunks/Animation2DStateMachine-BHcA6Cu3.js +18212 -0
  17. package/dist/chunks/{AnimationStateMachineController-BIcAh-sT.js → AnimationStateMachineController-4sKaqh9E.js} +2 -2
  18. package/dist/chunks/AnimationVisual2D-8y3qp0_U.js +230 -0
  19. package/dist/chunks/{RenderSystem2DBase-DDT61dAu.js → RenderSystem2DBase-CF2s00vg.js} +2 -2
  20. package/dist/controls/VirtualJoystickControls.d.ts +46 -0
  21. package/dist/controls/contracts.d.ts +91 -0
  22. package/dist/controls.d.ts +2 -0
  23. package/dist/controls.js +358 -0
  24. package/dist/deformable-animation/runtime/DeformableMesh2DRuntimeExtension.d.ts +11 -0
  25. package/dist/deformable-animation/runtime/DeformableMesh2DSampler.d.ts +11 -0
  26. package/dist/deformable-animation.d.ts +5 -0
  27. package/dist/deformable-animation.js +316 -0
  28. package/dist/experimental-indexed-sprite.d.ts +5 -0
  29. package/dist/experimental-indexed-sprite.js +482 -0
  30. package/dist/gltf-worker-runtime.js +9 -9
  31. package/dist/hya-state-machine.js +3 -2
  32. package/dist/index.js +1 -1
  33. package/dist/indexed-sprite/AtlasLayout.d.ts +2 -0
  34. package/dist/indexed-sprite/IndexedSpriteRenderer.d.ts +23 -0
  35. package/dist/indexed-sprite/contracts.d.ts +89 -0
  36. package/dist/internal/2d-ui-shader-artifact.js +232 -23
  37. package/dist/ray-tracing/acceleration/bvh.d.ts +37 -0
  38. package/dist/ray-tracing/acceleration/index.d.ts +6 -0
  39. package/dist/ray-tracing/acceleration/pack.d.ts +152 -0
  40. package/dist/ray-tracing/acceleration/query.d.ts +4 -0
  41. package/dist/ray-tracing/acceleration/runtime.d.ts +30 -0
  42. package/dist/ray-tracing/acceleration/tlas.d.ts +4 -0
  43. package/dist/ray-tracing/acceleration/types.d.ts +165 -0
  44. package/dist/ray-tracing/denoise/index.d.ts +3 -0
  45. package/dist/ray-tracing/denoise/layout.d.ts +9 -0
  46. package/dist/ray-tracing/denoise/runtime.d.ts +23 -0
  47. package/dist/ray-tracing/denoise/types.d.ts +28 -0
  48. package/dist/ray-tracing/hybrid/contract.d.ts +2 -0
  49. package/dist/ray-tracing/hybrid/index.d.ts +5 -0
  50. package/dist/ray-tracing/hybrid/layout.d.ts +17 -0
  51. package/dist/ray-tracing/hybrid/plan.d.ts +2 -0
  52. package/dist/ray-tracing/hybrid/runtime.d.ts +31 -0
  53. package/dist/ray-tracing/hybrid/shaders/hybrid-artifact.generated.d.ts +625 -0
  54. package/dist/ray-tracing/hybrid/types.d.ts +144 -0
  55. package/dist/ray-tracing/lifecycle/device-recovery.d.ts +31 -0
  56. package/dist/ray-tracing/lifecycle/index.d.ts +2 -0
  57. package/dist/ray-tracing/lifecycle/types.d.ts +37 -0
  58. package/dist/ray-tracing/material/index.d.ts +2 -0
  59. package/dist/ray-tracing/material/pack.d.ts +5 -0
  60. package/dist/ray-tracing/material/types.d.ts +63 -0
  61. package/dist/ray-tracing/reference/index.d.ts +122 -0
  62. package/dist/ray-tracing/renderer/index.d.ts +7 -0
  63. package/dist/ray-tracing/renderer/layout.d.ts +20 -0
  64. package/dist/ray-tracing/renderer/plan.d.ts +2 -0
  65. package/dist/ray-tracing/renderer/reference.d.ts +17 -0
  66. package/dist/ray-tracing/renderer/runtime.d.ts +39 -0
  67. package/dist/ray-tracing/renderer/scene.d.ts +4 -0
  68. package/dist/ray-tracing/renderer/types.d.ts +115 -0
  69. package/dist/ray-tracing/sampling/index.d.ts +8 -0
  70. package/dist/ray-tracing/sampling/layout.d.ts +12 -0
  71. package/dist/ray-tracing/sampling/plan.d.ts +5 -0
  72. package/dist/ray-tracing/sampling/revision.d.ts +15 -0
  73. package/dist/ray-tracing/sampling/runtime.d.ts +44 -0
  74. package/dist/ray-tracing/sampling/sequence.d.ts +2 -0
  75. package/dist/ray-tracing/sampling/types.d.ts +96 -0
  76. package/dist/ray-tracing/scene/index.d.ts +41 -0
  77. package/dist/ray-tracing/traversal/index.d.ts +4 -0
  78. package/dist/ray-tracing/traversal/layout.d.ts +16 -0
  79. package/dist/ray-tracing/traversal/plan.d.ts +2 -0
  80. package/dist/ray-tracing/traversal/runtime.d.ts +25 -0
  81. package/dist/ray-tracing/traversal/types.d.ts +79 -0
  82. package/dist/ray-tracing/worker/client.d.ts +25 -0
  83. package/dist/ray-tracing/worker/index.d.ts +5 -0
  84. package/dist/ray-tracing/worker/runtime.d.ts +11 -0
  85. package/dist/ray-tracing/worker/serialization.d.ts +16 -0
  86. package/dist/ray-tracing/worker/source.d.ts +1 -0
  87. package/dist/ray-tracing/worker/types.d.ts +91 -0
  88. package/dist/ray-tracing.d.ts +17 -0
  89. package/dist/ray-tracing.js +7666 -0
  90. package/dist/spine.js +1 -1
  91. package/dist/tilemap.js +1 -1
  92. package/package.json +26 -23
  93. package/dist/animation/Animation2DStateMachineVisualRuntime.d.ts +0 -27
  94. package/dist/animation/Particle2DDescriptorAdapter.d.ts +0 -5
  95. package/dist/animation3d/Animation3DResource.d.ts +0 -28
  96. package/dist/animation3d/hya/HyaAnimation3DStateMachineRuntime.d.ts +0 -65
  97. package/dist/animation3d/hya/index.d.ts +0 -4
  98. package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.d.ts +0 -81
  99. package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.d.ts +0 -39
  100. package/dist/animation3d/runtime/integration/index.d.ts +0 -2
  101. package/dist/animation3d/runtime/mixer/Action.d.ts +0 -150
  102. package/dist/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.d.ts +0 -6
  103. package/dist/animation3d/runtime/mixer/Mixer.d.ts +0 -59
  104. package/dist/animation3d/runtime/mixer/PoseBuffer.d.ts +0 -1
  105. package/dist/animation3d/runtime/mixer/TrackSampler.d.ts +0 -24
  106. package/dist/animation3d/runtime/mixer/index.d.ts +0 -4
  107. package/dist/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.d.ts +0 -1
  108. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.d.ts +0 -1
  109. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineController.d.ts +0 -1
  110. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.d.ts +0 -1
  111. package/dist/animation3d/runtime/state-machine/index.d.ts +0 -6
  112. package/dist/benchmark.d.ts +0 -30
  113. package/dist/chunks/2d-ui-animation-2d.generated-D4aHzFU2.js +0 -4
  114. package/dist/chunks/Animation2DStateMachine-9IonguNa.js +0 -3245
  115. package/dist/gltf/GltfAccessorReader.d.ts +0 -7
  116. package/dist/gltf/GltfDracoDecoder.d.ts +0 -4
  117. package/dist/gltf/GltfLoaderErrors.d.ts +0 -4
  118. package/dist/gltf/GltfMaterialDescriptor.d.ts +0 -23
  119. package/dist/gltf/GltfMaterialLoader.d.ts +0 -6
  120. package/dist/gltf-worker-runtime.d.ts +0 -7
  121. package/dist/plugins/pluginUtils.d.ts +0 -20
  122. package/dist/plugins.d.ts +0 -6
  123. package/dist/shaders/generated/2d-ui-artifact.generated.d.ts +0 -658
  124. package/dist/spine/Spine2DGpuRenderer.d.ts +0 -65
  125. package/dist/spine/Spine2DRuntime.d.ts +0 -103
  126. package/dist/spine/SpineAnimationBoneTimelines.d.ts +0 -8
  127. package/dist/spine/SpineFloatBuilder.d.ts +0 -15
  128. package/dist/spine/SpinePathConstraintSolver.d.ts +0 -32
  129. package/dist/spine/SpineSkeletonRuntime.d.ts +0 -185
  130. package/dist/spine/SpineSlotAnimation.d.ts +0 -17
  131. package/dist/spine/SpineTimelineRuntime.d.ts +0 -25
  132. package/dist/spine/SpineVertexBuilder.d.ts +0 -61
  133. package/dist/tilemap/Tilemap2DRenderer.d.ts +0 -24
  134. package/dist/utils/arrayAccess.d.ts +0 -4
  135. package/dist/utils/camera2d.d.ts +0 -12
  136. package/src/animation/Animation2DComponent.ts +0 -135
  137. package/src/animation/Animation2DExtensionRegistry.ts +0 -52
  138. package/src/animation/Animation2DRenderSystem.ts +0 -1026
  139. package/src/animation/Animation2DRuntime.ts +0 -1326
  140. package/src/animation/Animation2DStateMachine.ts +0 -39
  141. package/src/animation/Animation2DStateMachineComponent.ts +0 -228
  142. package/src/animation/Animation2DStateMachineRuntime.ts +0 -141
  143. package/src/animation/Animation2DStateMachineSystem.ts +0 -60
  144. package/src/animation/Animation2DStateMachineVisualRuntime.ts +0 -282
  145. package/src/animation/Animation2DSystem.ts +0 -72
  146. package/src/animation/AnimationAssetLoader.ts +0 -84
  147. package/src/animation/AnimationAudioClip.ts +0 -140
  148. package/src/animation/AnimationPathTessellator.ts +0 -691
  149. package/src/animation/AnimationTextRasterizer.ts +0 -340
  150. package/src/animation/AnimationVisual2D.ts +0 -153
  151. package/src/animation/HyaAnimation2DClipAdapter.ts +0 -845
  152. package/src/animation/Particle2DDescriptorAdapter.ts +0 -35
  153. package/src/animation/runtime/mixer/Action.ts +0 -876
  154. package/src/animation/runtime/mixer/Mixer.ts +0 -505
  155. package/src/animation/runtime/mixer/PoseBuffer.ts +0 -105
  156. package/src/animation/runtime/mixer/Sampler.ts +0 -252
  157. package/src/animation/runtime/mixer/Types.ts +0 -138
  158. package/src/animation/runtime/mixer/index.ts +0 -31
  159. package/src/animation-state-machine/AnimationStateMachine.ts +0 -146
  160. package/src/animation-state-machine/AnimationStateMachineChannels.ts +0 -116
  161. package/src/animation-state-machine/runtime/Animation2DStateMachineMixerAdapter.ts +0 -197
  162. package/src/animation-state-machine/runtime/AnimationStateMachineBlendTreeWeights.ts +0 -503
  163. package/src/animation-state-machine/runtime/AnimationStateMachineCompiler.ts +0 -763
  164. package/src/animation-state-machine/runtime/AnimationStateMachineController.ts +0 -987
  165. package/src/animation-state-machine/runtime/AnimationStateMachineMixerPort.ts +0 -54
  166. package/src/animation-state-machine/runtime/index.ts +0 -47
  167. package/src/animation.ts +0 -33
  168. package/src/animation3d/Animation3DAction.ts +0 -73
  169. package/src/animation3d/Animation3DBinding.ts +0 -116
  170. package/src/animation3d/Animation3DClip.ts +0 -22
  171. package/src/animation3d/Animation3DError.ts +0 -34
  172. package/src/animation3d/Animation3DMixer.ts +0 -82
  173. package/src/animation3d/Animation3DPose.ts +0 -100
  174. package/src/animation3d/Animation3DPoseBuffer.ts +0 -85
  175. package/src/animation3d/Animation3DResource.ts +0 -49
  176. package/src/animation3d/Animation3DStateMachine.ts +0 -126
  177. package/src/animation3d/Animation3DStateMachineRuntime.ts +0 -191
  178. package/src/animation3d/Animation3DTrack.ts +0 -29
  179. package/src/animation3d/hya/HyaAnimation3DRuntime.ts +0 -756
  180. package/src/animation3d/hya/HyaAnimation3DStateMachineRuntime.ts +0 -618
  181. package/src/animation3d/hya/HyaAnimation3DTypes.ts +0 -168
  182. package/src/animation3d/hya/index.ts +0 -10
  183. package/src/animation3d/index.ts +0 -76
  184. package/src/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.ts +0 -325
  185. package/src/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.ts +0 -158
  186. package/src/animation3d/runtime/integration/index.ts +0 -9
  187. package/src/animation3d/runtime/mixer/Action.ts +0 -833
  188. package/src/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.ts +0 -26
  189. package/src/animation3d/runtime/mixer/Mixer.ts +0 -562
  190. package/src/animation3d/runtime/mixer/PoseBuffer.ts +0 -1
  191. package/src/animation3d/runtime/mixer/TrackSampler.ts +0 -399
  192. package/src/animation3d/runtime/mixer/index.ts +0 -8
  193. package/src/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.ts +0 -4
  194. package/src/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.ts +0 -1
  195. package/src/animation3d/runtime/state-machine/Animation3DStateMachineController.ts +0 -1
  196. package/src/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.ts +0 -1
  197. package/src/animation3d/runtime/state-machine/index.ts +0 -45
  198. package/src/animation3d.ts +0 -5
  199. package/src/benchmark.ts +0 -168
  200. package/src/canvas-text/CanvasText2DRenderSystem.ts +0 -292
  201. package/src/canvas-text/CanvasTextComponent.ts +0 -47
  202. package/src/canvas-text.ts +0 -4
  203. package/src/experimental-gltf-worker.ts +0 -18
  204. package/src/experimental-spine-worker.ts +0 -9
  205. package/src/gltf/GltfAccessorReader.ts +0 -271
  206. package/src/gltf/GltfAnimation3DAdapter.ts +0 -680
  207. package/src/gltf/GltfAnimationRuntime.ts +0 -392
  208. package/src/gltf/GltfAssetWorkerClient.ts +0 -136
  209. package/src/gltf/GltfCompatibilityReport.ts +0 -214
  210. package/src/gltf/GltfConservativeBounds.ts +0 -219
  211. package/src/gltf/GltfDracoDecoder.ts +0 -251
  212. package/src/gltf/GltfExtensionAdapter.ts +0 -340
  213. package/src/gltf/GltfLoaderContract.ts +0 -333
  214. package/src/gltf/GltfLoaderErrors.ts +0 -59
  215. package/src/gltf/GltfMaterialDescriptor.ts +0 -134
  216. package/src/gltf/GltfMaterialEncoder.ts +0 -173
  217. package/src/gltf/GltfMaterialLoader.ts +0 -293
  218. package/src/gltf/GltfModelComponent.ts +0 -115
  219. package/src/gltf/GltfModelSystem.ts +0 -208
  220. package/src/gltf/GltfPlugin.ts +0 -84
  221. package/src/gltf/GltfSchema.ts +0 -229
  222. package/src/gltf/GltfUvSemanticPlanner.ts +0 -166
  223. package/src/gltf/gltfLoader.ts +0 -910
  224. package/src/gltf-animation3d.ts +0 -10
  225. package/src/gltf-worker-runtime.ts +0 -10
  226. package/src/gltf.ts +0 -56
  227. package/src/grid/Grid2DComponent.ts +0 -69
  228. package/src/grid.ts +0 -2
  229. package/src/hya-state-machine.ts +0 -19
  230. package/src/index.ts +0 -2
  231. package/src/plugins/pluginUtils.ts +0 -50
  232. package/src/plugins.ts +0 -6
  233. package/src/shaders/generated/2d-ui-animation-2d.generated.wgsl +0 -242
  234. package/src/shaders/generated/2d-ui-artifact.generated.ts +0 -867
  235. package/src/shaders/generated/2d-ui-canvas-text-2d.generated.wgsl +0 -38
  236. package/src/shaders/generated/2d-ui-spine2d.generated.wgsl +0 -41
  237. package/src/shaders/generated/2d-ui-tilemap2d.generated.wgsl +0 -36
  238. package/src/spine/Spine2DComponent.ts +0 -168
  239. package/src/spine/Spine2DGpuRenderer.ts +0 -449
  240. package/src/spine/Spine2DRenderSystem.ts +0 -199
  241. package/src/spine/Spine2DRuntime.ts +0 -1529
  242. package/src/spine/SpineAnimationBoneTimelines.ts +0 -30
  243. package/src/spine/SpineAssetParser.ts +0 -20
  244. package/src/spine/SpineAssetWorkerClient.ts +0 -112
  245. package/src/spine/SpineAssetWorkerContract.ts +0 -4
  246. package/src/spine/SpineAtlasParser.ts +0 -127
  247. package/src/spine/SpineFloatBuilder.ts +0 -66
  248. package/src/spine/SpinePathConstraintSolver.ts +0 -582
  249. package/src/spine/SpinePlugin.ts +0 -76
  250. package/src/spine/SpineSkeletonRuntime.ts +0 -521
  251. package/src/spine/SpineSlotAnimation.ts +0 -195
  252. package/src/spine/SpineTimelineRuntime.ts +0 -453
  253. package/src/spine/SpineVertexBuilder.ts +0 -686
  254. package/src/spine.ts +0 -7
  255. package/src/tilemap/Tilemap2DComponent.ts +0 -232
  256. package/src/tilemap/Tilemap2DRenderSystem.ts +0 -58
  257. package/src/tilemap/Tilemap2DRenderer.ts +0 -289
  258. package/src/tilemap/TilemapPlugin.ts +0 -75
  259. package/src/tilemap.ts +0 -6
  260. package/src/tween/Tween2DComponent.ts +0 -83
  261. package/src/tween/Tween2DSystem.ts +0 -78
  262. package/src/tween.ts +0 -4
  263. package/src/types/earcut.d.ts +0 -7
  264. package/src/types/wgsl.d.ts +0 -4
  265. package/src/utils/RenderSystem2DBase.ts +0 -164
  266. package/src/utils/arrayAccess.ts +0 -17
  267. package/src/utils/camera2d.ts +0 -68
  268. package/src/utils/render2dGpu.ts +0 -132
  269. package/test/fixtures/gltf/animation-characterization.gltf +0 -344
@@ -1,340 +0,0 @@
1
- import type {
2
- AnimationText2DComponent,
3
- AnimationTextAnimator,
4
- AnimationTextDocumentKeyframe,
5
- AnimationVectorValueTrack,
6
- } from '@haiyue/animation-spec';
7
-
8
- type TextTexture = HTMLCanvasElement | ImageBitmap;
9
-
10
- interface ResolvedTextDocument {
11
- text: string;
12
- fontFamily: string;
13
- fontSize: number;
14
- fontWeight: string | number;
15
- fontStyle: 'normal' | 'italic';
16
- lineHeight: number;
17
- tracking: number;
18
- textAlign: 'left' | 'center' | 'right';
19
- color: readonly [number, number, number, number];
20
- }
21
-
22
- interface GlyphState {
23
- x: number;
24
- y: number;
25
- scaleX: number;
26
- scaleY: number;
27
- rotation: number;
28
- opacity: number;
29
- tracking: number;
30
- color: [number, number, number, number];
31
- }
32
-
33
- interface SelectorIndexPlan {
34
- readonly indices: readonly number[];
35
- readonly count: number;
36
- }
37
-
38
- /** Runtime-private deterministic grapheme shaper and Canvas atlas uploader. */
39
- export class AnimationTextRasterizer {
40
- texture: TextTexture | null = null;
41
- textureVersion = 0;
42
- private readonly canvas: HTMLCanvasElement | null;
43
- private time = Number.NaN;
44
- private dirty = true;
45
- private updating = false;
46
- private updateToken = 0;
47
- private readonly animated: boolean;
48
-
49
- constructor(readonly component: Readonly<AnimationText2DComponent>) {
50
- this.canvas = typeof document === 'undefined' ? null : document.createElement('canvas');
51
- this.animated = (component.documents?.length ?? 0) > 1
52
- || (component.animators ?? []).some(animator => hasAnimatedAnimator(animator));
53
- void this.updateTexture();
54
- }
55
-
56
- setTime(time: number): void {
57
- if (!this.animated) return;
58
- if (this.time === time && !this.dirty) return;
59
- this.time = time;
60
- this.dirty = true;
61
- void this.updateTexture();
62
- }
63
-
64
- invalidateFont(): void {
65
- this.dirty = true;
66
- void this.updateTexture();
67
- }
68
-
69
- destroy(): void {
70
- this.updateToken++;
71
- closeTextTexture(this.texture, this.canvas);
72
- this.texture = null;
73
- }
74
-
75
- async updateTexture(): Promise<void> {
76
- if (!this.dirty || this.updating || !this.canvas) return;
77
- const token = ++this.updateToken;
78
- this.dirty = false;
79
- this.updating = true;
80
- try {
81
- this.draw(this.canvas, Number.isFinite(this.time) ? this.time : 0);
82
- this.texture = this.canvas;
83
- this.textureVersion++;
84
- if (typeof createImageBitmap !== 'function') return;
85
- const bitmap = await createImageBitmap(this.canvas, { colorSpaceConversion: 'none' });
86
- if (token !== this.updateToken) { bitmap.close(); return; }
87
- const retired = this.texture;
88
- this.texture = bitmap;
89
- this.textureVersion++;
90
- closeTextTexture(retired, this.canvas);
91
- } finally {
92
- this.updating = false;
93
- if (this.dirty) void this.updateTexture();
94
- }
95
- }
96
-
97
- private draw(canvas: HTMLCanvasElement, time: number): void {
98
- const component = this.component;
99
- const document = resolveDocument(component, time);
100
- const dpr = Math.max(1, Math.min(4, component.resolutionScale ?? 2));
101
- const width = Math.max(1, Math.ceil(component.size[0]));
102
- const height = Math.max(1, Math.ceil(component.size[1]));
103
- canvas.width = Math.max(1, Math.ceil(width * dpr));
104
- canvas.height = Math.max(1, Math.ceil(height * dpr));
105
- const context = canvas.getContext('2d');
106
- if (!context) return;
107
- context.setTransform(dpr, 0, 0, dpr, 0, 0);
108
- context.clearRect(0, 0, width, height);
109
- const background = component.backgroundColor;
110
- if (background && background[3] > 0) {
111
- context.fillStyle = cssColor(background);
112
- context.fillRect(0, 0, width, height);
113
- }
114
- const padding = Math.max(0, component.padding ?? 0);
115
- const contentWidth = Math.max(1, width - padding * 2);
116
- const contentHeight = Math.max(1, height - padding * 2);
117
- context.font = `${document.fontStyle} ${document.fontWeight} ${document.fontSize}px ${quoteFont(document.fontFamily)}`;
118
- context.textBaseline = 'alphabetic';
119
- context.textAlign = 'left';
120
- const lines = document.text.split(/\r?\n/).map(splitGraphemes);
121
- const animators = component.animators ?? [];
122
- const selectorPlans = animators.map(animator => createSelectorIndexPlan(lines, animator));
123
- const lineHeight = Math.max(1, document.lineHeight);
124
- const textHeight = lines.length * lineHeight;
125
- let top = padding;
126
- const vertical = component.verticalAlign ?? 'middle';
127
- if (vertical === 'middle') top += Math.max(0, (contentHeight - textHeight) / 2);
128
- else if (vertical === 'bottom') top += Math.max(0, contentHeight - textHeight);
129
- let globalIndex = 0;
130
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
131
- const line = lines[lineIndex]!;
132
- const advances = line.map(glyph => context.measureText(glyph).width);
133
- const baseWidth = advances.reduce((sum, advance) => sum + advance, 0) + Math.max(0, line.length - 1) * document.tracking;
134
- let x = padding;
135
- if (document.textAlign === 'center') x += Math.max(0, (contentWidth - baseWidth) / 2);
136
- else if (document.textAlign === 'right') x += Math.max(0, contentWidth - baseWidth);
137
- const baseline = top + lineIndex * lineHeight + document.fontSize;
138
- for (let glyphIndex = 0; glyphIndex < line.length; glyphIndex++, globalIndex++) {
139
- const glyph = line[glyphIndex]!;
140
- const advance = advances[glyphIndex] ?? 0;
141
- const state = resolveGlyphState(animators, selectorPlans, time, globalIndex, document.color);
142
- const centerX = x + advance / 2 + state.x;
143
- context.save();
144
- context.translate(centerX, baseline + state.y);
145
- context.rotate(state.rotation);
146
- context.scale(state.scaleX, state.scaleY);
147
- context.globalAlpha = state.opacity;
148
- context.fillStyle = cssColor(state.color);
149
- context.fillText(glyph, -advance / 2, 0);
150
- context.restore();
151
- x += advance + document.tracking + state.tracking;
152
- }
153
- }
154
- }
155
- }
156
-
157
- function resolveDocument(component: Readonly<AnimationText2DComponent>, time: number): ResolvedTextDocument {
158
- let keyframe: Readonly<AnimationTextDocumentKeyframe> | undefined;
159
- for (const candidate of component.documents ?? []) {
160
- if (candidate.time > time) break;
161
- keyframe = candidate;
162
- }
163
- return {
164
- text: keyframe?.text ?? component.text,
165
- fontFamily: keyframe?.fontFamily ?? component.fontFamily ?? 'sans-serif',
166
- fontSize: keyframe?.fontSize ?? component.fontSize ?? 28,
167
- fontWeight: keyframe?.fontWeight ?? component.fontWeight ?? 400,
168
- fontStyle: keyframe?.fontStyle ?? component.fontStyle ?? 'normal',
169
- lineHeight: keyframe?.lineHeight ?? component.lineHeight ?? (keyframe?.fontSize ?? component.fontSize ?? 28) * 1.2,
170
- tracking: keyframe?.tracking ?? component.tracking ?? 0,
171
- textAlign: keyframe?.textAlign ?? component.textAlign ?? 'center',
172
- color: keyframe?.color ?? component.color,
173
- };
174
- }
175
-
176
- function resolveGlyphState(
177
- animators: readonly Readonly<AnimationTextAnimator>[],
178
- selectorPlans: readonly SelectorIndexPlan[],
179
- time: number,
180
- index: number,
181
- baseColor: readonly [number, number, number, number],
182
- ): GlyphState {
183
- const state: GlyphState = { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0, opacity: 1, tracking: 0, color: [...baseColor] };
184
- for (let animatorIndex = 0; animatorIndex < animators.length; animatorIndex++) {
185
- const animator = animators[animatorIndex]!;
186
- const plan = selectorPlans[animatorIndex]!;
187
- const weight = selectorWeight(animator, time, plan.indices[index] ?? 0, plan.count);
188
- if (Math.abs(weight) < 1e-6) continue;
189
- const position = sampled(animator.positionTrack, time, animator.position ?? [0, 0]);
190
- const scale = sampled(animator.scaleTrack, time, animator.scale ?? [1, 1]);
191
- const rotation = sampled(animator.rotationTrack, time, [animator.rotation ?? 0])[0] ?? 0;
192
- const opacity = sampled(animator.opacityTrack, time, [animator.opacity ?? 1])[0] ?? 1;
193
- const tracking = sampled(animator.trackingTrack, time, [animator.tracking ?? 0])[0] ?? 0;
194
- const color = sampled(animator.fillColorTrack, time, animator.fillColor ?? state.color);
195
- state.x += (position[0] ?? 0) * weight;
196
- state.y += (position[1] ?? 0) * weight;
197
- state.scaleX *= mix(1, scale[0] ?? 1, weight);
198
- state.scaleY *= mix(1, scale[1] ?? 1, weight);
199
- state.rotation += rotation * weight;
200
- state.opacity *= mix(1, opacity, weight);
201
- state.tracking += tracking * weight;
202
- for (let channel = 0; channel < 4; channel++) state.color[channel] = mix(state.color[channel]!, color[channel] ?? state.color[channel]!, weight);
203
- }
204
- state.opacity = clamp(state.opacity, 0, 1);
205
- return state;
206
- }
207
-
208
- function selectorWeight(animator: Readonly<AnimationTextAnimator>, time: number, index: number, count: number): number {
209
- const selector = animator.selector;
210
- const units = selector.units ?? 'percent';
211
- const coordinate = units === 'index' ? index + 0.5 : (index + 0.5) / Math.max(1, count);
212
- const start = sampled(selector.startTrack, time, [selector.start])[0] ?? selector.start;
213
- const end = sampled(selector.endTrack, time, [selector.end])[0] ?? selector.end;
214
- const offset = sampled(selector.offsetTrack, time, [selector.offset ?? 0])[0] ?? selector.offset ?? 0;
215
- const amount = sampled(selector.amountTrack, time, [selector.amount ?? 1])[0] ?? selector.amount ?? 1;
216
- const low = Math.min(start + offset, end + offset);
217
- const high = Math.max(start + offset, end + offset);
218
- const span = Math.max(1e-6, high - low);
219
- const progress = clamp((coordinate - low) / span, 0, 1);
220
- let weight = coordinate >= low && coordinate <= high ? 1 : 0;
221
- switch (selector.shape ?? 'square') {
222
- case 'ramp-up': weight = progress; break;
223
- case 'ramp-down': weight = 1 - progress; break;
224
- case 'triangle': weight = 1 - Math.abs(progress * 2 - 1); break;
225
- case 'round': weight = Math.sqrt(Math.max(0, 1 - (progress * 2 - 1) ** 2)); break;
226
- case 'smooth': weight = progress * progress * (3 - 2 * progress); break;
227
- }
228
- if (selector.easing) weight = cubicBezierYForX(weight, ...selector.easing);
229
- const smoothness = selector.smoothness ?? 1;
230
- if (smoothness < 1) {
231
- const width = Math.max(0.01, smoothness);
232
- const edge = 0.5 - width * 0.5;
233
- weight = clamp((weight - edge) / width, 0, 1);
234
- }
235
- return clamp(weight * amount, -1, 1);
236
- }
237
-
238
- function createSelectorIndexPlan(
239
- lines: readonly (readonly string[])[],
240
- animator: Readonly<AnimationTextAnimator>,
241
- ): SelectorIndexPlan {
242
- const basedOn = animator.selector.basedOn ?? 'characters';
243
- const indices: number[] = [];
244
- let group = 0;
245
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
246
- const line = lines[lineIndex]!;
247
- for (let glyphIndex = 0; glyphIndex < line.length; glyphIndex++) {
248
- const glyph = line[glyphIndex]!;
249
- indices.push(basedOn === 'lines' ? lineIndex : group);
250
- if (basedOn === 'characters') group++;
251
- else if (basedOn === 'characters-excluding-spaces' && glyph !== ' ') group++;
252
- else if (basedOn === 'words' && (glyph === ' ' || glyphIndex === line.length - 1)) group++;
253
- }
254
- }
255
- const count = Math.max(1, basedOn === 'lines' ? lines.length : group);
256
- const seed = animator.selector.randomSeed;
257
- if (seed === undefined || count < 2) return { indices, count };
258
- const permutation = Array.from({ length: count }, (_, index) => index);
259
- let randomState = seed >>> 0;
260
- for (let index = permutation.length - 1; index > 0; index--) {
261
- randomState = deterministicRandom(randomState);
262
- const swap = randomState % (index + 1);
263
- [permutation[index], permutation[swap]] = [permutation[swap]!, permutation[index]!];
264
- }
265
- return { indices: indices.map(index => permutation[index] ?? index), count };
266
- }
267
-
268
- function deterministicRandom(state: number): number {
269
- let value = state || 0x6d2b79f5;
270
- value ^= value << 13;
271
- value ^= value >>> 17;
272
- value ^= value << 5;
273
- return value >>> 0;
274
- }
275
-
276
- function sampled(track: Readonly<AnimationVectorValueTrack> | undefined, time: number, fallback: readonly number[]): readonly number[] {
277
- if (!track || track.times.length === 0) return fallback;
278
- let frame = 0;
279
- while (frame + 1 < track.times.length && track.times[frame + 1]! <= time) frame++;
280
- const next = Math.min(frame + 1, track.times.length - 1);
281
- let progress = 0;
282
- if (next !== frame && track.interpolation !== 'step') {
283
- progress = clamp((time - track.times[frame]!) / Math.max(1e-8, track.times[next]! - track.times[frame]!), 0, 1);
284
- if (track.interpolation === 'cubic-bezier' && track.easings) {
285
- const offset = frame * 4;
286
- progress = cubicBezierYForX(progress, track.easings[offset]!, track.easings[offset + 1]!, track.easings[offset + 2]!, track.easings[offset + 3]!);
287
- }
288
- }
289
- const result = new Array<number>(track.valueSize);
290
- for (let index = 0; index < track.valueSize; index++) {
291
- const from = track.values[frame * track.valueSize + index] ?? 0;
292
- result[index] = mix(from, track.values[next * track.valueSize + index] ?? from, progress);
293
- }
294
- return result;
295
- }
296
-
297
- function splitGraphemes(text: string): string[] {
298
- const result: string[] = [];
299
- for (const codePoint of Array.from(text)) {
300
- if (codePoint === '\u200D' && result.length > 0) {
301
- result[result.length - 1] += codePoint;
302
- } else if (result.length > 0 && (/\p{Mark}/u.test(codePoint) || codePoint === '\uFE0F' || result[result.length - 1]!.endsWith('\u200D'))) {
303
- result[result.length - 1] += codePoint;
304
- } else result.push(codePoint);
305
- }
306
- return result;
307
- }
308
-
309
- function hasAnimatedAnimator(animator: Readonly<AnimationTextAnimator>): boolean {
310
- return animator.positionTrack !== undefined || animator.scaleTrack !== undefined || animator.rotationTrack !== undefined
311
- || animator.opacityTrack !== undefined || animator.fillColorTrack !== undefined || animator.trackingTrack !== undefined
312
- || animator.selector.startTrack !== undefined || animator.selector.endTrack !== undefined
313
- || animator.selector.offsetTrack !== undefined || animator.selector.amountTrack !== undefined;
314
- }
315
-
316
- function cubicBezierYForX(x: number, x1: number, y1: number, x2: number, y2: number): number {
317
- let low = 0; let high = 1; let t = x;
318
- for (let iteration = 0; iteration < 10; iteration++) {
319
- const estimate = cubic(t, 0, x1, x2, 1);
320
- if (estimate < x) low = t; else high = t;
321
- t = (low + high) * 0.5;
322
- }
323
- return cubic(t, 0, y1, y2, 1);
324
- }
325
-
326
- function cubic(t: number, a: number, b: number, c: number, d: number): number {
327
- const inverse = 1 - t;
328
- return inverse ** 3 * a + 3 * inverse ** 2 * t * b + 3 * inverse * t ** 2 * c + t ** 3 * d;
329
- }
330
-
331
- function cssColor(color: readonly number[]): string {
332
- return `rgba(${Math.round(clamp(color[0] ?? 0, 0, 1) * 255)},${Math.round(clamp(color[1] ?? 0, 0, 1) * 255)},${Math.round(clamp(color[2] ?? 0, 0, 1) * 255)},${clamp(color[3] ?? 1, 0, 1)})`;
333
- }
334
-
335
- function quoteFont(value: string): string { return `"${value.replace(/["\\]/g, '')}"`; }
336
- function closeTextTexture(texture: TextTexture | null, canvas: HTMLCanvasElement | null): void {
337
- if (texture && texture !== canvas && typeof (texture as ImageBitmap).close === 'function') (texture as ImageBitmap).close();
338
- }
339
- function mix(a: number, b: number, t: number): number { return a + (b - a) * t; }
340
- function clamp(value: number, minimum: number, maximum: number): number { return Math.max(minimum, Math.min(maximum, value)); }
@@ -1,153 +0,0 @@
1
- import type { AnimationComposite, AnimationCompositeLayer, AnimationCompositeMode } from '@haiyue/animation-spec';
2
- import { Component, Geometry2D } from '@haiyue/engine';
3
- import { UniqueCheckType } from '@haiyue/engine/ecs';
4
- import type { AssetHandle } from '@haiyue/engine/assets';
5
- import type { AnimationTextRasterizer } from './AnimationTextRasterizer';
6
-
7
- export interface AnimationVisual2DOptions {
8
- geometry: Geometry2D;
9
- color: readonly [number, number, number, number];
10
- instanceId: number;
11
- nodeId: string;
12
- order: number;
13
- sourceOnly?: boolean;
14
- composite?: Readonly<AnimationComposite>;
15
- texture?: GPUTexture | null;
16
- textureHandle?: AssetHandle<GPUTexture> | null;
17
- textMaterial?: AnimationTextRasterizer | null;
18
- /** Keeps sprites invisible until their asynchronous texture is ready. */
19
- requiresTexture?: boolean;
20
- uvRect?: readonly [number, number, number, number];
21
- gradient?: AnimationVisualGradient | null;
22
- effects?: readonly AnimationVisualEffect[];
23
- }
24
-
25
- export interface AnimationVisualGradient {
26
- kind: 'linear' | 'radial';
27
- start: [number, number];
28
- end: [number, number];
29
- /** Mutable packed offset,r,g,b,a tuples, bounded to eight stops. */
30
- stops: Float32Array;
31
- opacity: number;
32
- }
33
-
34
- export type AnimationVisualEffectKind = 'tint' | 'fill' | 'opacity' | 'color-matrix' | 'blur' | 'drop-shadow';
35
-
36
- export interface AnimationVisualEffect {
37
- readonly kind: AnimationVisualEffectKind;
38
- /** Mutable, kind-specific sampled values owned by the animation runtime. */
39
- readonly values: Float32Array;
40
- }
41
-
42
- /** Runtime-private render item consumed by Animation2DRenderSystem. */
43
- export class AnimationVisual2D extends Component {
44
- static override UniqueCheckType = UniqueCheckType.SYMBOL | UniqueCheckType.REPLACE;
45
- static override UniqueSymbol = Symbol.for('AnimationVisual2D');
46
-
47
- readonly geometry: Geometry2D;
48
- readonly instanceId: number;
49
- readonly nodeId: string;
50
- readonly nodeKey: string;
51
- readonly order: number;
52
- readonly sourceOnly: boolean;
53
- readonly compositeLayers: readonly Readonly<AnimationCompositeLayer>[];
54
- readonly compositeKeys: readonly string[];
55
- /** Compatibility accessors for single-layer renderer diagnostics. */
56
- readonly compositeSource: string | undefined;
57
- readonly compositeKey: string | undefined;
58
- readonly compositeMode: AnimationCompositeMode | undefined;
59
- readonly uvRect: [number, number, number, number];
60
- readonly color: [number, number, number, number];
61
- texture: GPUTexture | null;
62
- textureHandle: AssetHandle<GPUTexture> | null;
63
- readonly textMaterial: AnimationTextRasterizer | null;
64
- readonly requiresTexture: boolean;
65
- gradient: AnimationVisualGradient | null;
66
- readonly effects: readonly AnimationVisualEffect[];
67
- revision = 0;
68
-
69
- constructor(options: AnimationVisual2DOptions) {
70
- super('AnimationVisual2D');
71
- this.geometry = options.geometry;
72
- this.instanceId = options.instanceId;
73
- this.nodeId = options.nodeId;
74
- this.nodeKey = `${options.instanceId}:${options.nodeId}`;
75
- this.order = options.order;
76
- this.sourceOnly = options.sourceOnly ?? false;
77
- this.compositeLayers = Object.freeze(options.composite
78
- ? ('layers' in options.composite
79
- ? options.composite.layers.map(layer => ({ ...layer }))
80
- : [{ ...options.composite }])
81
- : []);
82
- this.compositeKeys = Object.freeze(this.compositeLayers.map(layer => `${options.instanceId}:${layer.source}`));
83
- this.compositeSource = this.compositeLayers[0]?.source;
84
- this.compositeKey = this.compositeKeys[0];
85
- this.compositeMode = this.compositeLayers[0]?.mode;
86
- this.uvRect = [...(options.uvRect ?? [0, 0, 1, 1])];
87
- this.color = [...options.color];
88
- this.texture = options.texture ?? null;
89
- this.textureHandle = options.textureHandle ?? null;
90
- this.textMaterial = options.textMaterial ?? null;
91
- this.requiresTexture = options.requiresTexture ?? false;
92
- this.gradient = options.gradient ?? null;
93
- this.effects = Object.freeze(options.effects ? [...options.effects] : []);
94
- }
95
-
96
- setTexture(texture: GPUTexture | null): void {
97
- if (this.texture === texture) return;
98
- this.texture = texture;
99
- this.revision++;
100
- }
101
-
102
- setTextureHandle(handle: AssetHandle<GPUTexture> | null): void {
103
- if (this.textureHandle === handle) return;
104
- this.textureHandle = handle;
105
- this.revision++;
106
- }
107
-
108
- setUvRect(value: readonly [number, number, number, number]): void {
109
- if (this.uvRect.every((current, index) => current === value[index])) return;
110
- this.uvRect[0] = value[0];
111
- this.uvRect[1] = value[1];
112
- this.uvRect[2] = value[2];
113
- this.uvRect[3] = value[3];
114
- this.revision++;
115
- }
116
-
117
- setCompositeExpansion(index: number, value: number): void {
118
- const layer = this.compositeLayers[index];
119
- if (!layer || layer.expansion === value) return;
120
- (layer as AnimationCompositeLayer).expansion = value;
121
- this.revision++;
122
- }
123
-
124
- resolveTexture(): GPUTexture | null {
125
- if (!this.textureHandle) return this.texture;
126
- try { return this.textureHandle.value; } catch { return null; }
127
- }
128
-
129
- override clone(): AnimationVisual2D {
130
- return new AnimationVisual2D({
131
- geometry: this.geometry,
132
- color: this.color,
133
- instanceId: this.instanceId,
134
- nodeId: this.nodeId,
135
- order: this.order,
136
- sourceOnly: this.sourceOnly,
137
- ...(this.compositeLayers.length === 0 ? {} : { composite: { layers: this.compositeLayers } }),
138
- texture: this.texture,
139
- textureHandle: this.textureHandle,
140
- textMaterial: this.textMaterial,
141
- requiresTexture: this.requiresTexture,
142
- uvRect: this.uvRect,
143
- gradient: this.gradient ? {
144
- kind: this.gradient.kind,
145
- start: [...this.gradient.start],
146
- end: [...this.gradient.end],
147
- stops: new Float32Array(this.gradient.stops),
148
- opacity: this.gradient.opacity,
149
- } : null,
150
- effects: this.effects.map(effect => ({ kind: effect.kind, values: new Float32Array(effect.values) })),
151
- });
152
- }
153
- }