pixi-effects 0.34.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 (79) hide show
  1. package/CHANGELOG.md +27 -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.cjs +130 -1
  13. package/dist/Controller.cjs.map +1 -1
  14. package/dist/Controller.d.cts +24 -2
  15. package/dist/Controller.d.ts +24 -2
  16. package/dist/Controller.js +130 -1
  17. package/dist/Controller.js.map +1 -1
  18. package/dist/{Movie-f0tQG9y5.d.ts → Movie-Bh1HPdKw.d.cts} +30 -2
  19. package/dist/{Movie-CL6SsAWe.d.cts → Movie-D5WFfZdZ.d.ts} +30 -2
  20. package/dist/{Spans-QRMN2EZO.cjs → Spans-4IRBSOLI.cjs} +11 -11
  21. package/dist/{Spans-QRMN2EZO.cjs.map → Spans-4IRBSOLI.cjs.map} +1 -1
  22. package/dist/{Spans-QHHWC2PB.js → Spans-MWP2FX3T.js} +6 -6
  23. package/dist/{Spans-QHHWC2PB.js.map → Spans-MWP2FX3T.js.map} +1 -1
  24. package/dist/{blendModes-WIUCYIMC.cjs → blendModes-KU7XLV7U.cjs} +5 -5
  25. package/dist/{blendModes-WIUCYIMC.cjs.map → blendModes-KU7XLV7U.cjs.map} +1 -1
  26. package/dist/{blendModes-PP5NPBXY.js → blendModes-OOI3T6BE.js} +4 -4
  27. package/dist/{blendModes-PP5NPBXY.js.map → blendModes-OOI3T6BE.js.map} +1 -1
  28. package/dist/{chunk-ITDYFBB6.js → chunk-2JFFKHXO.js} +5 -5
  29. package/dist/chunk-2JFFKHXO.js.map +1 -0
  30. package/dist/{chunk-GL2AQFLQ.cjs → chunk-42ML5AKN.cjs} +5 -5
  31. package/dist/chunk-42ML5AKN.cjs.map +1 -0
  32. package/dist/{chunk-XV4L72FW.js → chunk-AZ6AINSW.js} +5 -5
  33. package/dist/{chunk-XV4L72FW.js.map → chunk-AZ6AINSW.js.map} +1 -1
  34. package/dist/{chunk-FVZEBYLW.cjs → chunk-CC7SUR7Q.cjs} +237 -54
  35. package/dist/chunk-CC7SUR7Q.cjs.map +1 -0
  36. package/dist/{chunk-Y253JQOH.js → chunk-G3DX65HO.js} +110 -10
  37. package/dist/chunk-G3DX65HO.js.map +1 -0
  38. package/dist/{chunk-LEECUCTG.cjs → chunk-KJSEQVBV.cjs} +129 -29
  39. package/dist/chunk-KJSEQVBV.cjs.map +1 -0
  40. package/dist/{chunk-IMQJVRMT.cjs → chunk-NEUPEENI.cjs} +5 -5
  41. package/dist/{chunk-IMQJVRMT.cjs.map → chunk-NEUPEENI.cjs.map} +1 -1
  42. package/dist/{chunk-67LG7MN5.js → chunk-VF56PRAA.js} +205 -22
  43. package/dist/chunk-VF56PRAA.js.map +1 -0
  44. package/dist/{chunk-RTJD3ZSW.js → chunk-WIIU42FH.js} +4 -4
  45. package/dist/{chunk-RTJD3ZSW.js.map → chunk-WIIU42FH.js.map} +1 -1
  46. package/dist/{chunk-5IJENZOG.cjs → chunk-Y5UWFH6Y.cjs} +91 -91
  47. package/dist/{chunk-5IJENZOG.cjs.map → chunk-Y5UWFH6Y.cjs.map} +1 -1
  48. package/dist/index.cjs +33 -33
  49. package/dist/index.d.cts +20 -6
  50. package/dist/index.d.ts +20 -6
  51. package/dist/index.js +5 -5
  52. package/dist/loader.css +1 -1
  53. package/dist/present.cjs +5 -5
  54. package/dist/present.d.cts +2 -2
  55. package/dist/present.d.ts +2 -2
  56. package/dist/present.js +5 -5
  57. package/dist/three.cjs +6 -6
  58. package/dist/three.d.cts +2 -2
  59. package/dist/three.d.ts +2 -2
  60. package/dist/three.js +2 -2
  61. package/dist/{types-CavnUvd5.d.ts → types-B6zDTU2V.d.cts} +14 -1
  62. package/dist/{types-CavnUvd5.d.cts → types-B6zDTU2V.d.ts} +14 -1
  63. package/docs/api.md +15 -4
  64. package/docs/dsl.md +7 -5
  65. package/llms-full.txt +95 -18
  66. package/package.json +1 -1
  67. package/skills/pixi-effects/SKILL.md +3 -3
  68. package/skills/pixi-effects/reference/cheatsheet.md +7 -6
  69. package/skills/pixi-effects/reference/pitfalls.md +3 -0
  70. package/skills/pixi-effects/reference/recipes.md +60 -0
  71. package/skills/pixi-effects/template.html +6 -6
  72. package/dist/Composition-NDXKQHB4.js +0 -7
  73. package/dist/Composition-OLH7RC5Z.cjs +0 -16
  74. package/dist/chunk-67LG7MN5.js.map +0 -1
  75. package/dist/chunk-FVZEBYLW.cjs.map +0 -1
  76. package/dist/chunk-GL2AQFLQ.cjs.map +0 -1
  77. package/dist/chunk-ITDYFBB6.js.map +0 -1
  78. package/dist/chunk-LEECUCTG.cjs.map +0 -1
  79. package/dist/chunk-Y253JQOH.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkGL2AQFLQ_cjs = require('./chunk-GL2AQFLQ.cjs');
