pixi-effects 0.44.0 → 0.46.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 (70) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/check.mjs +117 -2
  5. package/ai/tools/lookup.mjs +1 -0
  6. package/dist/Composition-NSY7A32Y.js +7 -0
  7. package/dist/{Composition-YGBQX3HZ.js.map → Composition-NSY7A32Y.js.map} +1 -1
  8. package/dist/Composition-PME3P5LP.cjs +16 -0
  9. package/dist/{Composition-N6BOROJ2.cjs.map → Composition-PME3P5LP.cjs.map} +1 -1
  10. package/dist/Controller.d.cts +2 -2
  11. package/dist/Controller.d.ts +2 -2
  12. package/dist/{Movie-B3VOAgwc.d.cts → Movie-BqrAWN0g.d.cts} +13 -1
  13. package/dist/{Movie-CQCVUqRs.d.ts → Movie-sV8nJr_f.d.ts} +13 -1
  14. package/dist/{Spans-XTIWYSNO.cjs → Spans-2TZCNREQ.cjs} +9 -9
  15. package/dist/{Spans-XTIWYSNO.cjs.map → Spans-2TZCNREQ.cjs.map} +1 -1
  16. package/dist/{Spans-6L2ZVECX.js → Spans-TESJPQKX.js} +4 -4
  17. package/dist/{Spans-6L2ZVECX.js.map → Spans-TESJPQKX.js.map} +1 -1
  18. package/dist/{chunk-4MBTYREC.js → chunk-2E74SP63.js} +72 -17
  19. package/dist/chunk-2E74SP63.js.map +1 -0
  20. package/dist/{chunk-MAAM45ZX.cjs → chunk-7M63Y6MO.cjs} +114 -59
  21. package/dist/chunk-7M63Y6MO.cjs.map +1 -0
  22. package/dist/{chunk-3HZV3UHU.cjs → chunk-DKCMJILW.cjs} +67 -65
  23. package/dist/chunk-DKCMJILW.cjs.map +1 -0
  24. package/dist/{chunk-4QVQ5UJJ.js → chunk-DM2OLQOJ.js} +5 -6
  25. package/dist/chunk-DM2OLQOJ.js.map +1 -0
  26. package/dist/{chunk-ICI6ACHY.js → chunk-FPV3NPYC.js} +8 -6
  27. package/dist/chunk-FPV3NPYC.js.map +1 -0
  28. package/dist/{chunk-UHNSL2KJ.js → chunk-K24FF4BT.js} +10 -5
  29. package/dist/chunk-K24FF4BT.js.map +1 -0
  30. package/dist/{chunk-X62H3RBG.cjs → chunk-LALEBV63.cjs} +5 -6
  31. package/dist/chunk-LALEBV63.cjs.map +1 -0
  32. package/dist/{chunk-LQDDDIC4.cjs → chunk-O66JGPVV.cjs} +10 -4
  33. package/dist/chunk-O66JGPVV.cjs.map +1 -0
  34. package/dist/index.cjs +35 -35
  35. package/dist/index.d.cts +3 -3
  36. package/dist/index.d.ts +3 -3
  37. package/dist/index.js +4 -4
  38. package/dist/loader.css +1 -1
  39. package/dist/present.cjs +4 -4
  40. package/dist/present.d.cts +2 -2
  41. package/dist/present.d.ts +2 -2
  42. package/dist/present.js +4 -4
  43. package/dist/sound.cjs +2 -2
  44. package/dist/sound.js +1 -1
  45. package/dist/three.cjs +194 -15
  46. package/dist/three.cjs.map +1 -1
  47. package/dist/three.d.cts +75 -4
  48. package/dist/three.d.ts +75 -4
  49. package/dist/three.js +191 -13
  50. package/dist/three.js.map +1 -1
  51. package/dist/{types-C5xo0fzb.d.cts → types-CTSrPvY3.d.cts} +64 -58
  52. package/dist/{types-C5xo0fzb.d.ts → types-CTSrPvY3.d.ts} +64 -58
  53. package/docs/api.md +2 -0
  54. package/docs/dsl.md +26 -0
  55. package/llms-full.txt +66 -4
  56. package/package.json +1 -1
  57. package/skills/pixi-effects/SKILL.md +2 -2
  58. package/skills/pixi-effects/reference/cheatsheet.md +2 -2
  59. package/skills/pixi-effects/reference/recipes.md +34 -0
  60. package/skills/pixi-effects/template.html +6 -6
  61. package/dist/Composition-N6BOROJ2.cjs +0 -16
  62. package/dist/Composition-YGBQX3HZ.js +0 -7
  63. package/dist/chunk-3HZV3UHU.cjs.map +0 -1
  64. package/dist/chunk-4MBTYREC.js.map +0 -1
  65. package/dist/chunk-4QVQ5UJJ.js.map +0 -1
  66. package/dist/chunk-ICI6ACHY.js.map +0 -1
  67. package/dist/chunk-LQDDDIC4.cjs.map +0 -1
  68. package/dist/chunk-MAAM45ZX.cjs.map +0 -1
  69. package/dist/chunk-UHNSL2KJ.js.map +0 -1
  70. package/dist/chunk-X62H3RBG.cjs.map +0 -1
