pixi-effects 0.50.1 → 0.51.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 (80) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +6 -6
  3. package/ai/chat-template.html +4 -4
  4. package/ai/tools/check.mjs +10 -4
  5. package/dist/3d.cjs +37 -12
  6. package/dist/3d.cjs.map +1 -1
  7. package/dist/3d.d.cts +12 -1
  8. package/dist/3d.d.ts +12 -1
  9. package/dist/3d.js +28 -3
  10. package/dist/3d.js.map +1 -1
  11. package/dist/Composition-WNQOIRX7.js +8 -0
  12. package/dist/{Composition-7D6ERUVY.js.map → Composition-WNQOIRX7.js.map} +1 -1
  13. package/dist/Composition-Z2EGCGXV.cjs +17 -0
  14. package/dist/{Composition-5SBIDTBB.cjs.map → Composition-Z2EGCGXV.cjs.map} +1 -1
  15. package/dist/Controller.d.cts +2 -2
  16. package/dist/Controller.d.ts +2 -2
  17. package/dist/{Movie-Y7iHg2V8.d.ts → Movie-CbGxOdtH.d.ts} +10 -1
  18. package/dist/{Movie-DogzDBuW.d.cts → Movie-uuiyZPyi.d.cts} +10 -1
  19. package/dist/{Spans-WRQPIDPO.js → Spans-R4CWLDIM.js} +6 -6
  20. package/dist/{Spans-WRQPIDPO.js.map → Spans-R4CWLDIM.js.map} +1 -1
  21. package/dist/{Spans-OBNYI6LJ.cjs → Spans-ZWXK3MM5.cjs} +11 -11
  22. package/dist/{Spans-OBNYI6LJ.cjs.map → Spans-ZWXK3MM5.cjs.map} +1 -1
  23. package/dist/{blendModes-2IDILRN6.cjs → blendModes-4LPMH4AB.cjs} +5 -5
  24. package/dist/{blendModes-2IDILRN6.cjs.map → blendModes-4LPMH4AB.cjs.map} +1 -1
  25. package/dist/{blendModes-4QH44GOL.js → blendModes-QTMINP4W.js} +4 -4
  26. package/dist/{blendModes-4QH44GOL.js.map → blendModes-QTMINP4W.js.map} +1 -1
  27. package/dist/{chunk-24MVJFW4.js → chunk-4V65M7NW.js} +115 -8
  28. package/dist/chunk-4V65M7NW.js.map +1 -0
  29. package/dist/{chunk-NXMNBFHM.cjs → chunk-6KVW442J.cjs} +56 -30
  30. package/dist/chunk-6KVW442J.cjs.map +1 -0
  31. package/dist/{chunk-J4ZP76IL.js → chunk-BFFIXDTK.js} +3 -3
  32. package/dist/chunk-BFFIXDTK.js.map +1 -0
  33. package/dist/{chunk-GXGIISFS.cjs → chunk-HMI6W5V3.cjs} +5 -5
  34. package/dist/{chunk-GXGIISFS.cjs.map → chunk-HMI6W5V3.cjs.map} +1 -1
  35. package/dist/{chunk-SC7EWGHB.js → chunk-JWIST65R.js} +31 -5
  36. package/dist/{chunk-NXMNBFHM.cjs.map → chunk-JWIST65R.js.map} +1 -1
  37. package/dist/{chunk-2VMLNKHT.cjs → chunk-LEYRK5OT.cjs} +132 -92
  38. package/dist/chunk-LEYRK5OT.cjs.map +1 -0
  39. package/dist/{chunk-C2HL5236.js → chunk-MEACOV6Q.js} +4 -4
  40. package/dist/{chunk-C2HL5236.js.map → chunk-MEACOV6Q.js.map} +1 -1
  41. package/dist/{chunk-3FCDRLW3.cjs → chunk-PBGZ47ZS.cjs} +3 -3
  42. package/dist/chunk-PBGZ47ZS.cjs.map +1 -0
  43. package/dist/{chunk-OI75LGIB.js → chunk-T557JZPF.js} +45 -5
  44. package/dist/chunk-T557JZPF.js.map +1 -0
  45. package/dist/{chunk-2DC6TRJI.cjs → chunk-WKPPMOFU.cjs} +158 -51
  46. package/dist/chunk-WKPPMOFU.cjs.map +1 -0
  47. package/dist/index.cjs +35 -35
  48. package/dist/index.d.cts +3 -3
  49. package/dist/index.d.ts +3 -3
  50. package/dist/index.js +5 -5
  51. package/dist/loader.css +1 -1
  52. package/dist/present.cjs +5 -5
  53. package/dist/present.d.cts +2 -2
  54. package/dist/present.d.ts +2 -2
  55. package/dist/present.js +5 -5
  56. package/dist/three.cjs +39 -8
  57. package/dist/three.cjs.map +1 -1
  58. package/dist/three.d.cts +12 -1
  59. package/dist/three.d.ts +12 -1
  60. package/dist/three.js +34 -3
  61. package/dist/three.js.map +1 -1
  62. package/dist/{types-BZngm4nT.d.ts → types-CpCqYSZ0.d.cts} +8 -0
  63. package/dist/{types-BZngm4nT.d.cts → types-CpCqYSZ0.d.ts} +8 -0
  64. package/docs/api.md +9 -0
  65. package/docs/dsl.md +16 -0
  66. package/llms-full.txt +33 -2
  67. package/package.json +1 -1
  68. package/skills/pixi-effects/SKILL.md +2 -2
  69. package/skills/pixi-effects/reference/cheatsheet.md +1 -0
  70. package/skills/pixi-effects/reference/pitfalls.md +5 -0
  71. package/skills/pixi-effects/template.html +7 -7
  72. package/dist/Composition-5SBIDTBB.cjs +0 -17
  73. package/dist/Composition-7D6ERUVY.js +0 -8
  74. package/dist/chunk-24MVJFW4.js.map +0 -1
  75. package/dist/chunk-2DC6TRJI.cjs.map +0 -1
  76. package/dist/chunk-2VMLNKHT.cjs.map +0 -1
  77. package/dist/chunk-3FCDRLW3.cjs.map +0 -1
  78. package/dist/chunk-J4ZP76IL.js.map +0 -1
  79. package/dist/chunk-OI75LGIB.js.map +0 -1
  80. package/dist/chunk-SC7EWGHB.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.51.0
