pixi-effects 0.47.0 → 0.47.2

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 (69) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/render.mjs +8 -1
  5. package/dist/Composition-X6MCEFRH.cjs +16 -0
  6. package/dist/{Composition-PME3P5LP.cjs.map → Composition-X6MCEFRH.cjs.map} +1 -1
  7. package/dist/Composition-ZYMQ7ZRC.js +7 -0
  8. package/dist/{Composition-NSY7A32Y.js.map → Composition-ZYMQ7ZRC.js.map} +1 -1
  9. package/dist/{Spans-2TZCNREQ.cjs → Spans-6HSLKR5Q.cjs} +11 -11
  10. package/dist/{Spans-2TZCNREQ.cjs.map → Spans-6HSLKR5Q.cjs.map} +1 -1
  11. package/dist/{Spans-TESJPQKX.js → Spans-LCCZ4PW7.js} +6 -6
  12. package/dist/{Spans-TESJPQKX.js.map → Spans-LCCZ4PW7.js.map} +1 -1
  13. package/dist/{blendModes-FMYO6SQN.js → blendModes-EMANEHZS.js} +4 -4
  14. package/dist/{blendModes-FMYO6SQN.js.map → blendModes-EMANEHZS.js.map} +1 -1
  15. package/dist/{blendModes-FD3THLNC.cjs → blendModes-JUISKPCA.cjs} +5 -5
  16. package/dist/{blendModes-FD3THLNC.cjs.map → blendModes-JUISKPCA.cjs.map} +1 -1
  17. package/dist/{chunk-3SAS3ZE5.cjs → chunk-2YV7ISX6.cjs} +11 -2
  18. package/dist/chunk-2YV7ISX6.cjs.map +1 -0
  19. package/dist/{chunk-DKCMJILW.cjs → chunk-4RC4GXEM.cjs} +92 -92
  20. package/dist/{chunk-DKCMJILW.cjs.map → chunk-4RC4GXEM.cjs.map} +1 -1
  21. package/dist/{chunk-FPV3NPYC.js → chunk-AOQRB6AU.js} +5 -5
  22. package/dist/{chunk-FPV3NPYC.js.map → chunk-AOQRB6AU.js.map} +1 -1
  23. package/dist/{chunk-JN7JHNZR.cjs → chunk-DQVUKP5G.cjs} +5 -5
  24. package/dist/{chunk-JN7JHNZR.cjs.map → chunk-DQVUKP5G.cjs.map} +1 -1
  25. package/dist/{chunk-4SEL6W7H.js → chunk-G4PXCV2B.js} +13 -9
  26. package/dist/chunk-G4PXCV2B.js.map +1 -0
  27. package/dist/{chunk-W46VWAZC.js → chunk-M5P7TXZ2.js} +135 -63
  28. package/dist/chunk-M5P7TXZ2.js.map +1 -0
  29. package/dist/{chunk-32MMNDDN.js → chunk-P2P7C2KZ.js} +11 -2
  30. package/dist/chunk-P2P7C2KZ.js.map +1 -0
  31. package/dist/{chunk-VNP7QCJE.js → chunk-R43646JP.js} +4 -4
  32. package/dist/{chunk-VNP7QCJE.js.map → chunk-R43646JP.js.map} +1 -1
  33. package/dist/{chunk-LALEBV63.cjs → chunk-TVVC4GVK.cjs} +29 -29
  34. package/dist/{chunk-LALEBV63.cjs.map → chunk-TVVC4GVK.cjs.map} +1 -1
  35. package/dist/{chunk-I6VUQ2CR.cjs → chunk-UXPTDNNA.cjs} +181 -109
  36. package/dist/chunk-UXPTDNNA.cjs.map +1 -0
  37. package/dist/{chunk-DM2OLQOJ.js → chunk-YF4JC2AL.js} +5 -5
  38. package/dist/{chunk-DM2OLQOJ.js.map → chunk-YF4JC2AL.js.map} +1 -1
  39. package/dist/{chunk-23SXKSUI.cjs → chunk-ZAHCGFGC.cjs} +13 -9
  40. package/dist/chunk-ZAHCGFGC.cjs.map +1 -0
  41. package/dist/index.cjs +37 -37
  42. package/dist/index.d.cts +4 -4
  43. package/dist/index.d.ts +4 -4
  44. package/dist/index.js +6 -6
  45. package/dist/loader.css +1 -1
  46. package/dist/present.cjs +6 -6
  47. package/dist/present.js +6 -6
  48. package/dist/sound.cjs +2 -2
  49. package/dist/sound.js +1 -1
  50. package/dist/three.cjs +93 -26
  51. package/dist/three.cjs.map +1 -1
  52. package/dist/three.d.cts +19 -4
  53. package/dist/three.d.ts +19 -4
  54. package/dist/three.js +89 -22
  55. package/dist/three.js.map +1 -1
  56. package/docs/dsl.md +7 -2
  57. package/llms-full.txt +10 -4
  58. package/package.json +1 -1
  59. package/skills/pixi-effects/SKILL.md +2 -1
  60. package/skills/pixi-effects/reference/recipes.md +1 -1
  61. package/skills/pixi-effects/template.html +6 -6
  62. package/dist/Composition-NSY7A32Y.js +0 -7
  63. package/dist/Composition-PME3P5LP.cjs +0 -16
  64. package/dist/chunk-23SXKSUI.cjs.map +0 -1
  65. package/dist/chunk-32MMNDDN.js.map +0 -1
  66. package/dist/chunk-3SAS3ZE5.cjs.map +0 -1
  67. package/dist/chunk-4SEL6W7H.js.map +0 -1
  68. package/dist/chunk-I6VUQ2CR.cjs.map +0 -1
  69. package/dist/chunk-W46VWAZC.js.map +0 -1