@@ -1,6 +1,6 @@
1
- import { CompositionSequence, maskSourceOf } from './chunk-ICI6ACHY.js';
2
- import { EXPRESSION_NAMES, createTimeline, lengthen, ensureFilterLibrary, checkEase, parseEase, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, trimPolylines, rand, DEG, timeRemapOf, contentLength, resolveAt, parseSpring, springDuration, spansMeta } from './chunk-4QVQ5UJJ.js';
3
- import { analyzeAudio, fft, resampleThrough, playSpan } from './chunk-UHNSL2KJ.js';
1
+ import { CompositionSequence, maskSourceOf } from './chunk-FPV3NPYC.js';
2
+ import { EXPRESSION_NAMES, createTimeline, lengthen, ensureFilterLibrary, checkEase, parseEase, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, trimPolylines, rand, DEG, timeRemapOf, contentLength, resolveAt, parseSpring, springDuration, spansMeta } from './chunk-DM2OLQOJ.js';
3
+ import { analyzeAudio, fft, resampleThrough, measureLoudness, playSpan } from './chunk-K24FF4BT.js';
4
4
  import { usesAdvancedBlend, enableAdvancedBlend } from './chunk-VNP7QCJE.js';
5
5
  import { describeLayer } from './chunk-32MMNDDN.js';
6
6
  import { warnUnknownOptions, suggestName } from './chunk-BXOCEZ4U.js';
@@ -140,6 +140,20 @@ async function mixdown(audios, totalDuration, sampleRate = 44100, pace) {
140
140
  return await ctx.startRendering();
141
141
  }
142
142
  var KNEE = 0.9;
