pixi-effects 0.35.0 → 0.37.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 (77) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/check.mjs +38 -9
  5. package/ai/tools/lookup.mjs +15 -2
  6. package/dist/{Base-pNq6sVGl.d.cts → Base-BlA6SeqJ.d.cts} +1 -1
  7. package/dist/{Base-CXsMdmJB.d.ts → Base-YqM0Ljvp.d.ts} +1 -1
  8. package/dist/Composition-5KZETM4J.js +7 -0
  9. package/dist/{Composition-NDXKQHB4.js.map → Composition-5KZETM4J.js.map} +1 -1
  10. package/dist/Composition-MHDI7UNZ.cjs +16 -0
  11. package/dist/{Composition-OLH7RC5Z.cjs.map → Composition-MHDI7UNZ.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-CJN_gvLB.d.cts} +26 -2
  15. package/dist/{Movie-Db4QnMIH.d.cts → Movie-CfHaGmUP.d.ts} +26 -2
  16. package/dist/{Spans-QHHWC2PB.js → Spans-FSSO2HGX.js} +6 -6
  17. package/dist/{Spans-QHHWC2PB.js.map → Spans-FSSO2HGX.js.map} +1 -1
  18. package/dist/{Spans-QRMN2EZO.cjs → Spans-WVJH77BW.cjs} +11 -11
  19. package/dist/{Spans-QRMN2EZO.cjs.map → Spans-WVJH77BW.cjs.map} +1 -1
  20. package/dist/{blendModes-WIUCYIMC.cjs → blendModes-FD3THLNC.cjs} +5 -5
  21. package/dist/{blendModes-WIUCYIMC.cjs.map → blendModes-FD3THLNC.cjs.map} +1 -1
  22. package/dist/{blendModes-PP5NPBXY.js → blendModes-FMYO6SQN.js} +4 -4
  23. package/dist/{blendModes-PP5NPBXY.js.map → blendModes-FMYO6SQN.js.map} +1 -1
  24. package/dist/{chunk-ITDYFBB6.js → chunk-32MMNDDN.js} +7 -6
  25. package/dist/chunk-32MMNDDN.js.map +1 -0
  26. package/dist/{chunk-GL2AQFLQ.cjs → chunk-3SAS3ZE5.cjs} +7 -6
  27. package/dist/chunk-3SAS3ZE5.cjs.map +1 -0
  28. package/dist/{chunk-7TTCPHAV.cjs → chunk-E26M4NTL.cjs} +235 -49
  29. package/dist/chunk-E26M4NTL.cjs.map +1 -0
  30. package/dist/{chunk-XV4L72FW.js → chunk-IPQNT65V.js} +6 -6
  31. package/dist/chunk-IPQNT65V.js.map +1 -0
  32. package/dist/{chunk-IMQJVRMT.cjs → chunk-JN7JHNZR.cjs} +5 -5
  33. package/dist/{chunk-IMQJVRMT.cjs.map → chunk-JN7JHNZR.cjs.map} +1 -1
  34. package/dist/{chunk-2GNK6DPV.js → chunk-KNDEKWQO.js} +203 -18
  35. package/dist/chunk-KNDEKWQO.js.map +1 -0
  36. package/dist/{chunk-5IJENZOG.cjs → chunk-MJMOPWMK.cjs} +92 -92
  37. package/dist/chunk-MJMOPWMK.cjs.map +1 -0
  38. package/dist/{chunk-LEECUCTG.cjs → chunk-QHIOOK5S.cjs} +436 -29
  39. package/dist/chunk-QHIOOK5S.cjs.map +1 -0
  40. package/dist/{chunk-Y253JQOH.js → chunk-SBR75G73.js} +416 -11
  41. package/dist/chunk-SBR75G73.js.map +1 -0
  42. package/dist/{chunk-RTJD3ZSW.js → chunk-VNP7QCJE.js} +4 -4
  43. package/dist/{chunk-RTJD3ZSW.js.map → chunk-VNP7QCJE.js.map} +1 -1
  44. package/dist/index.cjs +41 -33
  45. package/dist/index.d.cts +47 -7
  46. package/dist/index.d.ts +47 -7
  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.cts → types-DvnXXm33.d.cts} +67 -3
  58. package/dist/{types-CavnUvd5.d.ts → types-DvnXXm33.d.ts} +67 -3
  59. package/docs/api.md +5 -4
  60. package/docs/dsl.md +42 -5
  61. package/llms-full.txt +305 -22
  62. package/package.json +1 -1
  63. package/skills/pixi-effects/SKILL.md +5 -4
  64. package/skills/pixi-effects/reference/cheatsheet.md +9 -6
  65. package/skills/pixi-effects/reference/pitfalls.md +7 -0
  66. package/skills/pixi-effects/reference/recipes.md +237 -3
  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-5IJENZOG.cjs.map +0 -1
  72. package/dist/chunk-7TTCPHAV.cjs.map +0 -1
  73. package/dist/chunk-GL2AQFLQ.cjs.map +0 -1
  74. package/dist/chunk-ITDYFBB6.js.map +0 -1
  75. package/dist/chunk-LEECUCTG.cjs.map +0 -1
  76. package/dist/chunk-XV4L72FW.js.map +0 -1
  77. 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 chunk3SAS3ZE5_cjs = require('./chunk-3SAS3ZE5.cjs');
4
4
  var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
5
5
  var PIXI = require('pixi.js');
6
6
 
@@ -992,6 +992,17 @@ function noise(x, seed = 0) {
992
992
  const u = f * f * f * (f * (f * 6 - 15) + 10);
993
993
  return a + (b - a) * u;
994
994
  }
995
+ function noise2(x, y, seed = 0) {
996
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return 0;
997
+ const i = Math.floor(x), j = Math.floor(y);
998
+ const fx = x - i, fy = y - j;
999
+ const s = fold(seed) * 668265261;
1000
+ const corner = (a, b) => mix2(Math.imul((a | 0) + s, 2654435761) ^ Math.imul((b | 0) + 2135587861, 2246822507)) / 2147483648 - 1;
1001
+ const ux = fx * fx * fx * (fx * (fx * 6 - 15) + 10), uy = fy * fy * fy * (fy * (fy * 6 - 15) + 10);
1002
+ const top = corner(i, j) + (corner(i + 1, j) - corner(i, j)) * ux;
1003
+ const bottom = corner(i, j + 1) + (corner(i + 1, j + 1) - corner(i, j + 1)) * ux;
1004
+ return top + (bottom - top) * uy;
1005
+ }
995
1006
 