4
+
5
+ Nothing breaks: a 2D layer can stay next to an object of a scene.
6
+
7
+ - **`follow: 'planets/Mars'`: a label next to an object, every frame.** Any 2D layer (text, shape, image, composition, null) is carried to where an object of a `scene3d`, `three` or `scene2d` layer is on screen (a name its setup returned in `objects`, or a node's label / `name`), or another layer's anchor point (`follow: 'title'`, a path through nested compositions, or a unique name found anywhere); its own `x` / `y` become an offset from there. It is placed after every layer is set for the frame (the scene layers' `update(t)` and `followCamera`), so it sits where the object is drawn in playback, a seek and an export, and it is hidden while the object is behind the camera or the followed layer is not on screen. An unknown layer or object is warned with the names there are (did you mean); a `threeD` follower, a target inside a `threeD` card and an offset larger than the composition are said in a sentence. In a trial (2026-11-14) both agents that labelled orbiting planets wrote the camera projection by hand and copied the camera's ease. Film `follow`.
8
+ - **`movie.project(target)`** → `{ x, y } | null`: the same point for your own code, in canvas pixels (the space `movie.pick` takes).
9
+ - **`check --do "click planets/Mars"`** clicks an object of a scene by name, where `movie.project` says it is (it knew only layer names).
10
+ - **`release:check` runs the files that failed in the full run once more, alone** (not when more than 8 failed): three full runs in a row had each timed out a different real-browser test under the load of the run itself; it names the files it ran again.
11
+
3
12
  ## 0.50.1
4
13
 
5
14
  Fixes from a trial of 8 fresh agents (Sonnet, Opus) making videos with `scene2d` / `scene3d` on 0.50.0; nothing breaks.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pixi-effects
2
2
 
3
- > **Status**: experimental — current release `0.50.1`. The API may still change between minor versions; see the [CHANGELOG](./CHANGELOG.md) for what each release changed.
3
+ > **Status**: experimental — current release `0.51.0`. The API may still change between minor versions; see the [CHANGELOG](./CHANGELOG.md) for what each release changed.
4
4
 
5
5
  **[Gallery →](https://yjmtmtk.github.io/pixi-effects/examples/gallery/)** · 43 pieces written as plain data by AI models · **[For AI →](https://yjmtmtk.github.io/pixi-effects/for-ai.html)** · what an AI reads and runs · **[Playground →](https://yjmtmtk.github.io/pixi-effects/examples/playground.html)** in the browser.
6
6
 
@@ -52,8 +52,8 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
52
52
  "imports": {
53
53
  "pixi.js": "https://esm.sh/pixi.js@8.23.0?bundle-deps",
54
54
  "mediabunny": "https://esm.sh/mediabunny",
55
- "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.50.1/dist/index.js",
56
- "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.50.1/dist/Controller.js"
55
+ "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.51.0/dist/index.js",
56
+ "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.51.0/dist/Controller.js"
57
57
  }
58
58
  }
59
59
  </script>
@@ -66,7 +66,7 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
66
66
 
67
67
  The `mediabunny` line is needed only to export (the download button, `movie.render()`) or for a `video` layer; a page that only plays can leave it out and never fetches it (or call `useMediabunny(url)` instead, see [the API](docs/api.md#where-the-encoder-comes-from-usemediabunnysource)).
68
68
 
69
- Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.50.1/dist/index.js`) rather than a CDN-rebundled build such as `esm.sh/pixi-effects` or jsDelivr's `+esm`: the entries (`pixi-effects`, `…/controller`, `…/three`) share internal chunks, which only works when each file is served untouched.
69
+ Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.51.0/dist/index.js`) rather than a CDN-rebundled build such as `esm.sh/pixi-effects` or jsDelivr's `+esm`: the entries (`pixi-effects`, `…/controller`, `…/three`) share internal chunks, which only works when each file is served untouched.
70
70
 
71
71
  > **A 3D scene?** Add three more entries (`pixi-effects/3d`, `@pixi/3d`, `@pixi/3d/extras`) — see [Adding a 3D scene](#adding-a-3d-scene-optional) below. **Using three.js?** Add two more entries (`three` and `pixi-effects/three`) to this importmap — see [Adding three.js](#adding-threejs-optional) below.
72
72
  >
@@ -81,7 +81,7 @@ Add three entries to the importmap above (PixiJS 3D needs `pixi.js` 8.23.0 or la
81
81
  {
82
82
  "imports": {
83
83
  "...": "(everything from the importmap above)",
84
- "pixi-effects/3d": "https://cdn.jsdelivr.net/npm/pixi-effects@0.50.1/dist/3d.js",
84
+ "pixi-effects/3d": "https://cdn.jsdelivr.net/npm/pixi-effects@0.51.0/dist/3d.js",
85
85
  "@pixi/3d": "https://cdn.jsdelivr.net/npm/@pixi/3d@0.1.0/dist/pixijs-3d.min.mjs",
86
86
  "@pixi/3d/extras": "https://cdn.jsdelivr.net/npm/@pixi/3d@0.1.0/dist/pixijs-3d-extras.min.mjs"
87
87
  }
@@ -125,7 +125,7 @@ Add two more entries to the importmap above: three.js itself, and the `pixi-effe
125
125
  "...": "(everything from the importmap above)",
126
126
  "three": "https://esm.sh/three@0.178.0",
127
127
  "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.178.0/examples/jsm/",
128
- "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.50.1/dist/three.js"
128
+ "pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.51.0/dist/three.js"
129
129
  }
130
130
  }
131
131
  </script>
@@ -65,10 +65,10 @@
65
65
  "imports": {
66
66
  "pixi.js": "https://cdn.jsdelivr.net/npm/pixi.js@8.23.0/dist/pixi.min.mjs",
67
67
  "mediabunny": "https://cdn.jsdelivr.net/npm/mediabunny@1.61.3/+esm",
68
- "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.50.1/dist/index.js",
69
- "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.50.1/dist/Controller.js",
70
- "pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.50.1/dist/present.js",
71
- "pixi-effects/3d": "https://cdn.jsdelivr.net/npm/pixi-effects@0.50.1/dist/3d.js",
68
+ "pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.51.0/dist/index.js",
69
+ "pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.51.0/dist/Controller.js",
70
+ "pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.51.0/dist/present.js",
71
+ "pixi-effects/3d": "https://cdn.jsdelivr.net/npm/pixi-effects@0.51.0/dist/3d.js",
72
72
  "@pixi/3d": "https://cdn.jsdelivr.net/npm/@pixi/3d@0.1.0/dist/pixijs-3d.min.mjs",
73
73
  "@pixi/3d/extras": "https://cdn.jsdelivr.net/npm/@pixi/3d@0.1.0/dist/pixijs-3d-extras.min.mjs"
74
74
  }
@@ -19,7 +19,7 @@
19
19
  * --pick X,Y (with --at, repeatable: what is under that canvas point at each moment, the top layer first, and the object a three layer met: `movie.pick`) ·
20
20
 
21
21
  * --do "click Lime; hover 640,360; drag card > 770,240; wait 1; key Space" (real mouse and key events on the page, one step after the other, after everything else: what was under the pointer, which
22
- * variables changed, whether the page warned, one picture per step in steps/; a point is x,y in canvas pixels or a layer name (the middle of its box); repeatable) ·
22
+ * variables changed, whether the page warned, one picture per step in steps/; a point is x,y in canvas pixels, a layer name (the middle of its box) or layer/object, an object of a scene3d, three or scene2d layer (where movie.project says it is); repeatable) ·
23
23
  * --onion A:B (one picture of the movement between A and B seconds, frames overlaid: onion.png) ·
24
24
  * --seam (a movie made to loop: the last frame to the first is compared with the steps between ordinary neighbouring frames; a jump is a problem) ·
25
25
  * --strict (text overlaps and stops where the picture is still changing fail the check; by default they are only listed for review) · --out DIR · --frames N (contact sheet tiles, default 12) · --formats mp4,webm (default mp4) · --no-export ·
@@ -104,7 +104,7 @@ const CRITICAL_TYPES = new Set(['Script', 'Document', 'Stylesheet', 'Other', und
104
104
  const DO_VERBS = ['click', 'hover', 'drag', 'wait', 'key'];
105
105
  const pointOf = (text) => { const m = text.trim().match(/^(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)$/); return m ? { x: Number(m[1]), y: Number(m[2]) } : { name: text.trim() }; };
106
106
  /**
107
- * `--do` steps: "click 640,360; hover Lime; drag card > 770,240; wait 0.5; key Space" (`;` or a new line between steps). A point is `x,y` in canvas pixels or a layer name; the points of a
107
+ * `--do` steps: "click 640,360; hover Lime; drag card > 770,240; wait 0.5; key Space" (`;` or a new line between steps). A point is `x,y` in canvas pixels, a layer name or `layer/object` (an object of a scene layer); the points of a
108
108
  * drag are separated by `>` or by spaces (a name with spaces needs `>`). Returns [{ verb, points?, seconds?, key? }].
109
109
  */
110
110
  export function parseDo(text) {
@@ -867,8 +867,14 @@ export async function runSteps(cdp, steps, outDir, size, consoleLines, seenLogs)
867
867
  const points = [];
868
868
  for (const pt of step.points) {
869
869
  if (pt.name === undefined) { points.push({ ...pt }); continue; }
870
- const box = await cdp.eval(`(async () => { const r = (await movie.inspect(movie.currentFrame)).layers.find(l => l.name === ${JSON.stringify(pt.name)} || l.path === ${JSON.stringify(pt.name)}); return r && r.bounds ? { x: r.bounds.x + r.bounds.width / 2, y: r.bounds.y + r.bounds.height / 2 } : null; })()`);
871
- if (!box) throw new Error(`no layer named "${pt.name}" at this moment (it may not be alive yet: put a wait before, or give x,y)`);
870
+ let box = await cdp.eval(`(async () => { const r = (await movie.inspect(movie.currentFrame)).layers.find(l => l.name === ${JSON.stringify(pt.name)} || l.path === ${JSON.stringify(pt.name)}); return r && r.bounds ? { x: r.bounds.x + r.bounds.width / 2, y: r.bounds.y + r.bounds.height / 2 } : null; })()`);
871
+ if (!box) {
872
+ // not a layer: an object of a scene ("world/mover"), where movie.project says it is
873
+ const p = await cdp.eval(`(() => { try { const p = movie.project(${JSON.stringify(pt.name)}); return p ? { x: p.x, y: p.y } : { hidden: true }; } catch (e) { return { error: String(e && e.message || e).replace(/^pixi-effects: /, '') }; } })()`);
874
+ if (p && Number.isFinite(p.x)) box = p;
875
+ else if (p?.hidden) throw new Error(`"${pt.name}" is not on screen at this moment (behind the camera, or its layer is not alive: put a wait before, or give x,y)`);
876
+ else throw new Error(pt.name.includes('/') && p?.error ? p.error : `no layer named "${pt.name}" at this moment (it may not be alive yet: put a wait before, or give x,y)`);
877
+ }
872
878
  points.push({ x: Math.round(box.x), y: Math.round(box.y), name: pt.name });
873
879
  }
874
880
  res.label = `${step.verb} ${points.map(pt => pt.name ? `${pt.name} (${pt.x}, ${pt.y})` : `${pt.x},${pt.y}`).join(' > ')}`;
package/dist/3d.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var chunkNXMNBFHM_cjs = require('./chunk-NXMNBFHM.cjs');
3
+ var chunk6KVW442J_cjs = require('./chunk-6KVW442J.cjs');
4
4
  require('./chunk-VQXZ2NOI.cjs');
5
- var chunkGXGIISFS_cjs = require('./chunk-GXGIISFS.cjs');
6
- var chunk3FCDRLW3_cjs = require('./chunk-3FCDRLW3.cjs');
5
+ var chunkHMI6W5V3_cjs = require('./chunk-HMI6W5V3.cjs');
6
+ var chunkPBGZ47ZS_cjs = require('./chunk-PBGZ47ZS.cjs');
7
7
  var chunkDOLTCXCL_cjs = require('./chunk-DOLTCXCL.cjs');
8
8
  var _3d = require('@pixi/3d');
9
9
  var pixi_js = require('pixi.js');
@@ -248,7 +248,7 @@ async function layerTexture(ctx, name, options = {}) {
248
248
  var OWN_KEYS = ["width", "height", "view", "resolution", "setup", "update", "dispose", "followCamera", "followLights", "followFog", "reuse"];
249
249
  var THREE_ONLY = ["renderer", "shareGpu", "antialias"];
250
250
  var LAYER_OWNED = ["width", "height", "autoResize"];
251
- var Scene3DSequence = class extends chunkNXMNBFHM_cjs.Sequence {
251
+ var Scene3DSequence = class extends chunk6KVW442J_cjs.Sequence {
252
252
  _view = null;
253
253
  _ctx = null;
254
254
  _objects = {};
@@ -273,7 +273,7 @@ var Scene3DSequence = class extends chunkNXMNBFHM_cjs.Sequence {
273
273
  /** True once another layer took this layer's scene: destroying this one must not let it go. */
274
274
  _donated = false;
275
275
  /** What setup left on the objects the keyframes reach, shared with a layer that takes the scene over (core/setupValues.ts). */
276
- _setupValues = new chunkNXMNBFHM_cjs.SetupValues();
276
+ _setupValues = new chunk6KVW442J_cjs.SetupValues();
277
277
  _pendingRebinds = [];
278
278
  _lightMirror = null;
279
279
  _fogMirror = null;
@@ -304,14 +304,14 @@ var Scene3DSequence = class extends chunkNXMNBFHM_cjs.Sequence {
304
304
  if (this.duration === void 0) this.duration = this.parent?.duration ?? this.root.duration;
305
305
  this._checkKeys();
306
306
  this._checkTint();
307
- if (typeof spec.update === "function" && chunkNXMNBFHM_cjs.NOT_PURE.test(spec.update.toString())) {
307
+ if (typeof spec.update === "function" && chunk6KVW442J_cjs.NOT_PURE.test(spec.update.toString())) {
308
308
  this._say("pure", "update 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. Derive the value from t");
309
309
  }
310
310
  const renderer = this.root.pixiRenderer;
311
311
  if (renderer && !renderer.renderPipes.view3d) {
312
312
  throw new Error(`${this._who()}: PixiJS 3D was loaded after the renderer started; import 'pixi-effects/3d' (and call register3D()) before movie.init`);
313
313
  }
314
- const scope = chunkNXMNBFHM_cjs.buildScope(this, this.parent, this.root);
314
+ const scope = chunk6KVW442J_cjs.buildScope(this, this.parent, this.root);
315
315
  const width = resolveDim(spec.width, scope) ?? this.parent?.width ?? this.root.width;
316
316
  const height = resolveDim(spec.height, scope) ?? this.parent?.height ?? this.root.height;
317
317
  const viewOptions = { ...spec.view ?? {} };
@@ -420,7 +420,7 @@ var Scene3DSequence = class extends chunkNXMNBFHM_cjs.Sequence {
420
420
  }
421
421
  /** Unknown keys (with the likely one), and the keys only a three layer has. */
422
422
  _checkKeys() {
423
- const valid = [...chunk3FCDRLW3_cjs.COMMON, ...OWN_KEYS];
423
+ const valid = [...chunkPBGZ47ZS_cjs.COMMON, ...OWN_KEYS];
424
424
  for (const key of Object.keys(this.spec)) {
425
425
  if (valid.includes(key)) continue;
426
426
  if (THREE_ONLY.includes(key)) {
@@ -501,8 +501,8 @@ var Scene3DSequence = class extends chunkNXMNBFHM_cjs.Sequence {
501
501
  const current = view.filters ?? [];
502
502
  const others = current.filter((f) => f !== this._look && f !== this._blend);
503
503
  const authored = String(this.spec.blendMode ?? "normal");
504
- if (others.length === 0 && chunkGXGIISFS_cjs.isAdvancedBlend(authored) && !this._blend) {
505
- this._blend = chunkGXGIISFS_cjs.blendFilterFor(authored);
504
+ if (others.length === 0 && chunkHMI6W5V3_cjs.isAdvancedBlend(authored) && !this._blend) {
505
+ this._blend = chunkHMI6W5V3_cjs.blendFilterFor(authored);
506
506
  if (this._blend) view.blendMode = "normal";
507
507
  }
508
508
  const mode = String(view.blendMode ?? "normal");
@@ -739,6 +739,31 @@ var Scene3DSequence = class extends chunkNXMNBFHM_cjs.Sequence {
739
739
  material: hit.target.material
740
740
  };
741
741
  }
742
+ /**
743
+ * `follow` / `movie.project`: where an object of the scene is on the canvas (a name setup returned in `objects`, or the label of any node), from the view's own projection
744
+ * of the node's origin; null while it is behind the camera, undefined for no such object.
745
+ */
746
+ screenPoint(object) {
747
+ const node = this._nodeNamed(object);
748
+ if (!node) return void 0;
749
+ const p = this._view?.worldToScreen(node.getGlobalPosition(new _3d.Vector3()));
750
+ return p ? { x: p.x, y: p.y } : null;
751
+ }
752
+ /** The names `screenPoint` knows: the objects setup returned, then the labelled nodes of the scene. */
753
+ objectNames() {
754
+ const names = new Set(Object.keys(this._objects).filter((k) => this._nodeNamed(k)));
755
+ const visit = (n) => {
756
+ if (n.label && n.label !== n.constructor?.name) names.add(n.label);
757
+ for (const c of n.children ?? []) visit(c);
758
+ };
759
+ if (this._view) for (const c of this._view.root.children) visit(c);
760
+ return [...names];
761
+ }
762
+ _nodeNamed(name) {
763
+ const own = this._objects[name];
764
+ if (own && typeof own.getGlobalPosition === "function") return own;
765
+ return this._view?.root.getChildByLabel(name, true) ?? null;
766
+ }
742
767
  /**
743
768
  * `hit.scene3d.grab(plane)`: a function of a point (u, v) of the layer's picture that gives where the pointer's ray meets a plane through the grabbed point, and how far that is
744
769
  * from it (`delta`): move the object by it. `plane`: 'screen' (facing the camera: the default), 'floor' (horizontal: y up), 'wall' (z constant), or a normal [x, y, z].
@@ -866,7 +891,7 @@ function reuseKeyOf(spec, width, height) {
866
891
  function resolveDim(v, scope) {
867
892
  if (v === void 0) return void 0;
868
893
  if (typeof v === "number") return v;
869
- return chunkNXMNBFHM_cjs.evaluateExpr(v, scope);
894
+ return chunk6KVW442J_cjs.evaluateExpr(v, scope);
870
895
  }
871
896
  var label2 = "pixi-effects: loadModel";
872
897
  async function loadModel(url, options = {}) {
@@ -952,7 +977,7 @@ function register3D() {
952
977
  }
953
978
  _3d.Model3D.defaultOptions.autoUpdate = false;
954
979
  _3d.AnimationPlayer.defaultOptions.autoUpdate = false;
955
- chunkNXMNBFHM_cjs.registerSequenceType("scene3d", Scene3DSequence);
980
+ chunk6KVW442J_cjs.registerSequenceType("scene3d", Scene3DSequence);
956
981
  }
957
982
  function scene3d(spec) {
958
983
  return spec;