143
+ var LOUDNESS_CEILING_DB = -1;
144
+ function normalizeMix(buffer, target) {
145
+ const chans = [];
146
+ for (let ch = 0; ch < buffer.numberOfChannels; ch++) chans.push(buffer.getChannelData(ch));
147
+ const m = measureLoudness(chans, buffer.sampleRate, { truePeak: false });
148
+ if (m.integratedLufs === null) return { target, gainDb: 0, reachedLufs: null, limited: false };
149
+ let gainDb = target - m.integratedLufs;
150
+ const room = LOUDNESS_CEILING_DB - m.samplePeakDb;
151
+ const limited = gainDb > room;
152
+ if (limited) gainDb = room;
153
+ const gain = 10 ** (gainDb / 20);
154
+ for (const d of chans) for (let i = 0; i < d.length; i++) d[i] = d[i] * gain;
155
+ return { target, gainDb: Math.round(gainDb * 10) / 10, reachedLufs: Math.round((m.integratedLufs + gainDb) * 10) / 10, limited };
156
+ }
143
157
  function limitMix(buffer, audios) {
144
158
  let peak = 0;
145
159
  let peakIndex = 0;
@@ -1618,7 +1632,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1618
1632
  const name = seq.spec.name;
1619
1633
  const label = name ?? `${seq.spec.type}#${i}`;
1620
1634
  const alpha = (t?.alpha ?? 1) * carriers.reduce((a, c) => a * (c.target?.alpha ?? 1), 1);
1621
- const alive = (threeD ? display.visible : t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true);
1635
+ const alive = (threeD ? display.visible : (t?.renderable ?? true) && t?.visible !== false) && carriers.every((c) => c.target?.renderable ?? true);
1622
1636
  const visible = parentVisible && alive && alpha > 0;
1623
1637
  let bounds = null;
1624
1638
  if (!insideThreeD) {
@@ -1798,7 +1812,22 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1798
1812
  return true;
1799
1813
  };
1800
1814
  const inside = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
1801
- const walk3 = (comp, prefix, parentVisible) => {
1815
+ const hiddenSources = /* @__PURE__ */ new Map();
1816
+ const forwards = [];
1817
+ const rays = /* @__PURE__ */ new Map();
1818
+ const rayOf = (seq, d, local, lb) => {
1819
+ if (typeof seq.pickAt !== "function") return null;
1820
+ if (!rays.has(seq)) rays.set(seq, seq.pickAt((local.x - lb.x) / lb.width, (local.y - lb.y) / lb.height));
1821
+ return rays.get(seq);
1822
+ };
1823
+ const forwardThrough = (seq, d, path, local, lb) => {
1824
+ const shows = seq.screenOf;
1825
+ if (!shows || !inside(lb, local)) return;
1826
+ const r = rayOf(seq, d, local, lb);
1827
+ const shown = r?.uv ? shows.call(seq, r.material) : null;
1828
+ if (r?.uv && shown) forwards.push({ source: shown.source, uv: r.uv, via: { path, object: String(r.object) }, order: orderOf(d) });
1829
+ };
1830
+ const walk3 = (comp, prefix, parentVisible, at = { x, y }, orderBase, via) => {
1802
1831
  comp.layers().forEach(({ seq, display, threeD, carriers, quad }, i) => {
1803
1832
  const d = display;
1804
1833
  if (!d) return;
@@ -1806,16 +1835,25 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1806
1835
  const name = seq.spec.name;
1807
1836
  const path = prefix + (name ?? `${seq.spec.type}#${i}`);
1808
1837
  const alpha = (t?.alpha ?? 1) * carriers.reduce((a, c) => a * (c.target?.alpha ?? 1), 1);
1809
- const alive = (threeD ? d.visible : t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true);
1838
+ const alive = (threeD ? d.visible : (t?.renderable ?? true) && t?.visible !== false) && carriers.every((c) => c.target?.renderable ?? true);
1810
1839
  const visible = parentVisible && alive && alpha > 0;
1811
- if (!visible) return;
1840
+ if (!visible) {
1841
+ if (seq instanceof CompositionSequence && !threeD && parentVisible && alpha > 0 && t?.visible === false && (t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true)) hiddenSources.set(seq, path);
1842
+ return;
1843
+ }
1812
1844
  const isComposition = seq instanceof CompositionSequence;
1813
1845
  if (isComposition && !threeD) {
1814
- walk3(seq, path + "/", visible);
1846
+ walk3(seq, path + "/", visible, at, orderBase, via);
1815
1847
  return;
1816
1848
  }
1817
- if (!wanted(name, path)) return;
1818
- const point = { x, y };
1849
+ if (!wanted(name, path)) {
1850
+ if (!threeD) {
1851
+ const lp = d.toLocal(at);
1852
+ forwardThrough(seq, d, path, lp, d.getLocalBounds());
1853
+ }
1854
+ return;
1855
+ }
1856
+ const point = at;
1819
1857
  let hit = null;
1820
1858
  if (threeD) {
1821
1859
  if (quad && pointInQuad(quad, d.toLocal(point).x, d.toLocal(point).y)) hit = { name, path, type: seq.spec.type, kind: "card", point };
@@ -1823,7 +1861,10 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1823
1861
  const local = d.toLocal(point);
1824
1862
  const lb = d.getLocalBounds();
1825
1863
  if (inside(lb, local)) {
1826
- const three = typeof seq.pickAt === "function" ? seq.pickAt((local.x - lb.x) / lb.width, (local.y - lb.y) / lb.height) : void 0;
1864
+ const isThree = typeof seq.pickAt === "function";
1865
+ const ray = isThree ? rayOf(seq, d, local, lb) : void 0;
1866
+ if (isThree) forwardThrough(seq, d, path, local, lb);
1867
+ const three = ray ? { object: ray.object, point: ray.point, distance: ray.distance, object3d: ray.object3d } : isThree ? null : void 0;
1827
1868
  if (three) {
1828
1869
  const planeOf = seq.dragPlane;
1829
1870
  if (planeOf) {
@@ -1858,10 +1899,16 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1858
1899
  const maskTarget = seq.maskSequence?.target;
1859
1900
  const maskSrc = maskSourceOf(seq.spec.mask);
1860
1901
  if (maskTarget && !(maskSrc.kind === "inline" && maskSrc.invert) && !inside(maskTarget.getBounds(), point)) return;
1861
- found.push({ hit, order: orderOf(d) });
1902
+ found.push({ hit: via ? { ...hit, point: { x, y }, via } : hit, order: orderBase ? [...orderBase, ...orderOf(d)] : orderOf(d) });
1862
1903
  });
1863
1904
  };
1864
1905
  walk3(root, "", true);
1906
+ for (const f of forwards) {
1907
+ const path = hiddenSources.get(f.source);
1908
+ if (path === void 0) continue;
1909
+ const g = f.source.target.toGlobal({ x: f.uv.u * f.source.intrinsicWidth, y: (1 - f.uv.v) * f.source.intrinsicHeight });
1910
+ walk3(f.source, path + "/", true, g, f.order, f.via);
1911
+ }
1865
1912
  return found.sort((a, b) => above(a.order, b.order)).map((f) => f.hit);
1866
1913
  }
1867
1914
  var cache = /* @__PURE__ */ new WeakMap();
@@ -2617,6 +2664,7 @@ var Movie = class {
2617
2664
  /** What went into the mix, and its peak before limiting — kept for inspectAudio(). */
2618
2665
  _audioSources = [];
2619
2666
  _mixStats = null;
2667
+ _loudness = null;
2620
2668
  on(event, fn) {
2621
2669
  (this._events[event] ??= []).push(fn);
2622
2670
  if (event === "ready" && this._initState === "ready") {
@@ -2691,7 +2739,11 @@ var Movie = class {
2691
2739
  return this._audioContext;
2692
2740
  }
2693
2741
  async init(options = {}) {
2694
- warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition", "vars", "poster", "motionBlur", "loader", "loop"]);
2742
+ warnUnknownOptions("movie.init()", options, ["width", "height", "duration", "frameRate", "background", "canvas", "assets", "composition", "vars", "poster", "motionBlur", "loader", "loop", "loudness"]);
2743
+ if (options.loudness !== void 0 && options.loudness !== false && !(typeof options.loudness === "number" && Number.isFinite(options.loudness) && options.loudness <= 0 && options.loudness >= -70)) {
2744
+ throw new Error(`pixi-effects: movie.init({ loudness }) is the loudness of the whole mix in LUFS: a number from -70 to 0 (-16 is usual for web video: loudness: -16), or false for no change; got ${JSON.stringify(options.loudness)}`);
2745
+ }
2746
+ this._loudness = options.loudness === void 0 || options.loudness === false ? null : options.loudness;
2695
2747
  this._initState = "pending";
2696
2748
  try {
2697
2749
  this.width = options.width ?? 1920;
@@ -2799,6 +2851,7 @@ var Movie = class {
2799
2851
  frameRate: this.frameRate,
2800
2852
  vars: numericVars(vars),
2801
2853
  ...gpuField,
2854
+ ...this.app?.renderer ? { pixiRenderer: this.app.renderer } : {},
2802
2855
  ...reuse ? { reuse } : {},
2803
2856
  ...lp ? { onLayerBuilt: async () => {
2804
2857
  lp.tick();
@@ -2842,7 +2895,8 @@ var Movie = class {
2842
2895
  this._mixStats = null;
2843
2896
  this._mixCache.set(null, null);
2844
2897
  } else {
2845
- const key = mixKey(audios, this.duration, audioContext.sampleRate);
2898
+ const baseKey = mixKey(audios, this.duration, audioContext.sampleRate);
2899
+ const key = baseKey === null ? null : `${baseKey}|loudness:${this._loudness}`;
2846
2900
  const hit = this._mixCache.get(key);
2847
2901
  if (hit) {
2848
2902
  this.audioBuffer = hit.buffer;
@@ -2853,7 +2907,8 @@ var Movie = class {
2853
2907
  lp.part(done);
2854
2908
  await lp.yieldIfDue();
2855
2909
  } : void 0);
2856
- this._mixStats = this.audioBuffer ? limitMix(this.audioBuffer, audios) : null;
2910
+ const loudness = this.audioBuffer && this._loudness !== null ? normalizeMix(this.audioBuffer, this._loudness) : void 0;
2911
+ this._mixStats = this.audioBuffer ? { ...limitMix(this.audioBuffer, audios), ...loudness ? { loudness } : {} } : null;
2857
2912
  this._mixCache.set(key, this.audioBuffer, this._mixStats);
2858
2913
  status = "mixed";
2859
2914
  }
@@ -4793,5 +4848,5 @@ function withFade(spec, opts) {
4793
4848
  }
4794
4849
 
4795
4850
  export { Movie, animateText, audioEnvelope, bpmEnvelope, cameraPath, computeEnvelope, dismissLoader, failLoader, followPath, kenBurns, measureText, musicBuffer, musicEnvelope, musicEvents, orbit, parseEase2 as parseEase, particles, react, resolveLoader, samplePath, splitText, stagger, wiggle, withFade };
4796
- //# sourceMappingURL=chunk-4MBTYREC.js.map
4797
- //# sourceMappingURL=chunk-4MBTYREC.js.map
4851
+ //# sourceMappingURL=chunk-2E74SP63.js.map
4852
+ //# sourceMappingURL=chunk-2E74SP63.js.map