pixi-effects 0.47.4 → 0.48.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 (61) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +8 -6
  3. package/ai/chat-template.html +3 -3
  4. package/dist/Composition-PMWLJQPI.cjs +16 -0
  5. package/dist/{Composition-ZYRDRLYB.cjs.map → Composition-PMWLJQPI.cjs.map} +1 -1
  6. package/dist/Composition-ZCRJHBB5.js +7 -0
  7. package/dist/{Composition-HTHQ2RLF.js.map → Composition-ZCRJHBB5.js.map} +1 -1
  8. package/dist/Controller.cjs +3 -0
  9. package/dist/Controller.cjs.map +1 -1
  10. package/dist/Controller.d.cts +1 -1
  11. package/dist/Controller.d.ts +1 -1
  12. package/dist/Controller.js +3 -0
  13. package/dist/Controller.js.map +1 -1
  14. package/dist/{Movie-sV8nJr_f.d.ts → Movie--qSYKgAQ.d.ts} +2 -2
  15. package/dist/{Movie-BqrAWN0g.d.cts → Movie-svZQF-9Z.d.cts} +2 -2
  16. package/dist/{Spans-JYCTJV4V.js → Spans-FMAVPATS.js} +4 -4
  17. package/dist/{Spans-JYCTJV4V.js.map → Spans-FMAVPATS.js.map} +1 -1
  18. package/dist/{Spans-I7P7ZVZO.cjs → Spans-KTUPDFSR.cjs} +9 -9
  19. package/dist/{Spans-I7P7ZVZO.cjs.map → Spans-KTUPDFSR.cjs.map} +1 -1
  20. package/dist/{chunk-34YNRCMT.js → chunk-4PYVHLIK.js} +20 -5
  21. package/dist/chunk-4PYVHLIK.js.map +1 -0
  22. package/dist/{chunk-CJS2S5XU.cjs → chunk-ECLBZV4N.cjs} +20 -5
  23. package/dist/chunk-ECLBZV4N.cjs.map +1 -0
  24. package/dist/{chunk-NCM2WU7R.js → chunk-GX2WQV4A.js} +58 -35
  25. package/dist/chunk-GX2WQV4A.js.map +1 -0
  26. package/dist/chunk-K3XOEC3C.js +53 -0
  27. package/dist/chunk-K3XOEC3C.js.map +1 -0
  28. package/dist/{chunk-AES2L75M.cjs → chunk-NGK4E536.cjs} +103 -80
  29. package/dist/chunk-NGK4E536.cjs.map +1 -0
  30. package/dist/{chunk-DS2EWRV4.cjs → chunk-OGLRSR6C.cjs} +63 -63
  31. package/dist/{chunk-DS2EWRV4.cjs.map → chunk-OGLRSR6C.cjs.map} +1 -1
  32. package/dist/{chunk-RTZ5MW3E.js → chunk-UI7CEUTP.js} +3 -3
  33. package/dist/{chunk-RTZ5MW3E.js.map → chunk-UI7CEUTP.js.map} +1 -1
  34. package/dist/chunk-VQXZ2NOI.cjs +56 -0
  35. package/dist/chunk-VQXZ2NOI.cjs.map +1 -0
  36. package/dist/index.cjs +38 -33
  37. package/dist/index.d.cts +19 -2
  38. package/dist/index.d.ts +19 -2
  39. package/dist/index.js +4 -3
  40. package/dist/loader.css +1 -1
  41. package/dist/present.cjs +4 -3
  42. package/dist/present.cjs.map +1 -1
  43. package/dist/present.d.cts +1 -1
  44. package/dist/present.d.ts +1 -1
  45. package/dist/present.js +4 -3
  46. package/dist/present.js.map +1 -1
  47. package/dist/three.cjs +5 -5
  48. package/dist/three.js +1 -1
  49. package/docs/api.md +17 -2
  50. package/docs/dsl.md +7 -7
  51. package/llms-full.txt +26 -11
  52. package/package.json +1 -1
  53. package/skills/pixi-effects/SKILL.md +1 -1
  54. package/skills/pixi-effects/reference/pitfalls.md +1 -1
  55. package/skills/pixi-effects/template.html +6 -6
  56. package/dist/Composition-HTHQ2RLF.js +0 -7
  57. package/dist/Composition-ZYRDRLYB.cjs +0 -16
  58. package/dist/chunk-34YNRCMT.js.map +0 -1
  59. package/dist/chunk-AES2L75M.cjs.map +0 -1
  60. package/dist/chunk-CJS2S5XU.cjs.map +0 -1
  61. package/dist/chunk-NCM2WU7R.js.map +0 -1