package/dist/three.d.cts CHANGED
@@ -49,14 +49,20 @@ interface ThreeLayerHost {
49
49
  shared: boolean;
50
50
  /** Run `fn` just before every draw of the three layer; returns the function that stops it. */
51
51
  beforeDraw(fn: () => void): () => void;
52
- /** Remember a handle: a rebuild that keeps the three layer gives it the new tree's layer (`rebind`). */
53
- track(handle: Rebindable): void;
52
+ /** Remember a handle: a rebuild that keeps the three layer gives it the new tree's layer (`prepare`). */
53
+ track(handle: Rebindable): () => void;
54
54
  }
55
55
  /** What a rebuild that keeps the three layer needs: show the layer of the new tree with the same name. */
56
+ /** `prepare` looks for the layer in the new tree and throws if it is gone, but changes nothing; the function it returns puts the handle on the new tree's layer (called when that tree is on stage, so a rebuild that fails or is slow leaves the old movie showing its own screen). */
56
57
  interface Rebindable {
57
58
  material: Material;
58
- source(): Sequence;
59
- rebind(host: ThreeLayerHost): void;
59
+ source(): Sequence; /** Where in the source (0..1 across, 0..1 down) a point (u, v) of the mesh's uv shows: the texture's own repeat / offset / rotation taken into account. */
60
+ sourceAt(u: number, v: number): {
61
+ x: number;
62
+ y: number;
63
+ };
64
+ prepare(host: ThreeLayerHost): () => void;
65
+ keepHidden(): void;
60
66
  dispose(): void;
61
67
  }
62
68
  declare function threeLayerTexture(ctx: ThreeContext, name: string, options?: ThreeLayerTextureOptions): Promise<ThreeLayerTexture>;