996
1007
  // src/expr/Parser.ts
997
1008
  var FUNCS = {
@@ -2347,6 +2358,11 @@ function createFilter(spec) {
2347
2358
  return named;
2348
2359
  }
2349
2360
  const { type, name, ...given } = spec;
2361
+ const unset = Object.keys(given).filter((k) => given[k] === void 0);
2362
+ if (unset.length > 0) {
2363
+ 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.`);
2364
+ for (const k of unset) delete given[k];
2365
+ }
2350
2366
  const params = copyPlain(given);
2351
2367
  const Cls = registry[type];
2352
2368
  const inst = Cls ? new Cls(params) : createNamedFilter(type, params);
@@ -2862,7 +2878,37 @@ var TEXT_STYLE_DEFAULTS = {
2862
2878
  };
2863
2879
 
2864
2880
  // src/sequences/Text.ts
2865
- function formatValue(v, f) {
2881
+ var TIME_PATTERNS = ["hh:mm:ss", "h:mm:ss", "hh:mm", "mm:ss", "m:ss"];
2882
+ function formatTime(v, pattern, warned2) {
2883
+ if (!TIME_PATTERNS.includes(pattern)) {
2884
+ if (!warned2.has(pattern)) {
2885
+ warned2.add(pattern);
2886
+ console.warn(`pixi-effects: format.time "${pattern}" is not one of ${TIME_PATTERNS.map((p) => `"${p}"`).join(", ")}: the value is printed as a number`);
2887
+ }
2888
+ return null;
2889
+ }
2890
+ const total = Math.floor(Math.abs(v));
2891
+ const h = Math.floor(total / 3600), m = Math.floor(total % 3600 / 60), sec = total % 60;
2892
+ const two = (n) => String(n).padStart(2, "0");
2893
+ const sign = v < 0 && total > 0 ? "-" : "";
2894
+ switch (pattern) {
2895
+ case "hh:mm:ss":
2896
+ return `${sign}${two(h)}:${two(m)}:${two(sec)}`;
2897
+ case "h:mm:ss":
2898
+ return `${sign}${h}:${two(m)}:${two(sec)}`;
2899
+ case "hh:mm":
2900
+ return `${sign}${two(h)}:${two(m)}`;
2901
+ case "mm:ss":
2902
+ return `${sign}${two(Math.floor(total / 60))}:${two(sec)}`;
2903
+ default:
2904
+ return `${sign}${Math.floor(total / 60)}:${two(sec)}`;
2905
+ }
2906
+ }
2907
+ function formatValue(v, f, warned2 = /* @__PURE__ */ new Set()) {
2908
+ if (f?.time !== void 0) {
2909
+ const t = formatTime(v, String(f.time), warned2);
2910
+ if (t !== null) return t;
2911
+ }
2866
2912
  const fixed = v.toFixed(Math.max(0, Math.floor(f?.decimals ?? 0)));
2867
2913
  const pad = Math.max(0, Math.floor(f?.pad ?? 0));
2868
2914
  if (!f?.grouping && !pad) return fixed;
@@ -2881,6 +2927,8 @@ var TextSequence = class extends Sequence {
2881
2927
  /** `visibleChars` is in play (initial or animated): otherwise the whole text shows. */
2882
2928
  _typed = false;
2883
2929
  _lastText = "";
2930
+ /** The `format.time` patterns already said to be wrong on this layer. */
2931
+ _timeWarned = /* @__PURE__ */ new Set();
2884
2932
  /** A gradient fill for the text (`fillGradient`, static or animated): rebuilt as a FillGradient on each change (the text is re-rasterised). */
2885
2933
  _grad = null;
2886
2934
  _fillGradient = null;
@@ -2907,7 +2955,7 @@ var TextSequence = class extends Sequence {
2907
2955
  }
2908
2956
  /** Rewrite the displayed string from the template, the current `value` and `visibleChars`. */
2909
2957
  _refreshText() {
2910
- let next = this._template.includes("{value}") ? this._template.split("{value}").join(formatValue(this._counters.value, this.spec.format)) : this._template;
2958
+ let next = this._template.includes("{value}") ? this._template.split("{value}").join(formatValue(this._counters.value, this.spec.format, this._timeWarned)) : this._template;
2911
2959
  if (this._typed) next = Array.from(next).slice(0, Math.max(0, Math.floor(this._counters.visibleChars))).join("");
2912
2960
  if (next === this._lastText) return;
2913
2961
  this._lastText = next;
@@ -2923,6 +2971,11 @@ var TextSequence = class extends Sequence {
2923
2971
  label: this.spec.name
2924
2972
  });
2925
2973
  text.cullable = true;
2974
+ if (this.spec.resolution !== void 0) {
2975
+ const r = this.spec.resolution;
2976
+ if (typeof r === "number" && r >= 1 && r <= 8) text.resolution = r;
2977
+ else console.warn(`pixi-effects: ${chunk3SAS3ZE5_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`);
2978
+ }
2926
2979
  this.target = text;
2927
2980
  this.intrinsicWidth = text.width;
2928
2981
  this.intrinsicHeight = text.height;
@@ -2937,7 +2990,7 @@ var TextSequence = class extends Sequence {
2937
2990
  { skipKeys: STYLE_OPAQUE_KEYS }
2938
2991
  );
2939
2992
  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`);
2993
+ if (unset.length) console.warn(`pixi-effects: ${chunk3SAS3ZE5_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
2994
  const outline = resolved.stroke;
2942
2995
  if (outline && typeof outline === "object" && !("join" in outline) && !("lineJoin" in outline)) resolved.stroke = { ...outline, join: "round" };
2943
2996
  for (const k of Object.keys(resolved)) {
@@ -2947,7 +3000,9 @@ var TextSequence = class extends Sequence {
2947
3000
  this.intrinsicWidth = text.width;
2948
3001
  this.intrinsicHeight = text.height;
2949
3002
  }
2950
- validateGradientKeyframes(this.spec, chunkGL2AQFLQ_cjs.describeLayer(this.spec), void 0, "text");
3003
+ const startFill = initialProps.fill;
3004
+ if (typeof startFill === "string" || typeof startFill === "number") text.style.fill = startFill;
3005
+ validateGradientKeyframes(this.spec, chunk3SAS3ZE5_cjs.describeLayer(this.spec), void 0, "text");
2951
3006
  const gradientSpec = this.spec.fillGradient ?? initialProps.fillGradient;
2952
3007
  if (gradientSpec) {
2953
3008
  this._grad = { grad: gradStateFrom(gradientSpec) };
@@ -2983,7 +3038,7 @@ var TextSequence = class extends Sequence {
2983
3038
  bindTextKeyframes(timeline, keyframes, this.duration, origin, () => this._template, (t) => {
2984
3039
  this._template = t;
2985
3040
  refresh();
2986
- }, chunkGL2AQFLQ_cjs.describeLayer(this.spec));
3041
+ }, chunk3SAS3ZE5_cjs.describeLayer(this.spec));
2987
3042
  }
2988
3043
  };
2989
3044
  function runSuperWithStrippedSpec(inst, strippedSpec, timeline, offset) {
@@ -3433,7 +3488,7 @@ var CameraSequence = class extends Sequence {
3433
3488
  carrier.fogColor = "#000000";
3434
3489
  carrier.fogAmount = 1;
3435
3490
  this.target = carrier;
3436
- const keys = chunkGL2AQFLQ_cjs.collectPropKeys(this.spec);
3491
+ const keys = chunk3SAS3ZE5_cjs.collectPropKeys(this.spec);
3437
3492
  this.autoZ = !keys.has("z");
3438
3493
  this.dof = keys.has("focus") || keys.has("aperture");
3439
3494
  this.fog = keys.has("fogNear") || keys.has("fogFar") || keys.has("fogColor") || keys.has("fogAmount");
@@ -3751,10 +3806,10 @@ function resolveSpansStyle(style, scope, who, warn = defaultWarn) {
3751
3806
  }
3752
3807
  return out;
3753
3808
  }
3754
- var TEXT_ONLY = ["type", "spans", "text", "style", "format", "colorSpace", "fillGradient", "initial", "keyframes"];
3809
+ var TEXT_ONLY = ["type", "spans", "text", "style", "format", "colorSpace", "fillGradient", "initial", "keyframes", "resolution"];
3755
3810
  var NOT_FOR_A_GROUP = ["fill", "text", "value", "fillGradient"];
3756
3811
  function expandSpans(spec, warn = defaultWarn, scope) {
3757
- const who = chunkGL2AQFLQ_cjs.describeLayer(spec);
3812
+ const who = chunk3SAS3ZE5_cjs.describeLayer(spec);
3758
3813
  const s = spec;
3759
3814
  if (s.text !== void 0) warn(`pixi-effects: ${who}: "text" is ignored when the layer has spans: write the text as spans`);
3760
3815
  const style = resolveSpansStyle(s.style ?? {}, scope, who, warn);
@@ -3796,7 +3851,9 @@ function expandSpans(spec, warn = defaultWarn, scope) {
3796
3851
  name: `${baseName}.${i}`,
3797
3852
  text: p.text,
3798
3853
  style: { ...style, ...p.style },
3799
- initial: { x: p.x, y: p.y, ...typed ? { visibleChars: p.length } : {} }
3854
+ initial: { x: p.x, y: p.y, ...typed ? { visibleChars: p.length } : {} },
3855
+ ...s.resolution !== void 0 ? { resolution: s.resolution } : {}
3856
+ // the pieces are what is drawn
3800
3857
  }));
3801
3858
  const rest = {};
3802
3859
  for (const [k, v] of Object.entries(s)) if (!TEXT_ONLY.includes(k)) rest[k] = v;
@@ -3804,9 +3861,15 @@ function expandSpans(spec, warn = defaultWarn, scope) {
3804
3861
  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
3862
  return group;
3806
3863
  }
3864
+ var ORIGINAL_SCALE_MODE = /* @__PURE__ */ new WeakMap();
3807
3865
  var ImageSequence = class extends Sequence {
3808
3866
  async build() {
3809
3867
  const texture = await PIXI.Assets.get(this.spec.asset);
3868
+ const source = texture.source;
3869
+ if (source && !ORIGINAL_SCALE_MODE.has(source)) ORIGINAL_SCALE_MODE.set(source, source.scaleMode);
3870
+ const mode = this.spec.scaleMode;
3871
+ if (mode !== void 0 && mode !== "nearest" && mode !== "linear") console.warn(`pixi-effects: ${chunk3SAS3ZE5_cjs.describeLayer(this.spec)}: scaleMode "${String(mode)}" is not "nearest" or "linear": ignored`);
3872
+ if (source) source.scaleMode = mode === "nearest" || mode === "linear" ? mode : ORIGINAL_SCALE_MODE.get(source);
3810
3873
  const sprite = new PIXI.Sprite({ texture, label: this.spec.name });
3811
3874
  sprite.cullable = true;
3812
3875
  this.target = sprite;
@@ -3914,12 +3977,12 @@ var AudioSequence = class extends Sequence {
3914
3977
  const playable = this._remap && this._remap.timeKfs.length > 0 ? Infinity : (data.duration ?? Infinity) / constant;
3915
3978
  if (!spec.loop && Number.isFinite(playable) && this.duration > playable + 0.05) {
3916
3979
  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.`
3980
+ `pixi-effects: ${chunk3SAS3ZE5_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
3981
  );
3919
3982
  }
3920
3983
  }
3921
3984
  async _buildSfx(spec) {
3922
- const who = chunkGL2AQFLQ_cjs.describeLayer(spec);
3985
+ const who = chunk3SAS3ZE5_cjs.describeLayer(spec);
3923
3986
  const raw = spec;
3924
3987
  if (raw.asset !== void 0) console.warn(`pixi-effects: ${who}: has both asset and sfx \u2014 the sfx plays; remove one`);
3925
3988
  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 +4001,7 @@ var AudioSequence = class extends Sequence {
3938
4001
  });
3939
4002
  }
3940
4003
  async _buildMusic(spec) {
3941
- const who = chunkGL2AQFLQ_cjs.describeLayer(spec);
4004
+ const who = chunk3SAS3ZE5_cjs.describeLayer(spec);
3942
4005
  const raw = spec;
3943
4006
  if (raw.asset !== void 0) console.warn(`pixi-effects: ${who}: has both asset and music \u2014 the music plays; remove one`);
3944
4007
  if (raw.sfx !== void 0) console.warn(`pixi-effects: ${who}: has both sfx and music \u2014 the music plays; remove one`);
@@ -3973,7 +4036,7 @@ var AudioSequence = class extends Sequence {
3973
4036
  out.push({
3974
4037
  ...sourceMap ? { sourceMap } : {},
3975
4038
  ...this._audioBuffer ? { buffer: this._audioBuffer } : { synth: this._synth },
3976
- layer: chunkGL2AQFLQ_cjs.describeLayer(this.spec),
4039
+ layer: chunk3SAS3ZE5_cjs.describeLayer(this.spec),
3977
4040
  source: this._source,
3978
4041
  ...this._music ? { music: this._music } : {},
3979
4042
  loop: !this._synth && !!this.spec.loop,
@@ -4515,7 +4578,7 @@ var VideoSequence = class extends Sequence {
4515
4578
  out.push({
4516
4579
  ...sourceMap ? { sourceMap } : {},
4517
4580
  buffer: this._audioBuffer,
4518
- layer: chunkGL2AQFLQ_cjs.describeLayer(this.spec),
4581
+ layer: chunk3SAS3ZE5_cjs.describeLayer(this.spec),
4519
4582
  source: `video "${this.spec.asset}"`,
4520
4583
  loop: !!this.spec.loop,
4521
4584
  start: baseTime + this.at,
@@ -4663,6 +4726,21 @@ function slice(line, a, b) {
4663
4726
  }
4664
4727
  return out;
4665
4728
  }
4729
+ function splitEvenOdd(d) {
4730
+ const parts = d.split(/(?=[Mm])/).map((x) => x.trim()).filter(Boolean);
4731
+ if (parts.length < 2) return { main: d.trim(), holes: [] };
4732
+ const area = (sub2) => flattenSvgPath(sub2).reduce((sum, line) => {
4733
+ let a = 0;
4734
+ for (let i = 0; i < line.pts.length; i += 2) {
4735
+ const j = (i + 2) % line.pts.length;
4736
+ a += line.pts[i] * line.pts[j + 1] - line.pts[j] * line.pts[i + 1];
4737
+ }
4738
+ return sum + Math.abs(a) / 2;
4739
+ }, 0);
4740
+ const areas = parts.map(area);
4741
+ const big = areas.indexOf(Math.max(...areas));
4742
+ return { main: parts[big], holes: parts.filter((_, i) => i !== big) };
4743
+ }
4666
4744
 
4667
4745
  // src/sequences/morphPath.ts
4668
4746
  function resample(line, n) {
@@ -4725,6 +4803,11 @@ function buildMorph(from, to, n = 128) {
4725
4803
  for (let i = 0; i < count; i++) {
4726
4804
  const fa = from[i], fb = to[i];
4727
4805
  const closed = (fa?.closed ?? fb.closed) && (fb?.closed ?? fa.closed);
4806
+ if (fa && fb && fa.closed === fb.closed && fa.pts.length === fb.pts.length && fa.pts.length >= 6) {
4807
+ const a2 = [...fa.pts];
4808
+ pairs.push({ a: a2, b: closed ? align(a2, [...fb.pts]) : [...fb.pts], closed });
4809
+ continue;
4810
+ }
4728
4811
  const a = fa ? resample({ pts: fa.pts, closed }, n) : collapsed(resample({ pts: fb.pts, closed }, n));
4729
4812
  let b = fb ? resample({ pts: fb.pts, closed }, n) : collapsed(a);
4730
4813
  if (closed && fa && fb) b = align(a, b);
@@ -4783,20 +4866,20 @@ var ShapeSequence = class extends Sequence {
4783
4866
  for (const k of geomKeys) this._liveKeys.add(k);
4784
4867
  seedGeometry(this._state, this.spec, scope);
4785
4868
  seedStyle(this._state, this.spec.initial ?? {}, scope);
4786
- seedStrokeStyle(this._state, this.spec, this.spec.initial ?? {}, chunkGL2AQFLQ_cjs.describeLayer(this.spec));
4869
+ seedStrokeStyle(this._state, this.spec, this.spec.initial ?? {}, chunk3SAS3ZE5_cjs.describeLayer(this.spec));
4787
4870
  if (this.spec.shape === "arc") {
4788
4871
  this._state.startAngle ??= 0;
4789
4872
  this._state.endAngle ??= 360;
4790
4873
  const top = this.spec, ini = this.spec.initial ?? {};
4791
4874
  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`);
4875
+ console.warn(`pixi-effects: ${chunk3SAS3ZE5_cjs.describeLayer(this.spec)}: trimStart / trimEnd do not apply to an arc: animate its endAngle (and startAngle) instead`);
4793
4876
  }
4794
4877
  } else {
4795
4878
  this._state.trimStart ??= 0;
4796
4879
  this._state.trimEnd ??= 1;
4797
4880
  }