3
+ var chunk42ML5AKN_cjs = require('./chunk-42ML5AKN.cjs');
4
4
  var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
5
5
  var PIXI = require('pixi.js');
6
6
 
@@ -2347,6 +2347,11 @@ function createFilter(spec) {
2347
2347
  return named;
2348
2348
  }
2349
2349
  const { type, name, ...given } = spec;
2350
+ const unset = Object.keys(given).filter((k) => given[k] === void 0);
2351
+ if (unset.length > 0) {
2352
+ 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.`);
2353
+ for (const k of unset) delete given[k];
2354
+ }
2350
2355
  const params = copyPlain(given);
2351
2356
  const Cls = registry[type];
2352
2357
  const inst = Cls ? new Cls(params) : createNamedFilter(type, params);
@@ -2862,7 +2867,37 @@ var TEXT_STYLE_DEFAULTS = {
2862
2867
  };
2863
2868
 
2864
2869
  // src/sequences/Text.ts
2865
- function formatValue(v, f) {
2870
+ var TIME_PATTERNS = ["hh:mm:ss", "h:mm:ss", "hh:mm", "mm:ss", "m:ss"];
2871
+ function formatTime(v, pattern, warned2) {
2872
+ if (!TIME_PATTERNS.includes(pattern)) {
2873
+ if (!warned2.has(pattern)) {
2874
+ warned2.add(pattern);
2875
+ console.warn(`pixi-effects: format.time "${pattern}" is not one of ${TIME_PATTERNS.map((p) => `"${p}"`).join(", ")}: the value is printed as a number`);
2876
+ }
2877
+ return null;
2878
+ }
2879
+ const total = Math.floor(Math.abs(v));
2880
+ const h = Math.floor(total / 3600), m = Math.floor(total % 3600 / 60), sec = total % 60;
2881
+ const two = (n) => String(n).padStart(2, "0");
2882
+ const sign = v < 0 && total > 0 ? "-" : "";
2883
+ switch (pattern) {
2884
+ case "hh:mm:ss":
2885
+ return `${sign}${two(h)}:${two(m)}:${two(sec)}`;
2886
+ case "h:mm:ss":
2887
+ return `${sign}${h}:${two(m)}:${two(sec)}`;
2888
+ case "hh:mm":
2889
+ return `${sign}${two(h)}:${two(m)}`;
2890
+ case "mm:ss":
2891
+ return `${sign}${two(Math.floor(total / 60))}:${two(sec)}`;
2892
+ default:
2893
+ return `${sign}${Math.floor(total / 60)}:${two(sec)}`;
2894
+ }
2895
+ }
2896
+ function formatValue(v, f, warned2 = /* @__PURE__ */ new Set()) {
2897
+ if (f?.time !== void 0) {
2898
+ const t = formatTime(v, String(f.time), warned2);
2899
+ if (t !== null) return t;
2900
+ }
2866
2901
  const fixed = v.toFixed(Math.max(0, Math.floor(f?.decimals ?? 0)));
2867
2902
  const pad = Math.max(0, Math.floor(f?.pad ?? 0));
2868
2903
  if (!f?.grouping && !pad) return fixed;
@@ -2881,6 +2916,8 @@ var TextSequence = class extends Sequence {
2881
2916
  /** `visibleChars` is in play (initial or animated): otherwise the whole text shows. */
2882
2917
  _typed = false;
2883
2918
  _lastText = "";
2919
+ /** The `format.time` patterns already said to be wrong on this layer. */
2920
+ _timeWarned = /* @__PURE__ */ new Set();
2884
2921
  /** A gradient fill for the text (`fillGradient`, static or animated): rebuilt as a FillGradient on each change (the text is re-rasterised). */
2885
2922
  _grad = null;
2886
2923
  _fillGradient = null;
@@ -2907,7 +2944,7 @@ var TextSequence = class extends Sequence {
2907
2944
  }
2908
2945
  /** Rewrite the displayed string from the template, the current `value` and `visibleChars`. */
2909
2946
  _refreshText() {
2910
- let next = this._template.includes("{value}") ? this._template.split("{value}").join(formatValue(this._counters.value, this.spec.format)) : this._template;
2947
+ let next = this._template.includes("{value}") ? this._template.split("{value}").join(formatValue(this._counters.value, this.spec.format, this._timeWarned)) : this._template;
2911
2948
  if (this._typed) next = Array.from(next).slice(0, Math.max(0, Math.floor(this._counters.visibleChars))).join("");
2912
2949
  if (next === this._lastText) return;
2913
2950
  this._lastText = next;
@@ -2923,6 +2960,11 @@ var TextSequence = class extends Sequence {
2923
2960
  label: this.spec.name
2924
2961
  });
2925
2962
  text.cullable = true;
2963
+ if (this.spec.resolution !== void 0) {
2964
+ const r = this.spec.resolution;
2965
+ if (typeof r === "number" && r >= 1 && r <= 8) text.resolution = r;
2966
+ else console.warn(`pixi-effects: ${chunk42ML5AKN_cjs.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`);
2967
+ }
2926
2968
  this.target = text;
2927
2969
  this.intrinsicWidth = text.width;
2928
2970
  this.intrinsicHeight = text.height;
@@ -2937,7 +2979,7 @@ var TextSequence = class extends Sequence {
2937
2979
  { skipKeys: STYLE_OPAQUE_KEYS }
2938
2980
  );
2939
2981
  const unset = Object.keys(resolved).filter((k) => resolved[k] === void 0);
2940
- if (unset.length) console.warn(`pixi-effects: ${chunkGL2AQFLQ_cjs.describeLayer(this.spec)}: ${unset.map((k) => `style.${k}`).join(", ")} ${unset.length > 1 ? "are" : "is"} undefined (a variable that was never set?), so ${unset.length > 1 ? "they are" : "it is"} ignored and the default is used: omit the key instead`);
2982
+ if (unset.length) console.warn(`pixi-effects: ${chunk42ML5AKN_cjs.describeLayer(this.spec)}: ${unset.map((k) => `style.${k}`).join(", ")} ${unset.length > 1 ? "are" : "is"} undefined (a variable that was never set?), so ${unset.length > 1 ? "they are" : "it is"} ignored and the default is used: omit the key instead`);
2941
2983
  const outline = resolved.stroke;
2942
2984
  if (outline && typeof outline === "object" && !("join" in outline) && !("lineJoin" in outline)) resolved.stroke = { ...outline, join: "round" };
2943
2985
  for (const k of Object.keys(resolved)) {
@@ -2947,7 +2989,9 @@ var TextSequence = class extends Sequence {
2947
2989
  this.intrinsicWidth = text.width;
2948
2990
  this.intrinsicHeight = text.height;
2949
2991
  }
2950
- validateGradientKeyframes(this.spec, chunkGL2AQFLQ_cjs.describeLayer(this.spec), void 0, "text");
2992
+ const startFill = initialProps.fill;
2993
+ if (typeof startFill === "string" || typeof startFill === "number") text.style.fill = startFill;
2994
+ validateGradientKeyframes(this.spec, chunk42ML5AKN_cjs.describeLayer(this.spec), void 0, "text");
2951
2995
  const gradientSpec = this.spec.fillGradient ?? initialProps.fillGradient;
2952
2996
  if (gradientSpec) {
2953
2997
  this._grad = { grad: gradStateFrom(gradientSpec) };
@@ -2983,7 +3027,7 @@ var TextSequence = class extends Sequence {
2983
3027
  bindTextKeyframes(timeline, keyframes, this.duration, origin, () => this._template, (t) => {
2984
3028
  this._template = t;
2985
3029
  refresh();
2986
- }, chunkGL2AQFLQ_cjs.describeLayer(this.spec));
3030
+ }, chunk42ML5AKN_cjs.describeLayer(this.spec));
2987
3031
  }
2988
3032
  };
2989
3033
  function runSuperWithStrippedSpec(inst, strippedSpec, timeline, offset) {
@@ -3433,7 +3477,7 @@ var CameraSequence = class extends Sequence {
3433
3477
  carrier.fogColor = "#000000";
3434
3478
  carrier.fogAmount = 1;
3435
3479
  this.target = carrier;
3436
- const keys = chunkGL2AQFLQ_cjs.collectPropKeys(this.spec);
3480
+ const keys = chunk42ML5AKN_cjs.collectPropKeys(this.spec);
3437
3481
  this.autoZ = !keys.has("z");
3438
3482
  this.dof = keys.has("focus") || keys.has("aperture");
3439
3483
  this.fog = keys.has("fogNear") || keys.has("fogFar") || keys.has("fogColor") || keys.has("fogAmount");
@@ -3751,10 +3795,10 @@ function resolveSpansStyle(style, scope, who, warn = defaultWarn) {
3751
3795
  }
3752
3796
  return out;
3753
3797
  }
3754
- var TEXT_ONLY = ["type", "spans", "text", "style", "format", "colorSpace", "fillGradient", "initial", "keyframes"];
3798
+ var TEXT_ONLY = ["type", "spans", "text", "style", "format", "colorSpace", "fillGradient", "initial", "keyframes", "resolution"];
3755
3799
  var NOT_FOR_A_GROUP = ["fill", "text", "value", "fillGradient"];
3756
3800
  function expandSpans(spec, warn = defaultWarn, scope) {
3757
- const who = chunkGL2AQFLQ_cjs.describeLayer(spec);
3801
+ const who = chunk42ML5AKN_cjs.describeLayer(spec);
3758
3802
  const s = spec;
3759
3803
  if (s.text !== void 0) warn(`pixi-effects: ${who}: "text" is ignored when the layer has spans: write the text as spans`);
3760
3804
  const style = resolveSpansStyle(s.style ?? {}, scope, who, warn);
@@ -3796,7 +3840,9 @@ function expandSpans(spec, warn = defaultWarn, scope) {
3796
3840
  name: `${baseName}.${i}`,
3797
3841
  text: p.text,
3798
3842
  style: { ...style, ...p.style },
3799
- initial: { x: p.x, y: p.y, ...typed ? { visibleChars: p.length } : {} }
3843
+ initial: { x: p.x, y: p.y, ...typed ? { visibleChars: p.length } : {} },
3844
+ ...s.resolution !== void 0 ? { resolution: s.resolution } : {}
3845
+ // the pieces are what is drawn
3800
3846
  }));
3801
3847
  const rest = {};
3802
3848
  for (const [k, v] of Object.entries(s)) if (!TEXT_ONLY.includes(k)) rest[k] = v;
@@ -3804,9 +3850,15 @@ function expandSpans(spec, warn = defaultWarn, scope) {
3804
3850
  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("") });
3805
3851
  return group;
3806
3852
  }
3853
+ var ORIGINAL_SCALE_MODE = /* @__PURE__ */ new WeakMap();
3807
3854
  var ImageSequence = class extends Sequence {
3808
3855
  async build() {
3809
3856
  const texture = await PIXI.Assets.get(this.spec.asset);
3857
+ const source = texture.source;
3858
+ if (source && !ORIGINAL_SCALE_MODE.has(source)) ORIGINAL_SCALE_MODE.set(source, source.scaleMode);
3859
+ const mode = this.spec.scaleMode;
3860
+ if (mode !== void 0 && mode !== "nearest" && mode !== "linear") console.warn(`pixi-effects: ${chunk42ML5AKN_cjs.describeLayer(this.spec)}: scaleMode "${String(mode)}" is not "nearest" or "linear": ignored`);
3861
+ if (source) source.scaleMode = mode === "nearest" || mode === "linear" ? mode : ORIGINAL_SCALE_MODE.get(source);
3810
3862
  const sprite = new PIXI.Sprite({ texture, label: this.spec.name });
3811
3863
  sprite.cullable = true;
3812
3864
  this.target = sprite;
@@ -3914,12 +3966,12 @@ var AudioSequence = class extends Sequence {
3914
3966
  const playable = this._remap && this._remap.timeKfs.length > 0 ? Infinity : (data.duration ?? Infinity) / constant;
3915
3967
  if (!spec.loop && Number.isFinite(playable) && this.duration > playable + 0.05) {
3916
3968
  console.warn(
3917
- `pixi-effects: ${chunkGL2AQFLQ_cjs.describeLayer(spec)}: audio asset "${spec.asset}" plays for ${playable.toFixed(1)}s (${data.duration.toFixed(1)}s at speed ${constant}) but the layer lasts ${this.duration.toFixed(1)}s, so it goes silent after ${playable.toFixed(1)}s. Add loop: true, or shorten the layer's duration.`
3969
+ `pixi-effects: ${chunk42ML5AKN_cjs.describeLayer(spec)}: audio asset "${spec.asset}" plays for ${playable.toFixed(1)}s (${data.duration.toFixed(1)}s at speed ${constant}) but the layer lasts ${this.duration.toFixed(1)}s, so it goes silent after ${playable.toFixed(1)}s. Add loop: true, or shorten the layer's duration.`
3918
3970
  );
3919
3971
  }
