pixi-effects 0.25.0 → 0.26.1

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 (56) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +13 -13
  3. package/ai/CHAT.md +2 -2
  4. package/ai/chat-template.html +3 -3
  5. package/ai/tools/check.mjs +15 -3
  6. package/ai/tools/pixi-effects.mjs +48 -0
  7. package/ai/tools/render.mjs +3 -3
  8. package/ai/tools/view.mjs +3 -3
  9. package/dist/{Base-BgnRXbtJ.d.ts → Base-CpdDs4HT.d.ts} +1 -1
  10. package/dist/{Base-DHVKqA46.d.cts → Base-TbBhHPrG.d.cts} +1 -1
  11. package/dist/Composition-THQPG4E5.cjs +16 -0
  12. package/dist/{Composition-2GJSCHPP.cjs.map → Composition-THQPG4E5.cjs.map} +1 -1
  13. package/dist/Composition-Z2VY3KWZ.js +7 -0
  14. package/dist/{Composition-C4FSWJZF.js.map → Composition-Z2VY3KWZ.js.map} +1 -1
  15. package/dist/Controller.d.cts +2 -2
  16. package/dist/Controller.d.ts +2 -2
  17. package/dist/{Movie-FM5m67V0.d.ts → Movie-DVeDTdnc.d.ts} +25 -2
  18. package/dist/{Movie-CTpN1wYL.d.cts → Movie-i05Oh9mJ.d.cts} +25 -2
  19. package/dist/Presenter.d.cts +2 -2
  20. package/dist/Presenter.d.ts +2 -2
  21. package/dist/{chunk-DP37TTMO.cjs → chunk-EABL3UKJ.cjs} +5 -3
  22. package/dist/chunk-EABL3UKJ.cjs.map +1 -0
  23. package/dist/{chunk-SY27HZC5.cjs → chunk-H3RHEH5W.cjs} +59 -59
  24. package/dist/{chunk-SY27HZC5.cjs.map → chunk-H3RHEH5W.cjs.map} +1 -1
  25. package/dist/{chunk-PRZ4U6JN.js → chunk-JHJNEIDI.js} +5 -3
  26. package/dist/chunk-JHJNEIDI.js.map +1 -0
  27. package/dist/{chunk-XS6GSJCN.js → chunk-YEVAUQ6Y.js} +3 -3
  28. package/dist/{chunk-XS6GSJCN.js.map → chunk-YEVAUQ6Y.js.map} +1 -1
  29. package/dist/index.cjs +193 -32
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +4 -4
  32. package/dist/index.d.ts +4 -4
  33. package/dist/index.js +169 -8
  34. package/dist/index.js.map +1 -1
  35. package/dist/loader.css +35 -2
  36. package/dist/three.cjs +5 -5
  37. package/dist/three.d.cts +2 -2
  38. package/dist/three.d.ts +2 -2
  39. package/dist/three.js +1 -1
  40. package/dist/{types-CoT0ypcx.d.cts → types-DSGwN5w1.d.cts} +2 -0
  41. package/dist/{types-CoT0ypcx.d.ts → types-DSGwN5w1.d.ts} +2 -0
  42. package/docs/api.md +4 -3
  43. package/docs/dsl.md +2 -2
  44. package/llms-full.txt +34 -25
  45. package/llms.txt +6 -5
  46. package/package.json +3 -3
  47. package/{ai → skills/pixi-effects}/SKILL.md +11 -9
  48. package/{ai → skills/pixi-effects}/reference/cheatsheet.md +3 -3
  49. package/{ai → skills/pixi-effects}/reference/pitfalls.md +9 -3
  50. package/{ai → skills/pixi-effects}/reference/recipes.md +2 -2
  51. package/{ai/tools → skills/pixi-effects/scripts}/save-image.py +1 -1
  52. package/{ai → skills/pixi-effects}/template.html +7 -6
  53. package/dist/Composition-2GJSCHPP.cjs +0 -16
  54. package/dist/Composition-C4FSWJZF.js +0 -7
  55. package/dist/chunk-DP37TTMO.cjs.map +0 -1
  56. package/dist/chunk-PRZ4U6JN.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkSY27HZC5_cjs = require('./chunk-SY27HZC5.cjs');
