@multiplekex/shallot 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 (196) hide show
  1. package/dist/core/builder.d.ts +25 -0
  2. package/dist/core/builder.d.ts.map +1 -0
  3. package/dist/core/builder.js +88 -0
  4. package/dist/core/builder.js.map +1 -0
  5. package/dist/core/component.d.ts +29 -0
  6. package/dist/core/component.d.ts.map +1 -0
  7. package/dist/core/component.js +36 -0
  8. package/dist/core/component.js.map +1 -0
  9. package/dist/core/index.d.ts +13 -0
  10. package/dist/core/index.d.ts.map +1 -0
  11. package/dist/core/math.d.ts +32 -0
  12. package/dist/core/math.d.ts.map +1 -0
  13. package/dist/core/math.js +39 -0
  14. package/dist/core/math.js.map +1 -0
  15. package/dist/core/relation.d.ts +16 -0
  16. package/dist/core/relation.d.ts.map +1 -0
  17. package/dist/core/relation.js +32 -0
  18. package/dist/core/relation.js.map +1 -0
  19. package/dist/core/resource.d.ts +9 -0
  20. package/dist/core/resource.d.ts.map +1 -0
  21. package/dist/core/resource.js +12 -0
  22. package/dist/core/resource.js.map +1 -0
  23. package/dist/core/runtime.d.ts +13 -0
  24. package/dist/core/runtime.d.ts.map +1 -0
  25. package/dist/core/runtime.js +118 -0
  26. package/dist/core/runtime.js.map +1 -0
  27. package/dist/core/scheduler.d.ts +47 -0
  28. package/dist/core/scheduler.d.ts.map +1 -0
  29. package/dist/core/scheduler.js +138 -0
  30. package/dist/core/scheduler.js.map +1 -0
  31. package/dist/core/state.d.ts +62 -0
  32. package/dist/core/state.d.ts.map +1 -0
  33. package/dist/core/state.js +185 -0
  34. package/dist/core/state.js.map +1 -0
  35. package/dist/core/strings.d.ts +3 -0
  36. package/dist/core/strings.d.ts.map +1 -0
  37. package/dist/core/strings.js +11 -0
  38. package/dist/core/strings.js.map +1 -0
  39. package/dist/core/types.d.ts +33 -0
  40. package/dist/core/types.d.ts.map +1 -0
  41. package/dist/core/xml.d.ts +42 -0
  42. package/dist/core/xml.d.ts.map +1 -0
  43. package/dist/core/xml.js +349 -0
  44. package/dist/core/xml.js.map +1 -0
  45. package/dist/extras/arrows/index.d.ts +33 -0
  46. package/dist/extras/arrows/index.d.ts.map +1 -0
  47. package/dist/extras/arrows/index.js +288 -0
  48. package/dist/extras/arrows/index.js.map +1 -0
  49. package/dist/extras/index.d.ts +5 -0
  50. package/dist/extras/index.d.ts.map +1 -0
  51. package/dist/extras/index.js +31 -0
  52. package/dist/extras/index.js.map +1 -0
  53. package/dist/extras/lines/index.d.ts +36 -0
  54. package/dist/extras/lines/index.d.ts.map +1 -0
  55. package/dist/extras/lines/index.js +288 -0
  56. package/dist/extras/lines/index.js.map +1 -0
  57. package/dist/extras/orbit/index.d.ts +20 -0
  58. package/dist/extras/orbit/index.d.ts.map +1 -0
  59. package/dist/extras/orbit/index.js +93 -0
  60. package/dist/extras/orbit/index.js.map +1 -0
  61. package/dist/extras/text/index.d.ts +64 -0
  62. package/dist/extras/text/index.d.ts.map +1 -0
  63. package/dist/extras/text/index.js +423 -0
  64. package/dist/extras/text/index.js.map +1 -0
  65. package/dist/index.d.ts +4 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +187 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/rust/transforms/pkg/shallot_transforms.js +107 -0
  70. package/dist/rust/transforms/pkg/shallot_transforms.js.map +1 -0
  71. package/dist/standard/compute/graph.d.ts +37 -0
  72. package/dist/standard/compute/graph.d.ts.map +1 -0
  73. package/dist/standard/compute/graph.js +85 -0
  74. package/dist/standard/compute/graph.js.map +1 -0
  75. package/dist/standard/compute/index.d.ts +21 -0
  76. package/dist/standard/compute/index.d.ts.map +1 -0
  77. package/dist/standard/compute/index.js +81 -0
  78. package/dist/standard/compute/index.js.map +1 -0
  79. package/dist/standard/defaults.d.ts +3 -0
  80. package/dist/standard/defaults.d.ts.map +1 -0
  81. package/dist/standard/defaults.js +18 -0
  82. package/dist/standard/defaults.js.map +1 -0
  83. package/dist/standard/index.d.ts +8 -0
  84. package/dist/standard/index.d.ts.map +1 -0
  85. package/dist/standard/input/index.d.ts +5 -0
  86. package/dist/standard/input/index.d.ts.map +1 -0
  87. package/dist/standard/input/index.js +70 -0
  88. package/dist/standard/input/index.js.map +1 -0
  89. package/dist/standard/loading/index.d.ts +7 -0
  90. package/dist/standard/loading/index.d.ts.map +1 -0
  91. package/dist/standard/loading/index.js +91 -0
  92. package/dist/standard/loading/index.js.map +1 -0
  93. package/dist/standard/render/camera.d.ts +36 -0
  94. package/dist/standard/render/camera.d.ts.map +1 -0
  95. package/dist/standard/render/camera.js +71 -0
  96. package/dist/standard/render/camera.js.map +1 -0
  97. package/dist/standard/render/forward.d.ts +30 -0
  98. package/dist/standard/render/forward.d.ts.map +1 -0
  99. package/dist/standard/render/forward.js +158 -0
  100. package/dist/standard/render/forward.js.map +1 -0
  101. package/dist/standard/render/index.d.ts +22 -0
  102. package/dist/standard/render/index.d.ts.map +1 -0
  103. package/dist/standard/render/index.js +153 -0
  104. package/dist/standard/render/index.js.map +1 -0
  105. package/dist/standard/render/light.d.ts +25 -0
  106. package/dist/standard/render/light.d.ts.map +1 -0
  107. package/dist/standard/render/light.js +48 -0
  108. package/dist/standard/render/light.js.map +1 -0
  109. package/dist/standard/render/mesh/box.d.ts +3 -0
  110. package/dist/standard/render/mesh/box.d.ts.map +1 -0
  111. package/dist/standard/render/mesh/box.js +190 -0
  112. package/dist/standard/render/mesh/box.js.map +1 -0
  113. package/dist/standard/render/mesh/index.d.ts +52 -0
  114. package/dist/standard/render/mesh/index.d.ts.map +1 -0
  115. package/dist/standard/render/mesh/index.js +158 -0
  116. package/dist/standard/render/mesh/index.js.map +1 -0
  117. package/dist/standard/render/mesh/plane.d.ts +3 -0
  118. package/dist/standard/render/mesh/plane.d.ts.map +1 -0
  119. package/dist/standard/render/mesh/plane.js +33 -0
  120. package/dist/standard/render/mesh/plane.js.map +1 -0
  121. package/dist/standard/render/mesh/sphere.d.ts +3 -0
  122. package/dist/standard/render/mesh/sphere.d.ts.map +1 -0
  123. package/dist/standard/render/mesh/sphere.js +25 -0
  124. package/dist/standard/render/mesh/sphere.js.map +1 -0
  125. package/dist/standard/render/postprocess.d.ts +11 -0
  126. package/dist/standard/render/postprocess.d.ts.map +1 -0
  127. package/dist/standard/render/postprocess.js +190 -0
  128. package/dist/standard/render/postprocess.js.map +1 -0
  129. package/dist/standard/render/scene.d.ts +8 -0
  130. package/dist/standard/render/scene.d.ts.map +1 -0
  131. package/dist/standard/render/scene.js +67 -0
  132. package/dist/standard/render/scene.js.map +1 -0
  133. package/dist/standard/transforms/index.d.ts +27 -0
  134. package/dist/standard/transforms/index.d.ts.map +1 -0
  135. package/dist/standard/transforms/index.js +122 -0
  136. package/dist/standard/transforms/index.js.map +1 -0
  137. package/dist/standard/transforms/wasm.d.ts +17 -0
  138. package/dist/standard/transforms/wasm.d.ts.map +1 -0
  139. package/dist/standard/transforms/wasm.js +31 -0
  140. package/dist/standard/transforms/wasm.js.map +1 -0
  141. package/dist/standard/tween/easing.d.ts +5 -0
  142. package/dist/standard/tween/easing.d.ts.map +1 -0
  143. package/dist/standard/tween/easing.js +80 -0
  144. package/dist/standard/tween/easing.js.map +1 -0
  145. package/dist/standard/tween/index.d.ts +4 -0
  146. package/dist/standard/tween/index.d.ts.map +1 -0
  147. package/dist/standard/tween/sequence.d.ts +20 -0
  148. package/dist/standard/tween/sequence.d.ts.map +1 -0
  149. package/dist/standard/tween/sequence.js +95 -0
  150. package/dist/standard/tween/sequence.js.map +1 -0
  151. package/dist/standard/tween/tween.d.ts +28 -0
  152. package/dist/standard/tween/tween.d.ts.map +1 -0
  153. package/dist/standard/tween/tween.js +136 -0
  154. package/dist/standard/tween/tween.js.map +1 -0
  155. package/package.json +63 -0
  156. package/src/core/builder.ts +148 -0
  157. package/src/core/component.ts +71 -0
  158. package/src/core/index.ts +92 -0
  159. package/src/core/math.ts +128 -0
  160. package/src/core/relation.ts +46 -0
  161. package/src/core/resource.ts +18 -0
  162. package/src/core/runtime.ts +185 -0
  163. package/src/core/scheduler.ts +238 -0
  164. package/src/core/state.ts +295 -0
  165. package/src/core/strings.ts +10 -0
  166. package/src/core/types.ts +37 -0
  167. package/src/core/xml.ts +676 -0
  168. package/src/extras/arrows/index.ts +363 -0
  169. package/src/extras/index.ts +4 -0
  170. package/src/extras/lines/index.ts +368 -0
  171. package/src/extras/orbit/index.ts +133 -0
  172. package/src/extras/text/index.ts +641 -0
  173. package/src/index.ts +3 -0
  174. package/src/standard/compute/graph.ts +165 -0
  175. package/src/standard/compute/index.ts +116 -0
  176. package/src/standard/defaults.ts +17 -0
  177. package/src/standard/index.ts +7 -0
  178. package/src/standard/input/index.ts +142 -0
  179. package/src/standard/loading/index.ts +136 -0
  180. package/src/standard/render/camera.ts +87 -0
  181. package/src/standard/render/forward.ts +212 -0
  182. package/src/standard/render/index.ts +175 -0
  183. package/src/standard/render/light.ts +81 -0
  184. package/src/standard/render/mesh/box.ts +20 -0
  185. package/src/standard/render/mesh/index.ts +227 -0
  186. package/src/standard/render/mesh/plane.ts +11 -0
  187. package/src/standard/render/mesh/sphere.ts +40 -0
  188. package/src/standard/render/postprocess.ts +235 -0
  189. package/src/standard/render/scene.ts +116 -0
  190. package/src/standard/transforms/index.ts +184 -0
  191. package/src/standard/transforms/wasm.ts +61 -0
  192. package/src/standard/tween/easing.ts +169 -0
  193. package/src/standard/tween/index.ts +13 -0
  194. package/src/standard/tween/sequence.ts +142 -0
  195. package/src/standard/tween/tween.ts +265 -0
  196. package/src/vite-env.d.ts +6 -0
