pixi-effects 0.35.0 → 0.36.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 (75) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/check.mjs +26 -7
  5. package/ai/tools/lookup.mjs +14 -2
  6. package/dist/{Base-CXsMdmJB.d.ts → Base-CdF7AJop.d.ts} +1 -1
  7. package/dist/{Base-pNq6sVGl.d.cts → Base-CmiFiwQM.d.cts} +1 -1
  8. package/dist/Composition-BMRCE5FH.js +7 -0
  9. package/dist/{Composition-NDXKQHB4.js.map → Composition-BMRCE5FH.js.map} +1 -1
  10. package/dist/Composition-XRLYR6GL.cjs +16 -0
  11. package/dist/{Composition-OLH7RC5Z.cjs.map → Composition-XRLYR6GL.cjs.map} +1 -1
  12. package/dist/Controller.d.cts +2 -2
  13. package/dist/Controller.d.ts +2 -2
  14. package/dist/{Movie-VYtUN4TS.d.ts → Movie-Bh1HPdKw.d.cts} +6 -1
  15. package/dist/{Movie-Db4QnMIH.d.cts → Movie-D5WFfZdZ.d.ts} +6 -1
  16. package/dist/{Spans-QRMN2EZO.cjs → Spans-4IRBSOLI.cjs} +11 -11
  17. package/dist/{Spans-QRMN2EZO.cjs.map → Spans-4IRBSOLI.cjs.map} +1 -1
  18. package/dist/{Spans-QHHWC2PB.js → Spans-MWP2FX3T.js} +6 -6
  19. package/dist/{Spans-QHHWC2PB.js.map → Spans-MWP2FX3T.js.map} +1 -1
  20. package/dist/{blendModes-WIUCYIMC.cjs → blendModes-KU7XLV7U.cjs} +5 -5
  21. package/dist/{blendModes-WIUCYIMC.cjs.map → blendModes-KU7XLV7U.cjs.map} +1 -1
  22. package/dist/{blendModes-PP5NPBXY.js → blendModes-OOI3T6BE.js} +4 -4
  23. package/dist/{blendModes-PP5NPBXY.js.map → blendModes-OOI3T6BE.js.map} +1 -1
  24. package/dist/{chunk-ITDYFBB6.js → chunk-2JFFKHXO.js} +5 -5
  25. package/dist/chunk-2JFFKHXO.js.map +1 -0
  26. package/dist/{chunk-GL2AQFLQ.cjs → chunk-42ML5AKN.cjs} +5 -5
  27. package/dist/chunk-42ML5AKN.cjs.map +1 -0
  28. package/dist/{chunk-XV4L72FW.js → chunk-AZ6AINSW.js} +5 -5
  29. package/dist/{chunk-XV4L72FW.js.map → chunk-AZ6AINSW.js.map} +1 -1
  30. package/dist/{chunk-7TTCPHAV.cjs → chunk-CC7SUR7Q.cjs} +153 -48
  31. package/dist/chunk-CC7SUR7Q.cjs.map +1 -0
  32. package/dist/{chunk-Y253JQOH.js → chunk-G3DX65HO.js} +110 -10
  33. package/dist/chunk-G3DX65HO.js.map +1 -0
  34. package/dist/{chunk-LEECUCTG.cjs → chunk-KJSEQVBV.cjs} +129 -29
  35. package/dist/chunk-KJSEQVBV.cjs.map +1 -0
  36. package/dist/{chunk-IMQJVRMT.cjs → chunk-NEUPEENI.cjs} +5 -5
  37. package/dist/{chunk-IMQJVRMT.cjs.map → chunk-NEUPEENI.cjs.map} +1 -1
  38. package/dist/{chunk-2GNK6DPV.js → chunk-VF56PRAA.js} +121 -16
  39. package/dist/chunk-VF56PRAA.js.map +1 -0
  40. package/dist/{chunk-RTJD3ZSW.js → chunk-WIIU42FH.js} +4 -4
  41. package/dist/{chunk-RTJD3ZSW.js.map → chunk-WIIU42FH.js.map} +1 -1
  42. package/dist/{chunk-5IJENZOG.cjs → chunk-Y5UWFH6Y.cjs} +91 -91
  43. package/dist/{chunk-5IJENZOG.cjs.map → chunk-Y5UWFH6Y.cjs.map} +1 -1
  44. package/dist/index.cjs +33 -33
  45. package/dist/index.d.cts +20 -6
  46. package/dist/index.d.ts +20 -6
  47. package/dist/index.js +5 -5
  48. package/dist/loader.css +1 -1
  49. package/dist/present.cjs +5 -5
  50. package/dist/present.d.cts +2 -2
  51. package/dist/present.d.ts +2 -2
  52. package/dist/present.js +5 -5
  53. package/dist/three.cjs +6 -6
  54. package/dist/three.d.cts +2 -2
  55. package/dist/three.d.ts +2 -2
  56. package/dist/three.js +2 -2
  57. package/dist/{types-CavnUvd5.d.ts → types-B6zDTU2V.d.cts} +14 -1
  58. package/dist/{types-CavnUvd5.d.cts → types-B6zDTU2V.d.ts} +14 -1
  59. package/docs/api.md +5 -4
  60. package/docs/dsl.md +7 -5
  61. package/llms-full.txt +85 -18
  62. package/package.json +1 -1
  63. package/skills/pixi-effects/SKILL.md +3 -3
  64. package/skills/pixi-effects/reference/cheatsheet.md +7 -6
  65. package/skills/pixi-effects/reference/pitfalls.md +3 -0
  66. package/skills/pixi-effects/reference/recipes.md +60 -0
  67. package/skills/pixi-effects/template.html +6 -6
  68. package/dist/Composition-NDXKQHB4.js +0 -7
  69. package/dist/Composition-OLH7RC5Z.cjs +0 -16
  70. package/dist/chunk-2GNK6DPV.js.map +0 -1
  71. package/dist/chunk-7TTCPHAV.cjs.map +0 -1
  72. package/dist/chunk-GL2AQFLQ.cjs.map +0 -1
  73. package/dist/chunk-ITDYFBB6.js.map +0 -1
  74. package/dist/chunk-LEECUCTG.cjs.map +0 -1
  75. package/dist/chunk-Y253JQOH.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { describeLayer, collectPropKeys } from './chunk-ITDYFBB6.js';
