pixi-effects 0.43.0 → 0.44.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 (40) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/check.mjs +131 -2
  5. package/dist/Controller.d.cts +2 -2
  6. package/dist/Controller.d.ts +2 -2
  7. package/dist/{Movie-BdUm2Mc9.d.cts → Movie-B3VOAgwc.d.cts} +3 -3
  8. package/dist/{Movie-B_qQIYV6.d.ts → Movie-CQCVUqRs.d.ts} +3 -3
  9. package/dist/{chunk-6N5ZBUOU.js → chunk-4MBTYREC.js} +81 -31
  10. package/dist/chunk-4MBTYREC.js.map +1 -0
  11. package/dist/{chunk-BEGOKE7Q.cjs → chunk-MAAM45ZX.cjs} +80 -30
  12. package/dist/chunk-MAAM45ZX.cjs.map +1 -0
  13. package/dist/index.cjs +25 -25
  14. package/dist/index.d.cts +3 -4
  15. package/dist/index.d.ts +3 -4
  16. package/dist/index.js +1 -1
  17. package/dist/loader.css +1 -1
  18. package/dist/present.cjs +1 -1
  19. package/dist/present.d.cts +2 -2
  20. package/dist/present.d.ts +2 -2
  21. package/dist/present.js +1 -1
  22. package/dist/three.cjs +86 -16
  23. package/dist/three.cjs.map +1 -1
  24. package/dist/three.d.cts +22 -2
  25. package/dist/three.d.ts +22 -2
  26. package/dist/three.js +86 -16
  27. package/dist/three.js.map +1 -1
  28. package/dist/{types-DiNHtdrS.d.cts → types-C5xo0fzb.d.cts} +74 -2
  29. package/dist/{types-DiNHtdrS.d.ts → types-C5xo0fzb.d.ts} +74 -2
  30. package/docs/api.md +4 -2
  31. package/docs/dsl.md +1 -0
  32. package/llms-full.txt +9 -6
  33. package/package.json +1 -1
  34. package/skills/pixi-effects/SKILL.md +2 -2
  35. package/skills/pixi-effects/reference/recipes.md +2 -2
  36. package/skills/pixi-effects/template.html +6 -6
  37. package/dist/Base-DKFQtqzW.d.ts +0 -60
  38. package/dist/Base-lb5Uhaqu.d.cts +0 -60
  39. package/dist/chunk-6N5ZBUOU.js.map +0 -1
  40. package/dist/chunk-BEGOKE7Q.cjs.map +0 -1
@@ -190,7 +190,7 @@ function inspectFonts(root) {
190
190
  });
191
191
  const missing = [];
192
192
  const used = /* @__PURE__ */ new Set();
