pixi-effects 0.26.2 → 0.27.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 (68) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +8 -10
  3. package/ai/CHAT.md +1 -1
  4. package/ai/chat-template.html +4 -6
  5. package/ai/tools/render.mjs +16 -11
  6. package/dist/{Base-CpdDs4HT.d.ts → Base-CVezIEdo.d.ts} +2 -4
  7. package/dist/{Base-TbBhHPrG.d.cts → Base-DzC3i919.d.cts} +2 -4
  8. package/dist/Composition-4TYDRRRY.js +7 -0
  9. package/dist/{Composition-H4QA24MV.js.map → Composition-4TYDRRRY.js.map} +1 -1
  10. package/dist/Composition-QCOWQSDB.cjs +16 -0
  11. package/dist/{Composition-HM4YT5L7.cjs.map → Composition-QCOWQSDB.cjs.map} +1 -1
  12. package/dist/Controller.d.cts +2 -3
  13. package/dist/Controller.d.ts +2 -3
  14. package/dist/{Movie-i05Oh9mJ.d.cts → Movie-CQ0Nz62a.d.cts} +12 -6
  15. package/dist/{Movie-DVeDTdnc.d.ts → Movie-M9dKm9dI.d.ts} +12 -6
  16. package/dist/Presenter.d.cts +2 -3
  17. package/dist/Presenter.d.ts +2 -3
  18. package/dist/{blendModes-YV43K6E7.cjs → blendModes-HDOP6R7T.cjs} +5 -5
  19. package/dist/{blendModes-YV43K6E7.cjs.map → blendModes-HDOP6R7T.cjs.map} +1 -1
  20. package/dist/{blendModes-G3NOAU3T.js → blendModes-PAVTW3BO.js} +4 -4
  21. package/dist/{blendModes-G3NOAU3T.js.map → blendModes-PAVTW3BO.js.map} +1 -1
  22. package/dist/{chunk-TLYTYBWX.js → chunk-45QKLD6M.js} +9 -11
  23. package/dist/chunk-45QKLD6M.js.map +1 -0
  24. package/dist/{chunk-GBUM7X2S.js → chunk-CKTS6G6I.js} +7 -11
  25. package/dist/chunk-CKTS6G6I.js.map +1 -0
  26. package/dist/{chunk-555URQHQ.cjs → chunk-NDR2VQFV.cjs} +91 -95
  27. package/dist/chunk-NDR2VQFV.cjs.map +1 -0
  28. package/dist/{chunk-3DLYAPK7.cjs → chunk-P4GY3Z4G.cjs} +5 -5
  29. package/dist/{chunk-3DLYAPK7.cjs.map → chunk-P4GY3Z4G.cjs.map} +1 -1
  30. package/dist/{chunk-WPXDRSGN.cjs → chunk-RQMI7EJT.cjs} +1170 -280
  31. package/dist/chunk-RQMI7EJT.cjs.map +1 -0
  32. package/dist/{chunk-X3HB4DSR.js → chunk-TGJACVLD.js} +1150 -263
  33. package/dist/chunk-TGJACVLD.js.map +1 -0
  34. package/dist/{chunk-AXZUKNXF.js → chunk-TYWFKCPY.js} +4 -4
  35. package/dist/{chunk-AXZUKNXF.js.map → chunk-TYWFKCPY.js.map} +1 -1
  36. package/dist/{chunk-ECA2WNRX.cjs → chunk-ZMAD3KJU.cjs} +9 -11
  37. package/dist/chunk-ZMAD3KJU.cjs.map +1 -0
  38. package/dist/index.cjs +189 -116
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +16 -14
  41. package/dist/index.d.ts +16 -14
  42. package/dist/index.js +136 -45
  43. package/dist/index.js.map +1 -1
  44. package/dist/loader.css +1 -1
  45. package/dist/three.cjs +6 -6
  46. package/dist/three.d.cts +2 -3
  47. package/dist/three.d.ts +2 -3
  48. package/dist/three.js +2 -2
  49. package/dist/{types-DSGwN5w1.d.ts → types-DUVefWiB.d.cts} +128 -3
  50. package/dist/{types-DSGwN5w1.d.cts → types-DUVefWiB.d.ts} +128 -3
  51. package/docs/api.md +16 -6
  52. package/docs/dsl.md +14 -14
  53. package/llms-full.txt +38 -28
  54. package/llms.txt +1 -1
  55. package/package.json +3 -4
  56. package/skills/pixi-effects/SKILL.md +1 -1
  57. package/skills/pixi-effects/reference/cheatsheet.md +3 -3
  58. package/skills/pixi-effects/reference/pitfalls.md +3 -3
  59. package/skills/pixi-effects/reference/recipes.md +1 -1
  60. package/skills/pixi-effects/template.html +6 -8
  61. package/dist/Composition-H4QA24MV.js +0 -7
  62. package/dist/Composition-HM4YT5L7.cjs +0 -16
  63. package/dist/chunk-555URQHQ.cjs.map +0 -1
  64. package/dist/chunk-ECA2WNRX.cjs.map +0 -1
  65. package/dist/chunk-GBUM7X2S.js.map +0 -1
  66. package/dist/chunk-TLYTYBWX.js.map +0 -1
  67. package/dist/chunk-WPXDRSGN.cjs.map +0 -1
  68. package/dist/chunk-X3HB4DSR.js.map +0 -1
@@ -1,9 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunkECA2WNRX_cjs = require('./chunk-ECA2WNRX.cjs');
3
+ var chunkZMAD3KJU_cjs = require('./chunk-ZMAD3KJU.cjs');
4
4
  var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
5
5
  var PIXI = require('pixi.js');
6
- var gsap = require('gsap');
7
6
 