4
- var chunkDP37TTMO_cjs = require('./chunk-DP37TTMO.cjs');
3
+ var chunkH3RHEH5W_cjs = require('./chunk-H3RHEH5W.cjs');
4
+ var chunkEABL3UKJ_cjs = require('./chunk-EABL3UKJ.cjs');
5
5
  var chunk3DLYAPK7_cjs = require('./chunk-3DLYAPK7.cjs');
6
6
  var chunkECA2WNRX_cjs = require('./chunk-ECA2WNRX.cjs');
7
7
  var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
@@ -80,11 +80,11 @@ function ensureLoadersRegistered(audioContext) {
80
80
  }
81
81
  });
82
82
  }
83
- async function loadAssetBundle(assets, audioContext) {
83
+ async function loadAssetBundle(assets, audioContext, onProgress) {
84
84
  ensureLoadersRegistered(audioContext);
85
85
  const bundle = assets.map((a) => ({ alias: a.name, src: a.src }));
86
86
  PIXI.Assets.addBundle("pixi-effects", bundle);
87
- return await PIXI.Assets.loadBundle("pixi-effects");
87
+ return await PIXI.Assets.loadBundle("pixi-effects", onProgress);
88
88
  }
89
89
 
90
90
  // src/core/audioRemap.ts
@@ -1407,8 +1407,8 @@ function expandTransitions(spec) {
1407
1407
  }
1408
1408
  const transitions = out.transitions;
1409
1409
  const sequences = out.sequences ?? [];
1410
- const remap = out.duration !== void 0 ? chunkDP37TTMO_cjs.timeRemapOf(out) : null;
1411
- const parentDuration = remap ? chunkDP37TTMO_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1410
+ const remap = out.duration !== void 0 ? chunkEABL3UKJ_cjs.timeRemapOf(out) : null;
1411
+ const parentDuration = remap ? chunkEABL3UKJ_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1412
1412
  const compW = out.width;
1413
1413
  const compH = out.height;
1414
1414
  const threeDByName = new Set(sequences.filter((s) => s.name && s.threeD).map((s) => s.name));
@@ -1476,16 +1476,16 @@ function expandTransitions(spec) {
1476
1476
  if (toUses > 1) {
1477
1477
  throw new Error(`pixi-effects: ${tag} sequence "${t.to}" is used as \`to\` in more than one transition`);
1478
1478
  }
1479
- const tStart = chunkDP37TTMO_cjs.resolveAt(t.at, parentDuration);
1479
+ const tStart = chunkEABL3UKJ_cjs.resolveAt(t.at, parentDuration);
1480
1480
  const tEnd = tStart + t.duration;
1481
1481
  const fromSeq = fromEntry.seq;
1482
- const fromStart = chunkDP37TTMO_cjs.resolveAt(fromSeq.at, parentDuration);
1482
+ const fromStart = chunkEABL3UKJ_cjs.resolveAt(fromSeq.at, parentDuration);
1483
1483
  const fromEnd = fromStart + (fromSeq.duration ?? parentDuration);
1484
1484
  if (tStart < fromStart - EPS || tEnd > fromEnd + EPS) {
1485
1485
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`from\` "${t.from}" (lives [${fromStart}, ${fromEnd}], so it ends at ${fromEnd})`);
1486
1486
  }
1487
1487
  const toSeq = toEntry.seq;
1488
- const toStart = chunkDP37TTMO_cjs.resolveAt(toSeq.at, parentDuration);
1488
+ const toStart = chunkEABL3UKJ_cjs.resolveAt(toSeq.at, parentDuration);
1489
1489
  const toEnd = toStart + (toSeq.duration ?? parentDuration);
1490
1490
  if (tStart < toStart - EPS || tEnd > toEnd + EPS) {
1491
1491
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`to\` "${t.to}" (lives [${toStart}, ${toEnd}], so it starts at ${toStart})`);
@@ -1545,7 +1545,7 @@ function wrapAsFullComposition(seq, compW, compH) {
1545
1545
  delete inner.at;
1546
1546
  delete inner.duration;
1547
1547
  const m = inner.mask;
1548
- const carriesMatte = m !== void 0 && chunkSY27HZC5_cjs.maskSourceOf(m).kind === "refs";
1548
+ const carriesMatte = m !== void 0 && chunkH3RHEH5W_cjs.maskSourceOf(m).kind === "refs";
1549
1549
  if (carriesMatte) delete inner.mask;
1550
1550
  return {
1551
1551
  ...carriesMatte ? { mask: m } : {},
@@ -1872,7 +1872,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1872
1872
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1873
1873
  if (group) scene.set(info, group);
1874
1874
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1875
- if (seq instanceof chunkSY27HZC5_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, moving, zero);
1875
+ if (seq instanceof chunkH3RHEH5W_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, moving, zero);
1876
1876
  });
1877
1877
  };