@@ -171,6 +177,11 @@ declare class ThreeSequence extends Sequence {
171
177
  /** Things to do just before every draw (the 2D layers shown on meshes: `threeLayerTexture`), and those layers' handles. Both go with the state to a layer that takes it over. */
172
178
  private _drawHooks;
173
179
  private _layerTextures;
180
+ private _pendingRebinds;
181
+ /** Every frame, whether this layer is alive or not: a 2D layer it shows stays off the stage (a tween of `autoAlpha` writes `visible = true` on it). */
182
+ syncFrame(): void;
183
+ /** True when this layer shows 2D layers on its meshes (`threeLayerTexture`): the movie draws it after the rest of the frame is ready. */
184
+ get showsLayers(): boolean;
174
185
  private _scene;
175
186
  private _camera;
176
187
  private _ctx;
@@ -191,6 +202,10 @@ declare class ThreeSequence extends Sequence {
191
202
  /** The 2D layer that a material shows (`threeLayerTexture`), for a click on the mesh to reach it (movie.pick); null for any other material. */
192
203
  screenOf(material: unknown): {
193
204
  source: Sequence;
205
+ at(u: number, v: number): {
206
+ x: number;
207
+ y: number;
208
+ };
194
209
  } | null;
195
210
  /** What `threeLayerTexture` asks of this layer: the layers listed before it, PixiJS's renderer, whether the GPU device is shared, and a place to hook in before every draw. */
196
211
  private _makeHost;
package/dist/three.d.ts CHANGED
@@ -49,14 +49,20 @@ interface ThreeLayerHost {
49
49
  shared: boolean;
50
50
  /** Run `fn` just before every draw of the three layer; returns the function that stops it. */
51
51
  beforeDraw(fn: () => void): () => void;
52
- /** Remember a handle: a rebuild that keeps the three layer gives it the new tree's layer (`rebind`). */
53
- track(handle: Rebindable): void;
52
+ /** Remember a handle: a rebuild that keeps the three layer gives it the new tree's layer (`prepare`). */
53
+ track(handle: Rebindable): () => void;
54
54
  }
55
55
  /** What a rebuild that keeps the three layer needs: show the layer of the new tree with the same name. */
56
+ /** `prepare` looks for the layer in the new tree and throws if it is gone, but changes nothing; the function it returns puts the handle on the new tree's layer (called when that tree is on stage, so a rebuild that fails or is slow leaves the old movie showing its own screen). */
56
57
  interface Rebindable {
57
58
  material: Material;
58
- source(): Sequence;
59
- rebind(host: ThreeLayerHost): void;
59
+ source(): Sequence; /** Where in the source (0..1 across, 0..1 down) a point (u, v) of the mesh's uv shows: the texture's own repeat / offset / rotation taken into account. */
60
+ sourceAt(u: number, v: number): {
61
+ x: number;
62
+ y: number;
63
+ };
64
+ prepare(host: ThreeLayerHost): () => void;
65
+ keepHidden(): void;
60
66
  dispose(): void;
61
67
  }
62
68
  declare function threeLayerTexture(ctx: ThreeContext, name: string, options?: ThreeLayerTextureOptions): Promise<ThreeLayerTexture>;
@@ -171,6 +177,11 @@ declare class ThreeSequence extends Sequence {
171
177
  /** Things to do just before every draw (the 2D layers shown on meshes: `threeLayerTexture`), and those layers' handles. Both go with the state to a layer that takes it over. */
172
178
  private _drawHooks;
173
179
  private _layerTextures;
180
+ private _pendingRebinds;
181
+ /** Every frame, whether this layer is alive or not: a 2D layer it shows stays off the stage (a tween of `autoAlpha` writes `visible = true` on it). */
182
+ syncFrame(): void;
183
+ /** True when this layer shows 2D layers on its meshes (`threeLayerTexture`): the movie draws it after the rest of the frame is ready. */
184
+ get showsLayers(): boolean;
174
185
  private _scene;
175
186
  private _camera;
176
187
  private _ctx;
@@ -191,6 +202,10 @@ declare class ThreeSequence extends Sequence {
191
202
  /** The 2D layer that a material shows (`threeLayerTexture`), for a click on the mesh to reach it (movie.pick); null for any other material. */
192
203
  screenOf(material: unknown): {
193
204
  source: Sequence;
205
+ at(u: number, v: number): {
206
+ x: number;
207
+ y: number;
208
+ };
194
209
  } | null;
195
210
  /** What `threeLayerTexture` asks of this layer: the layers listed before it, PixiJS's renderer, whether the GPU device is shared, and a place to hook in before every draw. */
196
211
  private _makeHost;
package/dist/three.js CHANGED
@@ -1,5 +1,5 @@
1
- import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-DM2OLQOJ.js';
2
- import './chunk-32MMNDDN.js';
1
+ import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-YF4JC2AL.js';
2
+ import './chunk-P2P7C2KZ.js';
3
3
  import './chunk-BXOCEZ4U.js';
4
4
  import { Texture, Sprite, TextureSource, RenderTexture, Matrix } from 'pixi.js';
5
5
  import { WebGLRenderer, Scene, PerspectiveCamera, Raycaster, Vector2, Vector3, Quaternion, Plane, Box3, RenderTarget, Group, AnimationMixer, LoopOnce, LoopRepeat, AmbientLight, DirectionalLight, SpotLight, PointLight, Color, SRGBColorSpace, Fog } from 'three';
@@ -192,6 +192,16 @@ var ThreeSequence = class extends Sequence {
192
192
  /** Things to do just before every draw (the 2D layers shown on meshes: `threeLayerTexture`), and those layers' handles. Both go with the state to a layer that takes it over. */
193
193
  _drawHooks = /* @__PURE__ */ new Set();
194
194
  _layerTextures = [];
195
+ _pendingRebinds = [];
196
+ /** Every frame, whether this layer is alive or not: a 2D layer it shows stays off the stage (a tween of `autoAlpha` writes `visible = true` on it). */
197
+ syncFrame() {
198
+ super.syncFrame();
199
+ for (const h of this._layerTextures) h.keepHidden();
200
+ }
201
+ /** True when this layer shows 2D layers on its meshes (`threeLayerTexture`): the movie draws it after the rest of the frame is ready. */
202
+ get showsLayers() {
203
+ return this._layerTextures.length > 0;
204
+ }
195
205
  _scene = null;
196
206
  _camera = null;
197
207
  _ctx = null;
@@ -326,7 +336,7 @@ var ThreeSequence = class extends Sequence {
326
336
  /** The 2D layer that a material shows (`threeLayerTexture`), for a click on the mesh to reach it (movie.pick); null for any other material. */
327
337
  screenOf(material) {
328
338
  const handle = this._layerTextures.find((h) => h.material === material);
329
- return handle ? { source: handle.source() } : null;
339
+ return handle ? { source: handle.source(), at: (u, v) => handle.sourceAt(u, v) } : null;
330
340
  }
331
341
  /** What `threeLayerTexture` asks of this layer: the layers listed before it, PixiJS's renderer, whether the GPU device is shared, and a place to hook in before every draw. */
332
342
  _makeHost() {
@@ -343,6 +353,10 @@ var ThreeSequence = class extends Sequence {
343
353
  },
344
354
  track: (handle) => {
345
355
  this._layerTextures.push(handle);
356
+ return () => {
357
+ const i = this._layerTextures.indexOf(handle);
358
+ if (i >= 0) this._layerTextures.splice(i, 1);
359
+ };
346
360
  }
347
361
  };
348
362
  }
@@ -374,7 +388,7 @@ var ThreeSequence = class extends Sequence {
374
388
  this._drawHooks = d._drawHooks;
375
389
  this._layerTextures = d._layerTextures;
376
390
  const host = this._makeHost();
377
- for (const h of this._layerTextures) h.rebind(host);
391
+ this._pendingRebinds = this._layerTextures.map((h) => h.prepare(host));
378
392
  if (this.spec && this._threeSpec.followCamera) this._checkFollowShape(width, height, name);
379
393
  this._makeSprite(this._texture, width, height, name);
380
394
  }
@@ -385,6 +399,8 @@ var ThreeSequence = class extends Sequence {
385
399
  d._donated = true;
386
400
  d._tracked = false;
387
401
  this._borrowed = null;
402
+ for (const apply of this._pendingRebinds) apply();
403
+ this._pendingRebinds = [];
388
404
  }
389
405
  /**
390
406
  * Per-frame sync hook. Movie._awaitVideoFrames duck-types on this method
@@ -897,18 +913,39 @@ async function threeLayerTexture(ctx, name, options = {}) {
897
913
  if (source.spec.type !== "composition") {
898
914
  throw new Error(`${label}: "${name}" is a ${source.spec.type} layer: a layer shown on a mesh must be a composition (wrap it: { type: 'composition', name: '${name}', width: 480, height: 270, sequences: [ \u2026 ] })`);
899
915
  }
900
- const width = Math.max(1, Math.round(source.intrinsicWidth * resolution)), height = Math.max(1, Math.round(source.intrinsicHeight * resolution));
901
- const sharedRoute = host.shared && pixi.name === "webgpu";
902
- const rt = RenderTexture.create({ width, height, resolution: 1, ...sharedRoute ? { autoGenerateMipmaps: true } : {} });
903
- const gpuTexture = sharedRoute ? pixi.texture.getGpuSource?.(rt.source) : void 0;
904
- let texture, material, readCanvas = null;
905
- if (gpuTexture) {
906
- let webgpu, tsl;
916
+ {
917
+ const ini = source.spec.initial ?? {};
918
+ const moved = ["x", "y", "rotation", "pivotX", "pivotY"].some((k) => typeof ini[k] === "number" && ini[k] !== 0) || ["scale", "scaleX", "scaleY"].some((k) => typeof ini[k] === "number" && ini[k] !== 1);
919
+ if (moved && (source.maskSequence || source.spec.mask !== void 0)) {
920
+ console.warn(`${label}: "${name}" has a mask and is not at x: 0, y: 0 (anchor 0, scale 1, rotation 0): on the mesh its content is drawn from (0, 0) but its mask stays where the parent puts it, so the mask lands shifted. Put the screen at the origin (or mask a layer INSIDE the screen: that mask moves with it).`);
921
+ }
922
+ }
923
+ const logicalW = Math.max(1, Math.round(source.intrinsicWidth)), logicalH = Math.max(1, Math.round(source.intrinsicHeight));
924
+ const width = Math.max(1, Math.round(logicalW * resolution)), height = Math.max(1, Math.round(logicalH * resolution));
925
+ const limit = (() => {
926
+ const g = pixi;
927
+ return g.gpu?.device?.limits?.maxTextureDimension2D ?? (g.gl ? g.gl.getParameter(g.gl.MAX_TEXTURE_SIZE) : null) ?? 8192;
928
+ })();
929
+ if (width > limit || height > limit) {
930
+ throw new Error(`${label}: the texture would be ${width} x ${height} pixels (the layer is ${logicalW} x ${logicalH}, resolution ${resolution}), above what this GPU allows (${limit}). Use resolution ${Math.max(1, Math.floor(limit / Math.max(logicalW, logicalH) * 10) / 10)} or lower, or a smaller composition.`);
931
+ }
932
+ let sharedRoute = host.shared && pixi.name === "webgpu";
933
+ let webgpu, tsl;
934
+ if (sharedRoute) {
907
935
  try {
908
936
  [webgpu, tsl] = await Promise.all([import('three/webgpu'), import('three/tsl')]);
909
937
  } catch (err) {
910
938
  throw new Error(`${label}: needs "three/webgpu" and "three/tsl" in the import map (${err instanceof Error ? err.message : err})`);
911
939
  }
940
+ if (typeof webgpu.ExternalTexture !== "function") {
941
+ console.warn(`${label}: this page's three has no ExternalTexture (it came with three 0.180): the screen is read back every frame (1.6 ms at 480 x 270) instead of shared with the GPU. Use three 0.180 or later for the no-copy route.`);
942
+ sharedRoute = false;
943
+ }
944
+ }
945
+ const rt = RenderTexture.create({ width: logicalW, height: logicalH, resolution, ...sharedRoute ? { autoGenerateMipmaps: true } : {} });
946
+ const gpuTexture = sharedRoute ? pixi.texture.getGpuSource?.(rt.source) : void 0;
947
+ let texture, material, readCanvas = null;
948
+ if (gpuTexture) {
912
949
  const ext = new webgpu.ExternalTexture(gpuTexture);
913
950
  ext.colorSpace = webgpu.NoColorSpace;
914
951
  ext.minFilter = webgpu.LinearMipmapLinearFilter;
@@ -941,7 +978,7 @@ async function threeLayerTexture(ctx, name, options = {}) {
941
978
  }
942
979
  let current = source;
943
980
  let display = source.target;
944
- let sx = width / source.intrinsicWidth, sy = height / source.intrinsicHeight;
981
+ let sx = logicalW / source.intrinsicWidth, sy = logicalH / source.intrinsicHeight;
945
982
  let hidden = false;
946
983
  const hide = () => {
947
984
  display.visible = false;
@@ -977,30 +1014,54 @@ async function threeLayerTexture(ctx, name, options = {}) {
977
1014
  }
978
1015
  g.putImageData(new ImageData(pixels, w, h), 0, 0);
979
1016
  } else {
980
- g.clearRect(0, 0, width, height);
981
- g.drawImage(pixi.extract.canvas(rt), 0, 0);
1017
+ if (pixi.name === "webgl") {
1018
+ const { pixels, width: w, height: h } = pixi.extract.pixels(rt);
1019
+ for (let i = 3; i < pixels.length; i += 4) pixels[i] = 255;
1020
+ g.putImageData(new ImageData(pixels, w, h), 0, 0);
1021
+ } else {
1022
+ g.globalCompositeOperation = "source-over";
1023
+ g.fillStyle = "#000";
1024
+ g.fillRect(0, 0, width, height);
1025
+ g.drawImage(pixi.extract.canvas(rt), 0, 0);
1026
+ }
982
1027
  }
983
1028
  texture.needsUpdate = true;
984
1029
  }
985
1030
  };
986
1031
  const stop = host.beforeDraw(refresh);
987
1032
  refresh();
1033
+ let disposed = false;
988
1034
  const handle = {
989
1035
  material,
990
1036
  texture,
991
1037
  width,
992
1038
  height,
993
1039
  source: () => current,
994
- rebind(next) {
1040
+ sourceAt(u, v) {
1041
+ const t = texture;
1042
+ t.updateMatrix?.();
1043
+ const e = t.matrix?.elements;
1044
+ const tu = e ? e[0] * u + e[3] * v + e[6] : u, tv = e ? e[1] * u + e[4] * v + e[7] : v;
1045
+ return { x: tu, y: gpuTexture ? tv : 1 - tv };
1046
+ },
1047
+ prepare(next) {
995
1048
  const again = next.findLayer(name);
1049
+ if (disposed) return () => {
1050
+ };
996
1051
  if (!again) throw new Error(`${label}: after a rebuild there is no layer "${name}" before the three layer any more`);
997
- current = again;
998
- display = again.target;
999
- sx = width / again.intrinsicWidth;
1000
- sy = height / again.intrinsicHeight;
1001
- hide();
1002
- refresh();
1052
+ return () => {
1053
+ current = again;
1054
+ display = again.target;
1055
+ sx = logicalW / again.intrinsicWidth;
1056
+ sy = logicalH / again.intrinsicHeight;
1057
+ hide();
1058
+ refresh();
1059
+ };
1060
+ },
1061
+ keepHidden() {
1062
+ if (hidden && !disposed) display.visible = false;
1003
1063
  },
1064
+ // (the timeline can put it back: autoAlpha writes `visible`)
1004
1065
  dispose() {
1005
1066
  stop();
1006
1067
  if (hidden) {
@@ -1015,7 +1076,13 @@ async function threeLayerTexture(ctx, name, options = {}) {
1015
1076
  material.dispose?.();
1016
1077
  }
1017
1078
  };
1018
- host.track(handle);
1079
+ const untrack = host.track(handle);
1080
+ handle.dispose = /* @__PURE__ */ ((inner) => () => {
1081
+ if (disposed) return;
1082
+ disposed = true;
1083
+ untrack();
1084
+ inner();
1085
+ })(handle.dispose);
1019
1086
  return handle;
1020
1087
  }
1021
1088