8
7
  function _interopNamespace(e) {
9
8
  if (e && e.__esModule) return e;
@@ -25,40 +24,7 @@ function _interopNamespace(e) {
25
24
 
26
25
  var PIXI__namespace = /*#__PURE__*/_interopNamespace(PIXI);
27
26
 
28
- // src/expr/random.ts
29
- function mix(h) {
30
- h = Math.imul(h ^ h >>> 16, 2246822507);
31
- h = Math.imul(h ^ h >>> 13, 3266489909);
32
- return (h ^ h >>> 16) >>> 0;
33
- }
34
- function fold(n) {
35
- if (!Number.isFinite(n)) return 0;
36
- const i = Math.floor(n);
37
- const f = n - i;
38
- return (i | 0) ^ Math.imul(Math.floor(f * 1048576), 2654435761);
39
- }
40
- function rand(seed) {
41
- return mix(fold(seed) + 2654435769) / 4294967296;
42
- }
43
- function random(seed) {
44
- let a = fold(seed) + 1831565813 | 0;
45
- return () => {
46
- a = a + 1831565813 | 0;
47
- let t = Math.imul(a ^ a >>> 15, 1 | a);
48
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
49
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
50
- };
51
- }
52
- function noise(x, seed = 0) {
53
- if (!Number.isFinite(x)) return 0;
54
- const i = Math.floor(x);
55
- const f = x - i;
56
- const s = fold(seed) * 668265261;
57
- const a = mix((i | 0) + s) / 2147483648 - 1;
58
- const b = mix((i + 1 | 0) + s) / 2147483648 - 1;
59
- const u = f * f * f * (f * (f * 6 - 15) + 10);
60
- return a + (b - a) * u;
61
- }
27
+ // src/core/spring.ts
62
28
  var SETTLE_TOL = 5e-3;
63
29
  var DEFAULTS = { mass: 1, stiffness: 100, damping: 10 };
64
30
  function springResponse(t, { mass: m, stiffness: k, damping: c }) {
@@ -140,19 +106,6 @@ function parseSpring(name) {
140
106
  const [mass, stiffness, damping] = (m[2] ?? "").split(",").map((s) => s.trim()).filter(Boolean).map(Number);
141
107
  return complete({ mass, stiffness, damping });
142
108
  }
143
- var registered = false;
144
- function registerSpringEases() {
145
- if (registered) return;
146
- registered = true;
147
- const base = springEase();
148
- const ease = (p) => base(p);
149
- ease.config = (mass, stiffness, damping) => {
150
- const bad = [mass, stiffness, damping].some((v) => v !== void 0 && !(typeof v === "number" && v > 0 && Number.isFinite(v)));
151
- return springEase(bad ? {} : { mass, stiffness, damping });
152
- };
153
- gsap.gsap.registerEase("spring", ease);
154
- for (const [name, p] of Object.entries(SPRING_PRESETS)) gsap.gsap.registerEase(`spring.${name}`, springEase(p));
155
- }
156
109
  var autoWarned = /* @__PURE__ */ new Set();
157
110
  function kfDuration(kf) {
158
111
  if (kf.duration !== "auto") return kf.duration ?? 0;
@@ -165,7 +118,837 @@ function kfDuration(kf) {
165
118
  }
166
119
  return 0.5;
167
120
  }
168
- registerSpringEases();
121
+
122
+ // src/core/cubicBezier.ts
123
+ function cubicBezierEase(x1, y1, x2, y2) {
124
+ const cx = 3 * x1, bx = 3 * (x2 - x1) - cx, ax = 1 - cx - bx;
125
+ const cy = 3 * y1, by = 3 * (y2 - y1) - cy, ay = 1 - cy - by;
126
+ const X = (t) => ((ax * t + bx) * t + cx) * t;
127
+ const Y = (t) => ((ay * t + by) * t + cy) * t;
128
+ const dX = (t) => (3 * ax * t + 2 * bx) * t + cx;
129
+ return (p) => {
130
+ if (p <= 0) return 0;
131
+ if (p >= 1) return 1;
132
+ let t = p;
133
+ for (let i = 0; i < 8; i++) {
134
+ const err = X(t) - p;
135
+ if (Math.abs(err) < 1e-9) return Y(t);
136
+ const d = dX(t);
137
+ if (Math.abs(d) < 1e-6) break;
138
+ t -= err / d;
139
+ }
140
+ let lo = 0, hi = 1;
141
+ t = p;
142
+ for (let i = 0; i < 60; i++) {
143
+ const x = X(t);
144
+ if (Math.abs(x - p) < 1e-10) break;
145
+ if (x < p) lo = t;
146
+ else hi = t;
147
+ t = (lo + hi) / 2;
148
+ }
149
+ return Y(t);
150
+ };
151
+ }
152
+ var NUM = String.raw`\s*(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)\s*`;
153
+ var RE = new RegExp(`^\\s*cubic-bezier\\(${NUM},${NUM},${NUM},${NUM}\\)\\s*$`, "i");
154
+ function parseCubicBezier(name) {
155
+ const m = RE.exec(name);
156
+ return m && !cubicBezierProblem(name) ? [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])] : null;
157
+ }
158
+ function cubicBezierProblem(name) {
159
+ const m = RE.exec(name);
160
+ if (!m) return 'expected "cubic-bezier(x1, y1, x2, y2)" with four numbers, like CSS';
161
+ const x1 = Number(m[1]), x2 = Number(m[3]);
162
+ if (x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) return "x1 and x2 must be between 0 and 1 (y1 and y2 may overshoot)";
163
+ return null;
164
+ }
165
+
166
+ // src/core/pure/ease.ts
167
+ var PI = Math.PI;
168
+ var power = (strength) => ({
169
+ in: (p) => Math.pow(p, strength),
170
+ out: (p) => 1 - Math.pow(1 - p, strength),
171
+ inOut: (p) => p < 0.5 ? 0.5 * Math.pow(2 * p, strength) : 1 - 0.5 * Math.pow(2 * (1 - p), strength)
172
+ });
173
+ var fromOut = (out) => ({
174
+ in: (p) => 1 - out(1 - p),
175
+ out,
176
+ inOut: (p) => p < 0.5 ? 0.5 * (1 - out(1 - 2 * p)) : 0.5 + 0.5 * out(2 * p - 1)
177
+ });
178
+ var mirrored = (easeIn) => ({
179
+ in: easeIn,
180
+ out: (p) => 1 - easeIn(1 - p),
181
+ inOut: (p) => p < 0.5 ? easeIn(p * 2) / 2 : 1 - easeIn((1 - p) * 2) / 2
182
+ });
183
+ var sine = mirrored((p) => p === 1 ? 1 : -Math.cos(p * PI / 2) + 1);
184
+ var circ = mirrored((p) => -(Math.sqrt(1 - p * p) - 1));
185
+ var expoIn = (p) => Math.pow(2, 10 * (p - 1)) * p + p * p * p * p * p * p * (1 - p);
186
+ var expo = {
187
+ in: expoIn,
188
+ out: (p) => 1 - expoIn(1 - p),
189
+ inOut: (p) => p < 0.5 ? expoIn(p * 2) / 2 : 1 - expoIn((1 - p) * 2) / 2
190
+ };
191
+ var bounceOut = (p) => {
192
+ if (p < 1 / 2.75) return 7.5625 * p * p;
193
+ if (p < 2 / 2.75) {
194
+ p -= 1.5 / 2.75;
195
+ return 7.5625 * p * p + 0.75;
196
+ }
197
+ if (p < 2.5 / 2.75) {
198
+ p -= 2.25 / 2.75;
199
+ return 7.5625 * p * p + 0.9375;
200
+ }
201
+ p -= 2.625 / 2.75;
202
+ return 7.5625 * p * p + 0.984375;
203
+ };
204
+ var bounce = fromOut(bounceOut);
205
+ function back(s = 1.70158) {
206
+ const out = (p) => p ? (p -= 1, p * p * ((s + 1) * p + s) + 1) : 0;
207
+ return fromOut(out);
208
+ }
209
+ function elastic(amplitude, period) {
210
+ const make = (per0) => {
211
+ const amp = amplitude ?? 1;
212
+ const a = amp >= 1 ? amp : 1;
213
+ const per = per0 / (amp < 1 ? amp : 1);
214
+ const s = per / (2 * PI) * Math.asin(1 / a);
215
+ const k = 2 * PI / per;
216
+ return fromOut((p) => p === 0 ? 0 : p === 1 ? 1 : a * Math.pow(2, -10 * p) * Math.sin((p - s) * k) + 1);
217
+ };
218
+ const usual = make(period ?? 0.3);
219
+ return { in: usual.in, out: usual.out, inOut: period === void 0 ? make(0.45).inOut : usual.inOut };
220
+ }
221
+ function steps(n) {
222
+ const count = Math.max(1, Math.floor(n) || 1);
223
+ return (p) => p <= 0 ? 0 : p >= 1 ? 1 : Math.min(1, Math.floor(p * (count + 1)) / count);
224
+ }
225
+ var FAMILY = {
226
+ power1: () => power(2),
227
+ power2: () => power(3),
228
+ power3: () => power(4),
229
+ power4: () => power(5),
230
+ quad: () => power(2),
231
+ cubic: () => power(3),
232
+ quart: () => power(4),
233
+ quint: () => power(5),
234
+ strong: () => power(5),
235
+ sine: () => sine,
236
+ circ: () => circ,
237
+ expo: () => expo,
238
+ bounce: () => bounce,
239
+ back: (a) => back(a[0]),
240
+ elastic: (a) => elastic(a[0], a[1])
241
+ };
242
+ var LINEAR = (p) => p;
243
+ var DEFAULT = power(2).out;
244
+ var cache = /* @__PURE__ */ new Map();
245
+ function lookup(name) {
246
+ let hit = cache.get(name);
247
+ if (hit === void 0) {
248
+ hit = parse(name.trim());
249
+ cache.set(name, hit);
250
+ }
251
+ return hit;
252
+ }
253
+ function pureEase(name) {
254
+ return (typeof name === "string" ? lookup(name) : null) ?? DEFAULT;
255
+ }
256
+ function isEaseName(name) {
257
+ return typeof name === "string" && lookup(name) !== null;
258
+ }
259
+ function parse(raw) {
260
+ const s = raw.toLowerCase();
261
+ if (s === "none" || s === "linear" || s === "linear.easenone") return LINEAR;
262
+ if (/^spring/.test(s)) {
263
+ const sp = parseSpring(raw);
264
+ return sp ? springEase(sp) : null;
265
+ }
266
+ if (/^cubic-bezier/.test(s)) {
267
+ const c = parseCubicBezier(raw);
268
+ return c ? cubicBezierEase(...c) : LINEAR;
269
+ }
270
+ const st = /^steps\(\s*([^)]*)\)$/.exec(s);
271
+ if (st) return steps(Number(st[1]));
272
+ if (/^[a-z][a-z0-9]*$/.test(raw) && FAMILY[raw]) return FAMILY[raw]([]).out;
273
+ const m = /^([a-z0-9]+)\.(?:ease)?(in|out|inout)(?:\(([^)]*)\))?$/.exec(s);
274
+ if (!m) return null;
275
+ const make = FAMILY[m[1]];
276
+ if (!make) return null;
277
+ const args = (m[3] ?? "").split(",").map((x) => x.trim()).filter(Boolean).map(Number).filter((n) => Number.isFinite(n));
278
+ const trio = make(args);
279
+ return m[2] === "in" ? trio.in : m[2] === "out" ? trio.out : trio.inOut;
280
+ }
281
+
282
+ // src/core/pure/colorLerp.ts
283
+ function parseCssColor(s) {
284
+ const v = s.trim();
285
+ let m = /^#([0-9a-f]{3,4})$/i.exec(v);
286
+ if (m) {
287
+ const h = m[1];
288
+ const d = (i) => parseInt(h[i] + h[i], 16);
289
+ return [d(0), d(1), d(2), h.length === 4 ? d(3) / 255 : 1];
290
+ }
291
+ m = /^#([0-9a-f]{6})([0-9a-f]{2})?$/i.exec(v);
292
+ if (m) {
293
+ const n = parseInt(m[1], 16);
294
+ return [n >> 16 & 255, n >> 8 & 255, n & 255, m[2] ? parseInt(m[2], 16) / 255 : 1];
295
+ }
296
+ m = /^rgba?\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*(?:,\s*(-?[\d.]+)\s*)?\)$/i.exec(v);
297
+ if (m) return [Number(m[1]), Number(m[2]), Number(m[3]), m[4] === void 0 ? 1 : Number(m[4])];
298
+ return null;
299
+ }
300
+ function rgbInterp(a, b, fallback = () => null) {
301
+ const ca = parseCssColor(a) ?? fallback(a), cb = parseCssColor(b) ?? fallback(b);
302
+ if (!ca || !cb) return (p) => p >= 1 ? b : a;
303
+ return (p) => {
304
+ if (p === 0) return a;
305
+ if (p === 1) return b;
306
+ const ch = (i) => Math.round(ca[i] + (cb[i] - ca[i]) * p);
307
+ const al = Math.round((ca[3] + (cb[3] - ca[3]) * p) * 1e4) / 1e4;
308
+ return `rgba(${ch(0)},${ch(1)},${ch(2)},${al})`;
309
+ };
310
+ }
311
+
312
+ // src/core/pure/props.ts
313
+ var DEG = Math.PI / 180;
314
+ var num = (obj, key, id, unit = 1) => ({
315
+ id,
316
+ kind: "num",
317
+ unit,
318
+ round: 1e6,
319
+ get: () => Number(obj()?.[key]),
320
+ set: (v) => {
321
+ const o = obj();
322
+ if (o) o[key] = v;
323
+ }
324
+ });
325
+ function xy(target, base, which, unit = 1) {
326
+ const o = () => target[base];
327
+ const ax = num(o, "x", `${base}.x`, unit), ay = num(o, "y", `${base}.y`, unit);
328
+ return which === "x" ? [ax] : which === "y" ? [ay] : [ax, ay];
329
+ }
330
+ var VEC = [
331
+ ["scale", "scale", 1],
332
+ ["anchor", "anchor", 1],
333
+ ["pivot", "pivot", 1],
334
+ ["skew", "skew", DEG],
335
+ ["position", "position", 1],
336
+ ["tilePosition", "tilePosition", 1],
337
+ ["tileScale", "tileScale", 1]
338
+ ];
339
+ var PIXI_INERT = /* @__PURE__ */ new Set(["fillColor", "fillAlpha"]);
340
+ function pixiAccessors(target, key) {
341
+ for (const [name, base, unit] of VEC) {
342
+ if (key === name) return xy(target, base, "both", unit);
343
+ if (key === `${name}X`) return xy(target, base, "x", unit);
344
+ if (key === `${name}Y`) return xy(target, base, "y", unit);
345
+ }
346
+ switch (key) {
347
+ case "rotation":
348
+ return [{ id: "rotation", kind: "num", unit: DEG, round: 1e5, exactEnd: true, get: () => Number(target.rotation), set: (v) => {
349
+ target.rotation = v;
350
+ } }];
351
+ case "tint":
352
+ return [{ id: "tint", kind: "color", unit: 1, round: 1, get: () => Number(target.tint), set: (v) => {
353
+ target.tint = v;
354
+ } }];
355
+ case "autoAlpha":
356
+ return [
357
+ { id: "alpha", kind: "num", unit: 1, round: 1e6, get: () => Number(target.alpha), set: (v) => {
358
+ target.alpha = v;
359
+ } },
360
+ // `visible` follows the autoAlpha tweens only, not a plain `alpha` tween on the same layer: a channel of its own
361
+ { id: "visible#auto", kind: "num", unit: 1, round: 1e6, always: true, get: () => Number(target.alpha), set: (v) => {
362
+ target.visible = v !== 0;
363
+ } }
364
+ ];
365
+ default:
366
+ return null;
367
+ }
368
+ }
369
+ function rawAccessor(target, key) {
370
+ return { id: key, kind: "raw", unit: 1, round: 1, get: () => target[key], set: (v) => {
371
+ target[key] = v;
372
+ } };
373
+ }
374
+ function cssAccessor(target, key) {
375
+ return { id: key, kind: "css", unit: 1, round: 1, get: () => target[key], set: (v) => {
376
+ target[key] = v;
377
+ } };
378
+ }
379
+ function plainAccessor(target, key) {
380
+ return { id: key, kind: "num", unit: 1, round: 1e6, get: () => Number(target[key]), set: (v) => {
381
+ target[key] = v;
382
+ } };
383
+ }
384
+ function parseColor(v) {
385
+ if (typeof v === "number") return v;
386
+ if (typeof v !== "string") return null;
387
+ const s = v.trim();
388
+ let m = /^#([0-9a-f]{3})$/i.exec(s);
389
+ if (m) {
390
+ const h = m[1];
391
+ return parseInt(h[0] + h[0] + h[1] + h[1] + h[2] + h[2], 16);
392
+ }
393
+ m = /^#([0-9a-f]{6})(?:[0-9a-f]{2})?$/i.exec(s);
394
+ if (m) return parseInt(m[1], 16);
395
+ m = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i.exec(s);
396
+ if (m) return Number(m[1]) << 16 | Number(m[2]) << 8 | Number(m[3]);
397
+ return null;
398
+ }
399
+ function lerpColor(from, to, p) {
400
+ const r = lerpByte(from >> 16 & 255, to >> 16 & 255, p);
401
+ const g = lerpByte(from >> 8 & 255, to >> 8 & 255, p);
402
+ const b = lerpByte(from & 255, to & 255, p);
403
+ return r << 16 | g << 8 | b;
404
+ }
405
+ function lerpByte(a, b, p) {
406
+ return Math.trunc(a + (b - a) * p);
407
+ }
408
+
409
+ // src/core/pure/PureTimeline.ts
410
+ var quantize = (x) => Math.round(x * 1e7) / 1e7 || 0;
411
+ var TINY = 1e-8;
412
+ var spacer = (duration) => ({ spacer: true, duration });
413
+ var parseRel = (v) => {
414
+ if (typeof v === "number") return { rel: false, n: v };
415
+ if (typeof v === "string") {
416
+ const m = /^([+-])=\s*(-?\d*\.?\d+(?:e[+-]?\d+)?)$/i.exec(v.trim());
417
+ if (m) return { rel: true, n: (m[1] === "-" ? -1 : 1) * parseFloat(m[2]) };
418
+ const n = parseFloat(v);
419
+ if (Number.isFinite(n) && String(n) === v.trim()) return { rel: false, n };
420
+ }
421
+ return null;
422
+ };
423
+ var RESERVED = /* @__PURE__ */ new Set(["duration", "ease", "repeat", "yoyo", "repeatDelay", "onUpdate", "delay"]);
424
+ var CALLBACKS = ["onStart", "onComplete", "onRepeat", "onReverseComplete"];
425
+ var saidCallbacks = false;
426
+ var PureTimeline = class _PureTimeline {
427
+ defaultEase;
428
+ channels = /* @__PURE__ */ new Map();
429
+ channelList = [];
430
+ children = [];
431
+ spans = 0;
432
+ now = 0;
433
+ order = 0;
434
+ killed = false;
435
+ prepared = false;
436
+ constructor(opts = {}) {
437
+ this.defaultEase = opts.defaults?.ease ?? "none";
438
+ }
439
+ // --- building ------------------------------------------------------------------------------------------
440
+ /** Adds a child timeline (played with its local time) or a spacer that lengthens this one. */
441
+ add(child, at) {
442
+ const start = at ?? this.spans;
443
+ if ("spacer" in child) {
444
+ this.spans = Math.max(this.spans, start + child.duration);
445
+ } else if (!(child instanceof _PureTimeline)) {
446
+ this.spans = Math.max(this.spans, start + child.duration());
447
+ } else {
448
+ this.children.push({ child, start: quantize(start), order: this.order++, exclusive: false, last: NaN });
449
+ this.prepared = false;
450
+ this.spans = Math.max(this.spans, start + child.duration());
451
+ }
452
+ return this;
453
+ }
454
+ set(target, vars, at) {
455
+ return this.build(target, null, { ...vars, duration: 0 }, at, "set");
456
+ }
457
+ to(target, vars, at) {
458
+ return this.build(target, null, vars, at, "to");
459
+ }
460
+ from(target, vars, at) {
461
+ return this.build(target, null, vars, at, "from");
462
+ }
463
+ fromTo(target, fromVars, toVars, at) {
464
+ return this.build(target, fromVars, toVars, at, "fromTo");
465
+ }
466
+ build(target, fromVars, vars, at, kind) {
467
+ const t = target;
468
+ const start = quantize((at ?? this.spans) + (Number(vars.delay) || 0));
469
+ const dur = quantize(Math.max(0, vars.duration ?? 0));
470
+ for (const cb of CALLBACKS) if (cb in vars) {
471
+ if (!saidCallbacks) {
472
+ saidCallbacks = true;
473
+ console.warn(`pixi-effects: the timeline does not run ${cb}: what a tween does must be a value of the time (use onUpdate for a redraw); it is ignored`);
474
+ }
475
+ }
476
+ const repeat = Math.max(0, Math.floor(vars.repeat ?? 0));
477
+ const yoyo = !!vars.yoyo;
478
+ const repeatDelay = Math.max(0, vars.repeatDelay ?? 0);
479
+ const easeName = vars.ease ?? this.defaultEase;
480
+ const ease = typeof easeName === "function" ? easeName : pureEase(easeName);
481
+ const given = (v) => {
482
+ const out = /* @__PURE__ */ new Map();
483
+ for (const key of Object.keys(v)) {
484
+ if (RESERVED.has(key) || key === "pixi" || CALLBACKS.includes(key)) continue;
485
+ const value = v[key];
486
+ const acc = parseRel(value) ? plainAccessor(t, key) : typeof value === "string" && parseCssColor(value) ? cssAccessor(t, key) : rawAccessor(t, key);
487
+ out.set(acc.id, { acc, value });
488
+ }
489
+ if (v.pixi) for (const key of Object.keys(v.pixi)) {
490
+ const accs = pixiAccessors(t, key);
491
+ if (!accs) {
492
+ if (!PIXI_INERT.has(key)) console.warn(`pixi-effects: the timeline has no "${key}" shorthand yet; it is ignored`);
493
+ continue;
494
+ }
495
+ for (const acc of accs) out.set(acc.id, { acc, value: v.pixi[key] });
496
+ }
497
+ return out;
498
+ };
499
+ const ends = given(vars);
500
+ const starts = fromVars ? given(fromVars) : /* @__PURE__ */ new Map();
501
+ const ids = kind === "fromTo" ? [.../* @__PURE__ */ new Set([...starts.keys(), ...ends.keys()])] : [...ends.keys()];
502
+ let lastAlpha;
503
+ for (const id of ids) {
504
+ const e = ends.get(id), f = starts.get(id);
505
+ const acc = (e ?? f).acc;
506
+ const ch = this.channel(t, acc);
507
+ const cur = acc.get();
508
+ const color = acc.kind === "color";
509
+ const endSpec = e ? read(e.value, acc) : null;
510
+ if (e && !endSpec && acc.kind === "color") console.warn(`pixi-effects: the timeline cannot read the colour ${JSON.stringify(e.value)} (use #rgb, #rrggbb, rgb(...) or a number); this tween is skipped`);
511
+ const fromSpec = f ? read(f.value, acc) : null;
512
+ let from = null, to, toRel = false;
513
+ if (kind === "from") {
514
+ const g = endSpec ?? { rel: false, n: cur };
515
+ from = g.rel ? cur + g.n : g.n;
516
+ to = cur;
517
+ acc.set(from);
518
+ } else if (kind === "fromTo") {
519
+ if (fromSpec) from = fromSpec.rel ? cur + fromSpec.n : fromSpec.n;
520
+ if (endSpec) {
521
+ to = endSpec.n;
522
+ toRel = endSpec.rel;
523
+ } else to = cur;
524
+ if (from !== null) acc.set(from);
525
+ } else {
526
+ if (!endSpec) continue;
527
+ to = endSpec.n;
528
+ toRel = endSpec.rel;
529
+ }
530
+ if (color && toRel) toRel = false;
531
+ const seg = {
532
+ channel: ch,
533
+ kind,
534
+ createCur: cur,
535
+ start,
536
+ dur,
537
+ ease,
538
+ repeat,
539
+ yoyo,
540
+ repeatDelay,
541
+ from,
542
+ to,
543
+ toRel,
544
+ from0: from,
545
+ to0: to,
546
+ toRel0: toRel,
547
+ onUpdate: vars.onUpdate,
548
+ order: this.order++
549
+ };
550
+ if (id === "visible#auto") seg.partner = lastAlpha;
551
+ else if (id === "alpha") lastAlpha = seg;
552
+ ch.segs.push(seg);
553
+ ch.dirty = true;
554
+ this.prepared = false;
555
+ }
556
+ const total = dur * (repeat + 1) + repeatDelay * repeat;
557
+ this.spans = Math.max(this.spans, start + total);
558
+ return this;
559
+ }
560
+ channel(target, acc) {
561
+ let m = this.channels.get(target);
562
+ if (!m) this.channels.set(target, m = /* @__PURE__ */ new Map());
563
+ let ch = m.get(acc.id);
564
+ if (!ch) {
565
+ ch = { target, acc, pre: NaN, last: NaN, segs: [], dirty: false };
566
+ m.set(acc.id, ch);
567
+ this.channelList.push(ch);
568
+ }
569
+ return ch;
570
+ }
571
+ /**
572
+ * A property moved by a function of the caller (a colour in OKLab, a gradient): `to` and `from` say how to get the two ends from
573
+ * the value before the segment, `make` the interpolator between them. The value is only written while the segment is the last
574
+ * one that has started, so nothing is read or kept between two frames.
575
+ */
576
+ tweenValue(spec) {
577
+ const acc = { id: spec.id, kind: "fn", unit: 1, round: 1, get: spec.get, set: spec.set, make: spec.make };
578
+ const ch = this.channel(spec.holder, acc);
579
+ const start = quantize(spec.at);
580
+ const dur = quantize(Math.max(0, spec.duration));
581
+ const repeat = Math.max(0, Math.floor(spec.repeat ?? 0));
582
+ const repeatDelay = Math.max(0, spec.repeatDelay ?? 0);
583
+ const ease = typeof spec.ease === "function" ? spec.ease : pureEase(spec.ease);
584
+ ch.segs.push({
585
+ channel: ch,
586
+ kind: "to",
587
+ createCur: null,
588
+ start,
589
+ dur,
590
+ ease,
591
+ repeat,
592
+ yoyo: !!spec.yoyo,
593
+ repeatDelay,
594
+ from: null,
595
+ to: null,
596
+ toRel: false,
597
+ from0: null,
598
+ to0: null,
599
+ toRel0: false,
600
+ order: this.order++,
601
+ fnFrom: spec.from,
602
+ fnTo: spec.to,
603
+ fnMake: spec.make,
604
+ write: spec.set
605
+ });
606
+ ch.dirty = true;
607
+ this.prepared = false;
608
+ this.spans = Math.max(this.spans, start + dur * (repeat + 1) + repeatDelay * repeat);
609
+ return this;
610
+ }
611
+ /**
612
+ * A write that is undone when time goes back before it: the property is `value` from `at` on, and what it was before otherwise.
613
+ * `holder` and `id` name the property, so a set and the tweens of the same property are one channel; `write` is the way the caller
614
+ * writes it (it may do more than assign: redraw), and runs again whenever this set is what the property shows.
615
+ */
616
+ setValue(holder, id, at, read2, write2, value) {
617
+ const acc = { id, kind: typeof value === "number" ? "num" : "raw", unit: 1, round: 1e6, get: read2, set: write2 };
618
+ const ch = this.channel(holder, acc);
619
+ ch.segs.push({
620
+ channel: ch,
621
+ kind: "set",
622
+ createCur: null,
623
+ start: quantize(at),
624
+ dur: 0,
625
+ ease: pureEase("none"),
626
+ repeat: 0,
627
+ yoyo: false,
628
+ repeatDelay: 0,
629
+ from: null,
630
+ to: value,
631
+ toRel: false,
632
+ from0: null,
633
+ to0: value,
634
+ toRel0: false,
635
+ order: this.order++,
636
+ step: true,
637
+ write: write2,
638
+ onUpdate: () => write2(read2())
639
+ });
640
+ ch.dirty = true;
641
+ this.prepared = false;
642
+ this.spans = Math.max(this.spans, quantize(at));
643
+ return this;
644
+ }
645
+ // --- reading -------------------------------------------------------------------------------------------
646
+ /** What the timeline will write, for tools and tests: every segment of this timeline and of the ones under it, starts in this timeline's time. */
647
+ segments(offset = 0) {
648
+ const out = [];
649
+ for (const ch of this.channelList) {
650
+ for (const s of ch.segs) {
651
+ out.push({ target: ch.target, id: ch.acc.id, start: s.start + offset, duration: s.dur, repeat: s.repeat, yoyo: s.yoyo, repeatDelay: s.repeatDelay, kind: s.step ? "set" : s.kind });
652
+ }
653
+ }
654
+ for (const c of this.children) out.push(...c.child.segments(offset + c.start));
655
+ return out;
656
+ }
657
+ /** How many timelines are directly under this one. */
658
+ timelines() {
659
+ return this.children.length;
660
+ }
661
+ duration() {
662
+ return this.spans;
663
+ }
664
+ time(value) {
665
+ if (value === void 0) return this.now;
666
+ if (this.killed) return this;
667
+ this.now = value;
668
+ if (!this.prepared) {
669
+ this.prepareDeep();
670
+ const counts = /* @__PURE__ */ new Map();
671
+ this.countChannels(counts);
672
+ this.markExclusive(counts);
673
+ }
674
+ this.evaluate(quantize(value));
675
+ return this;
676
+ }
677
+ /** GSAP's name for `time(t)`. */
678
+ seek(t) {
679
+ return this.time(t);
680
+ }
681
+ progress(p) {
682
+ if (p === void 0) return this.spans > 0 ? this.now / this.spans : 0;
683
+ return this.time(p * this.spans);
684
+ }
685
+ kill() {
686
+ this.killed = true;
687
+ this.channels.clear();
688
+ this.channelList.length = 0;
689
+ this.children.length = 0;
690
+ }
691
+ // --- evaluation ----------------------------------------------------------------------------------------
692
+ /**
693
+ * What GSAP ended up with once it had been played to the end and back (what Movie used to do at build), as rules that depend
694
+ * on the segments of a property only:
695
+ * - a `from` / `fromTo` shows its start from the moment it is built, so a `to` that comes first starts at the start of the
696
+ * `from` built last, else at the property's own value;
697
+ * - a `to` (and a `set`) starts where the segment before it ended;
698
+ * - a `from` ends where the segment before it ended, or, with none before, where the property was when it was built;
699
+ * - before the first segment the property shows that segment's start.
700
+ */
701
+ prepare(ch) {
702
+ if (!ch.dirty) return;
703
+ ch.segs.sort((a, b) => a.start - b.start || a.order - b.order);
704
+ ch.dirty = false;
705
+ let latest = null;
706
+ for (const s of ch.segs) if ((s.kind === "from" || s.kind === "fromTo") && s.from0 !== null && (!latest || s.order > latest.order)) latest = s;
707
+ ch.base0 ??= { v: ch.acc.get() };
708
+ let cur = latest ? latest.from0 : ch.base0.v;
709
+ const base = cur;
710
+ let first = true;
711
+ for (const s of ch.segs) {
712
+ const before = cur;
713
+ s.from = s.from0;
714
+ s.to = s.to0;
715
+ s.toRel = s.toRel0;
716
+ if (s.fnTo) {
717
+ s.from = s.fnFrom ? s.fnFrom(cur) : cur;
718
+ s.to = s.fnTo(s.from, before, base);
719
+ s.interp = void 0;
720
+ } else {
721
+ if (s.partner) {
722
+ s.from = s.partner.from;
723
+ s.to = s.partner.to;
724
+ }
725
+ if (s.from === null) s.from = cur;
726
+ if (s.kind === "from") s.to = first ? s.createCur : cur;
727
+ if (s.toRel) {
728
+ s.to = s.from + s.to;
729
+ s.toRel = false;
730
+ }
731
+ }
732
+ if (first) ch.pre = s.fnTo ? before : s.from;
733
+ cur = endValue(s);
734
+ first = false;
735
+ }
736
+ }
737
+ /** Every channel of this timeline and of its children reads the property it starts from before any of them writes. */
738
+ prepareDeep() {
739
+ if (this.prepared) return;
740
+ this.prepared = true;
741
+ for (const ch of this.channelList) this.prepare(ch);
742
+ this.children.sort((a, b) => a.start - b.start || a.order - b.order);
743
+ for (const c of this.children) {
744
+ c.last = NaN;
745
+ c.child.prepareDeep();
746
+ }
747
+ }
748
+ /** How many channels write each (target, property) in this timeline and everything under it. */
749
+ countChannels(into) {
750
+ for (const ch of this.channelList) {
751
+ let m = into.get(ch.target);
752
+ if (!m) into.set(ch.target, m = /* @__PURE__ */ new Map());
753
+ m.set(ch.acc.id, (m.get(ch.acc.id) ?? 0) + 1);
754
+ }
755
+ for (const c of this.children) c.child.countChannels(into);
756
+ }
757
+ isExclusive(counts) {
758
+ for (const ch of this.channelList) if ((counts.get(ch.target)?.get(ch.acc.id) ?? 0) > 1) return false;
759
+ return this.children.every((c) => c.child.isExclusive(counts));
760
+ }
761
+ markExclusive(counts) {
762
+ for (const c of this.children) {
763
+ c.exclusive = c.child.isExclusive(counts);
764
+ c.child.markExclusive(counts);
765
+ }
766
+ }
767
+ evaluate(t) {
768
+ this.prepareDeep();
769
+ for (const ch of this.channelList) {
770
+ const segs = ch.segs;
771
+ let lo = 0, hi = segs.length;
772
+ while (lo < hi) {
773
+ const mid = lo + hi >> 1;
774
+ if (segs[mid].start <= t + TINY) lo = mid + 1;
775
+ else hi = mid;
776
+ }
777
+ let k = lo - 1;
778
+ while (k >= 0 && segs[k].repeat > 0 && t - segs[k].start <= TINY) k--;
779
+ const seg = k >= 0 ? segs[k] : null;
780
+ if (!seg) {
781
+ const pre = settle(ch.acc, ch.pre);
782
+ write(ch, segs[0], pre);
783
+ const head = segs[0];
784
+ if (pre !== ch.last) {
785
+ ch.last = pre;
786
+ head?.onUpdate?.();
787
+ }
788
+ continue;
789
+ }
790
+ const v = mix(seg, ratioAt(seg, t), over);
791
+ write(ch, seg, v);
792
+ if (v !== ch.last) {
793
+ ch.last = v;
794
+ seg.onUpdate?.();
795
+ }
796
+ }
797
+ for (const c of this.children) {
798
+ const local = Math.min(Math.max(t - c.start, 0), c.child.duration());
799
+ if (c.exclusive && local === c.last) continue;
800
+ c.last = local;
801
+ c.child.now = local;
802
+ c.child.evaluate(quantize(local));
803
+ }
804
+ }
805
+ };
806
+ function write(ch, seg, v) {
807
+ if (!ch.acc.always && ch.acc.get() === v) return;
808
+ (seg?.write ?? ch.acc.set)(v);
809
+ }
810
+ function endValue(s) {
811
+ if (s.dur === 0 && s.repeat === 0) return s.to;
812
+ return s.yoyo && s.repeat % 2 === 1 ? s.from : s.to;
813
+ }
814
+ function read(value, acc) {
815
+ if (acc.kind === "raw" || acc.kind === "css") return { rel: false, n: value };
816
+ if (acc.kind === "color") {
817
+ const c = parseColor(value);
818
+ return c === null ? null : { rel: false, n: c };
819
+ }
820
+ const r = parseRel(value);
821
+ return r ? { rel: r.rel, n: r.n * acc.unit } : null;
822
+ }
823
+ function settle(acc, v) {
824
+ return acc.kind !== "num" ? v : Math.round(v * acc.round) / acc.round;
825
+ }
826
+ var over = false;
827
+ function mix(s, r, ended) {
828
+ const acc = s.channel.acc;
829
+ if (s.step) return s.to;
830
+ if (s.fnMake) {
831
+ const interp = s.interp ??= s.fnMake(s.from, s.to);
832
+ return interp(Math.round(r * 1e6) / 1e6);
833
+ }
834
+ if (acc.kind === "raw") return s.to;
835
+ if (acc.kind === "css") return (s.interp ??= rgbInterp(String(s.from), String(s.to)))(r);
836
+ if (acc.kind === "color") return ended ? endValue(s) : lerpColor(s.from, s.to, r);
837
+ if ((ended || r === 1) && acc.exactEnd) return endValue(s);
838
+ const k = acc.round;
839
+ return Math.round((s.from + (s.to - s.from) * r) * k) / k;
840
+ }
841
+ function ratioAt(s, t) {
842
+ const lt = t - s.start;
843
+ over = true;
844
+ if (s.dur === 0) return 1;
845
+ const total = s.dur * (s.repeat + 1) + s.repeatDelay * s.repeat;
846
+ if (quantize(lt) >= quantize(total) - TINY) return endRatio(s);
847
+ over = false;
848
+ let i = 0;
849
+ let within = lt;
850
+ if (s.repeat > 0) {
851
+ const period = s.dur + s.repeatDelay;
852
+ const q = quantize(lt);
853
+ i = Math.min(s.repeat, Math.max(0, Math.floor(lt / period)));
854
+ while (i > 0 && q <= quantize(i * period)) i--;
855
+ while (i < s.repeat && q > quantize((i + 1) * period)) i++;
856
+ within = quantize(lt - i * period);
857
+ }
858
+ let p = within >= s.dur ? 1 : within <= 0 ? 0 : within / s.dur;
859
+ if (s.yoyo && i % 2 === 1) p = 1 - p;
860
+ return s.ease(p);
861
+ }
862
+ function endRatio(s) {
863
+ return s.yoyo && s.repeat % 2 === 1 ? 0 : 1;
864
+ }
865
+
866
+ // src/core/timelineEngine.ts
867
+ function createTimeline(opts = {}) {
868
+ return new PureTimeline({ paused: opts.paused, defaults: { ease: opts.defaults?.ease ?? "power1.out" } });
869
+ }
870
+ function lengthen(timeline, duration) {
871
+ timeline.add(spacer(duration));
872
+ }
873
+ function parseEase(name) {
874
+ return pureEase(name);
875
+ }
876
+ function setNow(target, vars) {
877
+ const t = target;
878
+ for (const key of Object.keys(vars)) {
879
+ if (key === "pixi") {
880
+ for (const k of Object.keys(vars.pixi)) {
881
+ const accs = pixiAccessors(t, k);
882
+ if (!accs) {
883
+ if (!PIXI_INERT.has(k)) console.warn(`pixi-effects: "${k}" is not a property a keyframe or initial can set; it is ignored`);
884
+ continue;
885
+ }
886
+ const raw = vars.pixi[k];
887
+ for (const acc of accs) {
888
+ const n = acc.kind === "color" ? colorNumber(raw) : Number(raw) * acc.unit;
889
+ if (!Number.isNaN(n)) acc.set(n);
890
+ }
891
+ }
892
+ } else {
893
+ const v = vars[key];
894
+ plainAccessor(t, key).set(typeof v === "number" ? Math.round(v * 1e6) / 1e6 : v);
895
+ }
896
+ }
897
+ }
898
+ function colorNumber(v) {
899
+ const c = parseColor(v);
900
+ if (c === null) {
901
+ console.warn(`pixi-effects: cannot read the colour ${JSON.stringify(v)} (use #rgb, #rrggbb, rgb(...) or a number); it is left as it was`);
902
+ return NaN;
903
+ }
904
+ return c;
905
+ }
906
+ function viaPixi(s) {
907
+ try {
908
+ const [r, g, b, a] = new PIXI.Color(s).toArray();
909
+ return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a];
910
+ } catch {
911
+ return null;
912
+ }
913
+ }
914
+ function interpolateColors(a, b) {
915
+ return rgbInterp(a, b, viaPixi);
916
+ }
917
+
918
+ // src/expr/random.ts
919
+ function mix2(h) {
920
+ h = Math.imul(h ^ h >>> 16, 2246822507);
921
+ h = Math.imul(h ^ h >>> 13, 3266489909);
922
+ return (h ^ h >>> 16) >>> 0;
923
+ }
924
+ function fold(n) {
925
+ if (!Number.isFinite(n)) return 0;
926
+ const i = Math.floor(n);
927
+ const f = n - i;
928
+ return (i | 0) ^ Math.imul(Math.floor(f * 1048576), 2654435761);
929
+ }
930
+ function rand(seed) {
931
+ return mix2(fold(seed) + 2654435769) / 4294967296;
932
+ }
933
+ function random(seed) {
934
+ let a = fold(seed) + 1831565813 | 0;
935
+ return () => {
936
+ a = a + 1831565813 | 0;
937
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
938
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
939
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
940
+ };
941
+ }
942
+ function noise(x, seed = 0) {
943
+ if (!Number.isFinite(x)) return 0;
944
+ const i = Math.floor(x);
945
+ const f = x - i;
946
+ const s = fold(seed) * 668265261;
947
+ const a = mix2((i | 0) + s) / 2147483648 - 1;
948
+ const b = mix2((i + 1 | 0) + s) / 2147483648 - 1;
949
+ const u = f * f * f * (f * (f * 6 - 15) + 10);
950
+ return a + (b - a) * u;
951
+ }
169
952
 