@@ -0,0 +1,53 @@
1
+ // src/core/mediabunny.ts
2
+ var source = null;
3
+ var pending = null;
4
+ function useMediabunny(from) {
5
+ if (from == null || typeof from !== "string" && typeof from !== "function" && typeof from !== "object") {
6
+ throw new TypeError(`pixi-effects: useMediabunny() takes the mediabunny module, a URL or a function that returns the module; it got ${String(from)}`);
7
+ }
8
+ source = from;
9
+ pending = null;
10
+ }
11
+ function loadMediabunny() {
12
+ if (!pending) {
13
+ const from = source;
14
+ const p = fetchModule(from).then(
15
+ (mod) => {
16
+ if (!mod || typeof mod.Output !== "function") {
17
+ throw new Error(`pixi-effects: ${describe(from)} did not give the mediabunny module (no Output in it). ${HOW}`);
18
+ }
19
+ return mod;
20
+ },
21
+ (err) => {
22
+ const why = err instanceof Error ? err.message : String(err);
23
+ throw new Error(`pixi-effects: could not load mediabunny from ${describe(from)} (${why}). ${HOW}`, { cause: err });
24
+ }
25
+ );
26
+ pending = p;
27
+ p.catch(() => {
28
+ if (pending === p) pending = null;
29
+ });
30
+ }
31
+ return pending;
32
+ }
33
+ var HOW = 'mediabunny is needed to export (movie.render(), the download button) and for a video layer; playing needs nothing. Add it to the import map ("mediabunny": "https://cdn.jsdelivr.net/npm/mediabunny@1/+esm"), install it next to pixi-effects, or call useMediabunny(module | url | () => import(...)) before you export.';
34
+ function fetchModule(from) {
35
+ if (from === null) return import('mediabunny');
36
+ if (typeof from === "string") return import(
37
+ /* @vite-ignore */
38
+ /* webpackIgnore: true */
39
+ from
40
+ );
41
+ if (typeof from === "function") return Promise.resolve().then(from);
42
+ return Promise.resolve(from);
43
+ }
44
+ function describe(from) {
45
+ if (from === null) return "'mediabunny' (the bare name: an import map or a bundler resolves it)";
46
+ if (typeof from === "string") return from;
47
+ if (typeof from === "function") return "the function given to useMediabunny()";
48
+ return "the object given to useMediabunny()";
49
+ }
50
+
51
+ export { loadMediabunny, useMediabunny };
52
+ //# sourceMappingURL=chunk-K3XOEC3C.js.map
53
+ //# sourceMappingURL=chunk-K3XOEC3C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/mediabunny.ts"],"names":[],"mappings":";AAeA,IAAI,MAAA,GAAkC,IAAA;AACtC,IAAI,OAAA,GAA4C,IAAA;AAOzC,SAAS,cAAc,IAAA,EAA8B;AAC1D,EAAA,IAAI,IAAA,IAAQ,IAAA,IAAS,OAAO,IAAA,KAAS,QAAA,IAAY,OAAO,IAAA,KAAS,UAAA,IAAc,OAAO,IAAA,KAAS,QAAA,EAAW;AACxG,IAAA,MAAM,IAAI,SAAA,CAAU,CAAA,+GAAA,EAAkH,MAAA,CAAO,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,EACtJ;AACA,EAAA,MAAA,GAAS,IAAA;AACT,EAAA,OAAA,GAAU,IAAA;AACZ;AAGO,SAAS,cAAA,GAA4C;AAC1D,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAA,GAAO,MAAA;AACb,IAAA,MAAM,CAAA,GAAI,WAAA,CAAY,IAAI,CAAA,CAAE,IAAA;AAAA,MAC1B,CAAC,GAAA,KAAQ;AACP,QAAA,IAAI,CAAC,GAAA,IAAO,OAAQ,GAAA,CAA6B,WAAW,UAAA,EAAY;AACtE,UAAA,MAAM,IAAI,MAAM,CAAA,cAAA,EAAiB,QAAA,CAAS,IAAI,CAAC,CAAA,uDAAA,EAA0D,GAAG,CAAA,CAAE,CAAA;AAAA,QAChH;AACA,QAAA,OAAO,GAAA;AAAA,MACT,CAAA;AAAA,MACA,CAAC,GAAA,KAAiB;AAChB,QAAA,MAAM,MAAM,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAC3D,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,6CAAA,EAAgD,QAAA,CAAS,IAAI,CAAC,CAAA,EAAA,EAAK,GAAG,CAAA,GAAA,EAAM,GAAG,CAAA,CAAA,EAAI,EAAE,KAAA,EAAO,KAAK,CAAA;AAAA,MACnH;AAAA,KACF;AACA,IAAA,OAAA,GAAU,CAAA;AACV,IAAA,CAAA,CAAE,MAAM,MAAM;AAAE,MAAA,IAAI,OAAA,KAAY,GAAG,OAAA,GAAU,IAAA;AAAA,IAAM,CAAC,CAAA;AAAA,EACtD;AACA,EAAA,OAAO,OAAA;AACT;AAEA,IAAM,GAAA,GAAM,2TAAA;AAIZ,SAAS,YAAY,IAAA,EAA0D;AAC7E,EAAA,IAAI,IAAA,KAAS,IAAA,EAAM,OAAO,OAAO,YAAY,CAAA;AAC7C,EAAA,IAAI,OAAO,IAAA,KAAS,QAAA,EAAU,OAAO;AAAA;AAAA;AAAA,IAAoD;AAAA,GAAA;AACzF,EAAA,IAAI,OAAO,SAAS,UAAA,EAAY,OAAO,QAAQ,OAAA,EAAQ,CAAE,KAAK,IAAI,CAAA;AAClE,EAAA,OAAO,OAAA,CAAQ,QAAQ,IAAI,CAAA;AAC7B;AAEA,SAAS,SAAS,IAAA,EAAuC;AACvD,EAAA,IAAI,IAAA,KAAS,MAAM,OAAO,sEAAA;AAC1B,EAAA,IAAI,OAAO,IAAA,KAAS,QAAA,EAAU,OAAO,IAAA;AACrC,EAAA,IAAI,OAAO,IAAA,KAAS,UAAA,EAAY,OAAO,uCAAA;AACvC,EAAA,OAAO,qCAAA;AACT","file":"chunk-K3XOEC3C.js","sourcesContent":["// mediabunny (about 180 KB gzipped) is what writes a video file and decodes a `video` layer. A page that only plays never needs it, so it is\n// fetched the first time something asks for it: `movie.render()`, a video asset. Nothing imports it at the top of a module.\nimport type * as Mediabunny from 'mediabunny';\n\n/** The mediabunny module, as `import * as mb from 'mediabunny'` gives it. */\nexport type MediabunnyModule = typeof Mediabunny;\n\n/**\n * Where mediabunny comes from, for {@link useMediabunny}:\n * - the module itself (`import * as mb from 'mediabunny'`: it is ready, nothing is fetched),\n * - a URL to `import()` (a page with no import map entry for it),\n * - a function that returns the module (`() => import('mediabunny')`, your own copy, a bundler chunk).\n */\nexport type MediabunnySource = MediabunnyModule | string | (() => Promise<MediabunnyModule>);\n\nlet source: MediabunnySource | null = null;\nlet pending: Promise<MediabunnyModule> | null = null;\n\n/**\n * Says where mediabunny comes from. Without it, the bare name `'mediabunny'` is imported the first time it is needed, which your bundler or the\n * page's import map resolves. A page that only plays (no export, no `video` layer) needs neither this nor an import map entry for it.\n * Calling it again replaces the source for the next load; a module that was already loaded stays in use by what holds it.\n */\nexport function useMediabunny(from: MediabunnySource): void {\n if (from == null || (typeof from !== 'string' && typeof from !== 'function' && typeof from !== 'object')) {\n throw new TypeError(`pixi-effects: useMediabunny() takes the mediabunny module, a URL or a function that returns the module; it got ${String(from)}`);\n }\n source = from;\n pending = null;\n}\n\n/** The mediabunny module, fetched once (the first call starts it; later calls share it). A failed load is tried again on the next call. */\nexport function loadMediabunny(): Promise<MediabunnyModule> {\n if (!pending) {\n const from = source;\n const p = fetchModule(from).then(\n (mod) => {\n if (!mod || typeof (mod as { Output?: unknown }).Output !== 'function') {\n throw new Error(`pixi-effects: ${describe(from)} did not give the mediabunny module (no Output in it). ${HOW}`);\n }\n return mod;\n },\n (err: unknown) => {\n const why = err instanceof Error ? err.message : String(err);\n throw new Error(`pixi-effects: could not load mediabunny from ${describe(from)} (${why}). ${HOW}`, { cause: err });\n },\n );\n pending = p;\n p.catch(() => { if (pending === p) pending = null; }); // the next call tries again (a network blip, an import map added later)\n }\n return pending;\n}\n\nconst HOW = 'mediabunny is needed to export (movie.render(), the download button) and for a video layer; playing needs nothing. '\n + 'Add it to the import map (\"mediabunny\": \"https://cdn.jsdelivr.net/npm/mediabunny@1/+esm\"), install it next to pixi-effects, '\n + 'or call useMediabunny(module | url | () => import(...)) before you export.';\n\nfunction fetchModule(from: MediabunnySource | null): Promise<MediabunnyModule> {\n if (from === null) return import('mediabunny');\n if (typeof from === 'string') return import(/* @vite-ignore */ /* webpackIgnore: true */ from) as Promise<MediabunnyModule>;\n if (typeof from === 'function') return Promise.resolve().then(from);\n return Promise.resolve(from);\n}\n\nfunction describe(from: MediabunnySource | null): string {\n if (from === null) return \"'mediabunny' (the bare name: an import map or a bundler resolves it)\";\n if (typeof from === 'string') return from;\n if (typeof from === 'function') return 'the function given to useMediabunny()';\n return 'the object given to useMediabunny()';\n}\n"]}
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var chunkDS2EWRV4_cjs = require('./chunk-DS2EWRV4.cjs');
4
- var chunkCJS2S5XU_cjs = require('./chunk-CJS2S5XU.cjs');
3
+ var chunkOGLRSR6C_cjs = require('./chunk-OGLRSR6C.cjs');
4
+ var chunkVQXZ2NOI_cjs = require('./chunk-VQXZ2NOI.cjs');
5
+ var chunkECLBZV4N_cjs = require('./chunk-ECLBZV4N.cjs');
5
6
  var chunkZAHCGFGC_cjs = require('./chunk-ZAHCGFGC.cjs');
