pixi-effects 0.43.0 → 0.44.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.
- package/CHANGELOG.md +10 -0
- package/README.md +5 -5
- package/ai/chat-template.html +3 -3
- package/ai/tools/check.mjs +131 -2
- package/dist/Controller.d.cts +2 -2
- package/dist/Controller.d.ts +2 -2
- package/dist/{Movie-BdUm2Mc9.d.cts → Movie-B3VOAgwc.d.cts} +3 -3
- package/dist/{Movie-B_qQIYV6.d.ts → Movie-CQCVUqRs.d.ts} +3 -3
- package/dist/{chunk-6N5ZBUOU.js → chunk-4MBTYREC.js} +81 -31
- package/dist/chunk-4MBTYREC.js.map +1 -0
- package/dist/{chunk-BEGOKE7Q.cjs → chunk-MAAM45ZX.cjs} +80 -30
- package/dist/chunk-MAAM45ZX.cjs.map +1 -0
- package/dist/index.cjs +25 -25
- package/dist/index.d.cts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +1 -1
- package/dist/loader.css +1 -1
- package/dist/present.cjs +1 -1
- package/dist/present.d.cts +2 -2
- package/dist/present.d.ts +2 -2
- package/dist/present.js +1 -1
- package/dist/three.cjs +86 -16
- package/dist/three.cjs.map +1 -1
- package/dist/three.d.cts +22 -2
- package/dist/three.d.ts +22 -2
- package/dist/three.js +86 -16
- package/dist/three.js.map +1 -1
- package/dist/{types-DiNHtdrS.d.cts → types-C5xo0fzb.d.cts} +74 -2
- package/dist/{types-DiNHtdrS.d.ts → types-C5xo0fzb.d.ts} +74 -2
- package/docs/api.md +4 -2
- package/docs/dsl.md +1 -0
- package/llms-full.txt +9 -6
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +2 -2
- package/skills/pixi-effects/reference/recipes.md +2 -2
- package/skills/pixi-effects/template.html +6 -6
- package/dist/Base-DKFQtqzW.d.ts +0 -60
- package/dist/Base-lb5Uhaqu.d.cts +0 -60
- package/dist/chunk-6N5ZBUOU.js.map +0 -1
- package/dist/chunk-BEGOKE7Q.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.44.0
|
|
4
|
+
|
|
5
|
+
An AI can now click, hover and drag on a page it made and see what happened (`check --do`), a text layer can be picked by its letters, a rebuild keeps a `three` layer that did not change, and the Interactive page has three samples that click, hover and drag. Nothing breaks.
|
|
6
|
+
|
|
7
|
+
- **New: `setVars` / `resize` / `rebuild` keep a `three` layer that did not change.** A rebuild built every layer again, so a text variable changed with a model in the scene loaded the model again (and made a renderer again). A named `three` layer with the same `setup` function, size and options now takes over the old layer's scene, renderer, texture and mirrored lights: `setup` runs once (15 ms → 1 ms for a rebuild with a box scene on WebGL, 7 → 1 on WebGPU; more with a model). The new tree is built while the old one plays, the new layer only borrows until the swap, so a rebuild that fails changes nothing and the old layer is disposed once. `reuse: false` opts out (a `setup` that reads `movie.vars`). `tests/films/three-reuse`, `tests/three/ThreeSequence.reuse.test.ts`.
|
|
8
|
+
- **New: `check --do "click Lime; hover 640,360; drag card > 770,240; wait 1; key Space"` clicks, hovers, drags and presses keys on the page.** Real mouse and key events (the page's own handlers run), after everything else; a point is `x,y` in canvas pixels or a layer name. The report says per step what was under the pointer (`over Lime/shape#0`), which variables changed (`score 0→1`), whether the page warned, and keeps a picture of the canvas after each step in `steps/`. A layer that is not there, a point outside the picture, or a console warning during a step is a problem. Until now `check` could not click and every page had to be tested by hand over Chrome's protocol. `tests/tools/checkDo.test.ts` (a test page, `tests/films/check-do.html`).
|
|
9
|
+
- **New: `movie.pick(x, y, { alpha })` looks at the letters of a text layer.** A text layer was its box (the gap between letters and the hole of an O were hits); with `alpha` it is its glyphs, including the pieces of a `spans` line and an outline. The letters are drawn again with PixiJS's own generator once per text and style. `tests/films/pick-text`: over 95 % agreement with the picture on three texts.
|
|
10
|
+
- **New: three samples on the Interactive page that click, hover and drag.** `click.html` (pop the bubbles: `movie.hover` and `movie.pick` on layers of the film), `drop.html` (drag cards onto zones: `pick` and variables), `move3d.html` (drag three.js objects along a floor under the film's own light: `hit.three.grab('floor')`, `movie.redraw()`). Until now the page only showed variables. `tests/site/interactiveClicks.test.ts` drives each with real mouse events in Chrome.
|
|
11
|
+
- **Docs: what a `pick` hit names.** The hit is the layer drawn at the point, not its group: the group is the first segment of `hit.path` (found while writing the samples: `hit.name` of a bubble made of a group and an unnamed circle was undefined).
|
|
12
|
+
|
|
3
13
|
## 0.43.0
|
|
4
14
|
|
|
5
15
|
A `three` layer on WebGPU shares PixiJS's GPU device, and the tests stop depending on the gallery. Nothing breaks.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pixi-effects
|
|
2
2
|
|
|
3
|
-
> **Status**: experimental — current release `0.
|
|
3
|
+
> **Status**: experimental — current release `0.44.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
|
|
|
@@ -49,8 +49,8 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
|
|
|
49
49
|
"imports": {
|
|
50
50
|
"pixi.js": "https://esm.sh/pixi.js@8.22.0?bundle-deps",
|
|
51
51
|
"mediabunny": "https://esm.sh/mediabunny",
|
|
52
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
53
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
52
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.44.0/dist/index.js",
|
|
53
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.44.0/dist/Controller.js"
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
</script>
|
|
@@ -61,7 +61,7 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
|
|
|
61
61
|
</script>
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.
|
|
64
|
+
Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.44.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.
|
|
65
65
|
|
|
66
66
|
> **Using three.js?** Add two more entries (`three` and `pixi-effects/three`) to this importmap — see [Adding three.js](#adding-threejs-optional) below.
|
|
67
67
|
>
|
|
@@ -78,7 +78,7 @@ Add two more entries to the importmap above: three.js itself, and the `pixi-effe
|
|
|
78
78
|
"...": "(everything from the importmap above)",
|
|
79
79
|
"three": "https://esm.sh/three@0.178.0",
|
|
80
80
|
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.178.0/examples/jsm/",
|
|
81
|
-
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
81
|
+
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.44.0/dist/three.js"
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
</script>
|
package/ai/chat-template.html
CHANGED
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"imports": {
|
|
60
60
|
"pixi.js": "https://cdn.jsdelivr.net/npm/pixi.js@8.22.0/dist/pixi.min.mjs",
|
|
61
61
|
"mediabunny": "https://cdn.jsdelivr.net/npm/mediabunny@1.61.3/+esm",
|
|
62
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
63
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
64
|
-
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
62
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.44.0/dist/index.js",
|
|
63
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.44.0/dist/Controller.js",
|
|
64
|
+
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.44.0/dist/present.js"
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
</script>
|
package/ai/tools/check.mjs
CHANGED
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
* --query "a=1&b=2" (added to the page address: for a page that reads it, such as one video of a batch) ·
|
|
18
18
|
* --crop X,Y,W,H (with --at: each picture is that part of the canvas, in canvas pixels, enlarged up to 4x so a small figure can be judged) ·
|
|
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
|
+
|
|
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) ·
|
|
20
23
|
* --onion A:B (one picture of the movement between A and B seconds, frames overlaid: onion.png) ·
|
|
21
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) ·
|
|
22
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 ·
|
|
@@ -82,10 +85,41 @@ export function findChrome(env = process.env, exists = fs.existsSync, platform =
|
|
|
82
85
|
return candidates.find(c => c && exists(c)) ?? null;
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
export const USAGE = 'usage: node ai/tools/check.mjs <page.html> [--out DIR] [--frames N] [--formats mp4,webm] [--no-export] [--draft] [--at 3.5,title@end] [--crop X,Y,W,H] [--pick X,Y] [--onion 1:3] [--seam] [--query a=1&b=2] [--var name=value] [--size 1920x1080,1080x1920] [--timeout S] [--root DIR] [--chrome PATH] [--strict] [--help]\n(the header of ai/tools/check.mjs explains each option)';
|
|
88
|
+
export const USAGE = 'usage: node ai/tools/check.mjs <page.html> [--out DIR] [--frames N] [--formats mp4,webm] [--no-export] [--draft] [--at 3.5,title@end] [--crop X,Y,W,H] [--pick X,Y] [--do "click Lime; drag card > 770,240"] [--onion 1:3] [--seam] [--query a=1&b=2] [--var name=value] [--size 1920x1080,1080x1920] [--timeout S] [--root DIR] [--chrome PATH] [--strict] [--help]\n(the header of ai/tools/check.mjs explains each option)';
|
|
89
|
+
|
|
90
|
+
const DO_VERBS = ['click', 'hover', 'drag', 'wait', 'key'];
|
|
91
|
+
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() }; };
|
|
92
|
+
/**
|
|
93
|
+
* `--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
|
|
94
|
+
* drag are separated by `>` or by spaces (a name with spaces needs `>`). Returns [{ verb, points?, seconds?, key? }].
|
|
95
|
+
*/
|
|
96
|
+
export function parseDo(text) {
|
|
97
|
+
const steps = [];
|
|
98
|
+
for (const raw of String(text).split(/[;\n]/).map(s => s.trim()).filter(Boolean)) {
|
|
99
|
+
const [verb, ...rest] = raw.split(/\s+/);
|
|
100
|
+
const arg = rest.join(' ');
|
|
101
|
+
if (!DO_VERBS.includes(verb)) throw new Error(`--do: "${verb}" is not a step; the steps are ${DO_VERBS.join(', ')} (for example --do "click 640,360; drag card > 770,240")`);
|
|
102
|
+
if (verb === 'wait') {
|
|
103
|
+
if (!arg || !Number.isFinite(Number(arg)) || Number(arg) < 0) throw new Error(`--do: wait needs a number of seconds, got "${arg}"`);
|
|
104
|
+
steps.push({ verb, seconds: Number(arg) });
|
|
105
|
+
} else if (verb === 'key') {
|
|
106
|
+
if (!arg) throw new Error('--do: key needs a key name (Space, Enter, ArrowLeft, a, …)');
|
|
107
|
+
steps.push({ verb, key: arg });
|
|
108
|
+
} else if (verb === 'drag') {
|
|
109
|
+
const parts = arg.includes('>') ? arg.split('>') : arg.split(/\s+/);
|
|
110
|
+
const points = parts.map(p => p.trim()).filter(Boolean).map(pointOf);
|
|
111
|
+
if (points.length < 2) throw new Error(`--do: drag needs two points or more (from > to), got "${arg}"`);
|
|
112
|
+
steps.push({ verb, points });
|
|
113
|
+
} else {
|
|
114
|
+
if (!arg) throw new Error(`--do: ${verb} needs a point (x,y in canvas pixels, or a layer name), for example "${verb} 640,360"`);
|
|
115
|
+
steps.push({ verb, points: [pointOf(arg)] });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return steps;
|
|
119
|
+
}
|
|
86
120
|
|
|
87
121
|
export function parseArgs(argv) {
|
|
88
|
-
const o = { page: null, out: null, frames: 12, formats: ['mp4'], export: true, timeout: 240, root: null, chrome: null, strict: false, at: null, crop: null, draft: false, onion: null, seam: false, query: null, pick: [], vars: [], sizes: [], help: false };
|
|
122
|
+
const o = { page: null, out: null, frames: 12, formats: ['mp4'], export: true, timeout: 240, root: null, chrome: null, strict: false, at: null, crop: null, draft: false, onion: null, seam: false, query: null, pick: [], do: [], vars: [], sizes: [], help: false };
|
|
89
123
|
const need = (i, name) => { if (i + 1 >= argv.length) throw new Error(`${name} needs a value`); return argv[i + 1]; };
|
|
90
124
|
for (let i = 0; i < argv.length; i++) {
|
|
91
125
|
const a = argv[i];
|
|
@@ -108,6 +142,7 @@ export function parseArgs(argv) {
|
|
|
108
142
|
if (m.length !== 2 || !m.every(Number.isFinite)) throw new Error(`--pick must look like 640,360 (x, y in canvas pixels), got "${argv[i]}"`);
|
|
109
143
|
o.pick.push({ x: m[0], y: m[1] });
|
|
110
144
|
}
|
|
145
|
+
else if (a === '--do') o.do.push(...parseDo(need(i++, a)));
|
|
111
146
|
else if (a === '--var') o.vars.push(parseVar(need(i++, a)));
|
|
112
147
|
else if (a === '--size') o.sizes.push(...parseSizes(need(i++, a)));
|
|
113
148
|
else if (a === '--seam') o.seam = true;
|
|
@@ -620,6 +655,15 @@ export async function runCheck(opts, log = console.log) {
|
|
|
620
655
|
report.logs.push(...fresh);
|
|
621
656
|
report.problems.push(`${fresh.length} more console warning(s) / error(s) while the page was built at the other size(s) — each says what to change`);
|
|
622
657
|
}
|
|
658
|
+
// --do: real clicks, hovers, drags and keys on the page, last (they change what the page shows and remembers)
|
|
659
|
+
if (opts.do.length) {
|
|
660
|
+
report.steps = await runSteps(cdp, opts.do, outDir, { width: info.width, height: info.height }, consoleLines, report.logs);
|
|
661
|
+
report.files.steps = shown(path.join(outDir, 'steps'));
|
|
662
|
+
report.steps.forEach((st, i) => {
|
|
663
|
+
if (st.error) report.problems.push(`step ${i + 1} (${st.label}): ${st.error}`);
|
|
664
|
+
if (st.warnings?.length) report.problems.push(`step ${i + 1} (${st.label}): ${st.warnings.length} console warning(s) — ${st.warnings[0].slice(0, 200)}`);
|
|
665
|
+
});
|
|
666
|
+
}
|
|
623
667
|
return finish(report, outDir, log);
|
|
624
668
|
} finally {
|
|
625
669
|
try { chromeProc?.kill(); } catch { /* already gone */ }
|
|
@@ -673,6 +717,85 @@ export function exportLine(ex) {
|
|
|
673
717
|
return ` export ${ex.format}${ex.draft ? ' (draft)' : ''} ok · ${kb(ex.bytes)}${rate} · ${v} · ${au}`;
|
|
674
718
|
}
|
|
675
719
|
|
|
720
|
+
const KEYS = { Space: [' ', 'Space'], Enter: ['Enter', 'Enter'], Escape: ['Escape', 'Escape'], Tab: ['Tab', 'Tab'], Backspace: ['Backspace', 'Backspace'], ArrowLeft: ['ArrowLeft', 'ArrowLeft'], ArrowRight: ['ArrowRight', 'ArrowRight'], ArrowUp: ['ArrowUp', 'ArrowUp'], ArrowDown: ['ArrowDown', 'ArrowDown'] };
|
|
721
|
+
const keyEvent = (name) => KEYS[name] ? { key: KEYS[name][0], code: KEYS[name][1] } : { key: name, code: /^[a-z]$/i.test(name) ? `Key${name.toUpperCase()}` : /^\d$/.test(name) ? `Digit${name}` : name };
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* `check --do`: the steps, run on the page with real mouse and key events (the page's own handlers run: nothing is simulated), one picture of the canvas after each, in `steps/`.
|
|
725
|
+
* Returns [{ verb, label, hits, vars, frame, playing, warnings, picture, error? }]: what was under the last point, the variables that changed ([before, after]), what the page warned.
|
|
726
|
+
*/
|
|
727
|
+
export async function runSteps(cdp, steps, outDir, size, consoleLines, seenLogs) {
|
|
728
|
+
const dir = path.join(outDir, 'steps');
|
|
729
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
730
|
+
for (const old of fs.readdirSync(dir)) if (old.endsWith('.png')) fs.rmSync(path.join(dir, old));
|
|
731
|
+
await cdp.send('Emulation.setDeviceMetricsOverride', { width: 1400, height: 1000, deviceScaleFactor: 1, mobile: false });
|
|
732
|
+
await cdp.eval(`movie.app.canvas.scrollIntoView({ block: 'center', inline: 'center' })`);
|
|
733
|
+
await sleep(150);
|
|
734
|
+
const seen = new Set(seenLogs);
|
|
735
|
+
const results = [];
|
|
736
|
+
const pageLogs = async () => [...new Set([...(await cdp.eval('window.__logs || []').catch(() => [])), ...consoleLines])];
|
|
737
|
+
const geometry = () => cdp.eval(`(() => { const r = movie.app.canvas.getBoundingClientRect(); return { left: r.left, top: r.top, w: r.width, h: r.height }; })()`);
|
|
738
|
+
const mouse = async (g, p, type, extra = {}) => cdp.send('Input.dispatchMouseEvent', { type, x: g.left + p.x * g.w / size.width, y: g.top + p.y * g.h / size.height, ...extra });
|
|
739
|
+
let n = 0;
|
|
740
|
+
for (const step of steps) {
|
|
741
|
+
n++;
|
|
742
|
+
const res = { verb: step.verb, label: step.verb + (step.points ? ' ' + step.points.map(pt => pt.name ?? `${pt.x},${pt.y}`).join(' > ') : '') }; // (a name that is found gets its place added below)
|
|
743
|
+
const before = await cdp.eval('JSON.parse(JSON.stringify(movie.vars || {}))').catch(() => ({}));
|
|
744
|
+
let last = null;
|
|
745
|
+
try {
|
|
746
|
+
if (step.verb === 'wait') { res.label = `wait ${step.seconds}`; await sleep(step.seconds * 1000); }
|
|
747
|
+
else if (step.verb === 'key') {
|
|
748
|
+
res.label = `key ${step.key}`;
|
|
749
|
+
const e = keyEvent(step.key);
|
|
750
|
+
await cdp.send('Input.dispatchKeyEvent', { type: 'keyDown', ...e, text: e.key.length === 1 ? e.key : undefined });
|
|
751
|
+
await cdp.send('Input.dispatchKeyEvent', { type: 'keyUp', ...e });
|
|
752
|
+
} else {
|
|
753
|
+
const points = [];
|
|
754
|
+
for (const pt of step.points) {
|
|
755
|
+
if (pt.name === undefined) { points.push({ ...pt }); continue; }
|
|
756
|
+
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; })()`);
|
|
757
|
+
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)`);
|
|
758
|
+
points.push({ x: Math.round(box.x), y: Math.round(box.y), name: pt.name });
|
|
759
|
+
}
|
|
760
|
+
res.label = `${step.verb} ${points.map(pt => pt.name ? `${pt.name} (${pt.x}, ${pt.y})` : `${pt.x},${pt.y}`).join(' > ')}`;
|
|
761
|
+
res.points = points.map(({ x, y }) => ({ x, y }));
|
|
762
|
+
for (const pt of points) if (!(pt.x >= 0 && pt.x <= size.width && pt.y >= 0 && pt.y <= size.height)) throw new Error(`the point ${pt.x},${pt.y} is outside the picture (0..${size.width} × 0..${size.height})`);
|
|
763
|
+
const g = await geometry();
|
|
764
|
+
last = points[points.length - 1];
|
|
765
|
+
if (step.verb === 'hover') await mouse(g, points[0], 'mouseMoved');
|
|
766
|
+
else if (step.verb === 'click') {
|
|
767
|
+
await mouse(g, points[0], 'mouseMoved'); await sleep(60);
|
|
768
|
+
await mouse(g, points[0], 'mousePressed', { button: 'left', clickCount: 1 }); await sleep(40);
|
|
769
|
+
await mouse(g, points[0], 'mouseReleased', { button: 'left', clickCount: 1 });
|
|
770
|
+
} else { // drag: press at the first point, move through the others in small steps, release at the last
|
|
771
|
+
await mouse(g, points[0], 'mouseMoved'); await sleep(60);
|
|
772
|
+
await mouse(g, points[0], 'mousePressed', { button: 'left', clickCount: 1, buttons: 1 }); await sleep(60);
|
|
773
|
+
for (let i = 1; i < points.length; i++) for (let k = 1; k <= 8; k++) {
|
|
774
|
+
const a = points[i - 1], b = points[i];
|
|
775
|
+
await mouse(g, { x: a.x + (b.x - a.x) * k / 8, y: a.y + (b.y - a.y) * k / 8 }, 'mouseMoved', { buttons: 1 }); await sleep(25);
|
|
776
|
+
}
|
|
777
|
+
await mouse(g, last, 'mouseReleased', { button: 'left', clickCount: 1 });
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
} catch (e) { res.error = e.message; }
|
|
781
|
+
await sleep(300); // the page's handlers and rebuilds settle
|
|
782
|
+
const after = await cdp.eval('JSON.parse(JSON.stringify(movie.vars || {}))').catch(() => ({}));
|
|
783
|
+
res.vars = Object.fromEntries(Object.keys({ ...before, ...after }).filter(k => JSON.stringify(before[k]) !== JSON.stringify(after[k])).map(k => [k, [before[k], after[k]]]));
|
|
784
|
+
if (last) res.hits = await cdp.eval(`movie.pickAll(${last.x}, ${last.y}).map(h => h.kind === 'three' ? h.path + ' (three: ' + (h.three && h.three.object || '?') + ')' : h.path)`).catch(() => []);
|
|
785
|
+
res.frame = await cdp.eval('movie.currentFrame').catch(() => null);
|
|
786
|
+
res.playing = await cdp.eval('!!movie.isPlaying').catch(() => null);
|
|
787
|
+
const now = await pageLogs();
|
|
788
|
+
res.warnings = now.filter(l => !seen.has(l));
|
|
789
|
+
for (const l of res.warnings) seen.add(l);
|
|
790
|
+
const g = await geometry();
|
|
791
|
+
const shot = await cdp.send('Page.captureScreenshot', { format: 'png', clip: { x: g.left, y: g.top, width: g.w, height: g.h, scale: 1 } }).catch(() => null);
|
|
792
|
+
if (shot?.data) { const file = path.join(dir, `${String(n).padStart(2, '0')}-${step.verb}.png`); fs.writeFileSync(file, Buffer.from(shot.data, 'base64')); res.picture = shown(file); }
|
|
793
|
+
results.push(res);
|
|
794
|
+
}
|
|
795
|
+
return results;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const fmtVar = (v) => typeof v === 'string' ? JSON.stringify(v.length > 24 ? v.slice(0, 21) + '…' : v) : String(v);
|
|
676
799
|
function finish(report, outDir, log) {
|
|
677
800
|
const reportFile = path.join(outDir, 'report.json');
|
|
678
801
|
report.files.report = shown(reportFile);
|
|
@@ -727,6 +850,12 @@ function finish(report, outDir, log) {
|
|
|
727
850
|
const shown = Object.entries(w.light).sort((a, b) => b[1] - a[1]).slice(0, 8).map(([k, v]) => `${k} ${v}`).join(', '); // the most lit first
|
|
728
851
|
L.push(` light ${w.label} — ${shown}${Object.keys(w.light).length > 8 ? ', …' : ''} (the most lit threeD layers first: 1 = as bright as with no light, 0 = black; the number is the layer's middle, not what you can see)`);
|
|
729
852
|
}
|
|
853
|
+
for (const [i, st] of (report.steps ?? []).entries()) {
|
|
854
|
+
const vars = Object.entries(st.vars ?? {});
|
|
855
|
+
const changed = vars.length ? vars.slice(0, 6).map(([k, [a, b]]) => `${k} ${fmtVar(a)}→${fmtVar(b)}`).join(', ') + (vars.length > 6 ? `, … (${vars.length - 6} more)` : '') : 'no variable changed';
|
|
856
|
+
const over = st.hits ? ` — over ${st.hits.length ? st.hits.join(', ') : 'nothing'}` : '';
|
|
857
|
+
L.push(` do ${i + 1} ${st.label}${over} · ${st.error ? 'NOT RUN: ' + st.error : changed}${st.warnings?.length ? ` · ${st.warnings.length} warning(s)` : ''}${st.picture ? '' : ''}`);
|
|
858
|
+
}
|
|
730
859
|
if (report.stops) {
|
|
731
860
|
const s = report.stops;
|
|
732
861
|
L.push(` stops ${s.count} stop(s) on ${s.pages} page(s) — stops.png shows each one in order; the picture at a stop is what the audience sees while it waits`)
|
package/dist/Controller.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { M as Movie } from './Movie-
|
|
1
|
+
import { M as Movie } from './Movie-B3VOAgwc.cjs';
|
|
2
2
|
import 'pixi.js';
|
|
3
3
|
import './soundEntry-DT13DNt8.cjs';
|
|
4
|
-
import './types-
|
|
4
|
+
import './types-C5xo0fzb.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* The look of the bar. Each key sets a CSS custom property on the bar (so you can also set `--mc-accent` … in your own CSS, on the
|
package/dist/Controller.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { M as Movie } from './Movie-
|
|
1
|
+
import { M as Movie } from './Movie-CQCVUqRs.js';
|
|
2
2
|
import 'pixi.js';
|
|
3
3
|
import './soundEntry-DT13DNt8.js';
|
|
4
|
-
import './types-
|
|
4
|
+
import './types-C5xo0fzb.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* The look of the bar. Each key sets a CSS custom property on the bar (so you can also set `--mc-accent` … in your own CSS, on the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Application } from 'pixi.js';
|
|
2
2
|
import { A as AudioReport, a as AudioInspectOptions } from './soundEntry-DT13DNt8.cjs';
|
|
3
|
-
import {
|
|
3
|
+
import { a as Stop, A as AssetSpec, C as CompositionSpec } from './types-C5xo0fzb.cjs';
|
|
4
4
|
|
|
5
5
|
type EaseFn = (p: number) => number;
|
|
6
6
|
|
|
@@ -275,8 +275,8 @@ interface PickHit {
|
|
|
275
275
|
}
|
|
276
276
|
interface PickOptions {
|
|
277
277
|
/**
|
|
278
|
-
* Look at the picture, not the box: a pixel of an image, a video
|
|
279
|
-
* Off by default (the box is the picture, and nothing is read). Shapes are always hit by their outline,
|
|
278
|
+
* Look at the picture, not the box: a pixel of an image, a video, a `canvas` layer or the letters of a text layer whose alpha is at or below this (0..1; `0.1` leaves out what is nearly clear) is not a hit.
|
|
279
|
+
* Off by default (the box is the picture, and nothing is read). Shapes are always hit by their outline, a `shader` layer by its box (and text by its box too unless `alpha` is given); a picture that cannot be read (from another origin) stays a hit.
|
|
280
280
|
*/
|
|
281
281
|
alpha?: number;
|
|
282
282
|
/** Only these layers can be hit (by `name` or by `path`; a group's name stands for everything in it). */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Application } from 'pixi.js';
|
|
2
2
|
import { A as AudioReport, a as AudioInspectOptions } from './soundEntry-DT13DNt8.js';
|
|
3
|
-
import {
|
|
3
|
+
import { a as Stop, A as AssetSpec, C as CompositionSpec } from './types-C5xo0fzb.js';
|
|
4
4
|
|
|
5
5
|
type EaseFn = (p: number) => number;
|
|
6
6
|
|
|
@@ -275,8 +275,8 @@ interface PickHit {
|
|
|
275
275
|
}
|
|
276
276
|
interface PickOptions {
|
|
277
277
|
/**
|
|
278
|
-
* Look at the picture, not the box: a pixel of an image, a video
|
|
279
|
-
* Off by default (the box is the picture, and nothing is read). Shapes are always hit by their outline,
|
|
278
|
+
* Look at the picture, not the box: a pixel of an image, a video, a `canvas` layer or the letters of a text layer whose alpha is at or below this (0..1; `0.1` leaves out what is nearly clear) is not a hit.
|
|
279
|
+
* Off by default (the box is the picture, and nothing is read). Shapes are always hit by their outline, a `shader` layer by its box (and text by its box too unless `alpha` is given); a picture that cannot be read (from another origin) stays a hit.
|
|
280
280
|
*/
|
|
281
281
|
alpha?: number;
|
|
282
282
|
/** Only these layers can be hit (by `name` or by `path`; a group's name stands for everything in it). */
|
|
@@ -4,7 +4,7 @@ import { analyzeAudio, fft, resampleThrough, playSpan } from './chunk-UHNSL2KJ.j
|
|
|
4
4
|
import { usesAdvancedBlend, enableAdvancedBlend } from './chunk-VNP7QCJE.js';
|
|
5
5
|
import { describeLayer } from './chunk-32MMNDDN.js';
|
|
6
6
|
import { warnUnknownOptions, suggestName } from './chunk-BXOCEZ4U.js';
|
|
7
|
-
import { extensions, CullerPlugin, Application, Container, Rectangle, Culler, CanvasTextMetrics, TextStyle, Assets, ExtensionType, Filter, Texture, UniformGroup, GpuProgram, GlProgram, defaultFilterVert } from 'pixi.js';
|
|
7
|
+
import { extensions, CullerPlugin, Application, Container, Rectangle, Culler, CanvasTextMetrics, TextStyle, Assets, ExtensionType, Filter, Texture, UniformGroup, GpuProgram, GlProgram, defaultFilterVert, Text, CanvasTextGenerator, CanvasPool } from 'pixi.js';
|
|
8
8
|
import { Output, BufferTarget, CanvasSource, AudioBufferSource, Input, ALL_FORMATS, BlobSource, WavOutputFormat, Conversion, VideoSampleSink, OggOutputFormat, QUALITY_VERY_HIGH, QUALITY_HIGH, QUALITY_MEDIUM, QUALITY_LOW, QUALITY_VERY_LOW, MkvOutputFormat, WebMOutputFormat, MovOutputFormat, Mp4OutputFormat, canEncodeVideo, getFirstEncodableAudioCodec } from 'mediabunny';
|
|
9
9
|
|
|
10
10
|
// src/core/gpuShare.ts
|
|
@@ -188,7 +188,7 @@ function inspectFonts(root) {
|
|
|
188
188
|
});
|
|
189
189
|
const missing = [];
|
|
190
190
|
const used = /* @__PURE__ */ new Set();
|
|
191
|
-
const
|
|
191
|
+
const walk3 = (seq, index) => {
|
|
192
192
|
const spec = seq.spec;
|
|
193
193
|
if (spec.type === "text") {
|
|
194
194
|
const raw = spec.style?.fontFamily;
|
|
@@ -197,10 +197,10 @@ function inspectFonts(root) {
|
|
|
197
197
|
for (const f of list) used.add(f.replace(/^["']|["']$/g, "").toLowerCase());
|
|
198
198
|
if (list.length && !list.some(fontAvailable) && !list.some((f) => failed.has(f.replace(/^["']|["']$/g, "")))) missing.push({ layer: spec.name ?? `text#${index}`, family: family2 });
|
|
199
199
|
}
|
|
200
|
-
if (seq.maskSequence)
|
|
201
|
-
(seq._children ?? []).forEach((child, i) =>
|
|
200
|
+
if (seq.maskSequence) walk3(seq.maskSequence, index);
|
|
201
|
+
(seq._children ?? []).forEach((child, i) => walk3(child, i));
|
|
202
202
|
};
|
|
203
|
-
|
|
203
|
+
walk3(root, 0);
|
|
204
204
|
const all = [...failed];
|
|
205
205
|
return { missing, failed: all.filter((f) => used.has(f.toLowerCase())), failedUnused: all.filter((f) => !used.has(f.toLowerCase())) };
|
|
206
206
|
}
|
|
@@ -1526,16 +1526,16 @@ function canvasReport(root) {
|
|
|
1526
1526
|
const problem = threw.length ? `${name} could not draw at ${threw.map((t) => `${t} s`).join(", ")} (its ${kind === "three" ? "update or render threw" : "draw threw"}, or the layer failed to build): ${kind === "three" ? "the warning above says why" : "it shows a magenta checkerboard there; the warning above says why"}, so the layer cannot be called deterministic` : differs.length ? `${name} does not draw the same picture for the same time (checked at ${differs.map(describeDiff).join(", ")}; drawn in two orders after one draw that was thrown away): ` + (kind === "three" ? "usually its update uses Math.random, Date.now, a clock, or state that builds up from call to call: a mixer advanced with mixer.update(dt) is one (pose a clip with mixer.setTime(t), or threeGltf(url).pose(t)), and so is a value moved by the time since the last call. A skinned model of your own (not loaded by threeGltf) that moves and casts a shadow does it too: three culls a skinned mesh by a bounding sphere that does not follow it, so set mesh.frustumCulled = false. Set every object from t, so playback, a seek and an export agree" : "usually its draw uses Math.random, Date.now or state that outlives the call (a value it keeps from the last call), so playback, a seek and an export would not agree: use info.random() (seeded) and derive everything from t. If it uses none of these, look at the canvases it draws from: make sprites and masks with info.canvas(w, h) (not document.createElement) and draw them into the picture before the strokes that depend on them") : "";
|
|
1527
1527
|
out.push({ name: seq.spec.name ?? name, moments, drawMs, ok: !differs.length && !threw.length, problem, kind });
|
|
1528
1528
|
}
|
|
1529
|
-
if (seq instanceof CompositionSequence)
|
|
1529
|
+
if (seq instanceof CompositionSequence) walk3(seq);
|
|
1530
1530
|
};
|
|
1531
|
-
const
|
|
1531
|
+
const walk3 = (comp) => {
|
|
1532
1532
|
for (const { seq } of comp.layers()) {
|
|
1533
1533
|
visit(seq);
|
|
1534
1534
|
const mask = seq.maskSequence;
|
|
1535
1535
|
if (mask) visit(mask);
|
|
1536
1536
|
}
|
|
1537
1537
|
};
|
|
1538
|
-
|
|
1538
|
+
walk3(root);
|
|
1539
1539
|
return out;
|
|
1540
1540
|
}
|
|
1541
1541
|
var canvasProblems = (root) => canvasReport(root).filter((r) => !r.ok).map((r) => r.problem);
|
|
@@ -1602,7 +1602,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1602
1602
|
const specs = /* @__PURE__ */ new Map();
|
|
1603
1603
|
const chains = /* @__PURE__ */ new Map();
|
|
1604
1604
|
const chainAlpha = /* @__PURE__ */ new Map();
|
|
1605
|
-
const
|
|
1605
|
+
const walk3 = (comp, prefix, insideThreeD, parentVisible, parentScene, parentMotion = NO_MOTION, parentZero = false, parentAlpha = 1) => {
|
|
1606
1606
|
const blending = /* @__PURE__ */ new Set();
|
|
1607
1607
|
const now = comp.childTime(time);
|
|
1608
1608
|
const cameraMotion = (comp.spec.sequences ?? []).filter((q) => q.type === "camera").reduce((m, q) => either(m, motionOf(q.keyframes, now - comp.childBase - (q.at ?? 0), q.duration ?? comp.duration ?? Infinity, () => true)), NO_MOTION);
|
|
@@ -1668,10 +1668,10 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1668
1668
|
const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
|
|
1669
1669
|
if (group) scene.set(info, group);
|
|
1670
1670
|
if (zero || seq.showsNothingYet) scaledToNothing.add(info);
|
|
1671
|
-
if (seq instanceof CompositionSequence)
|
|
1671
|
+
if (seq instanceof CompositionSequence) walk3(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
|
|
1672
1672
|
});
|
|
1673
1673
|
};
|
|
1674
|
-
|
|
1674
|
+
walk3(root, "", false, true);
|
|
1675
1675
|
const issues = [];
|
|
1676
1676
|
const texts = layers.filter((l) => l.type === "text" && l.visible && l.alpha >= FAINT && l.bounds);
|
|
1677
1677
|
for (const l of texts) {
|
|
@@ -1798,7 +1798,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
|
|
|
1798
1798
|
return true;
|
|
1799
1799
|
};
|
|
1800
1800
|
const inside = (b, p) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
|
|
1801
|
-
const
|
|
1801
|
+
const walk3 = (comp, prefix, parentVisible) => {
|
|
1802
1802
|
comp.layers().forEach(({ seq, display, threeD, carriers, quad }, i) => {
|
|
1803
1803
|
const d = display;
|
|
1804
1804
|
if (!d) return;
|
|
@@ -1811,7 +1811,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
|
|
|
1811
1811
|
if (!visible) return;
|
|
1812
1812
|
const isComposition = seq instanceof CompositionSequence;
|
|
1813
1813
|
if (isComposition && !threeD) {
|
|
1814
|
-
|
|
1814
|
+
walk3(seq, path + "/", visible);
|
|
1815
1815
|
return;
|
|
1816
1816
|
}
|
|
1817
1817
|
if (!wanted(name, path)) return;
|
|
@@ -1839,7 +1839,7 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
|
|
|
1839
1839
|
hit = { name, path, type: seq.spec.type, kind: "three", point, three };
|
|
1840
1840
|
} else if (three === void 0 && (typeof d.containsPoint !== "function" || d.containsPoint(local))) {
|
|
1841
1841
|
let opaque = true;
|
|
1842
|
-
const tex = opts.alpha !== void 0 && seq.spec.type !== "shader" ? d.texture : void 0;
|
|
1842
|
+
const tex = opts.alpha !== void 0 && seq.spec.type !== "shader" ? d.texture ?? deps.textureOf?.(d) : void 0;
|
|
1843
1843
|
if (tex?.source?.resource) {
|
|
1844
1844
|
const res = tex.source.resolution ?? 1;
|
|
1845
1845
|
const a = readAlpha(tex.source.resource, (tex.frame.x + (local.x - lb.x) / lb.width * tex.frame.width) * res, (tex.frame.y + (local.y - lb.y) / lb.height * tex.frame.height) * res);
|
|
@@ -1861,9 +1861,56 @@ function pickScene(root, x, y, opts = {}, deps = {}) {
|
|
|
1861
1861
|
found.push({ hit, order: orderOf(d) });
|
|
1862
1862
|
});
|
|
1863
1863
|
};
|
|
1864
|
-
|
|
1864
|
+
walk3(root, "", true);
|
|
1865
1865
|
return found.sort((a, b) => above(a.order, b.order)).map((f) => f.hit);
|
|
1866
1866
|
}
|
|
1867
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
1868
|
+
function textPictureOf(display) {
|
|
1869
|
+
if (!(display instanceof Text)) return null;
|
|
1870
|
+
const style = display.style;
|
|
1871
|
+
const key = `${display.text}|${display.resolution}|${style.styleKey ?? ""}`;
|
|
1872
|
+
const kept = cache.get(display);
|
|
1873
|
+
if (kept?.key === key) return kept.picture;
|
|
1874
|
+
try {
|
|
1875
|
+
const { canvasAndContext, frame } = CanvasTextGenerator.getCanvasAndContext({ text: display.text, style: display.style, resolution: display.resolution });
|
|
1876
|
+
const copy = document.createElement("canvas");
|
|
1877
|
+
copy.width = canvasAndContext.canvas.width;
|
|
1878
|
+
copy.height = canvasAndContext.canvas.height;
|
|
1879
|
+
copy.getContext("2d").drawImage(canvasAndContext.canvas, 0, 0);
|
|
1880
|
+
CanvasPool.returnCanvasAndContext(canvasAndContext);
|
|
1881
|
+
const picture = { frame: { x: frame.x, y: frame.y, width: frame.width, height: frame.height }, source: { resolution: display.resolution, resource: copy } };
|
|
1882
|
+
cache.set(display, { key, picture });
|
|
1883
|
+
return picture;
|
|
1884
|
+
} catch {
|
|
1885
|
+
return null;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
// src/core/reuse.ts
|
|
1890
|
+
var lendable = (s) => typeof s.reuseKey === "function";
|
|
1891
|
+
var borrower = (s) => typeof s.commitReuse === "function";
|
|
1892
|
+
function walk(root, visit) {
|
|
1893
|
+
for (const { seq } of root.layers()) {
|
|
1894
|
+
visit(seq);
|
|
1895
|
+
const mask = seq.maskSequence;
|
|
1896
|
+
if (mask) visit(mask);
|
|
1897
|
+
if (seq instanceof CompositionSequence) walk(seq, visit);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
function reusePoolOf(old) {
|
|
1901
|
+
const free = /* @__PURE__ */ new Map();
|
|
1902
|
+
if (old) walk(old, (seq) => {
|
|
1903
|
+
if (!lendable(seq)) return;
|
|
1904
|
+
const key = seq.reuseKey();
|
|
1905
|
+
if (key !== null) free.set(key, [...free.get(key) ?? [], seq]);
|
|
1906
|
+
});
|
|
1907
|
+
return { claim: (key) => free.get(key)?.shift() ?? null };
|
|
1908
|
+
}
|
|
1909
|
+
function commitReuse(root) {
|
|
1910
|
+
walk(root, (seq) => {
|
|
1911
|
+
if (borrower(seq)) seq.commitReuse();
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1867
1914
|
|
|
1868
1915
|
// src/core/hover.ts
|
|
1869
1916
|
var keyOf = (h) => h ? `${h.path}${h.three ? `#${h.three.object}` : ""}` : null;
|
|
@@ -1957,7 +2004,7 @@ function detailOf(seq) {
|
|
|
1957
2004
|
if (s.music && typeof s.music === "object") return "music";
|
|
1958
2005
|
return void 0;
|
|
1959
2006
|
}
|
|
1960
|
-
function
|
|
2007
|
+
function walk2(comp, parentStart, parentEnd, depth, prefix, rows, transitions, local) {
|
|
1961
2008
|
const entries = [];
|
|
1962
2009
|
comp._children.forEach((seq, i) => {
|
|
1963
2010
|
const name = seq.spec.name ?? `${seq.spec.type}#${i}`;
|
|
@@ -1970,7 +2017,7 @@ function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions, lo
|
|
|
1970
2017
|
const isComp = seq instanceof CompositionSequence;
|
|
1971
2018
|
if (isComp) {
|
|
1972
2019
|
const label = seq.remapLabel();
|
|
1973
|
-
|
|
2020
|
+
walk2(seq, label ? 0 : start, label ? seq.contentSpan : end, depth + 1, prefix + name + "/", inner, transitions, label ? `${name} ${label}` : local);
|
|
1974
2021
|
}
|
|
1975
2022
|
entries.push({ row, inner, groupable: !isComp });
|
|
1976
2023
|
});
|
|
@@ -2018,7 +2065,7 @@ function collectTimeline(root, duration) {
|
|
|
2018
2065
|
const rows = [];
|
|
2019
2066
|
const transitions = [];
|
|
2020
2067
|
const label = root.remapLabel();
|
|
2021
|
-
|
|
2068
|
+
walk2(root, 0, label ? root.contentSpan : duration, 0, "", rows, transitions, label ? `movie ${label}` : void 0);
|
|
2022
2069
|
return { duration, rows, transitions };
|
|
2023
2070
|
}
|
|
2024
2071
|
var COLORS = {
|
|
@@ -2166,28 +2213,28 @@ function substitute(s, vars) {
|
|
|
2166
2213
|
}
|
|
2167
2214
|
function applyVars(spec, vars) {
|
|
2168
2215
|
if (Object.keys(vars).length === 0) return spec;
|
|
2169
|
-
const
|
|
2216
|
+
const walk3 = (v) => {
|
|
2170
2217
|
if (typeof v === "string") return substitute(v, vars);
|
|
2171
|
-
if (Array.isArray(v)) return v.map(
|
|
2218
|
+
if (Array.isArray(v)) return v.map(walk3);
|
|
2172
2219
|
if (isPlain(v)) {
|
|
2173
2220
|
const o = {};
|
|
2174
|
-
for (const [k, x] of Object.entries(v)) o[k] =
|
|
2221
|
+
for (const [k, x] of Object.entries(v)) o[k] = walk3(x);
|
|
2175
2222
|
return o;
|
|
2176
2223
|
}
|
|
2177
2224
|
return v;
|
|
2178
2225
|
};
|
|
2179
|
-
return
|
|
2226
|
+
return walk3(spec);
|
|
2180
2227
|
}
|
|
2181
2228
|
var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2182
2229
|
function lintVars(spec, vars) {
|
|
2183
2230
|
const declared = Object.keys(vars);
|
|
2184
2231
|
const strings = [];
|
|
2185
|
-
const
|
|
2232
|
+
const walk3 = (v) => {
|
|
2186
2233
|
if (typeof v === "string") strings.push(v);
|
|
2187
|
-
else if (Array.isArray(v)) v.forEach(
|
|
2188
|
-
else if (isPlain(v)) Object.values(v).forEach(
|
|
2234
|
+
else if (Array.isArray(v)) v.forEach(walk3);
|
|
2235
|
+
else if (isPlain(v)) Object.values(v).forEach(walk3);
|
|
2189
2236
|
};
|
|
2190
|
-
|
|
2237
|
+
walk3(spec);
|
|
2191
2238
|
const out = [];
|
|
2192
2239
|
for (const name of declared) {
|
|
2193
2240
|
const word = new RegExp(`(?<![A-Za-z0-9_.])${escapeRe(name)}(?![A-Za-z0-9_])`);
|
|
@@ -2741,7 +2788,7 @@ var Movie = class {
|
|
|
2741
2788
|
* Build the composition tree for these variables and this size, off stage: `{name}` replaced, the root shape (with the numeric variables for the
|
|
2742
2789
|
* expressions), transitions expanded, every layer built. Used by `init` and by `rebuild`.
|
|
2743
2790
|
*/
|
|
2744
|
-
async _construct(composition, vars, width, height, lp, sizeWins = this._resized) {
|
|
2791
|
+
async _construct(composition, vars, width, height, lp, sizeWins = this._resized, reuse) {
|
|
2745
2792
|
const spec = applyVars(composition, vars);
|
|
2746
2793
|
const gpu = sharedGpuOf(this.app?.renderer);
|
|
2747
2794
|
const gpuField = gpu ? { gpu } : {};
|
|
@@ -2752,6 +2799,7 @@ var Movie = class {
|
|
|
2752
2799
|
frameRate: this.frameRate,
|
|
2753
2800
|
vars: numericVars(vars),
|
|
2754
2801
|
...gpuField,
|
|
2802
|
+
...reuse ? { reuse } : {},
|
|
2755
2803
|
...lp ? { onLayerBuilt: async () => {
|
|
2756
2804
|
lp.tick();
|
|
2757
2805
|
await lp.yieldIfDue();
|
|
@@ -2908,7 +2956,7 @@ var Movie = class {
|
|
|
2908
2956
|
if (change.composition !== void 0) for (const w of lintVars(change.composition, vars)) console.warn(w);
|
|
2909
2957
|
changed = changed || sizeChanged || change.composition !== void 0 || reason === "rebuild";
|
|
2910
2958
|
if (!changed) return { reason, ms: 0, audio: "none", frame: this.currentFrame, playing: this.isPlaying, unchanged: true };
|
|
2911
|
-
sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged);
|
|
2959
|
+
sequence = await this._construct(composition, vars, width, height, void 0, this._resized || sizeChanged, reusePoolOf(this._rootSequence));
|
|
2912
2960
|
alive();
|
|
2913
2961
|
const timeline = createTimeline({ paused: true, defaults: { ease: "none" } });
|
|
2914
2962
|
lengthen(timeline, this.duration);
|
|
@@ -2948,6 +2996,7 @@ var Movie = class {
|
|
|
2948
2996
|
if (old?.target) root.removeChild(old.target);
|
|
2949
2997
|
if (sequence.target) root.addChild(sequence.target);
|
|
2950
2998
|
this._rootSequence = sequence;
|
|
2999
|
+
commitReuse(sequence);
|
|
2951
3000
|
this.timeline = timeline;
|
|
2952
3001
|
this._timeFilters = collectTimeFilters(sequence);
|
|
2953
3002
|
const unmute = muteBoundTextureWarning();
|
|
@@ -3222,7 +3271,8 @@ var Movie = class {
|
|
|
3222
3271
|
warnUnknownOptions("movie.pick()", opts, ["only", "ignore", "alpha"]);
|
|
3223
3272
|
this._requireReady("pick");
|
|
3224
3273
|
if (!Number.isFinite(x) || !Number.isFinite(y)) throw new Error(`pixi-effects: movie.pick(x, y) needs two numbers (canvas pixels), got ${x}, ${y}. For a pointer event: movie.pick(...movie.canvasPoint(event))`);
|
|
3225
|
-
|
|
3274
|
+
const textureOf = textPictureOf;
|
|
3275
|
+
return pickScene(this._rootSequence, x, y, opts, { textureOf });
|
|
3226
3276
|
}
|
|
3227
3277
|
/**
|
|
3228
3278
|
* Which layer the pointer is over, as callbacks: `onEnter(hit)` when the layer (or, in a `three` layer, the object) under the pointer changes to another, `onLeave(hit)` for the one it
|
|
@@ -4743,5 +4793,5 @@ function withFade(spec, opts) {
|
|
|
4743
4793
|
}
|
|
4744
4794
|
|
|
4745
4795
|
export { Movie, animateText, audioEnvelope, bpmEnvelope, cameraPath, computeEnvelope, dismissLoader, failLoader, followPath, kenBurns, measureText, musicBuffer, musicEnvelope, musicEvents, orbit, parseEase2 as parseEase, particles, react, resolveLoader, samplePath, splitText, stagger, wiggle, withFade };
|
|
4746
|
-
//# sourceMappingURL=chunk-
|
|
4747
|
-
//# sourceMappingURL=chunk-
|
|
4796
|
+
//# sourceMappingURL=chunk-4MBTYREC.js.map
|
|
4797
|
+
//# sourceMappingURL=chunk-4MBTYREC.js.map
|