1
+ import { describeLayer, collectPropKeys } from './chunk-2JFFKHXO.js';
2
2
  import { suggestName, warnUnknownOptions } from './chunk-BXOCEZ4U.js';
3
3
  import * as PIXI from 'pixi.js';
4
4
  import { Rectangle, Container, FillGradient, Text, Color, GraphicsPath, Sprite, Texture, Graphics, Assets, Filter, GlProgram, defaultFilterVert, GpuProgram, UniformGroup, TextStyle, CanvasTextMetrics } from 'pixi.js';
@@ -2326,6 +2326,11 @@ function createFilter(spec) {
2326
2326
  return named;
2327
2327
  }
2328
2328
  const { type, name, ...given } = spec;
2329
+ const unset = Object.keys(given).filter((k) => given[k] === void 0);
2330
+ if (unset.length > 0) {
2331
+ console.warn(`pixi-effects: the "${type}" filter has ${unset.map((k) => `"${k}"`).join(", ")} set to undefined (a misspelt variable or a missing field?): the filter's default is used instead.`);
2332
+ for (const k of unset) delete given[k];
2333
+ }
2329
2334
  const params = copyPlain(given);
2330
2335
  const Cls = registry[type];
2331
2336
  const inst = Cls ? new Cls(params) : createNamedFilter(type, params);
@@ -2841,7 +2846,37 @@ var TEXT_STYLE_DEFAULTS = {
2841
2846
  };
2842
2847
 
2843
2848
  // src/sequences/Text.ts