170
953
  // src/expr/Parser.ts
171
954
  var FUNCS = {
@@ -326,7 +1109,7 @@ function parsePrimary(c) {
326
1109
  }
327
1110
  throw new Error(`unexpected token "${t.type}"`);
328
1111
  }
329
- function parse(src) {
1112
+ function parse2(src) {
330
1113
  const c = new Cursor(tokenize(src));
331
1114
  const node = parseExpr(c);
332
1115
  if (c.peek()) throw new Error("trailing input");
@@ -376,7 +1159,7 @@ function evaluateExpr(source, scope) {
376
1159
  let ast = astCache.get(source);
377
1160
  if (ast === void 0) {
378
1161
  try {
379
- ast = parse(source);
1162
+ ast = parse2(source);
380
1163
  } catch (e) {
381
1164
  console.warn(`pixi-effects: expression failed: ${source}`, e);
382
1165
  ast = ZERO_NODE;
@@ -394,19 +1177,29 @@ function evaluateExpr(source, scope) {
394
1177
  // src/expr/normalizeProps.ts
395
1178
  function normalizeProps(input, scope, options = {}) {
396
1179
  const skip = new Set(options.skipKeys ?? []);
397
- return walk(input, scope, skip, null);
1180
+ return walk(input, scope, skip, null, options);
398
1181
  }
399
- function walk(value, scope, skip, currentKey) {
1182
+ var RELATIVE = /^\s*([+-])=([\s\S]*)$/;
1183
+ function walk(value, scope, skip, currentKey, options) {
400
1184
  if (Array.isArray(value)) {
401
- return value.map((v) => walk(v, scope, skip, currentKey));
1185
+ return value.map((v) => walk(v, scope, skip, currentKey, options));
402
1186
  }
403
1187
  if (value !== null && typeof value === "object") {
404
1188
  const out = {};
405
1189
  for (const k of Object.keys(value)) {
406
- out[k] = walk(value[k], scope, skip, k);
1190
+ out[k] = walk(value[k], scope, skip, k, options);
407
1191
  }
408
1192
  return out;
409
1193
  }
1194
+ if (typeof value === "string" && !(currentKey !== null && skip.has(currentKey))) {
1195
+ const rel = RELATIVE.exec(value);
1196
+ if (rel) {
1197
+ const n = evaluateExpr(rel[2].trim(), scope);
1198
+ if (options.relative === "allow") return `${rel[1]}=${n}`;
1199
+ console.warn(`pixi-effects: "${value.trim()}"${currentKey ? ` (${currentKey})` : ""} is a relative value (measured from where the layer is): it works in a keyframe's to / from / set, not in ${options.where ?? "an initial or a style"}; it is read as the number ${rel[1] === "-" ? -n : n}`);
1200
+ return rel[1] === "-" ? -n : n;
1201
+ }
1202
+ }
410
1203
  if (isExpr(value) && !(currentKey !== null && skip.has(currentKey))) {
411
1204
  if (looksLikeColorString(value)) return value;
412
1205
  return evaluateExpr(value, scope);
@@ -418,56 +1211,6 @@ function looksLikeColorString(s) {
418
1211
  if (s.charCodeAt(0) === 35) return true;
419
1212
  return /^(rgb|hsl|oklab|oklch|lab|lch)\b/i.test(s);
420
1213
  }
421
- function cubicBezierEase(x1, y1, x2, y2) {
422
- const cx = 3 * x1, bx = 3 * (x2 - x1) - cx, ax = 1 - cx - bx;
423
- const cy = 3 * y1, by = 3 * (y2 - y1) - cy, ay = 1 - cy - by;
424
- const X = (t) => ((ax * t + bx) * t + cx) * t;
425
- const Y = (t) => ((ay * t + by) * t + cy) * t;
426
- const dX = (t) => (3 * ax * t + 2 * bx) * t + cx;
427
- return (p) => {
428
- if (p <= 0) return 0;
429
- if (p >= 1) return 1;
430
- let t = p;
431
- for (let i = 0; i < 8; i++) {
432
- const err = X(t) - p;
433
- if (Math.abs(err) < 1e-9) return Y(t);
434
- const d = dX(t);
435
- if (Math.abs(d) < 1e-6) break;
436
- t -= err / d;
437
- }
438
- let lo = 0, hi = 1;
439
- t = p;
440
- for (let i = 0; i < 60; i++) {
441
- const x = X(t);
442
- if (Math.abs(x - p) < 1e-10) break;
443
- if (x < p) lo = t;
444
- else hi = t;
445
- t = (lo + hi) / 2;
446
- }
447
- return Y(t);
448
- };
449
- }
450
- var NUM = String.raw`\s*(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)\s*`;
451
- var RE = new RegExp(`^\\s*cubic-bezier\\(${NUM},${NUM},${NUM},${NUM}\\)\\s*$`, "i");
452
- function cubicBezierProblem(name) {
453
- const m = RE.exec(name);
454
- if (!m) return 'expected "cubic-bezier(x1, y1, x2, y2)" with four numbers, like CSS';
455
- const x1 = Number(m[1]), x2 = Number(m[3]);
456
- if (x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) return "x1 and x2 must be between 0 and 1 (y1 and y2 may overshoot)";
457
- return null;
458
- }
459
- var registered2 = false;
460
- function registerCubicBezierEase() {
461
- if (registered2) return;
462
- registered2 = true;
463
- const base = ((p) => p);
464
- base.config = (x1, y1, x2, y2) => {
465
- const bad = [x1, y1, x2, y2].some((v) => typeof v !== "number" || !Number.isFinite(v)) || x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1;
466
- return bad ? (p) => p : cubicBezierEase(x1, y1, x2, y2);
467
- };
468
- gsap.gsap.registerEase("cubic-bezier", base);
469
- }
470
- registerCubicBezierEase();
471
1214
 
472
1215
  // src/core/ease.ts
473
1216
  var FAMILIES = ["power1", "power2", "power3", "power4", "back", "elastic", "bounce", "circ", "expo", "sine"];
@@ -479,7 +1222,7 @@ function checkEase(ease, where) {
479
1222
  const problem = springProblem(ease);
480
1223
  if (!problem || warned.has(ease)) return;
481
1224
  warned.add(ease);
482
- console.warn(`pixi-effects: ${where}: unknown ease "${ease}" \u2014 ${problem}. GSAP would run it as its default ease, power1.out.`);
1225
+ console.warn(`pixi-effects: ${where}: unknown ease "${ease}" \u2014 ${problem}. It runs as its default ease, power1.out.`);
483
1226
  return;
484
1227
  }
485
1228
  if (/^\s*cubic-bezier/i.test(ease)) {
@@ -489,20 +1232,19 @@ function checkEase(ease, where) {
489
1232
  console.warn(`pixi-effects: ${where}: unknown ease "${ease}" \u2014 ${problem}. It runs as 'none' (a straight line).`);
490
1233
  return;
491
1234
  }
492
- const found = gsap.gsap.parseEase(ease);
493
- if (typeof found === "function") return;
1235
+ if (isEaseName(ease)) return;
494
1236
  if (warned.has(ease)) return;
495
1237
  warned.add(ease);
496
1238
  if (ease === "") {
497
- console.warn(`pixi-effects: ${where}: an empty ease (GSAP would run its default, power1.out): write 'none' for no easing, or a name such as 'power2.out'`);
1239
+ console.warn(`pixi-effects: ${where}: an empty ease (it would run as power1.out): write 'none' for no easing, or a name such as 'power2.out'`);
498
1240
  return;
499
1241
  }
500
- if (found && /^power[1-4]$/i.test(ease)) {
1242
+ if (/^(power[1-4]|quad|cubic|quart|quint|strong|back|elastic|bounce|circ|expo|sine)$/i.test(ease)) {
501
1243
  console.warn(`pixi-effects: ${where}: "${ease}" is a family, not an ease \u2014 write ${ease.toLowerCase()}.out, ${ease.toLowerCase()}.in or ${ease.toLowerCase()}.inOut`);
502
1244
  return;
503
1245
  }
504
1246
  const guess = chunkTWZNOUM7_cjs.suggestName(ease, EASE_NAMES);
505
- console.warn(`pixi-effects: ${where}: unknown ease "${ease}"${guess ? ` \u2014 did you mean "${guess}"?` : ""} (GSAP would run it as its default ease, power1.out). Examples: ${EASE_NAMES.slice(0, 7).join(", ")}, back.out(1.7), elastic.out(1, 0.3), steps(8)`);
1247
+ console.warn(`pixi-effects: ${where}: unknown ease "${ease}"${guess ? ` \u2014 did you mean "${guess}"?` : ""} (it runs as its default ease, power1.out). Examples: ${EASE_NAMES.slice(0, 7).join(", ")}, back.out(1.7), elastic.out(1, 0.3), steps(8)`);
506
1248
  }
507
1249
 
508
1250
  // src/core/Timeline.ts
@@ -604,16 +1346,7 @@ var PIXI_SHORTHANDS = /* @__PURE__ */ new Set([
604
1346
  "tileScaleX",
605
1347
  "tileScaleY",
606
1348
  "tint",
607
- "colorize",
608
- "colorizeAmount",
609
- "colorMatrixFilter",
610
- "blur",
611
- "blurX",
612
- "blurY",
613
- "blurPadding",
614
1349
  "autoAlpha",
615
- "lineColor",
616
- "lineAlpha",
617
1350
  "fillColor",
618
1351
  "fillAlpha"
619
1352
  ]);
@@ -637,7 +1370,7 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
637
1370
  const kf = normalizeKeyframe(raw, parentDuration);
638
1371
  const at = offset + kf.at;
639
1372
  if (kf.kind === "set") {
640
- const resolved = normalizeProps(kf.set, scope, { skipKeys });
1373
+ const resolved = normalizeProps(kf.set, scope, { skipKeys, relative: "allow" });
641
1374
  const { rest, routed } = splitRouted(resolved, routers);
642
1375
  for (const r of routed) timeline.set(r.target, { [r.prop]: r.value }, at);
643
1376
  const { ownProps, filterProps } = partitionProps(rest);
@@ -648,7 +1381,7 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
648
1381
  timeline.set(f, props, at);
649
1382
  }
650
1383
  } else if (kf.kind === "to") {
651
- const resolved = normalizeProps(kf.to, scope, { skipKeys });
1384
+ const resolved = normalizeProps(kf.to, scope, { skipKeys, relative: "allow" });
652
1385
  const { rest, routed } = splitRouted(resolved, routers);
653
1386
  for (const r of routed)
654
1387
  timeline.to(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
@@ -661,7 +1394,7 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
661
1394
  timeline.to(f, { ...props, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
662
1395
  }
663
1396
  } else if (kf.kind === "from") {
664
- const resolved = normalizeProps(kf.from, scope, { skipKeys });
1397
+ const resolved = normalizeProps(kf.from, scope, { skipKeys, relative: "allow" });
665
1398
  const { rest, routed } = splitRouted(resolved, routers);
666
1399
  for (const r of routed)
667
1400
  timeline.from(r.target, { [r.prop]: r.value, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
@@ -674,8 +1407,8 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
674
1407
  timeline.from(f, { ...props, duration: kf.duration, ease: kf.ease, ...kf.loop }, at);
675
1408
  }
676
1409
  } else {
677
- const fromResolved = normalizeProps(kf.from, scope, { skipKeys });
678
- const toResolved = normalizeProps(kf.to, scope, { skipKeys });
1410
+ const fromResolved = normalizeProps(kf.from, scope, { skipKeys, where: "the from side of a from + to keyframe" });
1411
+ const toResolved = normalizeProps(kf.to, scope, { skipKeys, relative: "allow" });
679
1412
  const fromRouted = splitRouted(fromResolved, routers);
680
1413
  const toRouted = splitRouted(toResolved, routers);
681
1414
  const fromByKey = new Map(fromRouted.routed.map((r) => [r.key, r]));
@@ -725,15 +1458,15 @@ function applyKeyframes(timeline, target, keyframes, parentDuration, scope, skip
725
1458
  }
726
1459
  function applyInitial(target, initial, scope, skipKeys = [], routers) {
727
1460
  if (!initial) return;
728
- const resolved = normalizeProps(initial, scope, { skipKeys });
1461
+ const resolved = normalizeProps(initial, scope, { skipKeys, where: "initial" });
729
1462
  const { rest, routed } = splitRouted(resolved, routers);
730
- for (const r of routed) gsap.gsap.set(r.target, { [r.prop]: r.value });
1463
+ for (const r of routed) setNow(r.target, { [r.prop]: r.value });
731
1464
  const { ownProps, filterProps } = partitionProps(rest);
732
- if (Object.keys(ownProps).length > 0) gsap.gsap.set(target, pixiwrap(ownProps));
1465
+ if (Object.keys(ownProps).length > 0) setNow(target, pixiwrap(ownProps));
733
1466
  for (const [name, props] of Object.entries(filterProps)) {
734
1467
  const f = findFilter(target, name);
735
1468
  if (!f) continue;
736
- gsap.gsap.set(f, props);
1469
+ setNow(f, props);
737
1470
  }
738
1471
  }
739
1472
 
@@ -1168,7 +1901,7 @@ var GrainFilter = class extends PIXI.Filter {
1168
1901
  };
1169
1902
  var WARP_KINDS = ["wave", "haze"];
1170
1903
  var OPTION_KEYS = ["kind", "strength", "scale", "speed", "angle", "seed"];
1171
- var DEG = Math.PI / 180;
1904
+ var DEG2 = Math.PI / 180;
1172
1905
  var HASH_GL2 = `
1173
1906
  uint hash(uint x) { x ^= x >> 16; x *= 0x7feb352du; x ^= x >> 15; x *= 0x846ca68bu; x ^= x >> 16; return x; }
1174
1907
  float lattice(ivec2 p, uint k) {
@@ -1340,7 +2073,7 @@ var WarpFilter = class extends PIXI.Filter {
1340
2073
  uStrength: { value: strength, type: "f32" },
1341
2074
  uScale: { value: scale, type: "f32" },
1342
2075
  uSpeed: { value: speed, type: "f32" },
1343
- uAngle: { value: angle * DEG, type: "f32" },
2076
+ uAngle: { value: angle * DEG2, type: "f32" },
1344
2077
  uSeed: { value: Math.floor(seed), type: "f32" },
1345
2078
  uMode: { value: kind === "haze" ? 1 : 0, type: "f32" },
1346
2079
  uTime: { value: 0, type: "f32" }
@@ -1378,10 +2111,10 @@ var WarpFilter = class extends PIXI.Filter {
1378
2111
  this.u.uSpeed = v;
1379
2112
  }
1380
2113
  get angle() {
1381
- return this.u.uAngle / DEG;
2114
+ return this.u.uAngle / DEG2;
1382
2115
  }
1383
2116
  set angle(v) {
1384
- this.u.uAngle = v * DEG;
2117
+ this.u.uAngle = v * DEG2;
1385
2118
  }
1386
2119
  get seed() {
1387
2120
  return this.u.uSeed;
@@ -1659,6 +2392,8 @@ var NullSequence = class extends Sequence {
1659
2392
  this.buildFilters();
1660
2393
  }
1661
2394
  };
2395
+
2396
+ // src/core/remap.ts
1662
2397
  var BAGS = ["set", "to", "from"];
1663
2398
  function timeRemapOf(spec, prop = "time") {
1664
2399
  const hasInitial = !!spec.initial && "time" in spec.initial;
@@ -1739,7 +2474,7 @@ function bindClock(timeline, clock, prop, remap, duration, at, scope) {
1739
2474
  timeline.fromTo(clock, { [prop]: from }, { [prop]: from + s * duration, duration, ease: "none" }, at);
1740
2475
  }
1741
2476
  function clockTable(remap, prop, duration, at, scope, to) {
1742
- const probe = gsap.gsap.timeline({ paused: true });
2477
+ const probe = createTimeline({ paused: true });
1743
2478
  const clock = { [prop]: remap.start ?? 0 };
1744
2479
  bindClock(probe, clock, prop, remap, duration, at, scope);
1745
2480
  probe.time(probe.duration());
@@ -1771,7 +2506,7 @@ function clockTable(remap, prop, duration, at, scope, to) {
1771
2506
  }
1772
2507
 
1773
2508
  // src/space/math.ts
1774
- var DEG2 = Math.PI / 180;
2509
+ var DEG3 = Math.PI / 180;
1775
2510
  var DEFAULT_FOV = 40;
1776
2511
  var MIN_FOV = 1;
1777
2512
  var MAX_FOV = 179;
@@ -1781,7 +2516,7 @@ function clampFov(fov) {
1781
2516
  return Math.min(MAX_FOV, Math.max(MIN_FOV, fov));
1782
2517
  }
1783
2518
  function homeDistance(compHeight, fovDeg) {
1784
- return compHeight / 2 / Math.tan(clampFov(fovDeg) * DEG2 / 2);
2519
+ return compHeight / 2 / Math.tan(clampFov(fovDeg) * DEG3 / 2);
1785
2520
  }
1786
2521
  function homeCamera(compW, compH, fov = DEFAULT_FOV) {
1787
2522
  return {
@@ -1835,7 +2570,7 @@ function cameraBasis(cam, compW, compH) {
1835
2570
  fx,
1836
2571
  fy,
1837
2572
  fz,
1838
- focal: compH / 2 / Math.tan(clampFov(cam.fov) * DEG2 / 2),
2573
+ focal: compH / 2 / Math.tan(clampFov(cam.fov) * DEG3 / 2),
1839
2574
  halfW: compW / 2,
1840
2575
  halfH: compH / 2
1841
2576
  };
@@ -1899,12 +2634,12 @@ function looksLikeLayerName(s) {
1899
2634
  var isName = (v, known) => typeof v === "string" && (known.has(v) || looksLikeLayerName(v));
1900
2635
  function focusNames(props, known) {
1901
2636
  const out = [];
1902
- const read = (bag) => {
2637
+ const read2 = (bag) => {
1903
2638
  const v = bag?.focus;
1904
2639
  if (isName(v, known)) out.push(v);
1905
2640
  };
1906
- read(props.initial);
1907
- for (const kf of props.keyframes ?? []) for (const bag of [kf.set, kf.to, kf.from]) read(bag);
2641
+ read2(props.initial);
2642
+ for (const kf of props.keyframes ?? []) for (const bag of [kf.set, kf.to, kf.from]) read2(bag);
1908
2643
  return out;
1909
2644
  }
1910
2645
  function withFocusResolved(props, zOf, known = /* @__PURE__ */ new Set()) {
@@ -1984,7 +2719,7 @@ var CameraSequence = class extends Sequence {
1984
2719
  carrier.fogColor = "#000000";
1985
2720
  carrier.fogAmount = 1;
1986
2721
  this.target = carrier;
1987
- const keys = chunkECA2WNRX_cjs.collectPropKeys(this.spec);
2722
+ const keys = chunkZMAD3KJU_cjs.collectPropKeys(this.spec);
1988
2723
  this.autoZ = !keys.has("z");
1989
2724
  this.dof = keys.has("focus") || keys.has("aperture");
1990
2725
  this.fog = keys.has("fogNear") || keys.has("fogFar") || keys.has("fogColor") || keys.has("fogAmount");
@@ -2069,7 +2804,7 @@ function smoothstep(e0, e1, x) {
2069
2804
  const t = Math.min(1, Math.max(0, (x - e0) / (e1 - e0)));
2070
2805
  return t * t * (3 - 2 * t);
2071
2806
  }
2072
- var DEG3 = Math.PI / 180;
2807
+ var DEG4 = Math.PI / 180;
2073
2808
  function norm(v) {
2074
2809
  const l = Math.hypot(v.x, v.y, v.z) || 1;
2075
2810
  return { x: v.x / l, y: v.y / l, z: v.z / l };
@@ -2077,7 +2812,7 @@ function norm(v) {
2077
2812
  var dot = (a, b) => a.x * b.x + a.y * b.y + a.z * b.z;
2078
2813
  var sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y, z: a.z - b.z });
2079
2814
  function coneCos(l) {
2080
- const half = Math.min(179, Math.max(0, l.coneAngle)) * DEG3 / 2;
2815
+ const half = Math.min(179, Math.max(0, l.coneAngle)) * DEG4 / 2;
2081
2816
  const inner = half * (1 - Math.min(1, Math.max(0, l.coneFeather)));
2082
2817
  const cosOuter = Math.cos(half);
2083
2818
  const cosInner = Math.max(Math.cos(inner), cosOuter + 1e-4);
@@ -2211,24 +2946,8 @@ var LightSequence = class extends Sequence {
2211
2946
  };
2212
2947
 
2213
2948
  // src/core/revertibleSet.ts
2214
- function revertibleSet(timeline, at, read, write, value) {
2215
- let p = 0;
2216
- let before;
2217
- const proxy = {
2218
- get p() {
2219
- return p;
2220
- },
2221
- set p(next) {
2222
- if (next >= 1 && p < 1) {
2223
- before = read();
2224
- write(value);
2225
- } else if (next < 1 && p >= 1) {
2226
- write(before);
2227
- }
2228
- p = next;
2229
- }
2230
- };
2231
- timeline.set(proxy, { p: 1 }, at);
2949
+ function revertibleSet(timeline, at, read2, write2, value, property) {
2950
+ timeline.setValue(property?.[0] ?? read2, property?.[1] ?? "set", at, read2, write2, value);
2232
2951
  }
2233
2952
 
2234
2953
  // src/expr/colorInterp.ts
@@ -2367,30 +3086,26 @@ function shortestHueDelta(from, to) {
2367
3086
  // src/expr/colorTween.ts
2368
3087
  var css = (c) => typeof c === "number" ? "#" + c.toString(16).padStart(6, "0") : c;
2369
3088
  function tweenColor(timeline, target, key, fromValue, toValue, duration, ease, at, colorSpace, onUpdate, loop = {}) {
2370
- let interp = null;
2371
- const proxy = { p: 0 };
2372
- timeline.fromTo(
2373
- proxy,
2374
- { p: 0 },
2375
- {
2376
- p: 1,
2377
- duration,
2378
- ease,
2379
- ...loop,
2380
- onStart: () => {
2381
- const start = fromValue !== void 0 ? fromValue : target[key];
2382
- if (start === void 0) return;
2383
- interp = colorSpace === "rgb" ? gsap.gsap.utils.interpolate(css(start), css(toValue)) : buildColorInterp(start, toValue, colorSpace);
2384
- },
2385
- onUpdate: () => {
2386
- if (interp) {
2387
- target[key] = interp(proxy.p);
2388
- if (onUpdate) onUpdate();
2389
- }
2390
- }
3089
+ const none = () => void 0;
3090
+ timeline.tweenValue({
3091
+ holder: target,
3092
+ id: key,
3093
+ get: () => target[key],
3094
+ set: (v) => {
3095
+ if (v === void 0) return;
3096
+ target[key] = v;
3097
+ if (onUpdate) onUpdate();
2391
3098
  },
2392
- at
2393
- );
3099
+ from: fromValue !== void 0 ? () => fromValue : void 0,
3100
+ to: () => toValue,
3101
+ make: (a, b) => a === void 0 ? none : colorSpace === "rgb" ? interpolateColors(css(a), css(b)) : buildColorInterp(a, b, colorSpace),
3102
+ duration,
3103
+ ease,
3104
+ at,
3105
+ repeat: loop.repeat,
3106
+ yoyo: loop.yoyo,
3107
+ repeatDelay: loop.repeatDelay
3108
+ });
2394
3109
  }
2395
3110
 
2396
3111
  // src/sequences/Image.ts
@@ -2433,7 +3148,7 @@ var ImageSequence = class extends Sequence {
2433
3148
  const sprite = this.target;
2434
3149
  revertibleSet(timeline, at, () => sprite.tint, (c) => {
2435
3150
  sprite.tint = c;
2436
- }, setTint);
3151
+ }, setTint, [sprite, "tint"]);
2437
3152
  }
2438
3153
  if (toTint !== void 0) {
2439
3154
  tweenColor(
@@ -2588,37 +3303,37 @@ function validateGradientKeyframes(spec, who, warn = (m) => console.warn(m), kin
2588
3303
  if (animated && !start) say("a keyframe animates fillGradient but the layer has none to start from: give it a fillGradient first (top level or in initial)");
2589
3304
  }
2590
3305
  function tweenGradient(timeline, holder, fromPatch, toPatch, duration, ease, at, colorSpace, onChange, loop = {}) {
2591
- let a = null, b = null;
2592
- let resting = null;
2593
- let colors = [];
2594
- const proxy = { p: 0 };
2595
- timeline.fromTo(proxy, { p: 0 }, {
2596
- p: 1,
2597
- duration,
2598
- ease,
2599
- ...loop,
2600
- onStart: () => {
2601
- const live = holder.grad;
2602
- a = fromPatch !== void 0 ? mergeGrad(live, fromPatch) : live;
2603
- if (toPatch === void 0) resting ??= live;
2604
- b = toPatch !== void 0 ? mergeGrad(a, toPatch) : resting;
2605
- colors = a.stops.map((s, i) => colorSpace === "rgb" ? gsap.gsap.utils.interpolate(s.color, b.stops[i].color) : buildColorInterp(s.color, b.stops[i].color, colorSpace));
3306
+ timeline.tweenValue({
3307
+ holder,
3308
+ id: "grad",
3309
+ get: () => holder.grad,
3310
+ set: (g) => {
3311
+ holder.grad = g;
3312
+ onChange();
2606
3313
  },
2607
- onUpdate: () => {
2608
- if (!a || !b) return;
2609
- const t = proxy.p;
2610
- const l = (x, y) => x + (y - x) * t;
2611
- holder.grad = {
2612
- type: a.type,
2613
- angle: l(a.angle, b.angle),
2614
- center: [l(a.center[0], b.center[0]), l(a.center[1], b.center[1])],
2615
- innerRadius: l(a.innerRadius, b.innerRadius),
2616
- radius: l(a.radius, b.radius),
2617
- stops: a.stops.map((s, i) => ({ offset: l(s.offset, b.stops[i].offset), color: colors[i](t) }))
3314
+ from: fromPatch !== void 0 ? (prev) => mergeGrad(prev, fromPatch) : void 0,
3315
+ to: (start, prev) => toPatch !== void 0 ? mergeGrad(start, toPatch) : prev,
3316
+ make: (a, b) => {
3317
+ const colors = a.stops.map((s, i) => colorSpace === "rgb" ? interpolateColors(s.color, b.stops[i].color) : buildColorInterp(s.color, b.stops[i].color, colorSpace));
3318
+ return (t) => {
3319
+ const l = (x, y) => x + (y - x) * t;
3320
+ return {
3321
+ type: a.type,
3322
+ angle: l(a.angle, b.angle),
3323
+ center: [l(a.center[0], b.center[0]), l(a.center[1], b.center[1])],
3324
+ innerRadius: l(a.innerRadius, b.innerRadius),
3325
+ radius: l(a.radius, b.radius),
3326
+ stops: a.stops.map((s, i) => ({ offset: l(s.offset, b.stops[i].offset), color: colors[i](t) }))
3327
+ };
2618
3328
  };
2619
- onChange();
2620
- }
2621
- }, at);
3329
+ },
3330
+ duration,
3331
+ ease,
3332
+ at,
3333
+ repeat: loop.repeat,
3334
+ yoyo: loop.yoyo,
3335
+ repeatDelay: loop.repeatDelay
3336
+ });
2622
3337
  }
2623
3338
  var PAINT_SIZE = 512;
2624
3339
  var GradientPainter = class {
@@ -2663,16 +3378,20 @@ function bindGradientKeyframes(timeline, holder, keyframes, duration, offset, co
2663
3378
  const at = offset + resolveAt(kf.at, duration);
2664
3379
  const setV = bag(kf.set), fromV = bag(kf.from), toV = bag(kf.to);
2665
3380
  if (setV !== void 0) {
2666
- revertibleSet(
2667
- timeline,
2668
- at,
2669
- () => ({ state: holder.grad }),
2670
- (v) => {
2671
- holder.grad = v.state ?? mergeGrad(holder.grad, v.patch);
3381
+ timeline.tweenValue({
3382
+ holder,
3383
+ id: "grad",
3384
+ get: () => holder.grad,
3385
+ set: (g) => {
3386
+ holder.grad = g;
2672
3387
  onChange();
2673
3388
  },
2674
- { patch: setV }
2675
- );
3389
+ to: (start) => mergeGrad(start, setV),
3390
+ make: (_a, b) => () => b,
3391
+ duration: 0,
3392
+ ease: "none",
3393
+ at
3394
+ });
2676
3395
  }
2677
3396
  if (fromV !== void 0 || toV !== void 0) {
2678
3397
  tweenGradient(timeline, holder, fromV, toV, kfDuration(kf), kf.ease ?? "none", at, colorSpace, onChange, loopVars(kf));
@@ -2777,7 +3496,7 @@ var TextSequence = class extends Sequence {
2777
3496
  this.intrinsicWidth = text.width;
2778
3497
  this.intrinsicHeight = text.height;
2779
3498
  }
2780
- validateGradientKeyframes(this.spec, chunkECA2WNRX_cjs.describeLayer(this.spec), void 0, "text");
3499
+ validateGradientKeyframes(this.spec, chunkZMAD3KJU_cjs.describeLayer(this.spec), void 0, "text");
2781
3500
  const gradientSpec = this.spec.fillGradient ?? initialProps.fillGradient;
2782
3501
  if (gradientSpec) {
2783
3502
  this._grad = { grad: gradStateFrom(gradientSpec) };
@@ -2813,7 +3532,7 @@ var TextSequence = class extends Sequence {
2813
3532
  bindTextKeyframes(timeline, keyframes, this.duration, origin, () => this._template, (t) => {
2814
3533
  this._template = t;
2815
3534
  refresh();
2816
- }, chunkECA2WNRX_cjs.describeLayer(this.spec));
3535
+ }, chunkZMAD3KJU_cjs.describeLayer(this.spec));
2817
3536
  }
2818
3537
  };
2819
3538
  function runSuperWithStrippedSpec(inst, strippedSpec, timeline, offset) {
@@ -2864,7 +3583,7 @@ function bindCounterKeyframes(timeline, state, key, refresh, keyframes, parentDu
2864
3583
  revertibleSet(timeline, at, () => state[key], (n) => {
2865
3584
  state[key] = n;
2866
3585
  refresh();
2867
- }, v);
3586
+ }, v, [state, key]);
2868
3587
  }
2869
3588
  if (toV !== void 0 && fromV !== void 0) {
2870
3589
  timeline.fromTo(
@@ -2880,7 +3599,7 @@ function bindCounterKeyframes(timeline, state, key, refresh, keyframes, parentDu
2880
3599
  }
2881
3600
  }
2882
3601
  }
2883
- function bindTextKeyframes(timeline, keyframes, parentDuration, origin, read, write, who) {
3602
+ function bindTextKeyframes(timeline, keyframes, parentDuration, origin, read2, write2, who) {
2884
3603
  for (const kf of keyframes) {
2885
3604
  for (const [what, props] of [["to", kf.to], ["from", kf.from]]) {
2886
3605
  if (props && props.text !== void 0) {
@@ -2889,7 +3608,7 @@ function bindTextKeyframes(timeline, keyframes, parentDuration, origin, read, wr
2889
3608
  }
2890
3609
  const t = kf.set?.text;
2891
3610
  if (t === void 0) continue;
2892
- revertibleSet(timeline, origin + resolveAt(kf.at, parentDuration), read, write, String(t));
3611
+ revertibleSet(timeline, origin + resolveAt(kf.at, parentDuration), read2, write2, String(t));
2893
3612
  }
2894
3613
  }
2895
3614
  function animates(keyframes, key) {
@@ -2911,7 +3630,7 @@ function bindFillKeyframes(timeline, text, keyframes, parentDuration, offset, co
2911
3630
  revertibleSet(timeline, at, () => state.fill, (c) => {
2912
3631
  state.fill = c;
2913
3632
  writeFill();
2914
- }, setFill);
3633
+ }, setFill, [state, "fill"]);
2915
3634
  }
2916
3635
  if (toFill !== void 0) {
2917
3636
  tweenColor(
@@ -2958,12 +3677,12 @@ var AudioSequence = class extends Sequence {
2958
3677
  const playable = this._remap && this._remap.timeKfs.length > 0 ? Infinity : (data.duration ?? Infinity) / constant;
2959
3678
  if (!spec.loop && Number.isFinite(playable) && this.duration > playable + 0.05) {
2960
3679
  console.warn(
2961
- `pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(spec)}: audio asset "${spec.asset}" plays for ${playable.toFixed(1)}s (${data.duration.toFixed(1)}s at speed ${constant}) but the layer lasts ${this.duration.toFixed(1)}s, so it goes silent after ${playable.toFixed(1)}s. Add loop: true, or shorten the layer's duration.`
3680
+ `pixi-effects: ${chunkZMAD3KJU_cjs.describeLayer(spec)}: audio asset "${spec.asset}" plays for ${playable.toFixed(1)}s (${data.duration.toFixed(1)}s at speed ${constant}) but the layer lasts ${this.duration.toFixed(1)}s, so it goes silent after ${playable.toFixed(1)}s. Add loop: true, or shorten the layer's duration.`
2962
3681
  );
2963
3682
  }
2964
3683
  }
2965
3684
  async _buildSfx(spec) {
2966
- const who = chunkECA2WNRX_cjs.describeLayer(spec);
3685
+ const who = chunkZMAD3KJU_cjs.describeLayer(spec);
2967
3686
  const raw = spec;
2968
3687
  if (raw.asset !== void 0) console.warn(`pixi-effects: ${who}: has both asset and sfx \u2014 the sfx plays; remove one`);
2969
3688
  if (raw.loop) console.warn(`pixi-effects: ${who}: loop has no effect on an sfx \u2014 to repeat it, add one audio layer per hit (a JS loop)`);
@@ -2982,7 +3701,7 @@ var AudioSequence = class extends Sequence {
2982
3701
  });
2983
3702
  }
2984
3703
  async _buildMusic(spec) {
2985
- const who = chunkECA2WNRX_cjs.describeLayer(spec);
3704
+ const who = chunkZMAD3KJU_cjs.describeLayer(spec);
2986
3705
  const raw = spec;
2987
3706
  if (raw.asset !== void 0) console.warn(`pixi-effects: ${who}: has both asset and music \u2014 the music plays; remove one`);
2988
3707
  if (raw.sfx !== void 0) console.warn(`pixi-effects: ${who}: has both sfx and music \u2014 the music plays; remove one`);
@@ -3016,7 +3735,7 @@ var AudioSequence = class extends Sequence {
3016
3735
  out.push({
3017
3736
  ...sourceMap ? { sourceMap } : {},
3018
3737
  ...this._audioBuffer ? { buffer: this._audioBuffer } : { synth: this._synth },
3019
- layer: chunkECA2WNRX_cjs.describeLayer(this.spec),
3738
+ layer: chunkZMAD3KJU_cjs.describeLayer(this.spec),
3020
3739
  source: this._source,
3021
3740
  loop: !this._synth && !!this.spec.loop,
3022
3741
  start: t0,
@@ -3067,6 +3786,9 @@ var BUDGET_BYTES = 96 * 1024 * 1024;
3067
3786
  var MIN_HELD = 3;
3068
3787
  var JUMP_SECONDS = 1;
3069
3788
  var EPS = 1e-6;
3789
+ var LOW_BEHIND = 14;
3790
+ var MIN_HELD_TO_READ_BACK = 20;
3791
+ var MIN_HELD_TO_READ_START = 9;
3070
3792
  function reportDecodeError(err, time) {
3071
3793
  const isTransient = err instanceof DOMException && (err.name === "EncodingError" || err.name === "NotSupportedError");
3072
3794
  if (isTransient) console.debug("pixi-effects: transient video decode failure at", time, "s \u2014", err.name);
@@ -3085,9 +3807,22 @@ var FrameCache = class {
3085
3807
  _held = 0;
3086
3808
  _lock = Promise.resolve();
3087
3809
  _lastTime = null;
3810
+ _dir = 0;
3811
+ _gen = 0;
3812
+ _backBusy = false;
3813
+ _backGen = 0;
3814
+ _standbyBusy = false;
3815
+ /** A second pass at the start of the file, ready for the moment a looping video comes round. */
3816
+ _standby = null;
3817
+ _duration;
3818
+ _loop;
3819
+ /** How often it had to wait for a decoder (`restarts`, `singles`) and how many reads ahead it made: for tests and for `check`. */
3820
+ stats = { restarts: 0, singles: 0, prefetches: 0 };
3088
3821
  constructor(sink, options = {}) {
3089
3822
  this.sink = sink;
3090
3823
  this.capacity = options.capacity ?? 30;
3824
+ this._duration = options.duration ?? null;
3825
+ this._loop = !!options.loop;
3091
3826
  }
3092
3827
  /** How many decoded frames are held right now (the sequential reader's own list, plus the exact-time cache of the old path). */
3093
3828
  get heldFrames() {
@@ -3157,24 +3892,157 @@ var FrameCache = class {
3157
3892
  return null;
3158
3893
  }
3159
3894
  async _sequentialAt(time) {
3895
+ const last = this._lastTime;
3896
+ this._lastTime = time;
3897
+ if (last !== null && Math.abs(time - last) <= JUMP_SECONDS) this._dir = time > last + EPS ? 1 : time < last - EPS ? -1 : this._dir;
3898
+ let frame;
3160
3899
  try {
3161
- const last = this._lastTime;
3162
- this._lastTime = time;
3163
- const hit = this._covering(time);
3164
- if (hit) return hit.frame;
3165
- if (last === null || Math.abs(time - last) > JUMP_SECONDS) return await this._single(time);
3166
- const cur = this._cur;
3167
- if (this._iter && cur && time >= cur.ts && time - cur.ts <= JUMP_SECONDS) return await this._advance(time);
3168
- const backwards = !!cur && time < cur.ts;
3169
- return await this._restart(time, backwards ? Math.max(0, time - this._held / 30) : time);
3900
+ frame = await this._serve(time, last);
3170
3901
  } catch (err) {
3171
3902
  reportDecodeError(err, time);
3172
3903
  await this._closeIter();
3173
3904
  return null;
3174
3905
  }
3906
+ this._maybeReadAhead(time);
3907
+ return frame;
3908
+ }
3909
+ async _serve(time, last) {
3910
+ const hit = this._covering(time);
3911
+ if (hit) return hit.frame;
3912
+ const sb = this._standby;
3913
+ if (sb && time >= sb.cur.ts - EPS && time - sb.cur.ts <= JUMP_SECONDS && (!this._iter || !this._cur || this._cur.ts > time + JUMP_SECONDS)) return await this._adoptStandby(time);
3914
+ if (last === null || Math.abs(time - last) > JUMP_SECONDS) return await this._single(time);
3915
+ const cur = this._cur;
3916
+ if (this._iter && cur && time >= cur.ts && time - cur.ts <= JUMP_SECONDS) return await this._advance(time);
3917
+ const backwards = !!cur && time < cur.ts;
3918
+ return await this._restart(time, backwards ? Math.max(0, time - Math.max(1, this._held - 1) / 30) : time);
3919
+ }
3920
+ // ── reading ahead ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
3921
+ _maybeReadAhead(time) {
3922
+ if (this._held === 0) return;
3923
+ if (this._dir === -1) {
3924
+ this._dropStandby();
3925
+ let behind = 0, passed = 0, earliest = Infinity;
3926
+ for (const e of this._frames) {
3927
+ if (e.ts < time - EPS) behind++;
3928
+ else if (e.ts > time + EPS) passed++;
3929
+ if (e.ts < earliest) earliest = e.ts;
3930
+ }
3931
+ if (this._held >= MIN_HELD_TO_READ_BACK && behind < LOW_BEHIND && passed >= 3 && earliest > EPS && !this._backBusy) void this._readBack(earliest, passed);
3932
+ } else if (this._held >= MIN_HELD_TO_READ_START && this._dir === 1 && this._loop && this._duration !== null && time >= this._duration - JUMP_SECONDS && !this._standby && !this._standbyBusy) {
3933
+ void this._readStart();
3934
+ }
3935
+ }
3936
+ /** In the background: the window of frames before the earliest one held (reverse play comes to it next). */
3937
+ async _readBack(earliest, room) {
3938
+ this._backBusy = true;
3939
+ this.stats.prefetches++;
3940
+ const gen = this._gen, bg = this._backGen;
3941
+ const want = Math.max(3, Math.min(Math.floor(this._held * 0.6), room));
3942
+ let iter = null;
3943
+ const got = [];
3944
+ try {
3945
+ iter = this.sink.samples(Math.max(0, earliest - want / 30));
3946
+ for (; ; ) {
3947
+ const r = await iter.next();
3948
+ if (r.done) break;
3949
+ if (gen !== this._gen || r.value.timestamp >= earliest - EPS) {
3950
+ r.value.close?.();
3951
+ break;
3952
+ }
3953
+ got.push(r.value);
3954
+ if (bg !== this._backGen) break;
3955
+ }
3956
+ if (gen === this._gen && bg === this._backGen) {
3957
+ for (let k = 0; k < got.length; k++) {
3958
+ const s = got[k];
3959
+ const frame = s.toVideoFrame();
3960
+ this._keep({ ts: s.timestamp, end: got[k + 1]?.timestamp ?? earliest, frame }, frame);
3961
+ }
3962
+ }
3963
+ } catch (err) {
3964
+ reportDecodeError(err, earliest);
3965
+ } finally {
3966
+ for (const s of got) s.close?.();
3967
+ this._backBusy = false;
3968
+ try {
3969
+ await iter?.return?.(void 0);
3970
+ } catch {
3971
+ }
3972
+ }
3973
+ }
3974
+ /** In the background: the first frames of the file and a pass that stands behind them; held (pinned) until the loop comes round. */
3975
+ async _readStart() {
3976
+ this._standbyBusy = true;
3977
+ this.stats.prefetches++;
3978
+ const gen = this._gen;
3979
+ const want = Math.max(3, Math.min(10, Math.floor(this._held / 3)));
3980
+ let iter = null;
3981
+ const got = [];
3982
+ let keep = false;
3983
+ try {
3984
+ iter = this.sink.samples(0);
3985
+ for (; got.length < want + 1; ) {
3986
+ const r = await iter.next();
3987
+ if (r.done) break;
3988
+ if (gen !== this._gen) {
3989
+ r.value.close?.();
3990
+ break;
3991
+ }
3992
+ got.push(r.value);
3993
+ }
3994
+ if (gen === this._gen && got.length >= 2) {
3995
+ const next = got.pop();
3996
+ let cur = null;
3997
+ got.forEach((s, k) => {
3998
+ const frame = s.toVideoFrame();
3999
+ s.close?.();
4000
+ const entry = { ts: s.timestamp, end: (got[k + 1] ?? next).timestamp, frame, pinned: true };
4001
+ this._keep(entry, frame);
4002
+ cur = entry;
4003
+ });
4004
+ got.length = 0;
4005
+ if (cur) {
4006
+ this._standby = { iter, cur, next };
4007
+ keep = true;
4008
+ }
4009
+ }
4010
+ } catch (err) {
4011
+ reportDecodeError(err, 0);
4012
+ } finally {
4013
+ for (const s of got) s.close?.();
4014
+ this._standbyBusy = false;
4015
+ if (!keep) {
4016
+ try {
4017
+ await iter?.return?.(void 0);
4018
+ } catch {
4019
+ }
4020
+ }
4021
+ }
4022
+ }
4023
+ async _adoptStandby(time) {
4024
+ const sb = this._standby;
4025
+ this._standby = null;
4026
+ await this._closeIter();
4027
+ for (const e of this._frames) e.pinned = false;
4028
+ this._iter = sb.iter;
4029
+ this._next = sb.next;
4030
+ this._cur = sb.cur;
4031
+ return this._advance(time);
4032
+ }
4033
+ _dropStandby() {
4034
+ const sb = this._standby;
4035
+ if (!sb) return;
4036
+ this._standby = null;
4037
+ sb.next?.close?.();
4038
+ for (const e of this._frames.filter((x) => x.pinned)) {
4039
+ e.pinned = false;
4040
+ }
4041
+ void sb.iter.return?.(void 0);
3175
4042
  }
3176
4043
  /** One frame by itself (a seek): getSample, kept so that asking again for the same time costs nothing. The running pass, if any, is closed: it no longer follows the playhead. */
3177
4044
  async _single(time) {
4045
+ this.stats.singles++;
3178
4046
  await this._closeIter();
3179
4047
  this._cur = null;
3180
4048
  const sample = await this.sink.getSample(time);
@@ -3186,6 +4054,8 @@ var FrameCache = class {
3186
4054
  }
3187
4055
  /** Start a pass at `time` (a seek and the frames of one group of pictures), keeping what is still held for a step back. */
3188
4056
  async _restart(time, from = time) {
4057
+ this.stats.restarts++;
4058
+ this._backGen++;
3189
4059
  await this._closeIter();
3190
4060
  this._cur = null;
3191
4061
  const iter = this.sink.samples(from);
@@ -3249,12 +4119,27 @@ var FrameCache = class {
3249
4119
  }
3250
4120
  this._frames.push(entry);
3251
4121
  while (this._frames.length > this._held) {
3252
- const oldest = this._frames.find((e) => e !== this._cur && e !== entry);
3253
- if (!oldest) break;
3254
- this._frames.splice(this._frames.indexOf(oldest), 1);
3255
- oldest.frame.close?.();
4122
+ const victim = this._victim(entry);
4123
+ if (!victim) break;
4124
+ this._frames.splice(this._frames.indexOf(victim), 1);
4125
+ victim.frame.close?.();
3256
4126
  }
3257
4127
  }
4128
+ /**
4129
+ * The frame to let go: one the playhead has already passed (going forward those before it, going back those after it), the furthest
4130
+ * behind first; when there is none, the one furthest ahead of the playhead (the last to be needed). Least recently used when the
4131
+ * direction is not known. Frames of the standby (pinned) and the frame the pass stands on are never chosen.
4132
+ */
4133
+ _victim(except) {
4134
+ const cand = this._frames.filter((e) => e !== this._cur && e !== except && !e.pinned);
4135
+ if (cand.length === 0) return null;
4136
+ if (this._dir === 0) return cand[0];
4137
+ const at = this._lastTime ?? 0;
4138
+ const passed = cand.filter((e) => this._dir === 1 ? e.ts < at - EPS : e.ts > at + EPS);
4139
+ const farthest = (list, sign) => list.reduce((a, b) => sign * (b.ts - a.ts) > 0 ? b : a);
4140
+ if (passed.length) return farthest(passed, this._dir === 1 ? -1 : 1);
4141
+ return farthest(cand, this._dir === 1 ? 1 : -1);
4142
+ }
3258
4143
  async _closeIter() {
3259
4144
  const iter = this._iter;
3260
4145
  this._iter = null;
@@ -3277,6 +4162,8 @@ var FrameCache = class {
3277
4162
  }
3278
4163
  }
3279
4164
  dispose() {
4165
+ this._gen++;
4166
+ this._dropStandby();
3280
4167
  for (const f of this.cache.values()) f?.close?.();
3281
4168
  this.cache.clear();
3282
4169
  this._pending.clear();
@@ -3318,7 +4205,7 @@ var VideoSequence = class extends Sequence {
3318
4205
  this._sourceDuration = data.duration;
3319
4206
  this._remap = remapOf(this.spec, "currentTime", this._sourceDuration);
3320
4207
  this._audioBuffer = this.spec.audio !== false ? data.audioBuffer : null;
3321
- this._cache = new FrameCache(data.sink, { capacity: 30 });
4208
+ this._cache = new FrameCache(data.sink, { capacity: 30, duration: data.duration, loop: !!this.spec.loop });
3322
4209
  const probeFrame = await this._cache.getFrameAt(0);
3323
4210
  const w = probeFrame?.displayWidth ?? 1920;
3324
4211
  const h = probeFrame?.displayHeight ?? 1080;
@@ -3348,8 +4235,8 @@ var VideoSequence = class extends Sequence {
3348
4235
  currentTime = v;
3349
4236
  seq._cur = v;
3350
4237
  const mySeq = ++seq._drawSeq;
3351
- const lookup = sourceLookup(v, seq._sourceDuration, !!seq.spec.loop);
3352
- seq._cache.getFrameAt(lookup).then((frame) => {
4238
+ const lookup2 = sourceLookup(v, seq._sourceDuration, !!seq.spec.loop);
4239
+ seq._cache.getFrameAt(lookup2).then((frame) => {
3353
4240
  if (mySeq !== seq._drawSeq) return;
3354
4241
  if (!frame || !seq._ctx) return;
3355
4242
  seq._ctx.drawImage(frame, 0, 0);
@@ -3389,7 +4276,7 @@ var VideoSequence = class extends Sequence {
3389
4276
  out.push({
3390
4277
  ...sourceMap ? { sourceMap } : {},
3391
4278
  buffer: this._audioBuffer,
3392
- layer: chunkECA2WNRX_cjs.describeLayer(this.spec),
4279
+ layer: chunkZMAD3KJU_cjs.describeLayer(this.spec),
3393
4280
  source: `video "${this.spec.asset}"`,
3394
4281
  loop: !!this.spec.loop,
3395
4282
  start: baseTime + this.at,
@@ -3403,10 +4290,10 @@ var VideoSequence = class extends Sequence {
3403
4290
  const time = this._cur;
3404
4291
  if (!this.keepHidden && !this.target?.renderable) return;
3405
4292
  const mySeq = ++this._drawSeq;
3406
- const lookup = sourceLookup(time, this._sourceDuration, !!this.spec.loop);
4293
+ const lookup2 = sourceLookup(time, this._sourceDuration, !!this.spec.loop);
3407
4294
  let frame = null;
3408
4295
  try {
3409
- frame = await this._cache.getFrameAt(lookup);
4296
+ frame = await this._cache.getFrameAt(lookup2);
3410
4297
  } catch (err) {
3411
4298
  console.warn("pixi-effects: awaitFrameAt failed at", time, "s \u2014", err);
3412
4299
  return;
@@ -3657,20 +4544,20 @@ var ShapeSequence = class extends Sequence {
3657
4544
  for (const k of geomKeys) this._liveKeys.add(k);
3658
4545
  seedGeometry(this._state, this.spec, scope);
3659
4546
  seedStyle(this._state, this.spec.initial ?? {}, scope);
3660
- seedStrokeStyle(this._state, this.spec, this.spec.initial ?? {}, chunkECA2WNRX_cjs.describeLayer(this.spec));
4547
+ seedStrokeStyle(this._state, this.spec, this.spec.initial ?? {}, chunkZMAD3KJU_cjs.describeLayer(this.spec));
3661
4548
  if (this.spec.shape === "arc") {
3662
4549
  this._state.startAngle ??= 0;
3663
4550
  this._state.endAngle ??= 360;
3664
4551
  const top = this.spec, ini = this.spec.initial ?? {};
3665
4552
  if ("trimStart" in top || "trimEnd" in top || "trimStart" in ini || "trimEnd" in ini) {
3666
- console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.spec)}: trimStart / trimEnd do not apply to an arc: animate its endAngle (and startAngle) instead`);
4553
+ console.warn(`pixi-effects: ${chunkZMAD3KJU_cjs.describeLayer(this.spec)}: trimStart / trimEnd do not apply to an arc: animate its endAngle (and startAngle) instead`);
3667
4554
  }
3668
4555
  } else {
3669
4556
  this._state.trimStart ??= 0;
3670
4557
  this._state.trimEnd ??= 1;
3671
4558
  }
3672
4559
  const gradientSpec = this.spec.fillGradient ?? this.spec.initial?.fillGradient;
3673
- validateGradientKeyframes(this.spec, chunkECA2WNRX_cjs.describeLayer(this.spec));
4560
+ validateGradientKeyframes(this.spec, chunkZMAD3KJU_cjs.describeLayer(this.spec));
3674
4561
  if (gradientSpec) {
3675
4562
  if (hasGradientKeys(this.spec.keyframes)) {
3676
4563
  this._grad = { grad: gradStateFrom(gradientSpec) };
@@ -3712,9 +4599,9 @@ var ShapeSequence = class extends Sequence {
3712
4599
  }
3713
4600
  /** The prepared morph between `d` and `morphTo`, or null (and a warning when the spec asked for one that cannot work). */
3714
4601
  _setupMorph() {
3715
- const who = chunkECA2WNRX_cjs.describeLayer(this.spec);
4602
+ const who = chunkZMAD3KJU_cjs.describeLayer(this.spec);
3716
4603
  const top = this.spec;
3717
- const wanted = this.spec.shape === "path" && (this._state.morph !== void 0 || chunkECA2WNRX_cjs.collectPropKeys(this.spec).has("morph"));
4604
+ const wanted = this.spec.shape === "path" && (this._state.morph !== void 0 || chunkZMAD3KJU_cjs.collectPropKeys(this.spec).has("morph"));
3718
4605
  if (this.spec.shape !== "path") {
3719
4606
  if ("morphTo" in top) console.warn(`pixi-effects: ${who}: morphTo only works on a path shape (shape: 'path'); ignored`);
3720
4607
  return null;
@@ -3814,7 +4701,7 @@ function seedGeometry(state, spec, scope) {
3814
4701
  const initial = spec.initial ?? {};
3815
4702
  for (const k of keys) {
3816
4703
  const v = spec[k] ?? initial[k];
3817
- if (v !== void 0) state[k] = num(v, scope);
4704
+ if (v !== void 0) state[k] = num2(v, scope);
3818
4705
  }
3819
4706
  }
3820
4707
  var STROKE_CAPS = ["butt", "round", "square"];
@@ -3932,7 +4819,7 @@ function drawEllipseFromState(g, s) {
3932
4819
  const ay = s.anchorY ?? 0.5;
3933
4820
  g.ellipse((0.5 - ax) * 2 * rx, (0.5 - ay) * 2 * ry, rx, ry);
3934
4821
  }
3935
- var DEG4 = Math.PI / 180;
4822
+ var DEG5 = Math.PI / 180;
3936
4823
  function drawArcFromState(g, s) {
3937
4824
  const r = s.radius ?? 0;
3938
4825
  const ri = Math.max(0, Math.min(s.innerRadius ?? 0, r));
@@ -3947,7 +4834,7 @@ function drawArcFromState(g, s) {
3947
4834
  }
3948
4835
  endDeg = startDeg + Math.sign(endDeg - startDeg) * 359.999;
3949
4836
  }
3950
- const a0 = startDeg * DEG4, a1 = endDeg * DEG4;
4837
+ const a0 = startDeg * DEG5, a1 = endDeg * DEG5;
3951
4838
  const ccw = a1 < a0;
3952
4839
  if (filled) g.moveTo(r * Math.cos(a0), r * Math.sin(a0));
3953
4840
  g.arc(0, 0, r, a0, a1, ccw);
@@ -3961,10 +4848,10 @@ function drawArcFromState(g, s) {
3961
4848
  g.closePath();
3962
4849
  }
3963
4850
  function makeLineDraw(spec, scope) {
3964
- const fx = num(spec.from[0], scope);
3965
- const fy = num(spec.from[1], scope);
3966
- const tx = num(spec.to[0], scope);
3967
- const ty = num(spec.to[1], scope);
4851
+ const fx = num2(spec.from[0], scope);
4852
+ const fy = num2(spec.from[1], scope);
4853
+ const tx = num2(spec.to[0], scope);
4854
+ const ty = num2(spec.to[1], scope);
3968
4855
  return (g) => {
3969
4856
  g.moveTo(fx, fy);
3970
4857
  g.lineTo(tx, ty);
@@ -3974,7 +4861,7 @@ function makePolygonDraw(spec, scope) {
3974
4861
  if (spec.points.length === 0) return () => {
3975
4862
  };
3976
4863
  const flat = [];
3977
- for (const [x, y] of spec.points) flat.push(num(x, scope), num(y, scope));
4864
+ for (const [x, y] of spec.points) flat.push(num2(x, scope), num2(y, scope));
3978
4865
  const closed = !spec.open;
3979
4866
  return (g) => {
3980
4867
  g.poly(flat, closed);
@@ -4040,12 +4927,12 @@ function makeOutline(spec, scope, s, morph) {
4040
4927
  return [ellipseOutline((0.5 - (s.anchorX ?? 0.5)) * 2 * rx, (0.5 - (s.anchorY ?? 0.5)) * 2 * ry, rx, ry)];
4041
4928
  };
4042
4929
  case "line": {
4043
- const lines = [{ pts: [num(spec.from[0], scope), num(spec.from[1], scope), num(spec.to[0], scope), num(spec.to[1], scope)], closed: false }];
4930
+ const lines = [{ pts: [num2(spec.from[0], scope), num2(spec.from[1], scope), num2(spec.to[0], scope), num2(spec.to[1], scope)], closed: false }];
4044
4931
  return () => lines;
4045
4932
  }
4046
4933
  case "polygon": {
4047
4934
  const pts = [];
4048
- for (const [x, y] of spec.points) pts.push(num(x, scope), num(y, scope));
4935
+ for (const [x, y] of spec.points) pts.push(num2(x, scope), num2(y, scope));
4049
4936
  const lines = pts.length >= 4 ? [{ pts, closed: !spec.open }] : [];
4050
4937
  return () => lines;
4051
4938
  }
@@ -4073,7 +4960,7 @@ function bindLiveKeyframes(timeline, state, liveKeys, keyframes, parentDuration,
4073
4960
  const live2 = state;
4074
4961
  revertibleSet(timeline, at, () => live2[k], (v2) => {
4075
4962
  live2[k] = v2;
4076
- }, resolved);
4963
+ }, resolved, [live2, k]);
4077
4964
  }
4078
4965
  }
4079
4966
  }
@@ -4117,7 +5004,7 @@ function tweenLiveKey(timeline, state, key, fromValue, toValue, duration, ease,
4117
5004
  }
4118
5005
  }
4119
5006
  }
4120
- function num(v, scope) {
5007
+ function num2(v, scope) {
4121
5008
  if (typeof v === "number") return v;
4122
5009
  if (isExpr(v)) return evaluateExpr(v, scope);
4123
5010
  const parsed = parseFloat(v);
@@ -4125,7 +5012,7 @@ function num(v, scope) {
4125
5012
  }
4126
5013
  function numOrZero(v, scope) {
4127
5014
  if (typeof v === "number") return v;
4128
- if (typeof v === "string") return num(v, scope);
5015
+ if (typeof v === "string") return num2(v, scope);
4129
5016
  return 0;
4130
5017
  }
4131
5018
 
@@ -4174,13 +5061,13 @@ var SHADER_BUILTINS = ["iResolution", "iTime", "iFrame"];
4174
5061
  var SHADER_MISSING = ["iMouse", "iChannel0", "iChannel1", "iChannel2", "iChannel3", "iDate", "iTimeDelta", "iSampleRate"];
4175
5062
  var IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
4176
5063
  var HEX = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
4177
- var RESERVED = new Set(`attribute const uniform varying break continue do for while if else in out inout float int void bool true false discard return
5064
+ var RESERVED2 = new Set(`attribute const uniform varying break continue do for while if else in out inout float int void bool true false discard return
4178
5065
  mat2 mat3 mat4 mat2x2 mat2x3 mat2x4 mat3x2 mat3x3 mat3x4 mat4x2 mat4x3 mat4x4 vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 uvec2 uvec3 uvec4 uint
4179
5066
  sampler2D sampler3D samplerCube sampler2DShadow samplerCubeShadow sampler2DArray sampler2DArrayShadow isampler2D isampler3D isamplerCube isampler2DArray
4180
5067
  usampler2D usampler3D usamplerCube usampler2DArray struct layout centroid flat smooth precision invariant highp mediump lowp
4181
5068
  sample patch subroutine common partition active asm class union enum typedef template this resource goto inline noinline public static extern external
4182
5069
  interface long short double half fixed unsigned superp input output hvec2 hvec3 hvec4 fvec2 fvec3 fvec4 dvec2 dvec3 dvec4 filter sizeof cast namespace using`.split(/\s+/));
4183
- var isIdent = (name) => IDENT.test(name) && !name.startsWith("gl_") && !name.includes("__") && !RESERVED.has(name) && !SHADER_BUILTINS.includes(name);
5070
+ var isIdent = (name) => IDENT.test(name) && !name.startsWith("gl_") && !name.includes("__") && !RESERVED2.has(name) && !SHADER_BUILTINS.includes(name);
4184
5071
  function uniformType(v) {
4185
5072
  if (typeof v === "number") return Number.isFinite(v) ? "float" : null;
4186
5073
  if (typeof v === "string") return HEX.test(v) ? "vec3" : null;
@@ -4324,7 +5211,7 @@ var ShaderSequence = class extends Sequence {
4324
5211
  }
4325
5212
  async build() {
4326
5213
  const spec = this.shaderSpec;
4327
- const who = chunkECA2WNRX_cjs.describeLayer(spec);
5214
+ const who = chunkZMAD3KJU_cjs.describeLayer(spec);
4328
5215
  for (const m of shaderProblems(spec)) console.warn(`pixi-effects: ${who}: ${m}`);
4329
5216
  if (this.duration === void 0) this.duration = this.parent?.duration ?? this.root.duration;
4330
5217
  const scope = buildScope(this, this.parent, this.root);
@@ -4368,7 +5255,7 @@ var ShaderSequence = class extends Sequence {
4368
5255
  const warn = (why) => {
4369
5256
  if (this.warnedPaths.has(path)) return;
4370
5257
  this.warnedPaths.add(path);
4371
- console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.spec)}: keyframe path uniforms.${path} ${why}; skipped`);
5258
+ console.warn(`pixi-effects: ${chunkZMAD3KJU_cjs.describeLayer(this.spec)}: keyframe path uniforms.${path} ${why}; skipped`);
4372
5259
  };
4373
5260
  if (slot === void 0) {
4374
5261
  const guess = chunkTWZNOUM7_cjs.suggestName(name, Object.keys(this.carrier));
@@ -4403,7 +5290,7 @@ var ShaderSequence = class extends Sequence {
4403
5290
  this.failed = true;
4404
5291
  if (!this.warnedNoGl) {
4405
5292
  this.warnedNoGl = true;
4406
- console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.spec)}: this browser has no WebGL2, so the shader cannot run; the layer is drawn as a checkerboard`);
5293
+ console.warn(`pixi-effects: ${chunkZMAD3KJU_cjs.describeLayer(this.spec)}: this browser has no WebGL2, so the shader cannot run; the layer is drawn as a checkerboard`);
4407
5294
  }
4408
5295
  }
4409
5296
  compile() {
@@ -4434,7 +5321,7 @@ var ShaderSequence = class extends Sequence {
4434
5321
  gl.deleteProgram(p);
4435
5322
  this.failed = true;
4436
5323
  this.prog = null;
4437
- console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.spec)}: the fragment shader did not compile (the line numbers are the lines of your code):
5324
+ console.warn(`pixi-effects: ${chunkZMAD3KJU_cjs.describeLayer(this.spec)}: the fragment shader did not compile (the line numbers are the lines of your code):
4438
5325
  ${glslErrors(log).join("\n")}
4439
5326
  The layer is drawn as a checkerboard until it is fixed.`);
4440
5327
  return;
@@ -4518,7 +5405,7 @@ function resolveDim(v, scope) {
4518
5405
  var _CompositionSequence = null;
4519
5406
  async function getCompositionSequence() {
4520
5407
  if (!_CompositionSequence) {
4521
- _CompositionSequence = (await import('./Composition-HM4YT5L7.cjs')).CompositionSequence;
5408
+ _CompositionSequence = (await import('./Composition-QCOWQSDB.cjs')).CompositionSequence;
4522
5409
  }
4523
5410
  return _CompositionSequence;
4524
5411
  }
@@ -4562,7 +5449,7 @@ async function buildSequenceTree(specs, parent, root) {
4562
5449
 
4563
5450
  exports.CameraSequence = CameraSequence;
4564
5451
  exports.DEFAULT_FOV = DEFAULT_FOV;
4565
- exports.DEG = DEG2;
5452
+ exports.DEG = DEG3;
4566
5453
  exports.FALLOFFS = FALLOFFS;
4567
5454
  exports.LIGHT_KINDS = LIGHT_KINDS;
4568
5455
  exports.LIGHT_PROPS = LIGHT_PROPS;
@@ -4584,6 +5471,7 @@ exports.cameraBasis = cameraBasis;
4584
5471
  exports.checkEase = checkEase;
4585
5472
  exports.clockTable = clockTable;
4586
5473
  exports.contentLength = contentLength;
5474
+ exports.createTimeline = createTimeline;
4587
5475
  exports.ensureFilterLibrary = ensureFilterLibrary;
4588
5476
  exports.evaluateExpr = evaluateExpr;
4589
5477
  exports.flattenSvgPath = flattenSvgPath;
@@ -4592,9 +5480,11 @@ exports.homeCamera = homeCamera;
4592
5480
  exports.homeDistance = homeDistance;
4593
5481
  exports.layerInitialZ = layerInitialZ;
4594
5482
  exports.layerToWorld = layerToWorld;
5483
+ exports.lengthen = lengthen;
4595
5484
  exports.lightLevel = lightLevel;
4596
5485
  exports.noise = noise;
4597
5486
  exports.packLights = packLights;
5487
+ exports.parseEase = parseEase;
4598
5488
  exports.projectLayer = projectLayer;
4599
5489
  exports.projectPoint = projectPoint;
4600
5490
  exports.rand = rand;
@@ -4603,5 +5493,5 @@ exports.registerSequenceType = registerSequenceType;
4603
5493
  exports.remapOf = remapOf;
4604
5494
  exports.resolveAt = resolveAt;
4605
5495
  exports.timeRemapOf = timeRemapOf;
4606
- //# sourceMappingURL=chunk-WPXDRSGN.cjs.map
4607
- //# sourceMappingURL=chunk-WPXDRSGN.cjs.map
5496
+ //# sourceMappingURL=chunk-RQMI7EJT.cjs.map
5497
+ //# sourceMappingURL=chunk-RQMI7EJT.cjs.map