193
- const walk2 = (seq, index) => {
193
+ const walk3 = (seq, index) => {
194
194
  const spec = seq.spec;
195
195
  if (spec.type === "text") {
196
196
  const raw = spec.style?.fontFamily;
@@ -199,10 +199,10 @@ function inspectFonts(root) {
199
199
  for (const f of list) used.add(f.replace(/^["']|["']$/g, "").toLowerCase());
200
200
  if (list.length && !list.some(fontAvailable) && !list.some((f) => failed.has(f.replace(/^["']|["']$/g, "")))) missing.push({ layer: spec.name ?? `text#${index}`, family: family2 });
201
201
  }
202
- if (seq.maskSequence) walk2(seq.maskSequence, index);
203
- (seq._children ?? []).forEach((child, i) => walk2(child, i));
202
+ if (seq.maskSequence) walk3(seq.maskSequence, index);
203
+ (seq._children ?? []).forEach((child, i) => walk3(child, i));
204
204
  };
205
- walk2(root, 0);
205
+ walk3(root, 0);
206
206
  const all = [...failed];
207
207
  return { missing, failed: all.filter((f) => used.has(f.toLowerCase())), failedUnused: all.filter((f) => !used.has(f.toLowerCase())) };
208
208
  }
@@ -1528,16 +1528,16 @@ function canvasReport(root) {
1528
1528
  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") : "";
1529
1529
  out.push({ name: seq.spec.name ?? name, moments, drawMs, ok: !differs.length && !threw.length, problem, kind });
1530
1530
  }
1531
- if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk2(seq);
1531
+ if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk3(seq);
1532
1532
  };
1533
- const walk2 = (comp) => {
1533
+ const walk3 = (comp) => {
1534
1534
  for (const { seq } of comp.layers()) {
1535
1535
  visit(seq);
1536
1536
  const mask = seq.maskSequence;
1537
1537
  if (mask) visit(mask);
1538
1538
  }
1539
1539
  };
1540
- walk2(root);
1540
+ walk3(root);
1541
1541
  return out;
1542
1542
  }
1543
1543
  var canvasProblems = (root) => canvasReport(root).filter((r) => !r.ok).map((r) => r.problem);
@@ -1604,7 +1604,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1604
1604
  const specs = /* @__PURE__ */ new Map();
1605
1605
  const chains = /* @__PURE__ */ new Map();
1606
1606
  const chainAlpha = /* @__PURE__ */ new Map();
1607
- const walk2 = (comp, prefix, insideThreeD, parentVisible, parentScene, parentMotion = NO_MOTION, parentZero = false, parentAlpha = 1) => {
1607
+ const walk3 = (comp, prefix, insideThreeD, parentVisible, parentScene, parentMotion = NO_MOTION, parentZero = false, parentAlpha = 1) => {
1608
1608
  const blending = /* @__PURE__ */ new Set();
1609
1609
  const now = comp.childTime(time);
1610
1610
  const cameraMotion = (comp.spec.sequences ?? []).filter((q) => q.type === "camera").reduce((m, q) => either(m, motionOf(q.keyframes, now - comp.childBase - (q.at ?? 0), q.duration ?? comp.duration ?? Infinity, () => true)), NO_MOTION);
@@ -1670,10 +1670,10 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1670
1670
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1671
1671
  if (group) scene.set(info, group);
1672
1672
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1673
- if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1673
+ if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
1674
1674
  });
1675
1675
  };
1676
- walk2(root, "", false, true);
1676
+ walk3(root, "", false, true);
1677
1677
  const issues = [];
1678
1678
  const texts = layers.filter((l) => l.type === "text" && l.visible && l.alpha >= FAINT && l.bounds);
1679
1679
  for (const l of texts) {
@@ -1800,7 +1800,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1800
1800
  return true;
1801
1801
  };
1802
1802
  const inside = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
1803
- const walk2 = (comp, prefix, parentVisible) => {
1803
+ const walk3 = (comp, prefix, parentVisible) => {
1804
1804
  comp.layers().forEach(({ seq, display, threeD, carriers, quad }, i) => {
1805
1805
  const d = display;
1806
1806
  if (!d) return;
@@ -1813,7 +1813,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1813
1813
  if (!visible) return;
1814
1814
  const isComposition = seq instanceof chunk3HZV3UHU_cjs.CompositionSequence;
1815
1815
  if (isComposition && !threeD) {
1816
- walk2(seq, path + "/", visible);
1816
+ walk3(seq, path + "/", visible);
1817
1817
  return;
1818
1818
  }
1819
1819
  if (!wanted(name, path)) return;
@@ -1841,7 +1841,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1841
1841
  hit = { name, path, type: seq.spec.type, kind: "three", point, three };
1842
1842
  } else if (three === void 0 && (typeof d.containsPoint !== "function" || d.containsPoint(local))) {
1843
1843
  let opaque = true;
1844
- const tex = opts.alpha !== void 0 && seq.spec.type !== "shader" ? d.texture : void 0;
1844
+ const tex = opts.alpha !== void 0 && seq.spec.type !== "shader" ? d.texture ?? deps.textureOf?.(d) : void 0;
1845
1845
  if (tex?.source?.resource) {
1846
1846
  const res = tex.source.resolution ?? 1;
1847
1847
  const a = readAlpha(tex.source.resource, (tex.frame.x + (local.x - lb.x) / lb.width * tex.frame.width) * res, (tex.frame.y + (local.y - lb.y) / lb.height * tex.frame.height) * res);
@@ -1863,9 +1863,56 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
1863
1863
  found.push({ hit, order: orderOf(d) });
1864
1864
  });
1865
1865
  };
1866
- walk2(root, "", true);
1866
+ walk3(root, "", true);
1867
1867
  return found.sort((a, b) => above(a.order, b.order)).map((f) => f.hit);
1868
1868
  }
1869
+ var cache = /* @__PURE__ */ new WeakMap();
1870
+ function textPictureOf(display) {
1871
+ if (!(display instanceof pixi_js.Text)) return null;
1872
+ const style = display.style;
1873
+ const key = `${display.text}|${display.resolution}|${style.styleKey ?? ""}`;
1874
+ const kept = cache.get(display);
1875
+ if (kept?.key === key) return kept.picture;
1876
+ try {
1877
+ const { canvasAndContext, frame } = pixi_js.CanvasTextGenerator.getCanvasAndContext({ text: display.text, style: display.style, resolution: display.resolution });
1878
+ const copy = document.createElement("canvas");
1879
+ copy.width = canvasAndContext.canvas.width;
1880
+ copy.height = canvasAndContext.canvas.height;
1881
+ copy.getContext("2d").drawImage(canvasAndContext.canvas, 0, 0);
1882
+ pixi_js.CanvasPool.returnCanvasAndContext(canvasAndContext);
1883
+ const picture = { frame: { x: frame.x, y: frame.y, width: frame.width, height: frame.height }, source: { resolution: display.resolution, resource: copy } };
1884
+ cache.set(display, { key, picture });
1885
+ return picture;
1886
+ } catch {
1887
+ return null;
1888
+ }
1889
+ }
1890
+
1891
+ // src/core/reuse.ts
1892
+ var lendable = (s) => typeof s.reuseKey === "function";
1893
+ var borrower = (s) => typeof s.commitReuse === "function";
1894
+ function walk(root, visit) {
1895
+ for (const { seq } of root.layers()) {
1896
+ visit(seq);
1897
+ const mask = seq.maskSequence;
1898
+ if (mask) visit(mask);
1899
+ if (seq instanceof chunk3HZV3UHU_cjs.CompositionSequence) walk(seq, visit);
1900
+ }
1901
+ }
1902
+ function reusePoolOf(old) {
1903
+ const free = /* @__PURE__ */ new Map();
1904
+ if (old) walk(old, (seq) => {
1905
+ if (!lendable(seq)) return;
1906
+ const key = seq.reuseKey();
1907
+ if (key !== null) free.set(key, [...free.get(key) ?? [], seq]);
1908
+ });
1909
+ return { claim: (key) => free.get(key)?.shift() ?? null };
1910
+ }
1911
+ function commitReuse(root) {
1912
+ walk(root, (seq) => {
1913
+ if (borrower(seq)) seq.commitReuse();
1914
+ });
1915
+ }
1869
1916
 
1870
1917
  // src/core/hover.ts
1871
1918
  var keyOf = (h) => h ? `${h.path}${h.three ? `#${h.three.object}` : ""}` : null;
@@ -1959,7 +2006,7 @@ function detailOf(seq) {
1959
2006
  if (s.music && typeof s.music === "object") return "music";
1960
2007
  return void 0;
1961
2008
  }
1962
- function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions, local) {
2009
+ function walk2(comp, parentStart, parentEnd, depth, prefix, rows, transitions, local) {
1963
2010
  const entries = [];
1964
2011
  comp._children.forEach((seq, i) => {
1965
2012
  const name = seq.spec.name ?? `${seq.spec.type}#${i}`;
@@ -1972,7 +2019,7 @@ function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions, lo
1972
2019
  const isComp = seq instanceof chunk3HZV3UHU_cjs.CompositionSequence;
1973
2020
  if (isComp) {
1974
2021
  const label = seq.remapLabel();
1975
- walk(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
2022
+ walk2(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
1976
2023
  }
1977
2024
  entries.push({ row, inner, groupable: !isComp });
1978
2025
  });
@@ -2020,7 +2067,7 @@ function collectTimeline(root, duration) {
2020
2067
  const rows = [];
2021
2068
  const transitions = [];
2022
2069
  const label = root.remapLabel();
2023
- walk(root, 0, label ? root.contentSpan : duration, 0, "", rows, transitions, label ? `movie ${label}` : void 0);
2070
+ walk2(root, 0, label ? root.contentSpan : duration, 0, "", rows, transitions, label ? `movie ${label}` : void 0);
2024
2071
  return { duration, rows, transitions };
2025
2072
  }
2026
2073
  var COLORS = {
@@ -2168,28 +2215,28 @@ function substitute(s, vars) {
2168
2215
  }
2169
2216
  function applyVars(spec, vars) {
2170
2217
  if (Object.keys(vars).length === 0) return spec;
2171
- const walk2 = (v) => {
2218
+ const walk3 = (v) => {
2172
2219
  if (typeof v === "string") return substitute(v, vars);
2173
- if (Array.isArray(v)) return v.map(walk2);
2220
+ if (Array.isArray(v)) return v.map(walk3);
2174
2221
  if (isPlain(v)) {
2175
2222
  const o = {};
2176
- for (const [k, x] of Object.entries(v)) o[k] = walk2(x);
2223
+ for (const [k, x] of Object.entries(v)) o[k] = walk3(x);
2177
2224
  return o;
2178
2225
  }
2179
2226
  return v;
2180
2227
  };
2181
- return walk2(spec);
2228
+ return walk3(spec);
2182
2229
  }
2183
2230
  var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2184
2231
  function lintVars(spec, vars) {
2185
2232
  const declared = Object.keys(vars);
2186
2233
  const strings = [];
2187
- const walk2 = (v) => {
2234
+ const walk3 = (v) => {
2188
2235
  if (typeof v === "string") strings.push(v);
2189
- else if (Array.isArray(v)) v.forEach(walk2);
2190
- else if (isPlain(v)) Object.values(v).forEach(walk2);
2236
+ else if (Array.isArray(v)) v.forEach(walk3);
2237
+ else if (isPlain(v)) Object.values(v).forEach(walk3);
2191
2238
  };
2192
- walk2(spec);
2239
+ walk3(spec);
2193
2240
  const out = [];
2194
2241
  for (const name of declared) {
2195
2242
  const word = new RegExp(`(?<![A-Za-z0-9_.])${escapeRe(name)}(?![A-Za-z0-9_])`);
@@ -2743,7 +2790,7 @@ var Movie = class {
2743
2790
  * Build the composition tree for these variables and this size, off stage: `{name}` replaced, the root shape (with the numeric variables for the
2744
2791
  * expressions), transitions expanded, every layer built. Used by `init` and by `rebuild`.
2745
2792
  */
2746
- async _construct(composition, vars, width, height, lp, sizeWins = this._resized) {
2793
+ async _construct(composition, vars, width, height, lp, sizeWins = this._resized, reuse) {
2747
2794
  const spec = applyVars(composition, vars);
2748
2795
  const gpu = sharedGpuOf(this.app?.renderer);
2749
2796
  const gpuField = gpu ? { gpu } : {};
@@ -2754,6 +2801,7 @@ var Movie = class {
2754
2801
  frameRate: this.frameRate,
2755
2802
  vars: numericVars(vars),
2756
2803
  ...gpuField,
2804
+ ...reuse ? { reuse } : {},
2757
2805
  ...lp ? { onLayerBuilt: async () => {
2758
2806
  lp.tick();
2759
2807
  await lp.yieldIfDue();
@@ -2910,7 +2958,7 @@ var Movie = class {
2910
2958
  if (change.composition !== void 0) for (const w of lintVars(change.composition, vars)) console.warn(w);
2911
2959
  changed = changed || sizeChanged || change.composition !== void 0 || reason === "rebuild";
2912
2960
  if (!changed) return { reason, ms: 0, audio: "none", frame: this.currentFrame, playing: this.isPlaying, unchanged: true };
2913
- sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged);
2961
+ sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged, reusePoolOf(this._rootSequence));
2914
2962
  alive();
2915
2963
  const timeline = chunkX62H3RBG_cjs.createTimeline({ paused: true, defaults: { ease: "none" } });
2916
2964
  chunkX62H3RBG_cjs.lengthen(timeline, this.duration);
@@ -2950,6 +2998,7 @@ var Movie = class {
2950
2998
  if (old?.target) root.removeChild(old.target);
2951
2999
  if (sequence.target) root.addChild(sequence.target);
2952
3000
  this._rootSequence = sequence;
3001
+ commitReuse(sequence);
2953
3002
  this.timeline = timeline;
2954
3003
  this._timeFilters = collectTimeFilters(sequence);
2955
3004
  const unmute = muteBoundTextureWarning();
@@ -3224,7 +3273,8 @@ var Movie = class {
3224
3273
  chunkTWZNOUM7_cjs.warnUnknownOptions("movie.pick()", opts, ["only", "ignore", "alpha"]);
3225
3274
  this._requireReady("pick");
3226
3275
  if (!Number.isFinite(x) || !Number.isFinite(y)) throw new Error(`pixi-effects: movie.pick(x, y) needs two numbers (canvas pixels), got ${x}, ${y}. For a pointer event: movie.pick(...movie.canvasPoint(event))`);
3227
- return pickScene(this._rootSequence, x, y, opts);
3276
+ const textureOf = textPictureOf;
3277
+ return pickScene(this._rootSequence, x, y, opts, { textureOf });
3228
3278
  }
3229
3279
  /**
3230
3280
  * Which layer the pointer is over, as callbacks: `onEnter(hit)` when the layer (or, in a `three` layer, the object) under the pointer changes to another, `onLeave(hit)` for the one it
@@ -4768,5 +4818,5 @@ exports.splitText = splitText;
4768
4818
  exports.stagger = stagger;
4769
4819
  exports.wiggle = wiggle;
4770
4820
  exports.withFade = withFade;
4771
- //# sourceMappingURL=chunk-BEGOKE7Q.cjs.map
4772
- //# sourceMappingURL=chunk-BEGOKE7Q.cjs.map
4821
+ //# sourceMappingURL=chunk-MAAM45ZX.cjs.map
4822
+ //# sourceMappingURL=chunk-MAAM45ZX.cjs.map