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.
- package/CHANGELOG.md +35 -0
- package/README.md +5 -5
- package/ai/chat-template.html +3 -3
- package/ai/tools/check.mjs +38 -9
- package/ai/tools/lookup.mjs +15 -2
- package/dist/{Base-pNq6sVGl.d.cts → Base-BlA6SeqJ.d.cts} +1 -1
- package/dist/{Base-CXsMdmJB.d.ts → Base-YqM0Ljvp.d.ts} +1 -1
- package/dist/Composition-5KZETM4J.js +7 -0
- package/dist/{Composition-NDXKQHB4.js.map → Composition-5KZETM4J.js.map} +1 -1
- package/dist/Composition-MHDI7UNZ.cjs +16 -0
- package/dist/{Composition-OLH7RC5Z.cjs.map → Composition-MHDI7UNZ.cjs.map} +1 -1
- package/dist/Controller.d.cts +2 -2
- package/dist/Controller.d.ts +2 -2
- package/dist/{Movie-VYtUN4TS.d.ts → Movie-CJN_gvLB.d.cts} +26 -2
- package/dist/{Movie-Db4QnMIH.d.cts → Movie-CfHaGmUP.d.ts} +26 -2
- package/dist/{Spans-QHHWC2PB.js → Spans-FSSO2HGX.js} +6 -6
- package/dist/{Spans-QHHWC2PB.js.map → Spans-FSSO2HGX.js.map} +1 -1
- package/dist/{Spans-QRMN2EZO.cjs → Spans-WVJH77BW.cjs} +11 -11
- package/dist/{Spans-QRMN2EZO.cjs.map → Spans-WVJH77BW.cjs.map} +1 -1
- package/dist/{blendModes-WIUCYIMC.cjs → blendModes-FD3THLNC.cjs} +5 -5
- package/dist/{blendModes-WIUCYIMC.cjs.map → blendModes-FD3THLNC.cjs.map} +1 -1
- package/dist/{blendModes-PP5NPBXY.js → blendModes-FMYO6SQN.js} +4 -4
- package/dist/{blendModes-PP5NPBXY.js.map → blendModes-FMYO6SQN.js.map} +1 -1
- package/dist/{chunk-ITDYFBB6.js → chunk-32MMNDDN.js} +7 -6
- package/dist/chunk-32MMNDDN.js.map +1 -0
- package/dist/{chunk-GL2AQFLQ.cjs → chunk-3SAS3ZE5.cjs} +7 -6
- package/dist/chunk-3SAS3ZE5.cjs.map +1 -0
- package/dist/{chunk-7TTCPHAV.cjs → chunk-E26M4NTL.cjs} +235 -49
- package/dist/chunk-E26M4NTL.cjs.map +1 -0
- package/dist/{chunk-XV4L72FW.js → chunk-IPQNT65V.js} +6 -6
- package/dist/chunk-IPQNT65V.js.map +1 -0
- package/dist/{chunk-IMQJVRMT.cjs → chunk-JN7JHNZR.cjs} +5 -5
- package/dist/{chunk-IMQJVRMT.cjs.map → chunk-JN7JHNZR.cjs.map} +1 -1
- package/dist/{chunk-2GNK6DPV.js → chunk-KNDEKWQO.js} +203 -18
- package/dist/chunk-KNDEKWQO.js.map +1 -0
- package/dist/{chunk-5IJENZOG.cjs → chunk-MJMOPWMK.cjs} +92 -92
- package/dist/chunk-MJMOPWMK.cjs.map +1 -0
- package/dist/{chunk-LEECUCTG.cjs → chunk-QHIOOK5S.cjs} +436 -29
- package/dist/chunk-QHIOOK5S.cjs.map +1 -0
- package/dist/{chunk-Y253JQOH.js → chunk-SBR75G73.js} +416 -11
- package/dist/chunk-SBR75G73.js.map +1 -0
- package/dist/{chunk-RTJD3ZSW.js → chunk-VNP7QCJE.js} +4 -4
- package/dist/{chunk-RTJD3ZSW.js.map → chunk-VNP7QCJE.js.map} +1 -1
- package/dist/index.cjs +41 -33
- package/dist/index.d.cts +47 -7
- package/dist/index.d.ts +47 -7
- package/dist/index.js +5 -5
- package/dist/loader.css +1 -1
- package/dist/present.cjs +5 -5
- package/dist/present.d.cts +2 -2
- package/dist/present.d.ts +2 -2
- package/dist/present.js +5 -5
- package/dist/three.cjs +6 -6
- package/dist/three.d.cts +2 -2
- package/dist/three.d.ts +2 -2
- package/dist/three.js +2 -2
- package/dist/{types-CavnUvd5.d.cts → types-DvnXXm33.d.cts} +67 -3
- package/dist/{types-CavnUvd5.d.ts → types-DvnXXm33.d.ts} +67 -3
- package/docs/api.md +5 -4
- package/docs/dsl.md +42 -5
- package/llms-full.txt +305 -22
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +5 -4
- package/skills/pixi-effects/reference/cheatsheet.md +9 -6
- package/skills/pixi-effects/reference/pitfalls.md +7 -0
- package/skills/pixi-effects/reference/recipes.md +237 -3
- package/skills/pixi-effects/template.html +6 -6
- package/dist/Composition-NDXKQHB4.js +0 -7
- package/dist/Composition-OLH7RC5Z.cjs +0 -16
- package/dist/chunk-2GNK6DPV.js.map +0 -1
- package/dist/chunk-5IJENZOG.cjs.map +0 -1
- package/dist/chunk-7TTCPHAV.cjs.map +0 -1
- package/dist/chunk-GL2AQFLQ.cjs.map +0 -1
- package/dist/chunk-ITDYFBB6.js.map +0 -1
- package/dist/chunk-LEECUCTG.cjs.map +0 -1
- package/dist/chunk-XV4L72FW.js.map +0 -1
- package/dist/chunk-Y253JQOH.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { CompositionSequence, maskSourceOf } from './chunk-
|
|
2
|
-
import { EXPRESSION_NAMES, createTimeline, lengthen, ensureFilterLibrary, checkEase, parseEase, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, rand, DEG, timeRemapOf, contentLength, resolveAt, parseSpring, springDuration, spansMeta } from './chunk-
|
|
3
|
-
import { usesAdvancedBlend, enableAdvancedBlend } from './chunk-
|
|
1
|
+
import { CompositionSequence, maskSourceOf } from './chunk-IPQNT65V.js';
|
|
2
|
+
import { EXPRESSION_NAMES, createTimeline, lengthen, ensureFilterLibrary, checkEase, parseEase, homeDistance, DEFAULT_FOV, random, noise, flattenSvgPath, trimPolylines, rand, DEG, timeRemapOf, contentLength, resolveAt, parseSpring, springDuration, spansMeta } from './chunk-SBR75G73.js';
|
|
3
|
+
import { usesAdvancedBlend, enableAdvancedBlend } from './chunk-VNP7QCJE.js';
|
|
4
|
+
import { describeLayer } from './chunk-32MMNDDN.js';
|
|
4
5
|
import { warnUnknownOptions, suggestName } from './chunk-BXOCEZ4U.js';
|
|
5
6
|
import { extensions, CullerPlugin, Application, Container, Rectangle, Culler, CanvasTextMetrics, TextStyle, Assets, ExtensionType, Filter, Texture, UniformGroup, GpuProgram, GlProgram, defaultFilterVert } from 'pixi.js';
|
|
6
7
|
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';
|
|
@@ -390,6 +391,7 @@ function findSilences(channels, sampleRate, opts = {}) {
|
|
|
390
391
|
}
|
|
391
392
|
|
|
392
393
|
// src/core/inspectAudio.ts
|
|
394
|
+
var BURIED_DB = 12;
|
|
393
395
|
var INAUDIBLE_DB = -50;
|
|
394
396
|
var db = (x) => Math.round(10 * Math.max(-120, 20 * Math.log10(x))) / 10;
|
|
395
397
|
var mono = (chans) => {
|
|
@@ -475,6 +477,7 @@ function analyzeAudio(mix, sources, stats, movieDuration, opts = {}) {
|
|
|
475
477
|
return measure(Array.from({ length: b.numberOfChannels }, (_, c) => b.getChannelData(c)), b.sampleRate);
|
|
476
478
|
};
|
|
477
479
|
const scoreNotes = [];
|
|
480
|
+
const buried = [];
|
|
478
481
|
const list = [...sources].sort((a, b) => a.start - b.start).map((s) => {
|
|
479
482
|
const layer = s.layer ?? "an audio layer";
|
|
480
483
|
const source = s.source ?? "audio";
|
|
@@ -502,8 +505,22 @@ function analyzeAudio(mix, sources, stats, movieDuration, opts = {}) {
|
|
|
502
505
|
const over = s.music.seconds - plays;
|
|
503
506
|
if (over > 2 && over / s.music.seconds > 0.15) scoreNotes.push(`${layer} (${source}): the score is ${Math.round(s.music.seconds * 10) / 10}s long but only ${Math.round(plays * 100) / 100}s of it play, so it is cut off mid-phrase. Write fewer bars (or a faster tempo) so it ends with the movie`);
|
|
504
507
|
}
|
|
505
|
-
|
|
508
|
+
let belowMixDb;
|
|
509
|
+
if (!clock && !s.sourceMap && s.start < movieDuration && ownDb > INAUDIBLE_DB) {
|
|
510
|
+
const at = s.start + sound.loudestAt;
|
|
511
|
+
const around = db(peakIn(Math.max(0, at - 0.05), Math.min(movieDuration, at + 0.05)));
|
|
512
|
+
belowMixDb = Math.max(0, Math.round((around - ownDb) * 10) / 10);
|
|
513
|
+
if (!s.music && source.startsWith("sfx") && sound.length <= 4 && belowMixDb >= BURIED_DB) {
|
|
514
|
+
buried.push({ layer, source, ownDb, topVolume, around, belowMixDb, at: Math.round(at * 100) / 100 });
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return { layer, source, start: s.start, end: s.end, peakDb, sound, ...belowMixDb !== void 0 ? { belowMixDb } : {}, ...s.music ? { music: s.music } : {} };
|
|
506
518
|
});
|
|
519
|
+
if (buried.length > 0) {
|
|
520
|
+
const worst = buried.reduce((a, b) => b.belowMixDb > a.belowMixDb ? b : a);
|
|
521
|
+
const distinct = new Set(buried.map((x) => (x.layer === "an audio layer" ? `#${buried.indexOf(x)}` : x.layer) + x.source)).size;
|
|
522
|
+
scoreNotes.push(`${distinct === 1 ? "1 sound effect is" : `${distinct} sound effects are`} buried (${BURIED_DB} dB or more under the loudest sample of the mix around its loudest moment${buried.length > distinct ? `, ${buried.length} times in all` : ""}): the worst is ${worst.layer} (${worst.source}), its own peak ${worst.ownDb} dBFS at volume ${Math.round(worst.topVolume * 100) / 100} against ${worst.around} dBFS at ${worst.at} s, ${worst.belowMixDb} dB louder. If it is meant to be heard, raise its volume (an accent over music needs 2 to 3) or lower what plays there; every source's belowMixDb has its own number`);
|
|
523
|
+
}
|
|
507
524
|
const whole = measureLoudness(chans, sr);
|
|
508
525
|
const silences = findSilences(chans, sr, { minLength: 1 });
|
|
509
526
|
if (whole.clippedSamples > 0) issues.push(`${whole.clippedSamples} sample(s) of the mix are at full scale (clipped) \u2014 lower the volume of the layers that play loudest`);
|
|
@@ -677,12 +694,14 @@ async function reviewMovie(movie, opts = {}) {
|
|
|
677
694
|
if (r.issues.length) perFrame.push({ frame, issues: r.issues });
|
|
678
695
|
}
|
|
679
696
|
const grouped = splitIssues(groupIssues(perFrame), !!opts.strict);
|
|
697
|
+
const canvas = movie.canvasReport();
|
|
698
|
+
for (const r of canvas) if (!r.ok) grouped.problems.push({ message: r.problem, count: 1, firstFrame: 0, lastFrame: 0, kind: "canvas" });
|
|
680
699
|
const fonts = movie.inspectFonts();
|
|
681
700
|
for (const f of fonts.failedUnused) grouped.review.push({ message: `web font "${f}" failed to load, but no text layer uses it (a broken or unused @font-face)`, count: 1, firstFrame: 0, lastFrame: 0 });
|
|
682
701
|
for (const m of fonts.missing) {
|
|
683
702
|
(opts.strict ? grouped.problems : grouped.review).push({ message: `layer "${m.layer}": none of the fonts "${m.family}" is available here (it is drawn in a fallback font)`, count: 1, firstFrame: 0, lastFrame: 0 });
|
|
684
703
|
}
|
|
685
|
-
return { frames: frames.length, problems: grouped.problems, review: grouped.review, fonts, audio: movie.audioBuffer ? movie.inspectAudio() : null, at };
|
|
704
|
+
return { frames: frames.length, problems: grouped.problems, review: grouped.review, canvas, fonts, audio: movie.audioBuffer ? movie.inspectAudio() : null, at };
|
|
686
705
|
}
|
|
687
706
|
|
|
688
707
|
// src/core/onion.ts
|
|
@@ -1881,7 +1900,49 @@ function rejectReservedNameCollision(filters, seqName) {
|
|
|
1881
1900
|
}
|
|
1882
1901
|
}
|
|
1883
1902
|
|
|
1903
|
+
// src/core/canvasCheck.ts
|
|
1904
|
+
var isCanvas = (s) => typeof s.checkDeterminism === "function";
|
|
1905
|
+
var describeDiff = (d) => `${d.t} s${d.pixels >= 0 ? ` (${d.pixels} channel values differ, by up to ${d.maxDiff} of 255)` : ""}`;
|
|
1906
|
+
function canvasReport(root) {
|
|
1907
|
+
const out = [];
|
|
1908
|
+
const visit = (seq) => {
|
|
1909
|
+
if (isCanvas(seq)) {
|
|
1910
|
+
const length = seq.duration ?? 0;
|
|
1911
|
+
const moments = [0.15, 0.4, 0.65, 0.9].map((f) => Math.round(f * length * 1e3) / 1e3);
|
|
1912
|
+
const { differs, drawMs, threw } = seq.checkDeterminism(moments);
|
|
1913
|
+
const name = describeLayer(seq.spec);
|
|
1914
|
+
const problem = threw.length ? `${name} could not draw at ${threw.map((t) => `${t} s`).join(", ")} (its draw threw, or the layer failed to build): 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): 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` : "";
|
|
1915
|
+
out.push({ name: seq.spec.name ?? name, moments, drawMs, ok: !differs.length && !threw.length, problem });
|
|
1916
|
+
}
|
|
1917
|
+
if (seq instanceof CompositionSequence) walk2(seq);
|
|
1918
|
+
};
|
|
1919
|
+
const walk2 = (comp) => {
|
|
1920
|
+
for (const { seq } of comp.layers()) {
|
|
1921
|
+
visit(seq);
|
|
1922
|
+
const mask = seq.maskSequence;
|
|
1923
|
+
if (mask) visit(mask);
|
|
1924
|
+
}
|
|
1925
|
+
};
|
|
1926
|
+
walk2(root);
|
|
1927
|
+
return out;
|
|
1928
|
+
}
|
|
1929
|
+
var canvasProblems = (root) => canvasReport(root).filter((r) => !r.ok).map((r) => r.problem);
|
|
1930
|
+
|
|
1884
1931
|
// src/core/inspect.ts
|
|
1932
|
+
function chainOf(node) {
|
|
1933
|
+
const chain = [];
|
|
1934
|
+
for (let n = node; n && n.parent; n = n.parent) {
|
|
1935
|
+
const i = n.parent.children?.indexOf(n);
|
|
1936
|
+
if (i === void 0 || i < 0) return null;
|
|
1937
|
+
chain.unshift(i);
|
|
1938
|
+
}
|
|
1939
|
+
return chain.length ? chain : null;
|
|
1940
|
+
}
|
|
1941
|
+
function drawnAfter(a, b) {
|
|
1942
|
+
if (!a || !b) return null;
|
|
1943
|
+
for (let i = 0; i < Math.min(a.length, b.length); i++) if (a[i] !== b[i]) return a[i] > b[i];
|
|
1944
|
+
return null;
|
|
1945
|
+
}
|
|
1885
1946
|
var FAINT = 0.3;
|
|
1886
1947
|
var intersection = (a, b) => {
|
|
1887
1948
|
const w = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);
|
|
@@ -1925,7 +1986,11 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1925
1986
|
const scene = /* @__PURE__ */ new Map();
|
|
1926
1987
|
const scaledToNothing = /* @__PURE__ */ new Set();
|
|
1927
1988
|
const hasMotion = /* @__PURE__ */ new Set();
|
|
1928
|
-
const
|
|
1989
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
1990
|
+
const specs = /* @__PURE__ */ new Map();
|
|
1991
|
+
const chains = /* @__PURE__ */ new Map();
|
|
1992
|
+
const chainAlpha = /* @__PURE__ */ new Map();
|
|
1993
|
+
const walk2 = (comp, prefix, insideThreeD, parentVisible, parentScene, parentMotion = NO_MOTION, parentZero = false, parentAlpha = 1) => {
|
|
1929
1994
|
const blending = /* @__PURE__ */ new Set();
|
|
1930
1995
|
const now = comp.childTime(time);
|
|
1931
1996
|
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);
|
|
@@ -1969,6 +2034,10 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1969
2034
|
if (threeD) info.depthBlur = Number(depthBlur.toFixed(2));
|
|
1970
2035
|
if (threeD && light !== void 0) info.light = Number(light.toFixed(2));
|
|
1971
2036
|
layers.push(info);
|
|
2037
|
+
chainAlpha.set(info, parentAlpha * alpha);
|
|
2038
|
+
chains.set(info, chainOf(display));
|
|
2039
|
+
specs.set(info, seq.spec);
|
|
2040
|
+
kinds.set(info, seq.spec.type === "shape" ? String(seq.spec.shape) : seq.spec.type);
|
|
1972
2041
|
if (motion.ever) hasMotion.add(info);
|
|
1973
2042
|
let shown = bounds ? clip(bounds, view) : null;
|
|
1974
2043
|
const maskTarget = seq.maskSequence?.target;
|
|
@@ -1981,7 +2050,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
1981
2050
|
const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
|
|
1982
2051
|
if (group) scene.set(info, group);
|
|
1983
2052
|
if (zero || seq.showsNothingYet) scaledToNothing.add(info);
|
|
1984
|
-
if (seq instanceof CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero);
|
|
2053
|
+
if (seq instanceof CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, motion, zero, parentAlpha * alpha);
|
|
1985
2054
|
});
|
|
1986
2055
|
};
|
|
1987
2056
|
walk2(root, "", false, true);
|
|
@@ -2017,6 +2086,42 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
|
|
|
2017
2086
|
if (share > 0.25) issues.push(`text layers "${texts[i].path}" and "${texts[j].path}" overlap by ${Math.round(share * 100)}% of the smaller one`);
|
|
2018
2087
|
}
|
|
2019
2088
|
}
|
|
2089
|
+
const FILLED = /* @__PURE__ */ new Set(["rect", "circle", "ellipse", "image", "video"]);
|
|
2090
|
+
for (const t of texts) {
|
|
2091
|
+
const tb = seen.get(t);
|
|
2092
|
+
if (!tb) continue;
|
|
2093
|
+
const area = tb.width * tb.height;
|
|
2094
|
+
if (!(area > 0)) continue;
|
|
2095
|
+
for (const o of layers) {
|
|
2096
|
+
if (o === t || o.type === "text" || !o.visible || !o.bounds) continue;
|
|
2097
|
+
if (o.path.startsWith(t.path + "/") || t.path.startsWith(o.path + "/")) continue;
|
|
2098
|
+
const kind = kinds.get(o) ?? o.type, spec = specs.get(o) ?? {};
|
|
2099
|
+
const initial = spec.initial ?? {};
|
|
2100
|
+
const hairline = (kind === "line" || kind === "rect") && Math.min(o.bounds.width, o.bounds.height) < 12 && Math.max(o.bounds.width, o.bounds.height) >= 40;
|
|
2101
|
+
const soft = spec.blendMode !== void 0 && spec.blendMode !== "normal" || Array.isArray(spec.filters) && spec.filters.length > 0;
|
|
2102
|
+
const filled = kind === "image" || kind === "video" || initial.fillColor !== void 0 || spec.fillGradient !== void 0 || initial.fillGradient !== void 0;
|
|
2103
|
+
if (o.threeD || t.threeD) continue;
|
|
2104
|
+
const over = drawnAfter(chains.get(o) ?? null, chains.get(t) ?? null) ?? layers.indexOf(o) > layers.indexOf(t);
|
|
2105
|
+
const opacity = chainAlpha.get(o) ?? o.alpha;
|
|
2106
|
+
const hides = FILLED.has(kind) && !hairline && filled && !soft && over && opacity >= 0.8;
|
|
2107
|
+
const cuts = hairline && !soft && over && opacity >= FAINT;
|
|
2108
|
+
if (!hides && !cuts) continue;
|
|
2109
|
+
const st = scene.get(t), so = scene.get(o);
|
|
2110
|
+
if (st && so && st !== so) continue;
|
|
2111
|
+
const ob = seen.get(o);
|
|
2112
|
+
if (!ob) continue;
|
|
2113
|
+
const covered = intersection(tb, ob) / area;
|
|
2114
|
+
if (cuts) {
|
|
2115
|
+
const horizontal = ob.width >= ob.height;
|
|
2116
|
+
const mid = horizontal ? ob.y + ob.height / 2 : ob.x + ob.width / 2;
|
|
2117
|
+
const lo = horizontal ? tb.y + tb.height * 0.2 : tb.x + tb.width * 0.2, hi = horizontal ? tb.y + tb.height * 0.8 : tb.x + tb.width * 0.8;
|
|
2118
|
+
const along = horizontal ? Math.min(ob.x + ob.width, tb.x + tb.width) - Math.max(ob.x, tb.x) : Math.min(ob.y + ob.height, tb.y + tb.height) - Math.max(ob.y, tb.y);
|
|
2119
|
+
if (mid < lo || mid > hi || along < 0.3 * (horizontal ? tb.width : tb.height)) continue;
|
|
2120
|
+
} else if (covered < 0.1) continue;
|
|
2121
|
+
const word = kind === "image" || kind === "video" ? kind : "shape";
|
|
2122
|
+
issues.push(`text layer "${t.path}" and ${word} layer "${o.path}" overlap: it covers ${Math.round(covered * 100)}% of the text (drawn ${over ? "over" : "under"} it)`);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2020
2125
|
const mode = opts.layers ?? "all";
|
|
2021
2126
|
const listed = mode === "none" ? [] : mode === "visible" ? layers.filter((l) => l.visible) : layers;
|
|
2022
2127
|
return { frame, time, canvas, summary: { layers: layers.length, visible: layers.filter((l) => l.visible).length }, issues, layers: listed };
|
|
@@ -3286,6 +3391,16 @@ var Movie = class {
|
|
|
3286
3391
|
await this.gotoFrame(f, true);
|
|
3287
3392
|
return inspectScene(this._rootSequence, f, f / this.frameRate, { width: this.width, height: this.height }, opts);
|
|
3288
3393
|
}
|
|
3394
|
+
/** The canvas layers whose same frame is not the same picture (what `check` reports as a problem): one sentence each. */
|
|
3395
|
+
canvasProblems() {
|
|
3396
|
+
this._requireReady("canvasProblems");
|
|
3397
|
+
return canvasProblems(this._rootSequence);
|
|
3398
|
+
}
|
|
3399
|
+
/** Every canvas layer as the determinism check saw it: the moments drawn, the usual time of a draw in ms, and whether the same time gave the same picture (`check` prints it even when all pass). */
|
|
3400
|
+
canvasReport() {
|
|
3401
|
+
this._requireReady("canvasReport");
|
|
3402
|
+
return canvasReport(this._rootSequence);
|
|
3403
|
+
}
|
|
3289
3404
|
/**
|
|
3290
3405
|
* Every layer as a row with absolute start / end / keyframe times (seconds), plus the transition windows. Runs of
|
|
3291
3406
|
* similar layers (`pop-1` … `pop-12`) are one row with a `parts` span each. Read it, or draw it with `timelineChart()`.
|
|
@@ -3716,7 +3831,7 @@ function measureText(text, style = {}) {
|
|
|
3716
3831
|
return { width: m.width, height: m.height, lines: text.split("\n").length };
|
|
3717
3832
|
}
|
|
3718
3833
|
function splitText(text, style = {}, opts = {}) {
|
|
3719
|
-
warnUnknownOptions("splitText()", opts, ["by", "x", "y", "align"]);
|
|
3834
|
+
warnUnknownOptions("splitText()", opts, ["by", "x", "y", "align", "vertical"]);
|
|
3720
3835
|
let by = opts.by ?? "chars";
|
|
3721
3836
|
if (!BY.includes(by)) {
|
|
3722
3837
|
const g = suggestName(String(by), BY);
|
|
@@ -3735,6 +3850,7 @@ function splitText(text, style = {}, opts = {}) {
|
|
|
3735
3850
|
}
|
|
3736
3851
|
if (text === "") return [];
|
|
3737
3852
|
const ts = new TextStyle(style);
|
|
3853
|
+
if (opts.vertical) return splitVertical(text, style, ts, x0, y0, align, by);
|
|
3738
3854
|
const lineHeight = CanvasTextMetrics.measureText("M", ts).lineHeight ?? CanvasTextMetrics.measureText("M", ts).height;
|
|
3739
3855
|
const sentinel = widthOf("|", ts);
|
|
3740
3856
|
const out = [];
|
|
@@ -3758,6 +3874,36 @@ function splitText(text, style = {}, opts = {}) {
|
|
|
3758
3874
|
});
|
|
3759
3875
|
return out;
|
|
3760
3876
|
}
|
|
3877
|
+
var PUNCT = new Set("\u3001\u3002\uFF0C\uFF0E\uFF64\uFF61");
|
|
3878
|
+
var SMALL = new Set("\u3041\u3043\u3045\u3047\u3049\u3063\u3083\u3085\u3087\u308E\u30A1\u30A3\u30A5\u30A7\u30A9\u30C3\u30E3\u30E5\u30E7\u30EE\u30F5\u30F6");
|
|
3879
|
+
var TURNED = new Set("\u30FC\u2015\u2014\uFF0D\uFF5E\u301C\u2026\u2025\uFF08\uFF09()\uFF3B\uFF3D[]\uFF5B\uFF5D{}\u300C\u300D\u300E\u300F\u3008\u3009\u300A\u300B\u3010\u3011\u3014\u3015<>\u226A\u226B");
|
|
3880
|
+
function splitVertical(text, style, ts, right, top, align, by) {
|
|
3881
|
+
if (by !== "chars") console.warn(`pixi-effects: splitText(): vertical text has only a "chars" form (by "${by}" has no vertical layout); using "chars"`);
|
|
3882
|
+
const em = typeof style.fontSize === "number" ? style.fontSize : Number(ts.fontSize) || 26;
|
|
3883
|
+
const spacing = typeof style.letterSpacing === "number" ? style.letterSpacing : 0;
|
|
3884
|
+
const cell = em + spacing;
|
|
3885
|
+
const lineHeight = CanvasTextMetrics.measureText("M", ts).lineHeight ?? CanvasTextMetrics.measureText("M", ts).height;
|
|
3886
|
+
const out = [];
|
|
3887
|
+
text.split("\n").forEach((line, lineNo) => {
|
|
3888
|
+
const chars = Array.from(line);
|
|
3889
|
+
const start = align === "center" ? top - chars.length * cell / 2 : align === "right" ? top - chars.length * cell : top;
|
|
3890
|
+
const column = right - lineHeight / 2 - lineNo * lineHeight;
|
|
3891
|
+
chars.forEach((ch, i) => {
|
|
3892
|
+
if (/\s/.test(ch)) return;
|
|
3893
|
+
let cx = column, cy = start + i * cell + cell / 2;
|
|
3894
|
+
if (PUNCT.has(ch)) {
|
|
3895
|
+
cx += 0.5 * em;
|
|
3896
|
+
cy -= 0.5 * em;
|
|
3897
|
+
} else if (SMALL.has(ch)) {
|
|
3898
|
+
cx += 0.1 * em;
|
|
3899
|
+
cy -= 0.1 * em;
|
|
3900
|
+
}
|
|
3901
|
+
const width = widthOf(ch, ts);
|
|
3902
|
+
out.push({ text: ch, x: cx - width / 2, y: cy - lineHeight / 2, width, index: out.length, line: lineNo, cx, cy, rotation: TURNED.has(ch) ? 90 : 0 });
|
|
3903
|
+
});
|
|
3904
|
+
});
|
|
3905
|
+
return out;
|
|
3906
|
+
}
|
|
3761
3907
|
|
|
3762
3908
|
// src/presets/_ease.ts
|
|
3763
3909
|
function parseEase2(name) {
|
|
@@ -4096,6 +4242,45 @@ function followPath(opts) {
|
|
|
4096
4242
|
return out;
|
|
4097
4243
|
}
|
|
4098
4244
|
|
|
4245
|
+
// src/core/pathSample.ts
|
|
4246
|
+
function samplePath(d) {
|
|
4247
|
+
if (typeof d !== "string" || d.trim() === "") throw new Error('samplePath(): d must be SVG path data, e.g. "M 40 300 C 200 20 400 20 560 300"');
|
|
4248
|
+
const lines = flattenSvgPath(d);
|
|
4249
|
+
const segments = [];
|
|
4250
|
+
let length = 0;
|
|
4251
|
+
for (const line of lines) {
|
|
4252
|
+
const p = line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
|
|
4253
|
+
for (let i = 2; i < p.length; i += 2) {
|
|
4254
|
+
const len = Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
|
|
4255
|
+
if (len === 0) continue;
|
|
4256
|
+
segments.push({ x0: p[i - 2], y0: p[i - 1], x1: p[i], y1: p[i + 1], start: length, len });
|
|
4257
|
+
length += len;
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
if (!(length > 0)) throw new Error(`samplePath(): the path "${d}" has no length (it needs at least two different points)`);
|
|
4261
|
+
const find = (s) => {
|
|
4262
|
+
let lo = 0, hi = segments.length - 1;
|
|
4263
|
+
while (lo < hi) {
|
|
4264
|
+
const mid = lo + hi >> 1;
|
|
4265
|
+
if (segments[mid].start + segments[mid].len < s) lo = mid + 1;
|
|
4266
|
+
else hi = mid;
|
|
4267
|
+
}
|
|
4268
|
+
return segments[lo];
|
|
4269
|
+
};
|
|
4270
|
+
return {
|
|
4271
|
+
length,
|
|
4272
|
+
at(u) {
|
|
4273
|
+
const s = Math.min(Math.max(Number.isFinite(u) ? u : 0, 0), 1) * length;
|
|
4274
|
+
const g = find(s);
|
|
4275
|
+
const t = Math.min(Math.max((s - g.start) / g.len, 0), 1);
|
|
4276
|
+
return { x: g.x0 + (g.x1 - g.x0) * t, y: g.y0 + (g.y1 - g.y0) * t, angle: Math.atan2(g.y1 - g.y0, g.x1 - g.x0) };
|
|
4277
|
+
},
|
|
4278
|
+
trim(start = 0, end = 1) {
|
|
4279
|
+
return trimPolylines(lines, start, end).map((l) => l.closed ? [...l.pts, l.pts[0], l.pts[1]] : l.pts);
|
|
4280
|
+
}
|
|
4281
|
+
};
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4099
4284
|
// src/presets/cameraPath.ts
|
|
4100
4285
|
var KEYS4 = ["points", "at", "duration", "ease", "look", "lookAhead", "smooth", "from", "to", "frameRate"];
|
|
4101
4286
|
var MAX_STEPS2 = 4e3;
|
|
@@ -4273,7 +4458,7 @@ function wiggle(opts) {
|
|
|
4273
4458
|
// src/presets/animateText.ts
|
|
4274
4459
|
var TWEEN_KEYS = ["preset", "from", "to", "duration", "ease"];
|
|
4275
4460
|
var TIME_KEYS = ["at", "delay", "start", "begin", "time"];
|
|
4276
|
-
var KEYS5 = ["by", "x", "y", "align", "name", "at", "duration", "in", "out", "stagger", "idle", "styleFor"];
|
|
4461
|
+
var KEYS5 = ["by", "x", "y", "align", "vertical", "name", "at", "duration", "in", "out", "stagger", "idle", "styleFor"];
|
|
4277
4462
|
var PRESETS = {
|
|
4278
4463
|
rise: { from: { y: 40, alpha: 0 }, duration: 0.5, ease: "back.out(2)" },
|
|
4279
4464
|
drop: { from: { y: -50, alpha: 0 }, duration: 0.7, ease: "bounce.out" },
|
|
@@ -4286,14 +4471,14 @@ var PRESETS = {
|
|
|
4286
4471
|
var PRESET_NAMES = Object.keys(PRESETS);
|
|
4287
4472
|
function animateText(text, style, options) {
|
|
4288
4473
|
warnUnknownOptions("animateText()", options, KEYS5);
|
|
4289
|
-
const { by = "chars", x = 0, y = 0, align = "left", name = "text", at = 0, duration, idle, styleFor } = options;
|
|
4474
|
+
const { by = "chars", x = 0, y = 0, align = "left", vertical = false, name = "text", at = 0, duration, idle, styleFor } = options;
|
|
4290
4475
|
for (const [n, v] of [["x", x], ["y", y]]) {
|
|
4291
4476
|
if (typeof v !== "number" || !Number.isFinite(v)) {
|
|
4292
4477
|
throw new Error(`animateText(): ${n} must be a number of pixels (e.g. ${n}: 640), got ${JSON.stringify(v)}. The letters are measured once, when the layers are made, so an expression such as 'GW/2' cannot be used here: compute the number (W / 2) yourself. \`y\` is the TOP of the line.`);
|
|
4293
4478
|
}
|
|
4294
4479
|
}
|
|
4295
4480
|
if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`animateText(): duration must be a positive number of seconds (how long until the last piece is gone), got ${duration}`);
|
|
4296
|
-
const pieces = splitText(text, style, { by, x, y, align });
|
|
4481
|
+
const pieces = splitText(text, style, { by, x, y, align, ...vertical ? { vertical } : {} });
|
|
4297
4482
|
if (pieces.length === 0) return [];
|
|
4298
4483
|
const lineHeight = measureText("M", style).height;
|
|
4299
4484
|
const inTween = options.in === false ? null : resolveTween(options.in ?? "rise", "in");
|
|
@@ -4312,12 +4497,12 @@ function animateText(text, style, options) {
|
|
|
4312
4497
|
if (inTween && outTween.at - at < inTween.duration - 1e-9) console.warn(`pixi-effects: animateText(): out.at ${outTween.at} is before the entrance has finished (it takes ${inTween.duration} s from at ${at}): use out.at \u2265 ${+(at + inTween.duration).toFixed(3)}`);
|
|
4313
4498
|
}
|
|
4314
4499
|
return pieces.map((p, i) => {
|
|
4315
|
-
const cx = p.x + p.width / 2, cy = p.y + lineHeight / 2;
|
|
4316
|
-
const rest = { x: cx, y: cy, alpha: 1, scale: 1, scaleX: 1, scaleY: 1, rotation: 0 };
|
|
4500
|
+
const cx = p.cx ?? p.x + p.width / 2, cy = p.cy ?? p.y + lineHeight / 2;
|
|
4501
|
+
const rest = { x: cx, y: cy, alpha: 1, scale: 1, scaleX: 1, scaleY: 1, rotation: p.rotation ?? 0 };
|
|
4317
4502
|
const start = at + delays[i];
|
|
4318
4503
|
const len = end - start;
|
|
4319
4504
|
const keyframes = [];
|
|
4320
|
-
const place = (key, v) => key === "x" || key === "y" ? rest[key] + v : v;
|
|
4505
|
+
const place = (key, v) => key === "x" || key === "y" || key === "rotation" ? rest[key] + v : v;
|
|
4321
4506
|
if (inTween) {
|
|
4322
4507
|
keyframes.push({
|
|
4323
4508
|
at: 0,
|
|
@@ -4356,7 +4541,7 @@ function animateText(text, style, options) {
|
|
|
4356
4541
|
style: styleFor ? { ...style, ...styleFor(p) } : style,
|
|
4357
4542
|
at: start,
|
|
4358
4543
|
duration: len,
|
|
4359
|
-
initial: { x: cx, y: cy, anchorX: 0.5, anchorY: 0.5 },
|
|
4544
|
+
initial: { x: cx, y: cy, anchorX: 0.5, anchorY: 0.5, ...rest.rotation !== 0 ? { rotation: rest.rotation } : {} },
|
|
4360
4545
|
keyframes
|
|
4361
4546
|
};
|
|
4362
4547
|
});
|
|
@@ -4697,6 +4882,6 @@ function withFade(spec, opts) {
|
|
|
4697
4882
|
return spec;
|
|
4698
4883
|
}
|
|
4699
4884
|
|
|
4700
|
-
export { Movie, animateText, audioEnvelope, bpmEnvelope, cameraPath, computeEnvelope, dismissLoader, failLoader, followPath, kenBurns, measureText, musicBuffer, musicEnvelope, musicEvents, orbit, parseEase2 as parseEase, particles, react, resolveLoader, splitText, stagger, wiggle, withFade };
|
|
4701
|
-
//# sourceMappingURL=chunk-
|
|
4702
|
-
//# sourceMappingURL=chunk-
|
|
4885
|
+
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 };
|
|
4886
|
+
//# sourceMappingURL=chunk-KNDEKWQO.js.map
|
|
4887
|
+
//# sourceMappingURL=chunk-KNDEKWQO.js.map
|