2844
- function formatValue(v, f) {
2849
+ var TIME_PATTERNS = ["hh:mm:ss", "h:mm:ss", "hh:mm", "mm:ss", "m:ss"];
2850
+ function formatTime(v, pattern, warned2) {
2851
+ if (!TIME_PATTERNS.includes(pattern)) {
2852
+ if (!warned2.has(pattern)) {
2853
+ warned2.add(pattern);
2854
+ console.warn(`pixi-effects: format.time "${pattern}" is not one of ${TIME_PATTERNS.map((p) => `"${p}"`).join(", ")}: the value is printed as a number`);
2855
+ }
2856
+ return null;
2857
+ }
2858
+ const total = Math.floor(Math.abs(v));
2859
+ const h = Math.floor(total / 3600), m = Math.floor(total % 3600 / 60), sec = total % 60;
2860
+ const two = (n) => String(n).padStart(2, "0");
2861
+ const sign = v < 0 && total > 0 ? "-" : "";
2862
+ switch (pattern) {
2863
+ case "hh:mm:ss":
2864
+ return `${sign}${two(h)}:${two(m)}:${two(sec)}`;
2865
+ case "h:mm:ss":
2866
+ return `${sign}${h}:${two(m)}:${two(sec)}`;
2867
+ case "hh:mm":
2868
+ return `${sign}${two(h)}:${two(m)}`;
2869
+ case "mm:ss":
2870
+ return `${sign}${two(Math.floor(total / 60))}:${two(sec)}`;
2871
+ default:
2872
+ return `${sign}${Math.floor(total / 60)}:${two(sec)}`;
2873
+ }
2874
+ }
2875
+ function formatValue(v, f, warned2 = /* @__PURE__ */ new Set()) {
2876
+ if (f?.time !== void 0) {
2877
+ const t = formatTime(v, String(f.time), warned2);
2878
+ if (t !== null) return t;
2879
+ }
2845
2880
  const fixed = v.toFixed(Math.max(0, Math.floor(f?.decimals ?? 0)));
2846
2881
  const pad = Math.max(0, Math.floor(f?.pad ?? 0));
2847
2882
  if (!f?.grouping && !pad) return fixed;
@@ -2860,6 +2895,8 @@ var TextSequence = class extends Sequence {
2860
2895
  /** `visibleChars` is in play (initial or animated): otherwise the whole text shows. */
2861
2896
  _typed = false;
2862
2897
  _lastText = "";
2898
+ /** The `format.time` patterns already said to be wrong on this layer. */
2899
+ _timeWarned = /* @__PURE__ */ new Set();
2863
2900
  /** A gradient fill for the text (`fillGradient`, static or animated): rebuilt as a FillGradient on each change (the text is re-rasterised). */
2864
2901
  _grad = null;
2865
2902
  _fillGradient = null;
@@ -2886,7 +2923,7 @@ var TextSequence = class extends Sequence {
2886
2923
  }
2887
2924
  /** Rewrite the displayed string from the template, the current `value` and `visibleChars`. */
2888
2925
  _refreshText() {
2889
- let next = this._template.includes("{value}") ? this._template.split("{value}").join(formatValue(this._counters.value, this.spec.format)) : this._template;
2926
+ let next = this._template.includes("{value}") ? this._template.split("{value}").join(formatValue(this._counters.value, this.spec.format, this._timeWarned)) : this._template;
2890
2927
  if (this._typed) next = Array.from(next).slice(0, Math.max(0, Math.floor(this._counters.visibleChars))).join("");
2891
2928
  if (next === this._lastText) return;
2892
2929
  this._lastText = next;
@@ -2902,6 +2939,11 @@ var TextSequence = class extends Sequence {
2902
2939
  label: this.spec.name
2903
2940
  });
2904
2941
  text.cullable = true;
2942
+ if (this.spec.resolution !== void 0) {
2943
+ const r = this.spec.resolution;
2944
+ if (typeof r === "number" && r >= 1 && r <= 8) text.resolution = r;
2945
+ else console.warn(`pixi-effects: ${describeLayer(this.spec)}: resolution must be a number from 1 to 8 (the text is drawn at that many times the pixels), got ${JSON.stringify(r)}: ignored`);
2946
+ }
2905
2947
  this.target = text;
2906
2948
  this.intrinsicWidth = text.width;
2907
2949
  this.intrinsicHeight = text.height;
@@ -2926,6 +2968,8 @@ var TextSequence = class extends Sequence {
2926
2968
  this.intrinsicWidth = text.width;
2927
2969
  this.intrinsicHeight = text.height;
2928
2970
  }
2971
+ const startFill = initialProps.fill;
2972
+ if (typeof startFill === "string" || typeof startFill === "number") text.style.fill = startFill;
2929
2973
  validateGradientKeyframes(this.spec, describeLayer(this.spec), void 0, "text");
2930
2974
  const gradientSpec = this.spec.fillGradient ?? initialProps.fillGradient;
2931
2975
  if (gradientSpec) {
@@ -3730,7 +3774,7 @@ function resolveSpansStyle(style, scope, who, warn = defaultWarn) {
3730
3774
  }
3731
3775
  return out;
3732
3776
  }
3733
- var TEXT_ONLY = ["type", "spans", "text", "style", "format", "colorSpace", "fillGradient", "initial", "keyframes"];
3777
+ var TEXT_ONLY = ["type", "spans", "text", "style", "format", "colorSpace", "fillGradient", "initial", "keyframes", "resolution"];
3734
3778
  var NOT_FOR_A_GROUP = ["fill", "text", "value", "fillGradient"];
3735
3779
  function expandSpans(spec, warn = defaultWarn, scope) {
3736
3780
  const who = describeLayer(spec);
@@ -3775,7 +3819,9 @@ function expandSpans(spec, warn = defaultWarn, scope) {
3775
3819
  name: `${baseName}.${i}`,
3776
3820
  text: p.text,
3777
3821
  style: { ...style, ...p.style },
3778
- initial: { x: p.x, y: p.y, ...typed ? { visibleChars: p.length } : {} }
3822
+ initial: { x: p.x, y: p.y, ...typed ? { visibleChars: p.length } : {} },
3823
+ ...s.resolution !== void 0 ? { resolution: s.resolution } : {}
3824
+ // the pieces are what is drawn
3779
3825
  }));
3780
3826
  const rest = {};
3781
3827
  for (const [k, v] of Object.entries(s)) if (!TEXT_ONLY.includes(k)) rest[k] = v;
@@ -3783,9 +3829,15 @@ function expandSpans(spec, warn = defaultWarn, scope) {
3783
3829
  spansMeta.set(group, { typed, pieces: drawn.map((p) => ({ start: p.start, length: p.length })), preview: layout.pieces.map((p, i) => (i > 0 && p.line !== layout.pieces[i - 1].line ? " " : "") + p.text).join("") });
3784
3830
  return group;
3785
3831
  }
3832
+ var ORIGINAL_SCALE_MODE = /* @__PURE__ */ new WeakMap();
3786
3833
  var ImageSequence = class extends Sequence {
3787
3834
  async build() {
3788
3835
  const texture = await Assets.get(this.spec.asset);
3836
+ const source = texture.source;
3837
+ if (source && !ORIGINAL_SCALE_MODE.has(source)) ORIGINAL_SCALE_MODE.set(source, source.scaleMode);
3838
+ const mode = this.spec.scaleMode;
3839
+ if (mode !== void 0 && mode !== "nearest" && mode !== "linear") console.warn(`pixi-effects: ${describeLayer(this.spec)}: scaleMode "${String(mode)}" is not "nearest" or "linear": ignored`);
3840
+ if (source) source.scaleMode = mode === "nearest" || mode === "linear" ? mode : ORIGINAL_SCALE_MODE.get(source);
3789
3841
  const sprite = new Sprite({ texture, label: this.spec.name });
3790
3842
  sprite.cullable = true;
3791
3843
  this.target = sprite;
@@ -4642,6 +4694,21 @@ function slice(line, a, b) {
4642
4694
  }
4643
4695
  return out;
4644
4696
  }
4697
+ function splitEvenOdd(d) {
4698
+ const parts = d.split(/(?=[Mm])/).map((x) => x.trim()).filter(Boolean);
4699
+ if (parts.length < 2) return { main: d.trim(), holes: [] };
4700
+ const area = (sub2) => flattenSvgPath(sub2).reduce((sum, line) => {
4701
+ let a = 0;
4702
+ for (let i = 0; i < line.pts.length; i += 2) {
4703
+ const j = (i + 2) % line.pts.length;
4704
+ a += line.pts[i] * line.pts[j + 1] - line.pts[j] * line.pts[i + 1];
4705
+ }
4706
+ return sum + Math.abs(a) / 2;
4707
+ }, 0);
4708
+ const areas = parts.map(area);
4709
+ const big = areas.indexOf(Math.max(...areas));
4710
+ return { main: parts[big], holes: parts.filter((_, i) => i !== big) };
4711
+ }
4645
4712
 
4646
4713
  // src/sequences/morphPath.ts
4647
4714
  function resample(line, n) {
@@ -4704,6 +4771,11 @@ function buildMorph(from, to, n = 128) {
4704
4771
  for (let i = 0; i < count; i++) {
4705
4772
  const fa = from[i], fb = to[i];
4706
4773
  const closed = (fa?.closed ?? fb.closed) && (fb?.closed ?? fa.closed);
4774
+ if (fa && fb && fa.closed === fb.closed && fa.pts.length === fb.pts.length && fa.pts.length >= 6) {
4775
+ const a2 = [...fa.pts];
4776
+ pairs.push({ a: a2, b: closed ? align(a2, [...fb.pts]) : [...fb.pts], closed });
4777
+ continue;
4778
+ }
4707
4779
  const a = fa ? resample({ pts: fa.pts, closed }, n) : collapsed(resample({ pts: fb.pts, closed }, n));
4708
4780
  let b = fb ? resample({ pts: fb.pts, closed }, n) : collapsed(a);
4709
4781
  if (closed && fa && fb) b = align(a, b);
@@ -4787,6 +4859,8 @@ var ShapeSequence = class extends Sequence {
4787
4859
  const morph = this._setupMorph();
4788
4860
  this._drawGeometry = makeDrawGeometry(this.spec, scope, this._state, morph);
4789
4861
  this._outline = makeOutline(this.spec, scope, this._state, morph);
4862
+ const eo = this.spec.shape === "path" && this.spec.fillRule === "evenodd" && !morph ? splitEvenOdd(this.spec.d) : null;
4863
+ this._evenOdd = eo && eo.holes.length > 0 ? eo : null;
4790
4864
  const { trimStart, trimEnd } = this._state;
4791
4865
  this._state.trimStart = 0;
4792
4866
  this._state.trimEnd = 1;
@@ -4852,6 +4926,27 @@ var ShapeSequence = class extends Sequence {
4852
4926
  _painter = null;
4853
4927
  _gradDirty = false;
4854
4928
  _outline = null;
4929
+ /** fillRule 'evenodd' on a path: the shape and the sub-paths cut out of it after it is filled (null: not used). */
4930
+ _evenOdd = null;
4931
+ /**
4932
+ * fillRule 'evenodd': the shape is filled, each hole is cut out of that fill, and the stroke goes round the shape and every hole. (PIXI's `cut()` only reaches the
4933
+ * LAST instruction, so it has to come between the fill and the stroke.)
4934
+ */
4935
+ _fillWithHoles(g) {
4936
+ const s = this._state, { main, holes } = this._evenOdd;
4937
+ applyFill(g, s);
4938
+ for (const hole of holes) {
4939
+ g.beginPath();
4940
+ g.path(new GraphicsPath(hole));
4941
+ g.cut();
4942
+ }
4943
+ if (s.strokeColor !== void 0 && s.strokeWidth > 0) {
4944
+ g.beginPath();
4945
+ g.path(new GraphicsPath(main));
4946
+ for (const hole of holes) g.path(new GraphicsPath(hole));
4947
+ applyStroke(g, s);
4948
+ }
4949
+ }
4855
4950
  _redraw() {
4856
4951
  const graphics = this.target;
4857
4952
  const s = this._state;
@@ -4863,6 +4958,10 @@ var ShapeSequence = class extends Sequence {
4863
4958
  const trimmed = (s.trimStart ?? 0) > 0 || (s.trimEnd ?? 1) < 1;
4864
4959
  if (!trimmed || !this._outline || !(s.strokeColor !== void 0 && s.strokeWidth > 0)) {
4865
4960
  this._drawGeometry(graphics);
4961
+ if (this._evenOdd) {
4962
+ this._fillWithHoles(graphics);
4963
+ return;
4964
+ }
4866
4965
  applyState(graphics, s);
4867
4966
  return;
4868
4967
  }
@@ -5086,8 +5185,9 @@ function makePolygonDraw(spec, scope) {
5086
5185
  };
5087
5186
  }
5088
5187
  function makePathDraw(spec) {
5188
+ const d = spec.fillRule === "evenodd" ? splitEvenOdd(spec.d).main : spec.d;
5089
5189
  return (g) => {
5090
- g.path(new GraphicsPath(spec.d));
5190
+ g.path(new GraphicsPath(d));
5091
5191
  };
5092
5192
  }
5093
5193
  var STATE_DRAWERS = {
@@ -5623,13 +5723,13 @@ function resolveDim(v, scope) {
5623
5723
  var _CompositionSequence = null;
5624
5724
  async function getCompositionSequence() {
5625
5725
  if (!_CompositionSequence) {
5626
- _CompositionSequence = (await import('./Composition-NDXKQHB4.js')).CompositionSequence;
5726
+ _CompositionSequence = (await import('./Composition-BMRCE5FH.js')).CompositionSequence;
5627
5727
  }
5628
5728
  return _CompositionSequence;
5629
5729
  }
5630
5730
  var _SpansSequence = null;
5631
5731
  async function getSpansSequence() {
5632
- if (!_SpansSequence) _SpansSequence = (await import('./Spans-QHHWC2PB.js')).SpansSequence;
5732
+ if (!_SpansSequence) _SpansSequence = (await import('./Spans-MWP2FX3T.js')).SpansSequence;
5633
5733
  return _SpansSequence;
5634
5734
  }
5635
5735
  var staticTypes = {
@@ -5675,5 +5775,5 @@ async function buildSequenceTree(specs, parent, root) {
5675
5775
  }
5676
5776
 
5677
5777
  export { CameraSequence, DEFAULT_FOV, DEG3 as DEG, EXPRESSION_NAMES, FALLOFFS, LIGHT_KINDS, LIGHT_PROPS, LightSequence, MANY_BLURRED, MAX_BLUR, MAX_CASTERS, MAX_LIGHTS, MIN_BLUR, NEAR, NullSequence, SHADOW_TAPS, Sequence, TextSequence, bindClock, bindCounterKeyframes, blurRadius, buildScope, buildSequenceTree, cameraBasis, checkEase, clockTable, contentLength, createTimeline, ensureFilterLibrary, evaluateExpr, flattenSvgPath, focusProblems, homeCamera, homeDistance, layerInitialZ, layerToWorld, lengthen, lightLevel, noise, packLights, parseEase, parseSpring, projectLayer, projectPoint, rand, random, registerSequenceType, remapOf, resolveAt, resolveNumber, spansMeta, springDuration, timeRemapOf };
5678
- //# sourceMappingURL=chunk-Y253JQOH.js.map
5679
- //# sourceMappingURL=chunk-Y253JQOH.js.map
5778
+ //# sourceMappingURL=chunk-G3DX65HO.js.map
5779
+ //# sourceMappingURL=chunk-G3DX65HO.js.map