3920
3972
  }
3921
3973
  async _buildSfx(spec) {
3922
- const who = chunkGL2AQFLQ_cjs.describeLayer(spec);
3974
+ const who = chunk42ML5AKN_cjs.describeLayer(spec);
3923
3975
  const raw = spec;
3924
3976
  if (raw.asset !== void 0) console.warn(`pixi-effects: ${who}: has both asset and sfx \u2014 the sfx plays; remove one`);
3925
3977
  if (raw.loop) console.warn(`pixi-effects: ${who}: loop has no effect on an sfx \u2014 to repeat it, add one audio layer per hit (a JS loop)`);
@@ -3938,7 +3990,7 @@ var AudioSequence = class extends Sequence {
3938
3990
  });
3939
3991
  }
3940
3992
  async _buildMusic(spec) {
3941
- const who = chunkGL2AQFLQ_cjs.describeLayer(spec);
3993
+ const who = chunk42ML5AKN_cjs.describeLayer(spec);
3942
3994
  const raw = spec;
3943
3995
  if (raw.asset !== void 0) console.warn(`pixi-effects: ${who}: has both asset and music \u2014 the music plays; remove one`);
3944
3996
  if (raw.sfx !== void 0) console.warn(`pixi-effects: ${who}: has both sfx and music \u2014 the music plays; remove one`);
@@ -3973,7 +4025,7 @@ var AudioSequence = class extends Sequence {
3973
4025
  out.push({
3974
4026
  ...sourceMap ? { sourceMap } : {},
3975
4027
  ...this._audioBuffer ? { buffer: this._audioBuffer } : { synth: this._synth },
3976
- layer: chunkGL2AQFLQ_cjs.describeLayer(this.spec),
4028
+ layer: chunk42ML5AKN_cjs.describeLayer(this.spec),
3977
4029
  source: this._source,
3978
4030
  ...this._music ? { music: this._music } : {},
3979
4031
  loop: !this._synth && !!this.spec.loop,
@@ -4515,7 +4567,7 @@ var VideoSequence = class extends Sequence {
4515
4567
  out.push({
4516
4568
  ...sourceMap ? { sourceMap } : {},
4517
4569
  buffer: this._audioBuffer,
4518
- layer: chunkGL2AQFLQ_cjs.describeLayer(this.spec),
4570
+ layer: chunk42ML5AKN_cjs.describeLayer(this.spec),
4519
4571
  source: `video "${this.spec.asset}"`,
4520
4572
  loop: !!this.spec.loop,
4521
4573
  start: baseTime + this.at,
@@ -4663,6 +4715,21 @@ function slice(line, a, b) {
4663
4715
  }
4664
4716
  return out;
4665
4717
  }
4718
+ function splitEvenOdd(d) {
4719
+ const parts = d.split(/(?=[Mm])/).map((x) => x.trim()).filter(Boolean);
4720
+ if (parts.length < 2) return { main: d.trim(), holes: [] };
4721
+ const area = (sub2) => flattenSvgPath(sub2).reduce((sum, line) => {
4722
+ let a = 0;
4723
+ for (let i = 0; i < line.pts.length; i += 2) {
4724
+ const j = (i + 2) % line.pts.length;
4725
+ a += line.pts[i] * line.pts[j + 1] - line.pts[j] * line.pts[i + 1];
4726
+ }
4727
+ return sum + Math.abs(a) / 2;
4728
+ }, 0);
4729
+ const areas = parts.map(area);
4730
+ const big = areas.indexOf(Math.max(...areas));
4731
+ return { main: parts[big], holes: parts.filter((_, i) => i !== big) };
4732
+ }
4666
4733
 
4667
4734
  // src/sequences/morphPath.ts
4668
4735
  function resample(line, n) {
@@ -4725,6 +4792,11 @@ function buildMorph(from, to, n = 128) {
4725
4792
  for (let i = 0; i < count; i++) {
4726
4793
  const fa = from[i], fb = to[i];
4727
4794
  const closed = (fa?.closed ?? fb.closed) && (fb?.closed ?? fa.closed);
4795
+ if (fa && fb && fa.closed === fb.closed && fa.pts.length === fb.pts.length && fa.pts.length >= 6) {
4796
+ const a2 = [...fa.pts];
4797
+ pairs.push({ a: a2, b: closed ? align(a2, [...fb.pts]) : [...fb.pts], closed });
4798
+ continue;
4799
+ }
4728
4800
  const a = fa ? resample({ pts: fa.pts, closed }, n) : collapsed(resample({ pts: fb.pts, closed }, n));
4729
4801
  let b = fb ? resample({ pts: fb.pts, closed }, n) : collapsed(a);
4730
4802
  if (closed && fa && fb) b = align(a, b);
@@ -4783,20 +4855,20 @@ var ShapeSequence = class extends Sequence {
4783
4855
  for (const k of geomKeys) this._liveKeys.add(k);
4784
4856
  seedGeometry(this._state, this.spec, scope);
4785
4857
  seedStyle(this._state, this.spec.initial ?? {}, scope);
4786
- seedStrokeStyle(this._state, this.spec, this.spec.initial ?? {}, chunkGL2AQFLQ_cjs.describeLayer(this.spec));
4858
+ seedStrokeStyle(this._state, this.spec, this.spec.initial ?? {}, chunk42ML5AKN_cjs.describeLayer(this.spec));
4787
4859
  if (this.spec.shape === "arc") {
4788
4860
  this._state.startAngle ??= 0;
4789
4861
  this._state.endAngle ??= 360;
4790
4862
  const top = this.spec, ini = this.spec.initial ?? {};
4791
4863
  if ("trimStart" in top || "trimEnd" in top || "trimStart" in ini || "trimEnd" in ini) {
4792
- console.warn(`pixi-effects: ${chunkGL2AQFLQ_cjs.describeLayer(this.spec)}: trimStart / trimEnd do not apply to an arc: animate its endAngle (and startAngle) instead`);
4864
+ console.warn(`pixi-effects: ${chunk42ML5AKN_cjs.describeLayer(this.spec)}: trimStart / trimEnd do not apply to an arc: animate its endAngle (and startAngle) instead`);
4793
4865
  }
4794
4866
  } else {
4795
4867
  this._state.trimStart ??= 0;
4796
4868
  this._state.trimEnd ??= 1;
4797
4869
  }
4798
4870
  const gradientSpec = this.spec.fillGradient ?? this.spec.initial?.fillGradient;
4799
- validateGradientKeyframes(this.spec, chunkGL2AQFLQ_cjs.describeLayer(this.spec));
4871
+ validateGradientKeyframes(this.spec, chunk42ML5AKN_cjs.describeLayer(this.spec));
4800
4872
  if (gradientSpec) {
4801
4873
  if (hasGradientKeys(this.spec.keyframes)) {
4802
4874
  this._grad = { grad: gradStateFrom(gradientSpec) };
@@ -4808,6 +4880,8 @@ var ShapeSequence = class extends Sequence {
4808
4880
  const morph = this._setupMorph();
4809
4881
  this._drawGeometry = makeDrawGeometry(this.spec, scope, this._state, morph);
4810
4882
  this._outline = makeOutline(this.spec, scope, this._state, morph);
4883
+ const eo = this.spec.shape === "path" && this.spec.fillRule === "evenodd" && !morph ? splitEvenOdd(this.spec.d) : null;
4884
+ this._evenOdd = eo && eo.holes.length > 0 ? eo : null;
4811
4885
  const { trimStart, trimEnd } = this._state;
4812
4886
  this._state.trimStart = 0;
4813
4887
  this._state.trimEnd = 1;
@@ -4838,9 +4912,9 @@ var ShapeSequence = class extends Sequence {
4838
4912
  }
4839
4913
  /** The prepared morph between `d` and `morphTo`, or null (and a warning when the spec asked for one that cannot work). */
4840
4914
  _setupMorph() {
4841
- const who = chunkGL2AQFLQ_cjs.describeLayer(this.spec);
4915
+ const who = chunk42ML5AKN_cjs.describeLayer(this.spec);
4842
4916
  const top = this.spec;
4843
- const wanted = this.spec.shape === "path" && (this._state.morph !== void 0 || chunkGL2AQFLQ_cjs.collectPropKeys(this.spec).has("morph"));
4917
+ const wanted = this.spec.shape === "path" && (this._state.morph !== void 0 || chunk42ML5AKN_cjs.collectPropKeys(this.spec).has("morph"));
4844
4918
  if (this.spec.shape !== "path") {
4845
4919
  if ("morphTo" in top) console.warn(`pixi-effects: ${who}: morphTo only works on a path shape (shape: 'path'); ignored`);
4846
4920
  return null;
@@ -4873,6 +4947,27 @@ var ShapeSequence = class extends Sequence {
4873
4947
  _painter = null;
4874
4948
  _gradDirty = false;
4875
4949
  _outline = null;
4950
+ /** fillRule 'evenodd' on a path: the shape and the sub-paths cut out of it after it is filled (null: not used). */
4951
+ _evenOdd = null;
4952
+ /**
4953
+ * 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
4954
+ * LAST instruction, so it has to come between the fill and the stroke.)
4955
+ */
4956
+ _fillWithHoles(g) {
4957
+ const s = this._state, { main, holes } = this._evenOdd;
4958
+ applyFill(g, s);
4959
+ for (const hole of holes) {
4960
+ g.beginPath();
4961
+ g.path(new PIXI.GraphicsPath(hole));
4962
+ g.cut();
4963
+ }
4964
+ if (s.strokeColor !== void 0 && s.strokeWidth > 0) {
4965
+ g.beginPath();
4966
+ g.path(new PIXI.GraphicsPath(main));
4967
+ for (const hole of holes) g.path(new PIXI.GraphicsPath(hole));
4968
+ applyStroke(g, s);
4969
+ }
4970
+ }
4876
4971
  _redraw() {
4877
4972
  const graphics = this.target;
4878
4973
  const s = this._state;
@@ -4884,6 +4979,10 @@ var ShapeSequence = class extends Sequence {
4884
4979
  const trimmed = (s.trimStart ?? 0) > 0 || (s.trimEnd ?? 1) < 1;
4885
4980
  if (!trimmed || !this._outline || !(s.strokeColor !== void 0 && s.strokeWidth > 0)) {
4886
4981
  this._drawGeometry(graphics);
4982
+ if (this._evenOdd) {
4983
+ this._fillWithHoles(graphics);
4984
+ return;
4985
+ }
4887
4986
  applyState(graphics, s);
4888
4987
  return;
4889
4988
  }
@@ -5107,8 +5206,9 @@ function makePolygonDraw(spec, scope) {
5107
5206
  };
5108
5207
  }
5109
5208
  function makePathDraw(spec) {
5209
+ const d = spec.fillRule === "evenodd" ? splitEvenOdd(spec.d).main : spec.d;
5110
5210
  return (g) => {
5111
- g.path(new PIXI.GraphicsPath(spec.d));
5211
+ g.path(new PIXI.GraphicsPath(d));
5112
5212
  };
5113
5213
  }
5114
5214
  var STATE_DRAWERS = {
@@ -5450,7 +5550,7 @@ var ShaderSequence = class extends Sequence {
5450
5550
  }
5451
5551
  async build() {
5452
5552
  const spec = this.shaderSpec;
5453
- const who = chunkGL2AQFLQ_cjs.describeLayer(spec);
5553
+ const who = chunk42ML5AKN_cjs.describeLayer(spec);
5454
5554
  for (const m of shaderProblems(spec)) console.warn(`pixi-effects: ${who}: ${m}`);
5455
5555
  if (this.duration === void 0) this.duration = this.parent?.duration ?? this.root.duration;
5456
5556
  const scope = buildScope(this, this.parent, this.root);
@@ -5494,7 +5594,7 @@ var ShaderSequence = class extends Sequence {
5494
5594
  const warn = (why) => {
5495
5595
  if (this.warnedPaths.has(path)) return;
5496
5596
  this.warnedPaths.add(path);
5497
- console.warn(`pixi-effects: ${chunkGL2AQFLQ_cjs.describeLayer(this.spec)}: keyframe path uniforms.${path} ${why}; skipped`);
5597
+ console.warn(`pixi-effects: ${chunk42ML5AKN_cjs.describeLayer(this.spec)}: keyframe path uniforms.${path} ${why}; skipped`);
5498
5598
  };
5499
5599
  if (slot === void 0) {
5500
5600
  const guess = chunkTWZNOUM7_cjs.suggestName(name, Object.keys(this.carrier));
@@ -5529,7 +5629,7 @@ var ShaderSequence = class extends Sequence {
5529
5629
  this.failed = true;
5530
5630
  if (!this.warnedNoGl) {
5531
5631
  this.warnedNoGl = true;
5532
- console.warn(`pixi-effects: ${chunkGL2AQFLQ_cjs.describeLayer(this.spec)}: this browser has no WebGL2, so the shader cannot run; the layer is drawn as a checkerboard`);
5632
+ console.warn(`pixi-effects: ${chunk42ML5AKN_cjs.describeLayer(this.spec)}: this browser has no WebGL2, so the shader cannot run; the layer is drawn as a checkerboard`);
5533
5633
  }
5534
5634
  }
5535
5635
  compile() {
@@ -5560,7 +5660,7 @@ var ShaderSequence = class extends Sequence {
5560
5660
  gl.deleteProgram(p);
5561
5661
  this.failed = true;
5562
5662
  this.prog = null;
5563
- console.warn(`pixi-effects: ${chunkGL2AQFLQ_cjs.describeLayer(this.spec)}: the fragment shader did not compile (the line numbers are the lines of your code):
5663
+ console.warn(`pixi-effects: ${chunk42ML5AKN_cjs.describeLayer(this.spec)}: the fragment shader did not compile (the line numbers are the lines of your code):
5564
5664
  ${glslErrors(log).join("\n")}
5565
5665
  The layer is drawn as a checkerboard until it is fixed.`);
5566
5666
  return;
@@ -5644,13 +5744,13 @@ function resolveDim(v, scope) {
5644
5744
  var _CompositionSequence = null;
5645
5745
  async function getCompositionSequence() {
5646
5746
  if (!_CompositionSequence) {
5647
- _CompositionSequence = (await import('./Composition-OLH7RC5Z.cjs')).CompositionSequence;
5747
+ _CompositionSequence = (await import('./Composition-XRLYR6GL.cjs')).CompositionSequence;
5648
5748
  }
5649
5749
  return _CompositionSequence;
5650
5750
  }
5651
5751
  var _SpansSequence = null;
5652
5752
  async function getSpansSequence() {
5653
- if (!_SpansSequence) _SpansSequence = (await import('./Spans-QRMN2EZO.cjs')).SpansSequence;
5753
+ if (!_SpansSequence) _SpansSequence = (await import('./Spans-4IRBSOLI.cjs')).SpansSequence;
5654
5754
  return _SpansSequence;
5655
5755
  }
5656
5756
  var staticTypes = {
@@ -5748,5 +5848,5 @@ exports.resolveNumber = resolveNumber;
5748
5848
  exports.spansMeta = spansMeta;
5749
5849
  exports.springDuration = springDuration;
5750
5850
  exports.timeRemapOf = timeRemapOf;
5751
- //# sourceMappingURL=chunk-LEECUCTG.cjs.map
5752
- //# sourceMappingURL=chunk-LEECUCTG.cjs.map
5851
+ //# sourceMappingURL=chunk-KJSEQVBV.cjs.map
5852
+ //# sourceMappingURL=chunk-KJSEQVBV.cjs.map