1878
1878
  walk2(root, "", false, true);
@@ -1933,10 +1933,10 @@ function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions, lo
1933
1933
  const start = parentStart + (seq.at ?? 0);
1934
1934
  const end = Math.min(parentEnd, seq.duration !== void 0 ? start + seq.duration : parentEnd);
1935
1935
  const span = end - start;
1936
- const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkDP37TTMO_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
1936
+ const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkEABL3UKJ_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
1937
1937
  const row = { path: prefix + name, name, type: seq.spec.type, depth, start: round(start), end: round(end), keys, detail: detailOf(seq), ...local ? { local } : {} };
1938
1938
  const inner = [];
1939
- const isComp = seq instanceof chunkSY27HZC5_cjs.CompositionSequence;
1939
+ const isComp = seq instanceof chunkH3RHEH5W_cjs.CompositionSequence;
1940
1940
  if (isComp) {
1941
1941
  const label = seq.remapLabel();
1942
1942
  walk(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
@@ -2120,6 +2120,9 @@ function startWhenRunning(ctx, start, gesture) {
2120
2120
  return stop;
2121
2121
  }
2122
2122
  var GESTURES = ["pointerdown", "mousedown", "touchend", "keydown"];
2123
+ function audioIsBlocked(ctx, hasAudio) {
2124
+ return !!ctx && hasAudio && ctx.state !== "running";
2125
+ }
2123
2126
 
2124
2127
  // src/core/poster.ts
2125
2128
  function normalizePoster(poster, duration, frameRate) {
@@ -2299,6 +2302,100 @@ function changedFraction(a, b, threshold = 12) {
2299
2302
  return changed / n;
2300
2303
  }
2301
2304
 
2305
+ // src/core/loadProgress.ts
2306
+ var STAGE_LABEL = {
2307
+ assets: "LOADING ASSETS",
2308
+ build: "BUILDING LAYERS",
2309
+ sound: "MIXING SOUND",
2310
+ frames: "PREPARING"
2311
+ };
2312
+ var ORDER = ["assets", "build", "sound", "frames"];
2313
+ var WEIGHT = { assets: 0.15, build: 0.6, sound: 0.2, frames: 0.05 };
2314
+ var YIELD_EVERY_MS = 50;
2315
+ function defaultYielder() {
2316
+ if (typeof MessageChannel === "undefined") return new Promise((resolve) => setTimeout(resolve, 0));
2317
+ return new Promise((resolve) => {
2318
+ const ch = new MessageChannel();
2319
+ ch.port1.onmessage = () => {
2320
+ ch.port1.close();
2321
+ resolve();
2322
+ };
2323
+ ch.port2.postMessage(0);
2324
+ });
2325
+ }
2326
+ var LoadProgress = class {
2327
+ constructor(_onChange, opts = {}) {
2328
+ this._onChange = _onChange;
2329
+ this._now = opts.now ?? (() => typeof performance !== "undefined" ? performance.now() : Date.now());
2330
+ this._yielder = opts.yielder ?? defaultYielder;
2331
+ this._hidden = opts.hidden ?? (() => typeof document !== "undefined" && document.hidden === true);
2332
+ this._every = opts.yieldEveryMs ?? YIELD_EVERY_MS;
2333
+ this._lastYield = this._now();
2334
+ }
2335
+ _onChange;
2336
+ _stage = null;
2337
+ _loaded = 0;
2338
+ _total = 0;
2339
+ _high = 0;
2340
+ _started = 0;
2341
+ _ms = { assets: 0, build: 0, sound: 0, frames: 0 };
2342
+ _lastYield;
2343
+ _now;
2344
+ _yielder;
2345
+ _every;
2346
+ _hidden;
2347
+ get progress() {
2348
+ return this._high;
2349
+ }
2350
+ /** Start a stage with `total` items (0 = nothing to wait for). Every earlier stage is done. */
2351
+ begin(stage, total) {
2352
+ this._closeTiming();
2353
+ this._stage = stage;
2354
+ this._loaded = 0;
2355
+ this._total = Math.max(0, total);
2356
+ this._started = this._now();
2357
+ this._emit();
2358
+ }
2359
+ tick(n = 1) {
2360
+ if (!this._stage) return;
2361
+ this._loaded = Math.min(this._total, this._loaded + n);
2362
+ this._emit();
2363
+ }
2364
+ /** Everything is done: 1, whatever stage it was in. */
2365
+ finish() {
2366
+ this._closeTiming();
2367
+ this._stage = this._stage ?? "frames";
2368
+ this._loaded = this._total;
2369
+ this._high = 1;
2370
+ this._onChange({ stage: this._stage, loaded: this._loaded, total: this._total, progress: 1 });
2371
+ }
2372
+ /** Milliseconds each stage took (a stage never begun is 0). */
2373
+ timings() {
2374
+ return { ...this._ms };
2375
+ }
2376
+ /** Await one macrotask when 50 ms of work have gone by since the last time, so the page can paint the bar. Cheap when it is not due. */
2377
+ async yieldIfDue() {
2378
+ const t = this._now();
2379
+ if (t - this._lastYield < this._every || this._hidden()) return;
2380
+ await this._yielder();
2381
+ this._lastYield = this._now();
2382
+ }
2383
+ _closeTiming() {
2384
+ if (this._stage) this._ms[this._stage] += Math.max(0, this._now() - this._started);
2385
+ }
2386
+ _emit() {
2387
+ const stage = this._stage;
2388
+ let done = 0;
2389
+ for (const s of ORDER) {
2390
+ if (s === stage) break;
2391
+ done += WEIGHT[s];
2392
+ }
2393
+ const within = this._total > 0 ? this._loaded / this._total : 1;
2394
+ this._high = Math.min(1, Math.max(this._high, done + WEIGHT[stage] * within));
2395
+ this._onChange({ stage, loaded: this._loaded, total: this._total, progress: this._high });
2396
+ }
2397
+ };
2398
+
2302
2399
  // src/core/loader.ts
2303
2400
  var FADE_FALLBACK_MS = 900;
2304
2401
  function resolveLoader(option, canvas) {
@@ -2324,15 +2421,31 @@ function dismissLoader(loader) {
2324
2421
  loader.remove();
2325
2422
  }
2326
2423
  };
2327
- loader.addEventListener("transitionend", remove, { once: true });
2424
+ loader.addEventListener("transitionend", (e) => {
2425
+ if (e.target === loader && e.propertyName === "opacity") remove();
2426
+ });
2328
2427
  setTimeout(remove, FADE_FALLBACK_MS);
2329
2428
  }
2330
2429
  function failLoader(loader, message) {
2331
2430
  if (!loader) return;
2332
2431
  loader.setAttribute("data-state", "error");
2432
+ for (const a of ["data-stage", "data-percent"]) loader.removeAttribute(a);
2433
+ loader.querySelector(".pe-loader__bar")?.remove();
2333
2434
  if (loader.hasAttribute("data-label")) loader.setAttribute("data-label", message);
2334
2435
  loader.setAttribute("aria-busy", "false");
2335
2436
  }
2437
+ function setLoaderProgress(loader, state) {
2438
+ if (!loader || loader.getAttribute("data-state") === "error" || loader.classList.contains("pe-loader--done")) return;
2439
+ if (!loader.querySelector(".pe-loader__bar")) {
2440
+ const bar = loader.ownerDocument.createElement("i");
2441
+ bar.className = "pe-loader__bar";
2442
+ bar.setAttribute("aria-hidden", "true");
2443
+ loader.appendChild(bar);
2444
+ }
2445
+ loader.style.setProperty("--pe-progress", String(Math.round(state.progress * 1e3) / 1e3));
2446
+ loader.setAttribute("data-stage", STAGE_LABEL[state.stage]);
2447
+ loader.setAttribute("data-percent", String(Math.round(state.progress * 100)));
2448
+ }
2336
2449
 
2337
2450
  // src/core/Movie.ts
2338
2451
  PIXI.extensions.add(PIXI.CullerPlugin);
@@ -2412,6 +2525,15 @@ var Movie = class {
2412
2525
  get isReady() {
2413
2526
  return this._initState === "ready";
2414
2527
  }
2528
+ /** Milliseconds `init()` spent in each stage (assets, build, sound, frames), or null before it has finished: `check` says which stage is the slow one. */
2529
+ get loadStages() {
2530
+ return this._loadStages;
2531
+ }
2532
+ _loadStages = null;
2533
+ /** True while the movie has sound but the browser keeps it silent until the viewer taps (iOS Safari, a page nobody has touched): a player can show "tap for sound"; the sound starts at a tap or key press that arrives while play() is pending, or at the next play() made from a tap. */
2534
+ get audioBlocked() {
2535
+ return audioIsBlocked(this._audioContext, !!this.audioBuffer);
2536
+ }
2415
2537
  /** The poster time as a frame number, or null. */
2416
2538
  get posterFrame() {
2417
2539
  return this._posterFrame;
@@ -2492,15 +2614,39 @@ var Movie = class {
2492
2614
  this.app.stage.addChild(root);
2493
2615
  this._rootContainer = root;
2494
2616
  const audioContext = this._ensureAudioContext();
2495
- await loadAssetBundle(options.assets ?? [], audioContext);
2496
- const rootShape = { width: this.width, height: this.height, duration: this.duration, frameRate: this.frameRate };
2617
+ let loaderEl;
2618
+ const lp = new LoadProgress((state) => {
2619
+ if (loaderEl === void 0) loaderEl = resolveLoader(options.loader, options.canvas ?? this.app?.canvas ?? null);
2620
+ setLoaderProgress(loaderEl, state);
2621
+ this.emit("loadprogress", state);
2622
+ }, { yieldEveryMs: globalThis.__peLoadYieldMs });
2623
+ const assetCount = (options.assets ?? []).length;
2624
+ lp.begin("assets", assetCount);
2625
+ let assetsSeen = 0;
2626
+ await loadAssetBundle(options.assets ?? [], audioContext, (p) => {
2627
+ const n = Math.round(p * assetCount);
2628
+ if (n > assetsSeen) {
2629
+ lp.tick(n - assetsSeen);
2630
+ assetsSeen = n;
2631
+ }
2632
+ });
2633
+ const rootShape = {
2634
+ width: this.width,
2635
+ height: this.height,
2636
+ duration: this.duration,
2637
+ frameRate: this.frameRate,
2638
+ onLayerBuilt: async () => {
2639
+ lp.tick();
2640
+ await lp.yieldIfDue();
2641
+ }
2642
+ };
2497
2643
  const seededComposition = options.composition ? {
2498
2644
  ...options.composition,
2499
2645
  width: options.composition.width ?? this.width,
2500
2646
  height: options.composition.height ?? this.height,
2501
2647
  duration: options.composition.duration ?? this.duration
2502
2648
  } : void 0;
2503
- await chunkDP37TTMO_cjs.ensureFilterLibrary(seededComposition);
2649
+ await chunkEABL3UKJ_cjs.ensureFilterLibrary(seededComposition);
2504
2650
  const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
2505
2651
  const rootSeqSpec = {
2506
2652
  type: "composition",
@@ -2510,7 +2656,8 @@ var Movie = class {
2510
2656
  ...userComposition
2511
2657
  };
2512
2658
  if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
2513
- const composition = new chunkSY27HZC5_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
2659
+ const composition = new chunkH3RHEH5W_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
2660
+ lp.begin("build", countLayers(rootSeqSpec));
2514
2661
  await composition.build();
2515
2662
  this._rootSequence = composition;
2516
2663
  if (composition.target) root.addChild(composition.target);
@@ -2518,15 +2665,20 @@ var Movie = class {
2518
2665
  this._timeFilters = collectTimeFilters(composition);
2519
2666
  const audios = [];
2520
2667
  composition.collectAudio(audios, 0);
2668
+ lp.begin("sound", audios.length);
2521
2669
  if (audios.length > 0) {
2522
2670
  this.audioBuffer = await mixdown(audios, this.duration, audioContext.sampleRate);
2671
+ lp.tick(audios.length);
2523
2672
  if (this.audioBuffer) this._mixStats = limitMix(this.audioBuffer, audios);
2524
2673
  }
2525
2674
  this._audioSources = audios;
2526
2675
  this.timeline.progress(1).progress(0);
2676
+ lp.begin("frames", 0);
2527
2677
  await this._awaitVideoFrames();
2528
2678
  this._updateSpace();
2529
2679
  this._renderNow();
2680
+ lp.finish();
2681
+ this._loadStages = lp.timings();
2530
2682
  this._initState = "ready";
2531
2683
  if (this._posterFrame !== null) await this._showPoster();
2532
2684
  this.emit("ready");
@@ -3166,6 +3318,15 @@ function collectVideoSequences(seq, out, clocks, clock = null) {
3166
3318
  collectVideoSequences(child, out, clocks, own);
3167
3319
  }
3168
3320
  }
3321
+ function countLayers(spec) {
3322
+ let n = 0;
3323
+ for (const s of spec?.sequences ?? []) {
3324
+ const mask = s.mask;
3325
+ n += 1 + countLayers(s);
3326
+ for (const m of Array.isArray(mask) ? mask : [mask]) if (m && typeof m === "object" && "type" in m) n += 1 + countLayers(m);
3327
+ }
3328
+ return n;
3329
+ }
3169
3330
  var BY = ["chars", "words", "lines"];
3170
3331
  var ALIGN = ["left", "center", "right"];
3171
3332
  var widthOf = (text, style) => PIXI.CanvasTextMetrics.measureText(text, style).width;
@@ -3330,7 +3491,7 @@ function clamp(v, lo, hi) {
3330
3491
  return Math.max(lo, Math.min(hi, v));
3331
3492
  }
3332
3493
  function parseEase(name) {
3333
- chunkDP37TTMO_cjs.checkEase(name, "a preset");
3494
+ chunkEABL3UKJ_cjs.checkEase(name, "a preset");
3334
3495
  return gsap.gsap.parseEase(name);
3335
3496
  }
3336
3497
 
@@ -3353,17 +3514,17 @@ function orbit(opts) {
3353
3514
  throw new Error("pixi-effects: orbit() cannot combine `radius` with `dollyZoom` \u2014 the radius follows the animated fov");
3354
3515
  }
3355
3516
  const [cx, cy] = opts.center ?? [width / 2, height / 2];
3356
- const fixedR = opts.radius ?? chunkDP37TTMO_cjs.homeDistance(height, fov ?? chunkDP37TTMO_cjs.DEFAULT_FOV);
3517
+ const fixedR = opts.radius ?? chunkEABL3UKJ_cjs.homeDistance(height, fov ?? chunkEABL3UKJ_cjs.DEFAULT_FOV);
3357
3518
  const start = opts.start ?? -degrees / 2;
3358
3519
  const steps = Math.max(1, Math.round(duration * stepsPerSecond));
3359
3520
  const easeFn = parseEase(ease);
3360
3521
  const dollyEase = dolly ? parseEase(dolly.ease ?? ease) : null;
3361
3522
  const pos = (i) => {
3362
3523
  const p = i / steps;
3363
- const a = (start + degrees * easeFn(p)) * chunkDP37TTMO_cjs.DEG;
3524
+ const a = (start + degrees * easeFn(p)) * chunkEABL3UKJ_cjs.DEG;
3364
3525
  if (!dolly) return { x: cx + fixedR * Math.sin(a), z: fixedR * Math.cos(a) };
3365
3526
  const f = dolly.from + (dolly.to - dolly.from) * dollyEase(p);
3366
- const R = chunkDP37TTMO_cjs.homeDistance(height, f);
3527
+ const R = chunkEABL3UKJ_cjs.homeDistance(height, f);
3367
3528
  return { x: cx + R * Math.sin(a), z: R * Math.cos(a), fov: f };
3368
3529
  };
3369
3530
  const keyframes = [];
@@ -3418,7 +3579,7 @@ function particles(opts) {
3418
3579
  const round3 = (v) => Math.round(v * 1e6) / 1e6;
3419
3580
  const out = [];
3420
3581
  for (let i = 0; i < count; i++) {
3421
- const r = chunkDP37TTMO_cjs.random(seed * 1000003 + i * 7919 + 17);
3582
+ const r = chunkEABL3UKJ_cjs.random(seed * 1000003 + i * 7919 + 17);
3422
3583
  const pick = ([a2, b]) => a2 + r() * (b - a2);
3423
3584
  const born = at + r() * emit;
3424
3585
  const lifetime = pick(life);
@@ -3431,7 +3592,7 @@ function particles(opts) {
3431
3592
  const turn = spin ? pick(spin) : 0;
3432
3593
  const startAngle = spin ? r() * 360 : 0;
3433
3594
  const swayAmp = sway?.amp ?? 0, swayFreq = sway?.freq ?? 1;
3434
- const n0 = sway ? chunkDP37TTMO_cjs.noise(0, seed * 1000003 + i) : 0;
3595
+ const n0 = sway ? chunkEABL3UKJ_cjs.noise(0, seed * 1000003 + i) : 0;
3435
3596
  const at_ = (t) => {
3436
3597
  const axis = (p0, v0, acc) => {
3437
3598
  if (drag > 0) {
@@ -3440,7 +3601,7 @@ function particles(opts) {
3440
3601
  }
3441
3602
  return p0 + v0 * t + 0.5 * acc * t * t;
3442
3603
  };
3443
- const sw = sway ? swayAmp * (chunkDP37TTMO_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
3604
+ const sw = sway ? swayAmp * (chunkEABL3UKJ_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
3444
3605
  return { x: axis(x0, vx, wind) + sw, y: axis(y0, vy, gravity) };
3445
3606
  };
3446
3607
  const kfs = [];
@@ -3502,7 +3663,7 @@ function followPath(opts) {
3502
3663
  if (steps > MAX_STEPS) throw new Error(`followPath(): ${steps} samples is too many (max ${MAX_STEPS}); lower frameRate or duration`);
3503
3664
  const segments = [];
3504
3665
  let total = 0;
3505
- for (const line of chunkDP37TTMO_cjs.flattenSvgPath(d)) {
3666
+ for (const line of chunkEABL3UKJ_cjs.flattenSvgPath(d)) {
3506
3667
  const p = line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
3507
3668
  for (let i = 2; i < p.length; i += 2) {
3508
3669
  const len = Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
@@ -3656,7 +3817,7 @@ function distances(n, from, grid, seed) {
3656
3817
  if (n === 0) return [];
3657
3818
  if (from === "random") {
3658
3819
  const order = Array.from({ length: n }, (_, i) => i);
3659
- const r = chunkDP37TTMO_cjs.random(seed);
3820
+ const r = chunkEABL3UKJ_cjs.random(seed);
3660
3821
  for (let i = n - 1; i > 0; i--) {
3661
3822
  const j = Math.floor(r() * (i + 1));
3662
3823
  [order[i], order[j]] = [order[j], order[i]];
@@ -3718,7 +3879,7 @@ function wiggle(opts) {
3718
3879
  to[n] = p.around;
3719
3880
  return;
3720
3881
  }
3721
- const off = (chunkDP37TTMO_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
3882
+ const off = (chunkEABL3UKJ_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
3722
3883
  to[n] = typeof p.around === "number" ? p.around + off : `(${p.around}) ${off < 0 ? "-" : "+"} ${Math.abs(off)}`;
3723
3884
  });
3724
3885
  out.push({ at: at + s * stepLen, to, duration: stepLen, ease });
@@ -4193,19 +4354,19 @@ function withFade(spec, opts) {
4193
4354
 
4194
4355
  Object.defineProperty(exports, "noise", {
4195
4356
  enumerable: true,
4196
- get: function () { return chunkDP37TTMO_cjs.noise; }
4357
+ get: function () { return chunkEABL3UKJ_cjs.noise; }
4197
4358
  });
4198
4359
  Object.defineProperty(exports, "rand", {
4199
4360
  enumerable: true,
4200
- get: function () { return chunkDP37TTMO_cjs.rand; }
4361
+ get: function () { return chunkEABL3UKJ_cjs.rand; }
4201
4362
  });
4202
4363
  Object.defineProperty(exports, "random", {
4203
4364
  enumerable: true,
4204
- get: function () { return chunkDP37TTMO_cjs.random; }
4365
+ get: function () { return chunkEABL3UKJ_cjs.random; }
4205
4366
  });
4206
4367
  Object.defineProperty(exports, "registerSequenceType", {
4207
4368
  enumerable: true,
4208
- get: function () { return chunkDP37TTMO_cjs.registerSequenceType; }
4369
+ get: function () { return chunkEABL3UKJ_cjs.registerSequenceType; }
4209
4370
  });
4210
4371
  exports.Movie = Movie;
4211
4372
  exports.animateText = animateText;