4798
4881
  const gradientSpec = this.spec.fillGradient ?? this.spec.initial?.fillGradient;
4799
- validateGradientKeyframes(this.spec, chunkGL2AQFLQ_cjs.describeLayer(this.spec));
4882
+ validateGradientKeyframes(this.spec, chunk3SAS3ZE5_cjs.describeLayer(this.spec));
4800
4883
  if (gradientSpec) {
4801
4884
  if (hasGradientKeys(this.spec.keyframes)) {
4802
4885
  this._grad = { grad: gradStateFrom(gradientSpec) };
@@ -4808,6 +4891,8 @@ var ShapeSequence = class extends Sequence {
4808
4891
  const morph = this._setupMorph();
4809
4892
  this._drawGeometry = makeDrawGeometry(this.spec, scope, this._state, morph);
4810
4893
  this._outline = makeOutline(this.spec, scope, this._state, morph);
4894
+ const eo = this.spec.shape === "path" && this.spec.fillRule === "evenodd" && !morph ? splitEvenOdd(this.spec.d) : null;
4895
+ this._evenOdd = eo && eo.holes.length > 0 ? eo : null;
4811
4896
  const { trimStart, trimEnd } = this._state;
4812
4897
  this._state.trimStart = 0;
4813
4898
  this._state.trimEnd = 1;
@@ -4838,9 +4923,9 @@ var ShapeSequence = class extends Sequence {
4838
4923
  }
4839
4924
  /** The prepared morph between `d` and `morphTo`, or null (and a warning when the spec asked for one that cannot work). */
4840
4925
  _setupMorph() {
4841
- const who = chunkGL2AQFLQ_cjs.describeLayer(this.spec);
4926
+ const who = chunk3SAS3ZE5_cjs.describeLayer(this.spec);
4842
4927
  const top = this.spec;
4843
- const wanted = this.spec.shape === "path" && (this._state.morph !== void 0 || chunkGL2AQFLQ_cjs.collectPropKeys(this.spec).has("morph"));
4928
+ const wanted = this.spec.shape === "path" && (this._state.morph !== void 0 || chunk3SAS3ZE5_cjs.collectPropKeys(this.spec).has("morph"));
4844
4929
  if (this.spec.shape !== "path") {
4845
4930
  if ("morphTo" in top) console.warn(`pixi-effects: ${who}: morphTo only works on a path shape (shape: 'path'); ignored`);
4846
4931
  return null;
@@ -4873,6 +4958,27 @@ var ShapeSequence = class extends Sequence {
4873
4958
  _painter = null;
4874
4959
  _gradDirty = false;
4875
4960
  _outline = null;
4961
+ /** fillRule 'evenodd' on a path: the shape and the sub-paths cut out of it after it is filled (null: not used). */
4962
+ _evenOdd = null;
4963
+ /**
4964
+ * 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
4965
+ * LAST instruction, so it has to come between the fill and the stroke.)
4966
+ */
4967
+ _fillWithHoles(g) {
4968
+ const s = this._state, { main, holes } = this._evenOdd;
4969
+ applyFill(g, s);
4970
+ for (const hole of holes) {
4971
+ g.beginPath();
4972
+ g.path(new PIXI.GraphicsPath(hole));
4973
+ g.cut();
4974
+ }
4975
+ if (s.strokeColor !== void 0 && s.strokeWidth > 0) {
4976
+ g.beginPath();
4977
+ g.path(new PIXI.GraphicsPath(main));
4978
+ for (const hole of holes) g.path(new PIXI.GraphicsPath(hole));
4979
+ applyStroke(g, s);
4980
+ }
4981
+ }
4876
4982
  _redraw() {
4877
4983
  const graphics = this.target;
4878
4984
  const s = this._state;
@@ -4884,6 +4990,10 @@ var ShapeSequence = class extends Sequence {
4884
4990
  const trimmed = (s.trimStart ?? 0) > 0 || (s.trimEnd ?? 1) < 1;
4885
4991
  if (!trimmed || !this._outline || !(s.strokeColor !== void 0 && s.strokeWidth > 0)) {
4886
4992
  this._drawGeometry(graphics);
4993
+ if (this._evenOdd) {
4994
+ this._fillWithHoles(graphics);
4995
+ return;
4996
+ }
4887
4997
  applyState(graphics, s);
4888
4998
  return;
4889
4999
  }
@@ -5107,8 +5217,9 @@ function makePolygonDraw(spec, scope) {
5107
5217
  };
5108
5218
  }
5109
5219
  function makePathDraw(spec) {
5220
+ const d = spec.fillRule === "evenodd" ? splitEvenOdd(spec.d).main : spec.d;
5110
5221
  return (g) => {
5111
- g.path(new PIXI.GraphicsPath(spec.d));
5222
+ g.path(new PIXI.GraphicsPath(d));
5112
5223
  };
5113
5224
  }
5114
5225
  var STATE_DRAWERS = {
@@ -5450,7 +5561,7 @@ var ShaderSequence = class extends Sequence {
5450
5561
  }
5451
5562
  async build() {
5452
5563
  const spec = this.shaderSpec;
5453
- const who = chunkGL2AQFLQ_cjs.describeLayer(spec);
5564
+ const who = chunk3SAS3ZE5_cjs.describeLayer(spec);
5454
5565
  for (const m of shaderProblems(spec)) console.warn(`pixi-effects: ${who}: ${m}`);
5455
5566
  if (this.duration === void 0) this.duration = this.parent?.duration ?? this.root.duration;
5456
5567
  const scope = buildScope(this, this.parent, this.root);
@@ -5494,7 +5605,7 @@ var ShaderSequence = class extends Sequence {
5494
5605
  const warn = (why) => {
5495
5606
  if (this.warnedPaths.has(path)) return;
5496
5607
  this.warnedPaths.add(path);
5497
- console.warn(`pixi-effects: ${chunkGL2AQFLQ_cjs.describeLayer(this.spec)}: keyframe path uniforms.${path} ${why}; skipped`);
5608
+ console.warn(`pixi-effects: ${chunk3SAS3ZE5_cjs.describeLayer(this.spec)}: keyframe path uniforms.${path} ${why}; skipped`);
5498
5609
  };
5499
5610
  if (slot === void 0) {
5500
5611
  const guess = chunkTWZNOUM7_cjs.suggestName(name, Object.keys(this.carrier));
@@ -5529,7 +5640,7 @@ var ShaderSequence = class extends Sequence {
5529
5640
  this.failed = true;
5530
5641
  if (!this.warnedNoGl) {
5531
5642
  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`);
5643
+ console.warn(`pixi-effects: ${chunk3SAS3ZE5_cjs.describeLayer(this.spec)}: this browser has no WebGL2, so the shader cannot run; the layer is drawn as a checkerboard`);
5533
5644
  }
5534
5645
  }
5535
5646
  compile() {
@@ -5560,7 +5671,7 @@ var ShaderSequence = class extends Sequence {
5560
5671
  gl.deleteProgram(p);
5561
5672
  this.failed = true;
5562
5673
  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):
5674
+ console.warn(`pixi-effects: ${chunk3SAS3ZE5_cjs.describeLayer(this.spec)}: the fragment shader did not compile (the line numbers are the lines of your code):
5564
5675
  ${glslErrors(log).join("\n")}
5565
5676
  The layer is drawn as a checkerboard until it is fixed.`);
5566
5677
  return;
@@ -5639,18 +5750,311 @@ function resolveDim(v, scope) {
5639
5750
  if (typeof v === "number") return v;
5640
5751
  return evaluateExpr(v, scope);
5641
5752
  }
5753
+ var MAX_SIZE = 8192;
5754
+ var MIN_RESOLUTION = 0.25;
5755
+ var MAX_RESOLUTION = 4;
5756
+ var NOT_PURE = /\bMath\s*\.\s*random\s*\(|\bDate\s*\.\s*now\s*\(|\bperformance\s*\.\s*now\s*\(|\bnew\s+Date\s*\(/;
5757
+ function seeded(seed) {
5758
+ let a = seed >>> 0;
5759
+ return () => {
5760
+ a = a + 1831565813 >>> 0;
5761
+ let t = a;
5762
+ t = Math.imul(t ^ t >>> 15, t | 1);
5763
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
5764
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
5765
+ };
5766
+ }
5767
+ function offscreen(width, height) {
5768
+ const c = document.createElement("canvas");
5769
+ c.width = Math.max(1, Math.round(width));
5770
+ c.height = Math.max(1, Math.round(height));
5771
+ c.getContext("2d", { willReadFrequently: true });
5772
+ return c;
5773
+ }
5774
+ var CanvasSequence = class extends Sequence {
5775
+ canvas = null;
5776
+ w = 1;
5777
+ h = 1;
5778
+ res = 1;
5779
+ fps = 30;
5780
+ layerW = 1;
5781
+ layerH = 1;
5782
+ seed = 0;
5783
+ smoothing = true;
5784
+ drawFn = null;
5785
+ state;
5786
+ /** The current value of every param: what keyframes move (`params.amount`). */
5787
+ carrier = {};
5788
+ failed = false;
5789
+ warnedDraw = false;
5790
+ warnedPaths = /* @__PURE__ */ new Set();
5791
+ lastKey = "";
5792
+ lastLocal = 0;
5793
+ get canvasSpec() {
5794
+ return this.spec;
5795
+ }
5796
+ pathRouters() {
5797
+ return { params: (path) => this.resolveParam(path) };
5798
+ }
5799
+ async build() {
5800
+ const spec = this.canvasSpec;
5801
+ const who = chunk3SAS3ZE5_cjs.describeLayer(spec);
5802
+ if (this.duration === void 0) this.duration = this.parent?.duration ?? this.root.duration;
5803
+ const scope = buildScope(this, this.parent, this.root);
5804
+ this.layerW = this.size("width", spec.width, scope, this.parent?.width ?? this.root.width, who);
5805
+ this.layerH = this.size("height", spec.height, scope, this.parent?.height ?? this.root.height, who);
5806
+ const r = spec.resolution;
5807
+ if (r === void 0) this.res = 1;
5808
+ else if (typeof r === "number" && Number.isFinite(r) && r >= MIN_RESOLUTION && r <= MAX_RESOLUTION) this.res = r;
5809
+ else {
5810
+ this.res = typeof r === "number" && Number.isFinite(r) ? Math.min(MAX_RESOLUTION, Math.max(MIN_RESOLUTION, r)) : 1;
5811
+ console.warn(`pixi-effects: ${who}: resolution must be a number from ${MIN_RESOLUTION} to ${MAX_RESOLUTION} (canvas pixels per layer pixel), got ${JSON.stringify(r)}: ${this.res} is used`);
5812
+ }
5813
+ const longest = Math.max(this.layerW, this.layerH);
5814
+ if (longest * this.res > MAX_SIZE) {
5815
+ const fitted = MAX_SIZE / longest;
5816
+ console.warn(`pixi-effects: ${who}: ${this.layerW}\xD7${this.layerH} at resolution ${this.res} would be a canvas of ${Math.round(longest * this.res)} pixels, more than ${MAX_SIZE}: resolution ${Math.round(fitted * 1e3) / 1e3} is used (lower the size or the resolution)`);
5817
+ this.res = fitted;
5818
+ }
5819
+ this.w = Math.max(1, Math.round(this.layerW * this.res));
5820
+ this.h = Math.max(1, Math.round(this.layerH * this.res));
5821
+ this.fps = this.root.frameRate ?? 30;
5822
+ this.seed = typeof spec.seed === "number" && Number.isFinite(spec.seed) ? Math.floor(spec.seed) : 0;
5823
+ this.smoothing = spec.smoothing !== false;
5824
+ const params = spec.params;
5825
+ if (params !== void 0) {
5826
+ if (params === null || typeof params !== "object" || Array.isArray(params)) {
5827
+ console.warn(`pixi-effects: ${who}: params must be an object of numbers, got ${JSON.stringify(params)}: ignored`);
5828
+ } else {
5829
+ const bad = [];
5830
+ for (const [k, v] of Object.entries(params)) {
5831
+ if (typeof v === "number" && Number.isFinite(v)) this.carrier[k] = v;
5832
+ else bad.push(`"${k}"`);
5833
+ }
5834
+ if (bad.length) console.warn(`pixi-effects: ${who}: params ${bad.join(", ")} ${bad.length > 1 ? "are" : "is"} not a number, so ${bad.length > 1 ? "they are" : "it is"} ignored (params holds numbers that keyframes can animate as 'params.<name>')`);
5835
+ }
5836
+ }
5837
+ if (typeof spec.draw === "function") {
5838
+ this.drawFn = spec.draw;
5839
+ if (NOT_PURE.test(spec.draw.toString())) {
5840
+ console.warn(`pixi-effects: ${who}: draw calls Math.random, Date.now, performance.now or new Date, so it is not deterministic: the same frame would draw differently in playback, in a seek and in an export. Use info.random() (seeded), or derive the value from t`);
5841
+ }
5842
+ } else {
5843
+ this.failed = true;
5844
+ console.warn(`pixi-effects: ${who}: a canvas layer needs draw: (ctx, t, info) => { \u2026 } (a function that draws the picture at time t); the layer is drawn as a checkerboard`);
5845
+ }
5846
+ this.canvas = document.createElement("canvas");
5847
+ this.canvas.width = this.w;
5848
+ this.canvas.height = this.h;
5849
+ const sprite = new PIXI.Sprite({ texture: PIXI.Texture.from(this.canvas), label: spec.name });
5850
+ if (!this.smoothing) sprite.texture.source.scaleMode = "nearest";
5851
+ sprite.width = this.layerW;
5852
+ sprite.height = this.layerH;
5853
+ this.target = sprite;
5854
+ this.intrinsicWidth = this.layerW;
5855
+ this.intrinsicHeight = this.layerH;
5856
+ this.buildFilters();
5857
+ if (typeof spec.setup === "function" && !this.failed) {
5858
+ try {
5859
+ this.state = await spec.setup(this.info(0));
5860
+ } catch (e) {
5861
+ this.failed = true;
5862
+ this.say(`setup failed: ${e instanceof Error ? e.message : String(e)}; the layer is drawn as a checkerboard`);
5863
+ }
5864
+ }
5865
+ this.drawAt(0);
5866
+ }
5867
+ async awaitFrameAt(local) {
5868
+ this.drawAt(Math.max(0, Math.min(local, this.duration ?? local)));
5869
+ }
5870
+ size(name, v, scope, fallback, who) {
5871
+ if (v === void 0) return fallback;
5872
+ const n = typeof v === "number" ? v : evaluateExpr(v, scope);
5873
+ if (!Number.isFinite(n) || n < 1) {
5874
+ console.warn(`pixi-effects: ${who}: ${name} must be a number of pixels of at least 1 (or an expression), got ${JSON.stringify(v)}: ${fallback} is used`);
5875
+ return fallback;
5876
+ }
5877
+ if (n > MAX_SIZE) {
5878
+ console.warn(`pixi-effects: ${who}: ${name} ${n} is more than ${MAX_SIZE}: ${MAX_SIZE} is used`);
5879
+ return MAX_SIZE;
5880
+ }
5881
+ return n;
5882
+ }
5883
+ say(why) {
5884
+ if (this.warnedDraw) return;
5885
+ this.warnedDraw = true;
5886
+ console.warn(`pixi-effects: ${chunk3SAS3ZE5_cjs.describeLayer(this.spec)}: ${why}`);
5887
+ }
5888
+ info(local) {
5889
+ const duration = this.duration ?? 0;
5890
+ const start = this.absoluteStart ?? this.at;
5891
+ return {
5892
+ frame: Math.round(local * this.fps),
5893
+ duration,
5894
+ progress: duration > 0 ? local / duration : 0,
5895
+ width: this.layerW,
5896
+ height: this.layerH,
5897
+ resolution: this.res,
5898
+ frameRate: this.fps,
5899
+ params: { ...this.carrier },
5900
+ state: this.state,
5901
+ random: seeded(this.seed),
5902
+ start,
5903
+ time: start + local,
5904
+ canvas: offscreen
5905
+ };
5906
+ }
5907
+ /** Resolve `params.amount` for the keyframe pipeline; a name nobody declared is said once and dropped. */
5908
+ resolveParam(path) {
5909
+ if (path in this.carrier && !path.includes(".")) return { target: this.carrier, prop: path };
5910
+ if (!this.warnedPaths.has(path)) {
5911
+ this.warnedPaths.add(path);
5912
+ const guess = chunkTWZNOUM7_cjs.suggestName(path.split(".")[0], Object.keys(this.carrier));
5913
+ console.warn(`pixi-effects: ${chunk3SAS3ZE5_cjs.describeLayer(this.spec)}: keyframe path params.${path} names no param "${path}"${guess ? `; did you mean "${guess}"?` : ""} (declare it in params with a starting value); skipped`);
5914
+ }
5915
+ return null;
5916
+ }
5917
+ drawAt(local) {
5918
+ const c = this.canvas;
5919
+ if (!c) return;
5920
+ const ctx = c.getContext("2d");
5921
+ if (!ctx) {
5922
+ this.say("this browser gave no 2D context for the layer's canvas, so nothing can be drawn");
5923
+ return;
5924
+ }
5925
+ const key = `${local}|${this.absoluteStart ?? this.at}|${JSON.stringify(this.carrier)}`;
5926
+ if (key === this.lastKey) return;
5927
+ this.lastKey = key;
5928
+ this.lastLocal = local;
5929
+ if (!this.paint(ctx, c, local)) this.lastKey = "";
5930
+ this.target?.texture.source.update();
5931
+ }
5932
+ /** Draws one frame of the layer into `ctx` (the layer's own canvas, or the scratch one of the determinism check). false: `draw` threw. */
5933
+ paint(ctx, c, local) {
5934
+ const hasReset = "reset" in ctx && typeof ctx.reset === "function";
5935
+ const fresh = () => {
5936
+ if (hasReset) ctx.reset();
5937
+ else {
5938
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
5939
+ ctx.clearRect(0, 0, c.width, c.height);
5940
+ }
5941
+ };
5942
+ fresh();
5943
+ if (this.failed || !this.drawFn) {
5944
+ this.checkerboard(ctx, c);
5945
+ return true;
5946
+ }
5947
+ if (!hasReset) ctx.save();
5948
+ let ok = true;
5949
+ try {
5950
+ ctx.scale(this.res, this.res);
5951
+ ctx.imageSmoothingEnabled = this.smoothing;
5952
+ this.drawFn(ctx, local, this.info(local));
5953
+ } catch (e) {
5954
+ if (!hasReset) {
5955
+ ctx.restore();
5956
+ ctx.save();
5957
+ }
5958
+ fresh();
5959
+ this.say(`draw threw at ${local.toFixed(2)} s: ${e instanceof Error ? e.message : String(e)}; that frame is drawn as a checkerboard (the other frames are tried again)`);
5960
+ this.checkerboard(ctx, c);
5961
+ ok = false;
5962
+ }
5963
+ if (!hasReset) ctx.restore();
5964
+ return ok;
5965
+ }
5966
+ /**
5967
+ * Draws each of `times` in two orders and returns the moments where the pictures differ (a draw that uses Math.random, the clock or state that outlives the call), with how much
5968
+ * (channel values that differ, the largest difference out of 255), and how long a draw took. One draw of every moment comes first and is thrown away: a browser can draw a canvas
5969
+ * made in `setup` differently the first time it is used as a source, and a cache a draw fills on its first call is fine. The layer's own canvas is not touched (everything is drawn in a scratch canvas).
5970
+ * A layer that failed, or a draw that throws, is reported in `threw`.
5971
+ */
5972
+ checkDeterminism(times) {
5973
+ const none = { differs: [], drawMs: 0, threw: [] };
5974
+ if (!this.canvas) return none;
5975
+ if (this.failed || !this.drawFn) return { ...none, threw: [...times] };
5976
+ const scratch = document.createElement("canvas");
5977
+ scratch.width = this.w;
5978
+ scratch.height = this.h;
5979
+ const ctx = scratch.getContext("2d", { willReadFrequently: true });
5980
+ if (!ctx) return none;
5981
+ const keep = this.w * this.h * 4 * times.length <= 96e6;
5982
+ const read2 = () => ctx.getImageData(0, 0, scratch.width, scratch.height).data;
5983
+ const hash = (d) => {
5984
+ let h = 2166136261;
5985
+ for (let i = 0; i < d.length; i++) {
5986
+ h ^= d[i];
5987
+ h = Math.imul(h, 16777619);
5988
+ }
5989
+ return h >>> 0;
5990
+ };
5991
+ const ms = [];
5992
+ const threw = /* @__PURE__ */ new Set();
5993
+ const once = (t, timed) => {
5994
+ const from = performance.now();
5995
+ if (!this.paint(ctx, scratch, t)) threw.add(t);
5996
+ if (timed) {
5997
+ ctx.getImageData(0, 0, 1, 1);
5998
+ ms.push(performance.now() - from);
5999
+ }
6000
+ return read2();
6001
+ };
6002
+ for (const t of times) once(t, false);
6003
+ const first = times.map((t) => {
6004
+ const d = once(t, true);
6005
+ return keep ? d.slice() : hash(d);
6006
+ });
6007
+ const second = [...times].reverse().map((t) => once(t, false)).reverse();
6008
+ scratch.width = scratch.height = 1;
6009
+ const differs = [];
6010
+ times.forEach((t, i) => {
6011
+ const a = first[i], b = second[i];
6012
+ if (typeof a === "number") {
6013
+ if (a !== hash(b)) differs.push({ t, pixels: -1, maxDiff: -1 });
6014
+ return;
6015
+ }
6016
+ let pixels = 0, maxDiff = 0;
6017
+ for (let k = 0; k < a.length; k++) {
6018
+ const d = Math.abs(a[k] - b[k]);
6019
+ if (d) {
6020
+ pixels++;
6021
+ if (d > maxDiff) maxDiff = d;
6022
+ }
6023
+ }
6024
+ if (pixels) differs.push({ t, pixels, maxDiff });
6025
+ });
6026
+ ms.sort((x, y) => x - y);
6027
+ return { differs: differs.filter((d) => !threw.has(d.t)), drawMs: ms.length ? ms[Math.floor(ms.length / 2)] : 0, threw: times.filter((t) => threw.has(t)) };
6028
+ }
6029
+ checkerboard(ctx, c) {
6030
+ const s = 32;
6031
+ for (let y = 0; y < c.height; y += s) for (let x = 0; x < c.width; x += s) {
6032
+ ctx.fillStyle = x / s + y / s & 1 ? "#ff00ff" : "#220022";
6033
+ ctx.fillRect(x, y, s, s);
6034
+ }
6035
+ }
6036
+ destroy() {
6037
+ const texture = this.target?.texture;
6038
+ if (this.canvas) {
6039
+ this.canvas.width = this.canvas.height = 1;
6040
+ this.canvas = null;
6041
+ }
6042
+ super.destroy();
6043
+ texture?.destroy(true);
6044
+ }
6045
+ };
5642
6046
 
5643
6047
  // src/core/Composition.ts
5644
6048
  var _CompositionSequence = null;
5645
6049
  async function getCompositionSequence() {
5646
6050
  if (!_CompositionSequence) {
5647
- _CompositionSequence = (await import('./Composition-OLH7RC5Z.cjs')).CompositionSequence;
6051
+ _CompositionSequence = (await import('./Composition-MHDI7UNZ.cjs')).CompositionSequence;
5648
6052
  }
5649
6053
  return _CompositionSequence;
5650
6054
  }
5651
6055
  var _SpansSequence = null;
5652
6056
  async function getSpansSequence() {
5653
- if (!_SpansSequence) _SpansSequence = (await import('./Spans-QRMN2EZO.cjs')).SpansSequence;
6057
+ if (!_SpansSequence) _SpansSequence = (await import('./Spans-WVJH77BW.cjs')).SpansSequence;
5654
6058
  return _SpansSequence;
5655
6059
  }
5656
6060
  var staticTypes = {
@@ -5662,6 +6066,7 @@ var staticTypes = {
5662
6066
  camera: CameraSequence,
5663
6067
  light: LightSequence,
5664
6068
  shader: ShaderSequence,
6069
+ canvas: CanvasSequence,
5665
6070
  null: NullSequence
5666
6071
  };
5667
6072
  var registry2 = {};
@@ -5734,6 +6139,7 @@ exports.layerToWorld = layerToWorld;
5734
6139
  exports.lengthen = lengthen;
5735
6140
  exports.lightLevel = lightLevel;
5736
6141
  exports.noise = noise;
6142
+ exports.noise2 = noise2;
5737
6143
  exports.packLights = packLights;
5738
6144
  exports.parseEase = parseEase;
5739
6145
  exports.parseSpring = parseSpring;
@@ -5748,5 +6154,6 @@ exports.resolveNumber = resolveNumber;
5748
6154
  exports.spansMeta = spansMeta;
5749
6155
  exports.springDuration = springDuration;
5750
6156
  exports.timeRemapOf = timeRemapOf;
5751
- //# sourceMappingURL=chunk-LEECUCTG.cjs.map
5752
- //# sourceMappingURL=chunk-LEECUCTG.cjs.map
6157
+ exports.trimPolylines = trimPolylines;
6158
+ //# sourceMappingURL=chunk-QHIOOK5S.cjs.map
6159
+ //# sourceMappingURL=chunk-QHIOOK5S.cjs.map