6
7
  var chunkWYR2AU4W_cjs = require('./chunk-WYR2AU4W.cjs');
7
8
  var chunkNGJXVJB7_cjs = require('./chunk-NGJXVJB7.cjs');
8
9
  var chunkDOLTCXCL_cjs = require('./chunk-DOLTCXCL.cjs');
9
10
  var pixi_js = require('pixi.js');
10
- var mediabunny = require('mediabunny');
11
11
 
12
12
  // src/core/gpuShare.ts
13
13
  function sharedGpuOf(renderer) {
@@ -43,8 +43,9 @@ function ensureLoadersRegistered(audioContext) {
43
43
  extension: { type: pixi_js.ExtensionType.LoadParser, name: "pixi-effects-video", priority: 1 },
44
44
  test: (url) => /\.(mp4|webm|mov|mkv)$/i.test(url),
45
45
  async load(url) {
46
- const blob = await fetch(url).then((r) => r.blob());
47
- const input = new mediabunny.Input({ source: new mediabunny.BlobSource(blob), formats: mediabunny.ALL_FORMATS });
46
+ const [M, blob] = await Promise.all([chunkVQXZ2NOI_cjs.loadMediabunny(), fetch(url).then((r) => r.blob())]);
47
+ const { Input, BlobSource, ALL_FORMATS, Output, WavOutputFormat, BufferTarget, Conversion, VideoSampleSink } = M;
48
+ const input = new Input({ source: new BlobSource(blob), formats: ALL_FORMATS });
48
49
  const videoTrack = await input.getPrimaryVideoTrack();
49
50
  if (!videoTrack || !await videoTrack.canDecode()) {
50
51
  throw new Error("pixi-effects: video track cannot be decoded");
@@ -55,8 +56,8 @@ function ensureLoadersRegistered(audioContext) {
55
56
  let audioBuffer = null;
56
57
  if (audioTrack) {
57
58
  try {
58
- const out = new mediabunny.Output({ format: new mediabunny.WavOutputFormat(), target: new mediabunny.BufferTarget() });
59
- const conv = await mediabunny.Conversion.init({ input, output: out, video: { discard: true } });
59
+ const out = new Output({ format: new WavOutputFormat(), target: new BufferTarget() });
60
+ const conv = await Conversion.init({ input, output: out, video: { discard: true } });
60
61
  await conv.execute();
61
62
  audioBuffer = await audioContext.decodeAudioData(out.target.buffer);
62
63
  } catch (e) {
@@ -69,7 +70,7 @@ function ensureLoadersRegistered(audioContext) {
69
70
  videoDuration,
70
71
  audioDuration,
71
72
  duration: Math.max(videoDuration, audioDuration),
72
- sink: new mediabunny.VideoSampleSink(videoTrack)
73
+ sink: new VideoSampleSink(videoTrack)
73
74
  };
74
75
  }
75
76
  });
@@ -250,6 +251,19 @@ async function normalizeMix(buffer, target, between) {
250
251
  gainDb = next;
251
252
  }
252
253
  }
254
+ if (original && final.integratedLufs !== null && m.integratedLufs !== null) {
255
+ const plainDb = Math.min(target - m.integratedLufs, LOUDNESS_CEILING_DB - m.samplePeakDb);
256
+ if (final.integratedLufs < m.integratedLufs + plainDb - 0.1) {
257
+ const k = 10 ** (plainDb / 20);
258
+ for (let ch = 0; ch < chans.length; ch++) {
259
+ const o = original[ch], d = chans[ch];
260
+ for (let i = 0; i < d.length; i++) d[i] = o[i] * k;
261
+ }
262
+ gainDb = plainDb;
263
+ applied = { reductionDb: 0, at: 0 };
264
+ final = chunkZAHCGFGC_cjs.measureLoudness(chans, sr);
265
+ }
266
+ }
253
267
  let trimDb = 0;
254
268
  if (final.truePeakDb > LOUDNESS_CEILING_DB) {
255
269
  trimDb = final.truePeakDb - LOUDNESS_CEILING_DB + 0.01;
@@ -587,10 +601,10 @@ var AUDIO_PREFERENCE = {
587
601
  mkv: ["opus", "vorbis", "aac", "flac", "mp3"],
588
602
  ogg: ["opus", "vorbis", "flac"]
589
603
  };
590
- async function chooseAudioCodec(fmt, requested, container, audio, bitrate) {
604
+ async function chooseAudioCodec(M, fmt, requested, container, audio, bitrate) {
591
605
  const supported = container.getSupportedAudioCodecs?.();
592
606
  const preferred = AUDIO_PREFERENCE[fmt].filter((c) => !supported || supported.includes(c));
593
- const canEncode = (list) => mediabunny.getFirstEncodableAudioCodec(list, { numberOfChannels: audio.numberOfChannels ?? 2, sampleRate: audio.sampleRate, bitrate });
607
+ const canEncode = (list) => M.getFirstEncodableAudioCodec(list, { numberOfChannels: audio.numberOfChannels ?? 2, sampleRate: audio.sampleRate, bitrate });
594
608
  if (requested) {
595
609
  if (await canEncode([requested])) return requested;
596
610
  const alt = await canEncode(preferred.filter((c) => c !== requested));
@@ -603,27 +617,34 @@ async function chooseAudioCodec(fmt, requested, container, audio, bitrate) {
603
617
  }
604
618
  return chosen;
605
619
  }
606
- var qualityMap = {
607
- "very-low": mediabunny.QUALITY_VERY_LOW,
608
- "low": mediabunny.QUALITY_LOW,
609
- "medium": mediabunny.QUALITY_MEDIUM,
610
- "high": mediabunny.QUALITY_HIGH,
611
- "very-high": mediabunny.QUALITY_VERY_HIGH
612
- };
613
- function bitrateOf(v, fallback) {
620
+ function qualityOf(M, name) {
621
+ switch (name) {
622
+ case "very-low":
623
+ return M.QUALITY_VERY_LOW;
624
+ case "low":
625
+ return M.QUALITY_LOW;
626
+ case "medium":
627
+ return M.QUALITY_MEDIUM;
628
+ case "very-high":
629
+ return M.QUALITY_VERY_HIGH;
630
+ default:
631
+ return M.QUALITY_HIGH;
632
+ }
633
+ }
634
+ function bitrateOf(M, v) {
614
635
  const b = parseBitrate(v);
615
- return b.bps ?? (b.quality ? qualityMap[b.quality] : fallback);
636
+ return b.bps ?? qualityOf(M, b.quality ?? "high");
616
637
  }
617
- function makeOutputFormat(name) {
638
+ function makeOutputFormat(M, name) {
618
639
  switch (name) {
619
640
  case "mp4":
620
- return new mediabunny.Mp4OutputFormat({ fastStart: "in-memory" });
641
+ return new M.Mp4OutputFormat({ fastStart: "in-memory" });
621
642
  case "mov":
622
- return new mediabunny.MovOutputFormat({ fastStart: "in-memory" });
643
+ return new M.MovOutputFormat({ fastStart: "in-memory" });
623
644
  case "webm":
624
- return new mediabunny.WebMOutputFormat();
645
+ return new M.WebMOutputFormat();
625
646
  case "mkv":
626
- return new mediabunny.MkvOutputFormat();
647
+ return new M.MkvOutputFormat();
627
648
  }
628
649
  }
629
650
  function sliceAudio(buffer, fromSec, toSec, fadeIn, fadeOut) {
@@ -638,18 +659,19 @@ async function exportAudioOnly(movie, options, fmt, range2) {
638
659
  throw new Error(`pixi-effects: render({ format: '${fmt}' }): this movie has no sound to export (add an audio layer, an sfx or a music score); for the picture use mp4, webm, mov or mkv`);
639
660
  }
640
661
  if (options.video) console.warn(`pixi-effects: render({ format: '${fmt}' }): video options are ignored, only the sound is written`);
662
+ const M = await chunkVQXZ2NOI_cjs.loadMediabunny();
641
663
  const audio = range2.partial ? sliceAudio(movie.audioBuffer, range2.fromSec, range2.toSec, range2.fromFrame > 0, range2.toFrame < movie.totalFrames) : movie.audioBuffer;
642
- const container = fmt === "wav" ? new mediabunny.WavOutputFormat() : new mediabunny.OggOutputFormat();
664
+ const container = fmt === "wav" ? new M.WavOutputFormat() : new M.OggOutputFormat();
643
665
  let codec;
644
666
  if (fmt === "wav") {
645
667
  codec = options.audio?.codec ?? "pcm-s16";
646
668
  const held = container.getSupportedAudioCodecs();
647
669
  if (!held.includes(codec)) throw new Error(`pixi-effects: render({ format: 'wav', audio: { codec: "${codec}" } }): wav holds ${held.join(", ")} (the usual is pcm-s16)`);
648
670
  } else {
649
- codec = await chooseAudioCodec("ogg", options.audio?.codec, container, audio, bitrateOf(options.audio?.bitrate, mediabunny.QUALITY_HIGH));
671
+ codec = await chooseAudioCodec(M, "ogg", options.audio?.codec, container, audio, bitrateOf(M, options.audio?.bitrate));
650
672
  }
651
- const output = new mediabunny.Output({ format: container, target: new mediabunny.BufferTarget() });
652
- const source = new mediabunny.AudioBufferSource({ codec, ...fmt === "wav" ? {} : { bitrate: bitrateOf(options.audio?.bitrate, mediabunny.QUALITY_HIGH) } });
673
+ const output = new M.Output({ format: container, target: new M.BufferTarget() });
674
+ const source = new M.AudioBufferSource({ codec, ...fmt === "wav" ? {} : { bitrate: bitrateOf(M, options.audio?.bitrate) } });
653
675
  output.addAudioTrack(source);
654
676
  movie.emit("progress", { progress: 0, frame: range2.fromFrame, totalFrames: movie.totalFrames });
655
677
  await output.start();
@@ -667,21 +689,22 @@ async function exportFrames(movie, options = {}) {
667
689
  if (!["mp4", "mov", "webm", "mkv"].includes(fmt)) throw new Error(`pixi-effects: render({ format: "${String(fmt)}" }): use mp4, mov, webm, mkv (a picture) or wav, ogg (only the sound)`);
668
690
  chunkDOLTCXCL_cjs.warnUnknownOptions("movie.render({ video })", options.video, ["codec", "bitrate", "hardware", "keyFrameInterval"]);
669
691
  chunkDOLTCXCL_cjs.warnUnknownOptions("movie.render({ audio })", options.audio, ["codec", "bitrate"]);
670
- const videoBitrate = bitrateOf(eff.bitrate ?? "high", mediabunny.QUALITY_HIGH);
692
+ const M = await chunkVQXZ2NOI_cjs.loadMediabunny();
693
+ const videoBitrate = bitrateOf(M, eff.bitrate ?? "high");
671
694
  const opts = {
672
695
  format: fmt,
673
696
  video: { codec: "", bitrate: videoBitrate },
674
697
  audio: {
675
698
  codec: options.audio?.codec ?? (fmt === "mp4" || fmt === "mov" ? "aac" : "opus"),
676
- bitrate: bitrateOf(options.audio?.bitrate, mediabunny.QUALITY_HIGH)
699
+ bitrate: bitrateOf(M, options.audio?.bitrate)
677
700
  }
678
701
  };
679
- const container = makeOutputFormat(opts.format);
702
+ const container = makeOutputFormat(M, opts.format);
680
703
  const audio = movie.audioBuffer && range2.partial ? sliceAudio(movie.audioBuffer, range2.fromSec, range2.toSec, range2.fromFrame > 0, range2.toFrame < movie.totalFrames) : movie.audioBuffer;
681
- if (audio) opts.audio.codec = await chooseAudioCodec(fmt, options.audio?.codec, container, audio, opts.audio.bitrate);
682
- const output = new mediabunny.Output({
704
+ if (audio) opts.audio.codec = await chooseAudioCodec(M, fmt, options.audio?.codec, container, audio, opts.audio.bitrate);
705
+ const output = new M.Output({
683
706
  format: container,
684
- target: new mediabunny.BufferTarget()
707
+ target: new M.BufferTarget()
685
708
  });
686
709
  const mb = eff.motionBlur !== void 0 ? resolveMotionBlur(eff.motionBlur, "movie.render()") : movie.motionBlur ?? null;
687
710
  const stage = movie.app.canvas;
@@ -693,7 +716,7 @@ async function exportFrames(movie, options = {}) {
693
716
  fmt,
694
717
  requested: options.video?.codec,
695
718
  supported: container.getSupportedVideoCodecs(),
696
- canEncode: (c) => mediabunny.canEncodeVideo(c, { width: picture.width, height: picture.height, bitrate: opts.video.bitrate })
719
+ canEncode: (c) => M.canEncodeVideo(c, { width: picture.width, height: picture.height, bitrate: opts.video.bitrate })
697
720
  });
698
721
  if (chosen.fellBackFrom) console.warn(`pixi-effects: this browser cannot encode ${chosen.fellBackFrom.toUpperCase()} video, so the ${fmt} carries ${chosen.codec.toUpperCase()} instead (name a codec with render({ video: { codec } }) to choose yourself).`);
699
722
  opts.video.codec = chosen.codec;
@@ -701,14 +724,14 @@ async function exportFrames(movie, options = {}) {
701
724
  if (hardware !== void 0 && !["no-preference", "prefer-hardware", "prefer-software"].includes(hardware)) {
702
725
  throw new Error(`pixi-effects: render({ video: { hardware: "${String(hardware)}" } }): use 'prefer-hardware', 'prefer-software' or 'no-preference'`);
703
726
  }
704
- const canvasSource = new mediabunny.CanvasSource(picture, {
727
+ const canvasSource = new M.CanvasSource(picture, {
705
728
  codec: opts.video.codec,
706
729
  bitrate: opts.video.bitrate,
707
730
  ...hardware ? { hardwareAcceleration: hardware } : {}
708
731
  });
709
732
  output.addVideoTrack(canvasSource, { frameRate: movie.frameRate });
710
733
  if (audio) {
711
- const audioSource = new mediabunny.AudioBufferSource({
734
+ const audioSource = new M.AudioBufferSource({
712
735
  codec: opts.audio.codec,
713
736
  bitrate: opts.audio.bitrate
714
737
  }, { startTimestamp: opts.audio.codec === "aac" ? -2112 / audio.sampleRate : 0 });
@@ -1245,8 +1268,8 @@ function expandTransitions(spec) {
1245
1268
  }
1246
1269
  const transitions = out.transitions;
1247
1270
  const sequences = out.sequences ?? [];
1248
- const remap = out.duration !== void 0 ? chunkCJS2S5XU_cjs.timeRemapOf(out) : null;
1249
- const parentDuration = remap ? chunkCJS2S5XU_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1271
+ const remap = out.duration !== void 0 ? chunkECLBZV4N_cjs.timeRemapOf(out) : null;
1272
+ const parentDuration = remap ? chunkECLBZV4N_cjs.contentLength(remap, out.duration) : out.duration ?? Infinity;
1250
1273
  const compW = out.width;
1251
1274
  const compH = out.height;
1252
1275
  const threeDByName = new Set(sequences.filter((s) => s.name && s.threeD).map((s) => s.name));
@@ -1314,16 +1337,16 @@ function expandTransitions(spec) {
1314
1337
  if (toUses > 1) {
1315
1338
  throw new Error(`pixi-effects: ${tag} sequence "${t.to}" is used as \`to\` in more than one transition`);
1316
1339
  }
1317
- const tStart = chunkCJS2S5XU_cjs.resolveAt(t.at, parentDuration);
1340
+ const tStart = chunkECLBZV4N_cjs.resolveAt(t.at, parentDuration);
1318
1341
  const tEnd = tStart + t.duration;
1319
1342
  const fromSeq = fromEntry.seq;
1320
- const fromStart = chunkCJS2S5XU_cjs.resolveAt(fromSeq.at, parentDuration);
1343
+ const fromStart = chunkECLBZV4N_cjs.resolveAt(fromSeq.at, parentDuration);
1321
1344
  const fromEnd = fromStart + (fromSeq.duration ?? parentDuration);
1322
1345
  if (tStart < fromStart - EPS || tEnd > fromEnd + EPS) {
1323
1346
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`from\` "${t.from}" (lives [${fromStart}, ${fromEnd}], so it ends at ${fromEnd})`);
1324
1347
  }
1325
1348
  const toSeq = toEntry.seq;
1326
- const toStart = chunkCJS2S5XU_cjs.resolveAt(toSeq.at, parentDuration);
1349
+ const toStart = chunkECLBZV4N_cjs.resolveAt(toSeq.at, parentDuration);
1327
1350
  const toEnd = toStart + (toSeq.duration ?? parentDuration);
1328
1351
  if (tStart < toStart - EPS || tEnd > toEnd + EPS) {
1329
1352
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`to\` "${t.to}" (lives [${toStart}, ${toEnd}], so it starts at ${toStart})`);
@@ -1383,7 +1406,7 @@ function wrapAsFullComposition(seq, compW, compH) {
1383
1406
  delete inner.at;
1384
1407
  delete inner.duration;
1385
1408
  const m = inner.mask;
1386
- const carriesMatte = m !== void 0 && chunkDS2EWRV4_cjs.maskSourceOf(m).kind === "refs";
1409
+ const carriesMatte = m !== void 0 && chunkOGLRSR6C_cjs.maskSourceOf(m).kind === "refs";
1387
1410
  if (carriesMatte) delete inner.mask;
1388
1411
  return {
1389
1412
  ...carriesMatte ? { mask: m } : {},
@@ -1653,7 +1676,7 @@ function canvasReport(root) {
1653
1676
  const problem = threw.length ? `${name} could not draw at ${threw.map((t) => `${t} s`).join(", ")} (its ${kind === "three" ? "update or render threw" : "draw threw"}, or the layer failed to build): ${kind === "three" ? "the warning above says why" : "it shows a magenta checkerboard there; the warning above says why"}, so the layer cannot be called deterministic` : differs.length ? `${name} does not draw the same picture for the same time (checked at ${differs.map(describeDiff).join(", ")}; drawn in two orders after one draw that was thrown away): ` + (kind === "three" ? "usually its update uses Math.random, Date.now, a clock, or state that builds up from call to call: a mixer advanced with mixer.update(dt) is one (pose a clip with mixer.setTime(t), or threeGltf(url).pose(t)), and so is a value moved by the time since the last call. A skinned model of your own (not loaded by threeGltf) that moves and casts a shadow does it too: three culls a skinned mesh by a bounding sphere that does not follow it, so set mesh.frustumCulled = false. Set every object from t, so playback, a seek and an export agree" : "usually its draw uses Math.random, Date.now or state that outlives the call (a value it keeps from the last call), so playback, a seek and an export would not agree: use info.random() (seeded) and derive everything from t. If it uses none of these, look at the canvases it draws from: make sprites and masks with info.canvas(w, h) (not document.createElement) and draw them into the picture before the strokes that depend on them") : "";
1654
1677
  out.push({ name: seq.spec.name ?? name, moments, drawMs, ok: !differs.length && !threw.length, problem, kind });
1655
1678
  }
1656
- if (seq instanceof chunkDS2EWRV4_cjs.CompositionSequence) walk3(seq);
1679
+ if (seq instanceof chunkOGLRSR6C_cjs.CompositionSequence) walk3(seq);
1657
1680
  };
1658
1681
  const walk3 = (comp) => {
1659
1682
  for (const { seq } of comp.layers()) {
@@ -1705,8 +1728,8 @@ function motionOf(keyframes, local, lives, keys = (k) => GEOMETRY.has(k)) {
1705
1728
  let length = 0;
1706
1729
  if (typeof kf.duration === "number") length = kf.duration;
1707
1730
  else if (kf.duration === "auto") {
1708
- const sp = chunkCJS2S5XU_cjs.parseSpring(kf.ease);
1709
- length = sp ? Math.round(chunkCJS2S5XU_cjs.springDuration(sp) * 1e3) / 1e3 : 0.5;
1731
+ const sp = chunkECLBZV4N_cjs.parseSpring(kf.ease);
1732
+ length = sp ? Math.round(chunkECLBZV4N_cjs.springDuration(sp) * 1e3) / 1e3 : 0.5;
1710
1733
  }
1711
1734
  if (!(length > 0)) continue;
1712
1735
  const kat = kf.at ?? 0;
@@ -1786,7 +1809,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1786
1809
  if (motion.ever) hasMotion.add(info);
1787
1810
  let shown = bounds ? clip(bounds, view) : null;
1788
1811
  const maskTarget = seq.maskSequence?.target;
1789
- const maskSrc = chunkDS2EWRV4_cjs.maskSourceOf(seq.spec.mask);
1812
+ const maskSrc = chunkOGLRSR6C_cjs.maskSourceOf(seq.spec.mask);
1790
1813
  if (shown && maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert)) {
1791
1814
  const m = maskTarget.getBounds();
1792
1815
  shown = clip(shown, { x: m.x, y: m.y, width: m.width, height: m.height });
@@ -1795,7 +1818,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1795
1818
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1796
1819
  if (group) scene.set(info, group);
1797
1820
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1798
- if (seq instanceof chunkDS2EWRV4_cjs.CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1821
+ if (seq instanceof chunkOGLRSR6C_cjs.CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1799
1822
  });
1800
1823
  };
1801
1824
  walk3(root, "", false, true);
@@ -1967,12 +1990,12 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1967
1990
  const alive = (threeD ? d.visible : (t?.renderable ?? true) && t?.visible !== false) && carriers.every((c) => c.target?.renderable ?? true);
1968
1991
  const visible = parentVisible && alive && alpha > 0;
1969
1992
  if (!visible) {
1970
- if (seq instanceof chunkDS2EWRV4_cjs.CompositionSequence && !threeD && parentVisible && alpha > 0 && t?.visible === false && (t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true)) hiddenSources.set(seq, path);
1993
+ if (seq instanceof chunkOGLRSR6C_cjs.CompositionSequence && !threeD && parentVisible && alpha > 0 && t?.visible === false && (t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true)) hiddenSources.set(seq, path);
1971
1994
  return;
1972
1995
  }
1973
- const isComposition = seq instanceof chunkDS2EWRV4_cjs.CompositionSequence;
1996
+ const isComposition = seq instanceof chunkOGLRSR6C_cjs.CompositionSequence;
1974
1997
  if (isComposition && !threeD) {
1975
- const gm = seq.maskSequence?.target, gs = chunkDS2EWRV4_cjs.maskSourceOf(seq.spec.mask);
1998
+ const gm = seq.maskSequence?.target, gs = chunkOGLRSR6C_cjs.maskSourceOf(seq.spec.mask);
1976
1999
  if (gm && gs.kind === "inline" && !gs.invert && !inMask(gm, at)) return;
1977
2000
  walk3(seq, path + "/", visible, at, orderBase, via);
1978
2001
  return;
@@ -2028,7 +2051,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
2028
2051
  }
2029
2052
  if (!hit) return;
2030
2053
  const maskTarget = seq.maskSequence?.target;
2031
- const maskSrc = chunkDS2EWRV4_cjs.maskSourceOf(seq.spec.mask);
2054
+ const maskSrc = chunkOGLRSR6C_cjs.maskSourceOf(seq.spec.mask);
2032
2055
  if (maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert) && !(maskSrc.kind === "inline" ? inMask(maskTarget, point) : inside(maskTarget.getBounds(), point))) return;
2033
2056
  found.push({ hit: via ? { ...hit, point: { x, y }, via } : hit, order: orderBase ? [...orderBase, ...orderOf(d)] : orderOf(d) });
2034
2057
  });
@@ -2038,7 +2061,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
2038
2061
  const path = hiddenSources.get(f.source);
2039
2062
  if (path === void 0) continue;
2040
2063
  const g = f.source.target.toGlobal({ x: f.at.x * f.source.intrinsicWidth, y: f.at.y * f.source.intrinsicHeight });
2041
- const sm = f.source.maskSequence?.target, ss = chunkDS2EWRV4_cjs.maskSourceOf(f.source.spec.mask);
2064
+ const sm = f.source.maskSequence?.target, ss = chunkOGLRSR6C_cjs.maskSourceOf(f.source.spec.mask);
2042
2065
  if (sm && ss.kind === "inline" && !ss.invert && !inMask(sm, g)) continue;
2043
2066
  walk3(f.source, path + "/", true, g, f.order, f.via);
2044
2067
  }
@@ -2074,7 +2097,7 @@ function walk(root, visit) {
2074
2097
  visit(seq);
2075
2098
  const mask = seq.maskSequence;
2076
2099
  if (mask) visit(mask);
2077
- if (seq instanceof chunkDS2EWRV4_cjs.CompositionSequence) walk(seq, visit);
2100
+ if (seq instanceof chunkOGLRSR6C_cjs.CompositionSequence) walk(seq, visit);
2078
2101
  }
2079
2102
  }
2080
2103
  function reusePoolOf(old) {
@@ -2175,7 +2198,7 @@ var round = (n) => Math.round(n * 1e6) / 1e6;
2175
2198
  var family = (name) => name.replace(/\d+/g, "#");
2176
2199
  function detailOf(seq) {
2177
2200
  const s = seq.spec;
2178
- const spans = chunkCJS2S5XU_cjs.spansMeta.get(seq.spec)?.preview;
2201
+ const spans = chunkECLBZV4N_cjs.spansMeta.get(seq.spec)?.preview;
2179
2202
  if (spans) return spans.length > 60 ? spans.slice(0, 57) + "\u2026" : spans;
2180
2203
  if (typeof s.text === "string") return s.text.length > 60 ? s.text.slice(0, 57) + "\u2026" : s.text;
2181
2204
  if (typeof s.asset === "string") return `asset \u201C${s.asset}\u201D`;
@@ -2191,10 +2214,10 @@ function walk2(comp, parentStart, parentEnd, depth, prefix, rows, transitions, l
2191
2214
  const start = parentStart + (seq.at ?? 0);
2192
2215
  const end = Math.min(parentEnd, seq.duration !== void 0 ? start + seq.duration : parentEnd);
2193
2216
  const span = end - start;
2194
- const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkCJS2S5XU_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
2217
+ const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkECLBZV4N_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
2195
2218
  const row = { path: prefix + name, name, type: seq.spec.type, depth, start: round(start), end: round(end), keys, detail: detailOf(seq), ...local ? { local } : {} };
2196
2219
  const inner = [];
2197
- const isComp = seq instanceof chunkDS2EWRV4_cjs.CompositionSequence;
2220
+ const isComp = seq instanceof chunkOGLRSR6C_cjs.CompositionSequence;
2198
2221
  if (isComp) {
2199
2222
  const label = seq.remapLabel();
2200
2223
  walk2(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
@@ -2353,7 +2376,7 @@ function sheetLayout(n, columns, cellW, srcW, srcH, labelH) {
2353
2376
  }
2354
2377
 
2355
2378
  // src/core/vars.ts
2356
- var RESERVED_NAMES = ["w", "h", "W", "H", "GW", "GH", "t", "d", "T", "contain", "cover", "value", ...chunkCJS2S5XU_cjs.EXPRESSION_NAMES];
2379
+ var RESERVED_NAMES = ["w", "h", "W", "H", "GW", "GH", "t", "d", "T", "contain", "cover", "value", ...chunkECLBZV4N_cjs.EXPRESSION_NAMES];
2357
2380
  var NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
2358
2381
  var isPlain = (v) => !!v && typeof v === "object" && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);
2359
2382
  function normalizeVars(raw, where, warn = (m) => console.warn(m)) {
@@ -2897,8 +2920,8 @@ var Movie = class {
2897
2920
  this.stops = normalizeStops(applyVars(options.composition, this._vars)?.stops, this.duration, this.frameRate);
2898
2921
  this._posterFrame = poster.frame;
2899
2922
  this.background = options.background ?? "#000000";
2900
- this.timeline = chunkCJS2S5XU_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2901
- chunkCJS2S5XU_cjs.lengthen(this.timeline, this.duration);
2923
+ this.timeline = chunkECLBZV4N_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2924
+ chunkECLBZV4N_cjs.lengthen(this.timeline, this.duration);
2902
2925
  const baseAppOptions = {
2903
2926
  width: this.width,
2904
2927
  height: this.height,
@@ -2997,7 +3020,7 @@ var Movie = class {
2997
3020
  height: sizeWins ? height : spec.height ?? height,
2998
3021
  duration: spec.duration ?? this.duration
2999
3022
  } : void 0;
3000
- await chunkCJS2S5XU_cjs.ensureFilterLibrary(seededComposition);
3023
+ await chunkECLBZV4N_cjs.ensureFilterLibrary(seededComposition);
3001
3024
  if (chunkWYR2AU4W_cjs.usesAdvancedBlend(seededComposition)) await chunkWYR2AU4W_cjs.enableAdvancedBlend(this.app.renderer);
3002
3025
  const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
3003
3026
  const rootSeqSpec = {
@@ -3008,7 +3031,7 @@ var Movie = class {
3008
3031
  ...userComposition
3009
3032
  };
3010
3033
  if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
3011
- const sequence = new chunkDS2EWRV4_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
3034
+ const sequence = new chunkOGLRSR6C_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
3012
3035
  lp?.begin("build", countLayers(rootSeqSpec));
3013
3036
  try {
3014
3037
  await sequence.build();
@@ -3147,8 +3170,8 @@ var Movie = class {
3147
3170
  if (!changed) return { reason, ms: 0, audio: "none", frame: this.currentFrame, playing: this.isPlaying, unchanged: true };
3148
3171
  sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged, reusePoolOf(this._rootSequence));
3149
3172
  alive();
3150
- const timeline = chunkCJS2S5XU_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
3151
- chunkCJS2S5XU_cjs.lengthen(timeline, this.duration);
3173
+ const timeline = chunkECLBZV4N_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
3174
+ chunkECLBZV4N_cjs.lengthen(timeline, this.duration);
3152
3175
  sequence.bindTimeline(timeline);
3153
3176
  const hadSound = this.audioBuffer !== null;
3154
3177
  const audio = await this._mixSound(sequence, this._ensureAudioContext());
@@ -3923,7 +3946,7 @@ function countLayers(spec) {
3923
3946
  n += 1 + countLayers(s);
3924
3947
  const spans = s.spans;
3925
3948
  if (Array.isArray(spans)) n += spans.length;
3926
- const src = chunkDS2EWRV4_cjs.maskSourceOf(mask);
3949
+ const src = chunkOGLRSR6C_cjs.maskSourceOf(mask);
3927
3950
  if (src.kind === "inline") n += 1 + countLayers(src.spec);
3928
3951
  }
3929
3952
  return n;
@@ -4012,8 +4035,8 @@ function splitVertical(text, style, ts, right, top, align, by) {
4012
4035
 
4013
4036
  // src/presets/_ease.ts
4014
4037
  function parseEase2(name) {
4015
- chunkCJS2S5XU_cjs.checkEase(name, "a preset");
4016
- return chunkCJS2S5XU_cjs.parseEase(name);
4038
+ chunkECLBZV4N_cjs.checkEase(name, "a preset");
4039
+ return chunkECLBZV4N_cjs.parseEase(name);
4017
4040
  }
4018
4041
 
4019
4042
  // src/presets/kenBurns.ts
@@ -4148,17 +4171,17 @@ function orbit(opts) {
4148
4171
  throw new Error("pixi-effects: orbit() cannot combine `radius` with `dollyZoom` \u2014 the radius follows the animated fov");
4149
4172
  }
4150
4173
  const [cx, cy] = opts.center ?? [width / 2, height / 2];
4151
- const fixedR = opts.radius ?? chunkCJS2S5XU_cjs.homeDistance(height, fov ?? chunkCJS2S5XU_cjs.DEFAULT_FOV);
4174
+ const fixedR = opts.radius ?? chunkECLBZV4N_cjs.homeDistance(height, fov ?? chunkECLBZV4N_cjs.DEFAULT_FOV);
4152
4175
  const start = opts.start ?? -degrees / 2;
4153
4176
  const steps = Math.max(1, Math.round(duration * stepsPerSecond));
4154
4177
  const easeFn = parseEase2(ease);
4155
4178
  const dollyEase = dolly ? parseEase2(dolly.ease ?? ease) : null;
4156
4179
  const pos = (i) => {
4157
4180
  const p = i / steps;
4158
- const a = (start + degrees * easeFn(p)) * chunkCJS2S5XU_cjs.DEG;
4181
+ const a = (start + degrees * easeFn(p)) * chunkECLBZV4N_cjs.DEG;
4159
4182
  if (!dolly) return { x: cx + fixedR * Math.sin(a), z: fixedR * Math.cos(a) };
4160
4183
  const f = dolly.from + (dolly.to - dolly.from) * dollyEase(p);
4161
- const R = chunkCJS2S5XU_cjs.homeDistance(height, f);
4184
+ const R = chunkECLBZV4N_cjs.homeDistance(height, f);
4162
4185
  return { x: cx + R * Math.sin(a), z: R * Math.cos(a), fov: f };
4163
4186
  };
4164
4187
  const keyframes = [];
@@ -4213,7 +4236,7 @@ function particles(opts) {
4213
4236
  const round2 = (v) => Math.round(v * 1e6) / 1e6;
4214
4237
  const out = [];
4215
4238
  for (let i = 0; i < count; i++) {
4216
- const r = chunkCJS2S5XU_cjs.random(seed * 1000003 + i * 7919 + 17);
4239
+ const r = chunkECLBZV4N_cjs.random(seed * 1000003 + i * 7919 + 17);
4217
4240
  const pick = ([a2, b]) => a2 + r() * (b - a2);
4218
4241
  const born = at + r() * emit;
4219
4242
  const lifetime = pick(life);
@@ -4226,7 +4249,7 @@ function particles(opts) {
4226
4249
  const turn = spin ? pick(spin) : 0;
4227
4250
  const startAngle = spin ? r() * 360 : 0;
4228
4251
  const swayAmp = sway?.amp ?? 0, swayFreq = sway?.freq ?? 1;
4229
- const n0 = sway ? chunkCJS2S5XU_cjs.noise(0, seed * 1000003 + i) : 0;
4252
+ const n0 = sway ? chunkECLBZV4N_cjs.noise(0, seed * 1000003 + i) : 0;
4230
4253
  const at_ = (t) => {
4231
4254
  const axis = (p0, v0, acc) => {
4232
4255
  if (drag > 0) {
@@ -4235,7 +4258,7 @@ function particles(opts) {
4235
4258
  }
4236
4259
  return p0 + v0 * t + 0.5 * acc * t * t;
4237
4260
  };
4238
- const sw = sway ? swayAmp * (chunkCJS2S5XU_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
4261
+ const sw = sway ? swayAmp * (chunkECLBZV4N_cjs.noise(t * swayFreq, seed * 1000003 + i) - n0) : 0;
4239
4262
  return { x: axis(x0, vx, wind) + sw, y: axis(y0, vy, gravity) };
4240
4263
  };
4241
4264
  const kfs = [];
@@ -4297,7 +4320,7 @@ function followPath(opts) {
4297
4320
  if (steps > MAX_STEPS) throw new Error(`followPath(): ${steps} samples is too many (max ${MAX_STEPS}); lower frameRate or duration`);
4298
4321
  const segments = [];
4299
4322
  let total = 0;
4300
- for (const line of chunkCJS2S5XU_cjs.flattenSvgPath(d)) {
4323
+ for (const line of chunkECLBZV4N_cjs.flattenSvgPath(d)) {
4301
4324
  const p = line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
4302
4325
  for (let i = 2; i < p.length; i += 2) {
4303
4326
  const len = Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
@@ -4350,7 +4373,7 @@ function followPath(opts) {
4350
4373
  // src/core/pathSample.ts
4351
4374
  function samplePath(d) {
4352
4375
  if (typeof d !== "string" || d.trim() === "") throw new Error('samplePath(): d must be SVG path data, e.g. "M 40 300 C 200 20 400 20 560 300"');
4353
- const lines = chunkCJS2S5XU_cjs.flattenSvgPath(d);
4376
+ const lines = chunkECLBZV4N_cjs.flattenSvgPath(d);
4354
4377
  const segments = [];
4355
4378
  let length = 0;
4356
4379
  for (const line of lines) {
@@ -4381,7 +4404,7 @@ function samplePath(d) {
4381
4404
  return { x: g.x0 + (g.x1 - g.x0) * t, y: g.y0 + (g.y1 - g.y0) * t, angle: Math.atan2(g.y1 - g.y0, g.x1 - g.x0) };
4382
4405
  },
4383
4406
  trim(start = 0, end = 1) {
4384
- return chunkCJS2S5XU_cjs.trimPolylines(lines, start, end).map((l) => l.closed ? [...l.pts, l.pts[0], l.pts[1]] : l.pts);
4407
+ return chunkECLBZV4N_cjs.trimPolylines(lines, start, end).map((l) => l.closed ? [...l.pts, l.pts[0], l.pts[1]] : l.pts);
4385
4408
  }
4386
4409
  };
4387
4410
  }
@@ -4492,7 +4515,7 @@ function distances(n, from, grid, seed) {
4492
4515
  if (n === 0) return [];
4493
4516
  if (from === "random") {
4494
4517
  const order = Array.from({ length: n }, (_, i) => i);
4495
- const r = chunkCJS2S5XU_cjs.random(seed);
4518
+ const r = chunkECLBZV4N_cjs.random(seed);
4496
4519
  for (let i = n - 1; i > 0; i--) {
4497
4520
  const j = Math.floor(r() * (i + 1));
4498
4521
  [order[i], order[j]] = [order[j], order[i]];
@@ -4554,7 +4577,7 @@ function wiggle(opts) {
4554
4577
  to[n] = p.around;
4555
4578
  return;
4556
4579
  }
4557
- const off = (chunkCJS2S5XU_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
4580
+ const off = (chunkECLBZV4N_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
4558
4581
  to[n] = typeof p.around === "number" ? p.around + off : `(${p.around}) ${off < 0 ? "-" : "+"} ${Math.abs(off)}`;
4559
4582
  });
4560
4583
  out.push({ at: at + s * stepLen, to, duration: stepLen, ease });
@@ -5025,5 +5048,5 @@ exports.splitText = splitText;
5025
5048
  exports.stagger = stagger;
5026
5049
  exports.wiggle = wiggle;
5027
5050
  exports.withFade = withFade;
5028
- //# sourceMappingURL=chunk-AES2L75M.cjs.map
5029
- //# sourceMappingURL=chunk-AES2L75M.cjs.map
5051
+ //# sourceMappingURL=chunk-NGK4E536.cjs.map
5052
+ //# sourceMappingURL=chunk-NGK4E536.cjs.map