@@ -0,0 +1,184 @@
1
+ import { Hierarchy, Not, Wildcard } from "bitecs";
2
+ import {
3
+ ChildOf,
4
+ MAX_ENTITIES,
5
+ rotateQuaternion,
6
+ eulerToQuaternion,
7
+ quaternionToEuler,
8
+ type State,
9
+ type System,
10
+ type Plugin,
11
+ } from "../../core";
12
+ import { setTraits } from "../../core/component";
13
+ import * as wasm from "./wasm";
14
+
15
+ export function rotate(eid: number, dx: number, dy: number, dz: number): void {
16
+ const q = rotateQuaternion(
17
+ Transform.quatX[eid],
18
+ Transform.quatY[eid],
19
+ Transform.quatZ[eid],
20
+ Transform.quatW[eid],
21
+ dx,
22
+ dy,
23
+ dz
24
+ );
25
+ Transform.quatX[eid] = q.x;
26
+ Transform.quatY[eid] = q.y;
27
+ Transform.quatZ[eid] = q.z;
28
+ Transform.quatW[eid] = q.w;
29
+ }
30
+
31
+ interface EulerProxy extends Array<number> {
32
+ get(eid: number): number;
33
+ set(eid: number, value: number): void;
34
+ }
35
+
36
+ function eulerProxy(axis: "x" | "y" | "z"): EulerProxy {
37
+ function getValue(eid: number): number {
38
+ const e = quaternionToEuler(
39
+ Transform.quatX[eid],
40
+ Transform.quatY[eid],
41
+ Transform.quatZ[eid],
42
+ Transform.quatW[eid]
43
+ );
44
+ return e[axis];
45
+ }
46
+
47
+ function setValue(eid: number, value: number): void {
48
+ const e = quaternionToEuler(
49
+ Transform.quatX[eid],
50
+ Transform.quatY[eid],
51
+ Transform.quatZ[eid],
52
+ Transform.quatW[eid]
53
+ );
54
+ e[axis] = value;
55
+ const q = eulerToQuaternion(e.x, e.y, e.z);
56
+ Transform.quatX[eid] = q.x;
57
+ Transform.quatY[eid] = q.y;
58
+ Transform.quatZ[eid] = q.z;
59
+ Transform.quatW[eid] = q.w;
60
+ }
61
+
62
+ return new Proxy([] as unknown as EulerProxy, {
63
+ get(_, prop) {
64
+ if (prop === "get") return getValue;
65
+ if (prop === "set") return setValue;
66
+ const eid = Number(prop);
67
+ if (Number.isNaN(eid)) return undefined;
68
+ return getValue(eid);
69
+ },
70
+ set(_, prop, value) {
71
+ const eid = Number(prop);
72
+ if (Number.isNaN(eid)) return false;
73
+ setValue(eid, value);
74
+ return true;
75
+ },
76
+ });
77
+ }
78
+
79
+ export const Transform: {
80
+ posX: Float32Array;
81
+ posY: Float32Array;
82
+ posZ: Float32Array;
83
+ quatX: Float32Array;
84
+ quatY: Float32Array;
85
+ quatZ: Float32Array;
86
+ quatW: Float32Array;
87
+ scaleX: Float32Array;
88
+ scaleY: Float32Array;
89
+ scaleZ: Float32Array;
90
+ eulerX: EulerProxy;
91
+ eulerY: EulerProxy;
92
+ eulerZ: EulerProxy;
93
+ } = {
94
+ posX: new Float32Array(MAX_ENTITIES),
95
+ posY: new Float32Array(MAX_ENTITIES),
96
+ posZ: new Float32Array(MAX_ENTITIES),
97
+ quatX: new Float32Array(MAX_ENTITIES),
98
+ quatY: new Float32Array(MAX_ENTITIES),
99
+ quatZ: new Float32Array(MAX_ENTITIES),
100
+ quatW: new Float32Array(MAX_ENTITIES),
101
+ scaleX: new Float32Array(MAX_ENTITIES),
102
+ scaleY: new Float32Array(MAX_ENTITIES),
103
+ scaleZ: new Float32Array(MAX_ENTITIES),
104
+ eulerX: eulerProxy("x"),
105
+ eulerY: eulerProxy("y"),
106
+ eulerZ: eulerProxy("z"),
107
+ };
108
+
109
+ export const WorldTransform: { data: Float32Array } = {
110
+ data: new Float32Array(MAX_ENTITIES * 16),
111
+ };
112
+
113
+ setTraits(Transform, {
114
+ defaults: () => ({
115
+ posX: 0,
116
+ posY: 0,
117
+ posZ: 0,
118
+ quatX: 0,
119
+ quatY: 0,
120
+ quatZ: 0,
121
+ quatW: 1,
122
+ scaleX: 1,
123
+ scaleY: 1,
124
+ scaleZ: 1,
125
+ }),
126
+ accessors: {
127
+ eulerX: Transform.eulerX,
128
+ eulerY: Transform.eulerY,
129
+ eulerZ: Transform.eulerZ,
130
+ },
131
+ });
132
+
133
+ async function init(): Promise<void> {
134
+ await wasm.init();
135
+
136
+ Transform.posX = wasm.posX;
137
+ Transform.posY = wasm.posY;
138
+ Transform.posZ = wasm.posZ;
139
+ Transform.quatX = wasm.quatX;
140
+ Transform.quatY = wasm.quatY;
141
+ Transform.quatZ = wasm.quatZ;
142
+ Transform.quatW = wasm.quatW;
143
+ Transform.scaleX = wasm.scaleX;
144
+ Transform.scaleY = wasm.scaleY;
145
+ Transform.scaleZ = wasm.scaleZ;
146
+ WorldTransform.data = wasm.matrices;
147
+ }
148
+
149
+ const TransformSystem: System = {
150
+ group: "simulation",
151
+ last: true,
152
+
153
+ update(state: State) {
154
+ for (const eid of state.query([Transform, Not(WorldTransform)])) {
155
+ state.addComponent(eid, WorldTransform);
156
+ }
157
+
158
+ let count = 0;
159
+
160
+ for (const eid of state.query([Transform, Not(ChildOf.relation(Wildcard))])) {
161
+ wasm.indices[count] = eid;
162
+ wasm.parents[count] = wasm.NoParent;
163
+ count++;
164
+ }
165
+
166
+ for (const eid of state.query([
167
+ Transform,
168
+ ChildOf.relation(Wildcard),
169
+ Hierarchy(ChildOf.relation),
170
+ ])) {
171
+ wasm.indices[count] = eid;
172
+ wasm.parents[count] = state.getRelationTargets(eid, ChildOf)[0];
173
+ count++;
174
+ }
175
+
176
+ wasm.compute(count);
177
+ },
178
+ };
179
+
180
+ export const TransformsPlugin: Plugin = {
181
+ systems: [TransformSystem],
182
+ components: { Transform, WorldTransform },
183
+ initialize: init,
184
+ };
@@ -0,0 +1,61 @@
1
+ import wasmInit, {
2
+ get_pos_x_ptr,
3
+ get_pos_y_ptr,
4
+ get_pos_z_ptr,
5
+ get_quat_x_ptr,
6
+ get_quat_y_ptr,
7
+ get_quat_z_ptr,
8
+ get_quat_w_ptr,
9
+ get_scale_x_ptr,
10
+ get_scale_y_ptr,
11
+ get_scale_z_ptr,
12
+ get_matrices_ptr,
13
+ get_indices_ptr,
14
+ get_parents_ptr,
15
+ get_max_entities,
16
+ get_no_parent,
17
+ init_data,
18
+ compute_transforms,
19
+ } from "../../../rust/transforms/pkg/shallot_transforms.js";
20
+
21
+ export let posX: Float32Array;
22
+ export let posY: Float32Array;
23
+ export let posZ: Float32Array;
24
+ export let quatX: Float32Array;
25
+ export let quatY: Float32Array;
26
+ export let quatZ: Float32Array;
27
+ export let quatW: Float32Array;
28
+ export let scaleX: Float32Array;
29
+ export let scaleY: Float32Array;
30
+ export let scaleZ: Float32Array;
31
+ export let matrices: Float32Array;
32
+ export let indices: Uint32Array;
33
+ export let parents: Uint32Array;
34
+ export let NoParent: number;
35
+
36
+ export async function init(): Promise<void> {
37
+ if (posX) return;
38
+ const wasm = await wasmInit();
39
+ init_data();
40
+ const buffer = wasm.memory.buffer;
41
+ const maxEntities = get_max_entities();
42
+
43
+ posX = new Float32Array(buffer, get_pos_x_ptr(), maxEntities);
44
+ posY = new Float32Array(buffer, get_pos_y_ptr(), maxEntities);
45
+ posZ = new Float32Array(buffer, get_pos_z_ptr(), maxEntities);
46
+ quatX = new Float32Array(buffer, get_quat_x_ptr(), maxEntities);
47
+ quatY = new Float32Array(buffer, get_quat_y_ptr(), maxEntities);
48
+ quatZ = new Float32Array(buffer, get_quat_z_ptr(), maxEntities);
49
+ quatW = new Float32Array(buffer, get_quat_w_ptr(), maxEntities);
50
+ scaleX = new Float32Array(buffer, get_scale_x_ptr(), maxEntities);
51
+ scaleY = new Float32Array(buffer, get_scale_y_ptr(), maxEntities);
52
+ scaleZ = new Float32Array(buffer, get_scale_z_ptr(), maxEntities);
53
+ matrices = new Float32Array(buffer, get_matrices_ptr(), maxEntities * 16);
54
+ indices = new Uint32Array(buffer, get_indices_ptr(), maxEntities);
55
+ parents = new Uint32Array(buffer, get_parents_ptr(), maxEntities);
56
+ NoParent = get_no_parent();
57
+ }
58
+
59
+ export function compute(count: number): void {
60
+ compute_transforms(count);
61
+ }
@@ -0,0 +1,169 @@
1
+ export type EasingFn = (t: number) => number;
2
+
3
+ const linear: EasingFn = (t) => t;
4
+
5
+ const easeInQuad: EasingFn = (t) => t * t;
6
+ const easeOutQuad: EasingFn = (t) => t * (2 - t);
7
+ const easeInOutQuad: EasingFn = (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t);
8
+
9
+ const easeInCubic: EasingFn = (t) => t * t * t;
10
+ const easeOutCubic: EasingFn = (t) => --t * t * t + 1;
11
+ const easeInOutCubic: EasingFn = (t) =>
12
+ t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
13
+
14
+ const easeInQuart: EasingFn = (t) => t * t * t * t;
15
+ const easeOutQuart: EasingFn = (t) => 1 - --t * t * t * t;
16
+ const easeInOutQuart: EasingFn = (t) => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t);
17
+
18
+ const easeInQuint: EasingFn = (t) => t * t * t * t * t;
19
+ const easeOutQuint: EasingFn = (t) => 1 + --t * t * t * t * t;
20
+ const easeInOutQuint: EasingFn = (t) =>
21
+ t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
22
+
23
+ const easeInSine: EasingFn = (t) => 1 - Math.cos((t * Math.PI) / 2);
24
+ const easeOutSine: EasingFn = (t) => Math.sin((t * Math.PI) / 2);
25
+ const easeInOutSine: EasingFn = (t) => -(Math.cos(Math.PI * t) - 1) / 2;
26
+
27
+ const easeInExpo: EasingFn = (t) => (t === 0 ? 0 : Math.pow(2, 10 * t - 10));
28
+ const easeOutExpo: EasingFn = (t) => (t === 1 ? 1 : 1 - Math.pow(2, -10 * t));
29
+ const easeInOutExpo: EasingFn = (t) =>
30
+ t === 0
31
+ ? 0
32
+ : t === 1
33
+ ? 1
34
+ : t < 0.5
35
+ ? Math.pow(2, 20 * t - 10) / 2
36
+ : (2 - Math.pow(2, -20 * t + 10)) / 2;
37
+
38
+ const easeInCirc: EasingFn = (t) => 1 - Math.sqrt(1 - t * t);
39
+ const easeOutCirc: EasingFn = (t) => Math.sqrt(1 - --t * t);
40
+ const easeInOutCirc: EasingFn = (t) =>
41
+ t < 0.5
42
+ ? (1 - Math.sqrt(1 - 4 * t * t)) / 2
43
+ : (Math.sqrt(1 - (-2 * t + 2) * (-2 * t + 2)) + 1) / 2;
44
+
45
+ const easeInBack: EasingFn = (t) => {
46
+ const c = 1.70158;
47
+ return (c + 1) * t * t * t - c * t * t;
48
+ };
49
+ const easeOutBack: EasingFn = (t) => {
50
+ const c = 1.70158;
51
+ return 1 + (c + 1) * Math.pow(t - 1, 3) + c * Math.pow(t - 1, 2);
52
+ };
53
+ const easeInOutBack: EasingFn = (t) => {
54
+ const c = 1.70158 * 1.525;
55
+ return t < 0.5
56
+ ? (Math.pow(2 * t, 2) * ((c + 1) * 2 * t - c)) / 2
57
+ : (Math.pow(2 * t - 2, 2) * ((c + 1) * (t * 2 - 2) + c) + 2) / 2;
58
+ };
59
+
60
+ const easeInElastic: EasingFn = (t) =>
61
+ t === 0
62
+ ? 0
63
+ : t === 1
64
+ ? 1
65
+ : -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * ((2 * Math.PI) / 3));
66
+ const easeOutElastic: EasingFn = (t) =>
67
+ t === 0
68
+ ? 0
69
+ : t === 1
70
+ ? 1
71
+ : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * ((2 * Math.PI) / 3)) + 1;
72
+ const easeInOutElastic: EasingFn = (t) =>
73
+ t === 0
74
+ ? 0
75
+ : t === 1
76
+ ? 1
77
+ : t < 0.5
78
+ ? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * ((2 * Math.PI) / 4.5))) / 2
79
+ : (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * ((2 * Math.PI) / 4.5))) /
80
+ 2 +
81
+ 1;
82
+
83
+ const easeOutBounce: EasingFn = (t) => {
84
+ const n1 = 7.5625;
85
+ const d1 = 2.75;
86
+ if (t < 1 / d1) return n1 * t * t;
87
+ if (t < 2 / d1) return n1 * (t -= 1.5 / d1) * t + 0.75;
88
+ if (t < 2.5 / d1) return n1 * (t -= 2.25 / d1) * t + 0.9375;
89
+ return n1 * (t -= 2.625 / d1) * t + 0.984375;
90
+ };
91
+ const easeInBounce: EasingFn = (t) => 1 - easeOutBounce(1 - t);
92
+ const easeInOutBounce: EasingFn = (t) =>
93
+ t < 0.5 ? (1 - easeOutBounce(1 - 2 * t)) / 2 : (1 + easeOutBounce(2 * t - 1)) / 2;
94
+
95
+ export const EASING_FUNCTIONS: readonly EasingFn[] = [
96
+ linear,
97
+ easeInQuad,
98
+ easeOutQuad,
99
+ easeInOutQuad,
100
+ easeInCubic,
101
+ easeOutCubic,
102
+ easeInOutCubic,
103
+ easeInQuart,
104
+ easeOutQuart,
105
+ easeInOutQuart,
106
+ easeInQuint,
107
+ easeOutQuint,
108
+ easeInOutQuint,
109
+ easeInSine,
110
+ easeOutSine,
111
+ easeInOutSine,
112
+ easeInExpo,
113
+ easeOutExpo,
114
+ easeInOutExpo,
115
+ easeInCirc,
116
+ easeOutCirc,
117
+ easeInOutCirc,
118
+ easeInBack,
119
+ easeOutBack,
120
+ easeInOutBack,
121
+ easeInElastic,
122
+ easeOutElastic,
123
+ easeInOutElastic,
124
+ easeInBounce,
125
+ easeOutBounce,
126
+ easeInOutBounce,
127
+ ] as const;
128
+
129
+ const EASING_INDEX: Record<string, number> = {
130
+ linear: 0,
131
+ "ease-in-quad": 1,
132
+ "ease-out-quad": 2,
133
+ "ease-in-out-quad": 3,
134
+ "ease-in-cubic": 4,
135
+ "ease-out-cubic": 5,
136
+ "ease-in-out-cubic": 6,
137
+ "ease-in-quart": 7,
138
+ "ease-out-quart": 8,
139
+ "ease-in-out-quart": 9,
140
+ "ease-in-quint": 10,
141
+ "ease-out-quint": 11,
142
+ "ease-in-out-quint": 12,
143
+ "ease-in-sine": 13,
144
+ "ease-out-sine": 14,
145
+ "ease-in-out-sine": 15,
146
+ "ease-in-expo": 16,
147
+ "ease-out-expo": 17,
148
+ "ease-in-out-expo": 18,
149
+ "ease-in-circ": 19,
150
+ "ease-out-circ": 20,
151
+ "ease-in-out-circ": 21,
152
+ "ease-in-back": 22,
153
+ "ease-out-back": 23,
154
+ "ease-in-out-back": 24,
155
+ "ease-in-elastic": 25,
156
+ "ease-out-elastic": 26,
157
+ "ease-in-out-elastic": 27,
158
+ "ease-in-bounce": 28,
159
+ "ease-out-bounce": 29,
160
+ "ease-in-out-bounce": 30,
161
+ };
162
+
163
+ export function getEasingIndex(name: string): number {
164
+ return EASING_INDEX[name] ?? 0;
165
+ }
166
+
167
+ export function getEasing(index: number): EasingFn {
168
+ return EASING_FUNCTIONS[index] ?? linear;
169
+ }
@@ -0,0 +1,13 @@
1
+ export {
2
+ Tween,
3
+ TweenState,
4
+ TweenTarget,
5
+ TweenSystem,
6
+ TweenPlugin,
7
+ createTween,
8
+ type TweenOptions,
9
+ } from "./tween";
10
+
11
+ export { Sequence, SequenceState, Pause, computeTweenDelays, finalizeSequences } from "./sequence";
12
+
13
+ export { EASING_FUNCTIONS, getEasing, getEasingIndex, type EasingFn } from "./easing";
@@ -0,0 +1,142 @@
1
+ import { Pair } from "bitecs";
2
+ import { setTraits } from "../../core/component";
3
+ import { ChildOf, type State, type ParseContext } from "../../core";
4
+ import { Tween, TweenState, ensureResolved, captureFromValue } from "./tween";
5
+
6
+ const compareNumbers = (a: number, b: number) => a - b;
7
+ const compareByEndTime = (a: { endTime: number }, b: { endTime: number }) => a.endTime - b.endTime;
8
+
9
+ const childrenBuffer: number[] = [];
10
+ const tweensBuffer: { eid: number; endTime: number }[] = [];
11
+
12
+ export const Pause = {
13
+ duration: [] as number[],
14
+ };
15
+
16
+ setTraits(Pause, {
17
+ defaults: () => ({ duration: 0.5 }),
18
+ });
19
+
20
+ export const SequenceState = {
21
+ IDLE: 0,
22
+ PLAYING: 1,
23
+ COMPLETE: 2,
24
+ } as const;
25
+
26
+ export const Sequence = {
27
+ state: [] as number[],
28
+ elapsed: [] as number[],
29
+ };
30
+
31
+ setTraits(Sequence, {
32
+ defaults: () => ({
33
+ state: SequenceState.IDLE,
34
+ elapsed: 0,
35
+ }),
36
+ });
37
+
38
+ export function finalizeSequences(_state: State, _context: ParseContext): void {}
39
+
40
+ function getChildrenSorted(state: State, parentEid: number): number[] {
41
+ childrenBuffer.length = 0;
42
+ for (const childEid of state.query([Pair(ChildOf.relation, parentEid)])) {
43
+ childrenBuffer.push(childEid);
44
+ }
45
+ childrenBuffer.sort(compareNumbers);
46
+ return childrenBuffer;
47
+ }
48
+
49
+ export function computeTweenDelays(state: State, seqEid: number): void {
50
+ const children = getChildrenSorted(state, seqEid);
51
+ let cumulativeDelay = 0;
52
+
53
+ for (const childEid of children) {
54
+ if (state.hasComponent(childEid, Pause)) {
55
+ cumulativeDelay += Pause.duration[childEid] ?? 0;
56
+ } else if (state.hasComponent(childEid, Tween)) {
57
+ Tween.delay[childEid] = cumulativeDelay;
58
+ }
59
+ }
60
+ }
61
+
62
+ function updateSequencePlayheads(state: State, dt: number): void {
63
+ for (const seqEid of state.query([Sequence])) {
64
+ if (Sequence.state[seqEid] !== SequenceState.PLAYING) continue;
65
+
66
+ const prevElapsed = Sequence.elapsed[seqEid] ?? 0;
67
+ const elapsed = prevElapsed + dt;
68
+ Sequence.elapsed[seqEid] = elapsed;
69
+
70
+ for (const childEid of state.query([Pair(ChildOf.relation, seqEid), Tween])) {
71
+ if (Tween.state[childEid] !== TweenState.IDLE) continue;
72
+
73
+ const delay = Tween.delay[childEid] ?? 0;
74
+ const shouldStart = elapsed >= delay;
75
+ const wasStarted = prevElapsed >= delay;
76
+
77
+ if (shouldStart) {
78
+ captureFromValue(state, childEid);
79
+ Tween.state[childEid] = TweenState.PLAYING;
80
+ Tween.elapsed[childEid] = wasStarted ? 0 : elapsed - delay - dt;
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+ function ensureSequenceResolved(state: State, seqEid: number): void {
87
+ tweensBuffer.length = 0;
88
+
89
+ for (const childEid of state.query([Pair(ChildOf.relation, seqEid), Tween])) {
90
+ if (
91
+ Tween.state[childEid] === TweenState.COMPLETE &&
92
+ Tween.elapsed[childEid] >= Tween.duration[childEid]
93
+ ) {
94
+ continue;
95
+ }
96
+ const delay = Tween.delay[childEid] ?? 0;
97
+ const duration = Tween.duration[childEid] ?? 0;
98
+ tweensBuffer.push({ eid: childEid, endTime: delay + duration });
99
+ }
100
+
101
+ tweensBuffer.sort(compareByEndTime);
102
+
103
+ for (const { eid } of tweensBuffer) {
104
+ Tween.state[eid] = TweenState.COMPLETE;
105
+ ensureResolved(state, eid);
106
+ }
107
+ }
108
+
109
+ function ensureSequencesResolved(state: State): void {
110
+ for (const seqEid of state.query([Sequence])) {
111
+ if (Sequence.state[seqEid] === SequenceState.COMPLETE) {
112
+ ensureSequenceResolved(state, seqEid);
113
+ }
114
+ }
115
+ }
116
+
117
+ function checkSequenceCompletion(state: State): void {
118
+ for (const seqEid of state.query([Sequence])) {
119
+ if (Sequence.state[seqEid] !== SequenceState.PLAYING) continue;
120
+
121
+ let allComplete = true;
122
+ let hasChildren = false;
123
+
124
+ for (const childEid of state.query([Pair(ChildOf.relation, seqEid), Tween])) {
125
+ hasChildren = true;
126
+ if (Tween.state[childEid] !== TweenState.COMPLETE) {
127
+ allComplete = false;
128
+ break;
129
+ }
130
+ }
131
+
132
+ if (hasChildren && allComplete) {
133
+ Sequence.state[seqEid] = SequenceState.COMPLETE;
134
+ }
135
+ }
136
+ }
137
+
138
+ export function updateSequences(state: State, dt: number): void {
139
+ updateSequencePlayheads(state, dt);
140
+ }
141
+
142
+ export { ensureSequencesResolved, checkSequenceCompletion };