@vosjs/studio-core 0.17.0 → 0.18.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/dist/index.d.ts +128 -7
- package/dist/index.js +446 -71
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -456,9 +456,35 @@ var CARD_ENTER_KINDS = [
|
|
|
456
456
|
"fade",
|
|
457
457
|
"rise",
|
|
458
458
|
"tilt-in",
|
|
459
|
-
"pull-out"
|
|
459
|
+
"pull-out",
|
|
460
|
+
"slide"
|
|
460
461
|
];
|
|
461
462
|
var CARD_EXIT_KINDS = ["none", "fade", "recede"];
|
|
463
|
+
var SEGMENT_ENTER_KINDS = [
|
|
464
|
+
"none",
|
|
465
|
+
"slide",
|
|
466
|
+
"fade",
|
|
467
|
+
"scale"
|
|
468
|
+
];
|
|
469
|
+
var SEGMENT_EXIT_KINDS = [
|
|
470
|
+
"none",
|
|
471
|
+
"slide",
|
|
472
|
+
"fade",
|
|
473
|
+
"scale"
|
|
474
|
+
];
|
|
475
|
+
var TRANSITION_SECONDS = 0.6;
|
|
476
|
+
var TRANSITION_SECONDS_MIN = 0.15;
|
|
477
|
+
var TRANSITION_SECONDS_MAX = 2;
|
|
478
|
+
function transitionSeconds(step) {
|
|
479
|
+
if (!step || step.kind === "none") return 0;
|
|
480
|
+
return Math.max(
|
|
481
|
+
TRANSITION_SECONDS_MIN,
|
|
482
|
+
Math.min(TRANSITION_SECONDS_MAX, step.seconds ?? TRANSITION_SECONDS)
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
function slideSide(step, which) {
|
|
486
|
+
return step?.side ?? (which === "enter" ? "right" : "left");
|
|
487
|
+
}
|
|
462
488
|
var TEXT_ENTER_KINDS = [
|
|
463
489
|
"none",
|
|
464
490
|
"fade",
|
|
@@ -797,7 +823,7 @@ function cardPoseTrack(enter, exit, footageEnd, outputEnd2 = footageEnd) {
|
|
|
797
823
|
const keyframes = [];
|
|
798
824
|
const s = entranceSeconds(enter);
|
|
799
825
|
if (s) {
|
|
800
|
-
const from = enter?.kind === "rise" ? [0.96, 0.08, 0.7] : enter?.kind === "pull-out" || enter?.kind === "fade" ? enter.kind === "fade" ? [1, 0, 0] : [1, 0, 1] : [0.94, 0.05, 0.7];
|
|
826
|
+
const from = enter?.kind === "rise" ? [0.96, 0.08, 0.7] : enter?.kind === "pull-out" || enter?.kind === "fade" || enter?.kind === "slide" ? enter.kind === "fade" ? [1, 0, 0] : [1, 0, 1] : [0.94, 0.05, 0.7];
|
|
801
827
|
keyframes.push({ t: 0, value: from, ease: "none" });
|
|
802
828
|
keyframes.push({ t: s, value: [1, 0, 1], ease: "power3.out" });
|
|
803
829
|
}
|
|
@@ -1698,8 +1724,8 @@ import {
|
|
|
1698
1724
|
segmentRate as segmentRate2,
|
|
1699
1725
|
sortKeyframes as sortKeyframes2,
|
|
1700
1726
|
sourceToTimeline,
|
|
1701
|
-
splitBySpeed as
|
|
1702
|
-
totalDuration as
|
|
1727
|
+
splitBySpeed as splitBySpeed3,
|
|
1728
|
+
totalDuration as totalDuration3
|
|
1703
1729
|
} from "@vosjs/timeline";
|
|
1704
1730
|
import { timelineRuntimeCode as timelineRuntimeCode2 } from "@vosjs/timeline/bundle";
|
|
1705
1731
|
|
|
@@ -2188,6 +2214,142 @@ function resolveText3dAsset(asset) {
|
|
|
2188
2214
|
};
|
|
2189
2215
|
}
|
|
2190
2216
|
|
|
2217
|
+
// src/lower/segmentStarts.ts
|
|
2218
|
+
import { splitBySpeed as splitBySpeed2, totalDuration as totalDuration2 } from "@vosjs/timeline";
|
|
2219
|
+
function effectiveSegments(doc) {
|
|
2220
|
+
if (isRecordingDoc(doc) && doc.segments.length) return doc.segments;
|
|
2221
|
+
return [{ in: 0, out: anchorSourceDuration(doc) }];
|
|
2222
|
+
}
|
|
2223
|
+
var contiguous = (segments, i) => i > 0 && sameMedia(segments[i - 1].media, segments[i].media) && Math.abs(segments[i - 1].out - segments[i].in) < 1e-9;
|
|
2224
|
+
var segmentPieces = (seg, speeds, freezes, leading) => {
|
|
2225
|
+
const own = freezes.filter(
|
|
2226
|
+
(f) => sameMedia(f.media, seg.media) && f.at <= seg.out + 1e-9 && (leading ? f.at >= seg.in - 1e-9 : f.at > seg.in + 1e-9)
|
|
2227
|
+
);
|
|
2228
|
+
return withFreezes(
|
|
2229
|
+
splitBySpeed2(
|
|
2230
|
+
[seg],
|
|
2231
|
+
speeds.filter((sp) => sameMedia(sp.media, seg.media))
|
|
2232
|
+
),
|
|
2233
|
+
own
|
|
2234
|
+
);
|
|
2235
|
+
};
|
|
2236
|
+
var outputLen = (seg, speeds, freezes = [], leading = true) => totalDuration2(segmentPieces(seg, speeds, freezes, leading));
|
|
2237
|
+
var segmentStarts = (segments, speeds, freezes = []) => {
|
|
2238
|
+
const starts = [];
|
|
2239
|
+
let acc = 0;
|
|
2240
|
+
segments.forEach((s, i) => {
|
|
2241
|
+
starts.push(acc);
|
|
2242
|
+
acc += outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i));
|
|
2243
|
+
});
|
|
2244
|
+
return starts;
|
|
2245
|
+
};
|
|
2246
|
+
function segmentOutputExtents(doc) {
|
|
2247
|
+
const segments = effectiveSegments(doc);
|
|
2248
|
+
const speeds = doc.speed ?? [];
|
|
2249
|
+
const freezes = isRecordingDoc(doc) ? docFreezes(doc) : [];
|
|
2250
|
+
const starts = segmentStarts(segments, speeds, freezes);
|
|
2251
|
+
return segments.map((s, i) => ({
|
|
2252
|
+
start: starts[i],
|
|
2253
|
+
end: starts[i] + outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i))
|
|
2254
|
+
}));
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
// src/lower/transitions.ts
|
|
2258
|
+
var TRANSITION_TRAVEL = 1.04;
|
|
2259
|
+
var TRANSITION_SCALE_STEP = 0.08;
|
|
2260
|
+
var GHOST_Z_LIFT = 4e-3;
|
|
2261
|
+
var GHOST_RENDER_ORDER = 1.1;
|
|
2262
|
+
var LAST_FRAME_BACK = 2e-3;
|
|
2263
|
+
var round32 = (v) => Math.round(v * 1e3) / 1e3;
|
|
2264
|
+
function move(step, which, cap) {
|
|
2265
|
+
const secs = transitionSeconds(step);
|
|
2266
|
+
if (!secs || !step) return null;
|
|
2267
|
+
const kind = step.kind;
|
|
2268
|
+
if (kind !== "slide" && kind !== "fade" && kind !== "scale") return null;
|
|
2269
|
+
const d = Math.min(secs, cap);
|
|
2270
|
+
if (d < TRANSITION_SECONDS_MIN) return null;
|
|
2271
|
+
return { kind, side: slideSide(step, which), d: round32(d) };
|
|
2272
|
+
}
|
|
2273
|
+
function docTransitions(doc) {
|
|
2274
|
+
const out = [];
|
|
2275
|
+
const segments = doc.segments;
|
|
2276
|
+
const extents = segmentOutputExtents(doc);
|
|
2277
|
+
const open = enterOf(doc.frame.anim);
|
|
2278
|
+
if (open?.kind === "slide" && extents.length) {
|
|
2279
|
+
const first = extents[0];
|
|
2280
|
+
const m = move(open, "enter", Math.max(0, first.end - first.start) / 2);
|
|
2281
|
+
if (m) out.push({ t: 0, d: m.d, in: m });
|
|
2282
|
+
}
|
|
2283
|
+
for (let i = 0; i + 1 < segments.length; i++) {
|
|
2284
|
+
const a = segments[i];
|
|
2285
|
+
const b = segments[i + 1];
|
|
2286
|
+
const la = extents[i].end - extents[i].start;
|
|
2287
|
+
const lb = extents[i + 1].end - extents[i + 1].start;
|
|
2288
|
+
const cap = Math.min(la, lb) / 2;
|
|
2289
|
+
const exit = move(exitOf(a.anim), "exit", cap);
|
|
2290
|
+
const enter = move(enterOf(b.anim), "enter", cap);
|
|
2291
|
+
if (!exit && !enter) continue;
|
|
2292
|
+
const t = round32(extents[i + 1].start);
|
|
2293
|
+
const rec = {
|
|
2294
|
+
t,
|
|
2295
|
+
d: Math.max(exit?.d ?? 0, enter?.d ?? 0)
|
|
2296
|
+
};
|
|
2297
|
+
if (enter) rec.in = enter;
|
|
2298
|
+
if (exit)
|
|
2299
|
+
rec.out = {
|
|
2300
|
+
...exit,
|
|
2301
|
+
media: a.media ?? "",
|
|
2302
|
+
at: round32(Math.max(a.in, a.out - LAST_FRAME_BACK))
|
|
2303
|
+
};
|
|
2304
|
+
out.push(rec);
|
|
2305
|
+
}
|
|
2306
|
+
return out;
|
|
2307
|
+
}
|
|
2308
|
+
var REST_LEAD = 0.1;
|
|
2309
|
+
function restSpansThroughTransitions(spans, rated, transitions, minLen) {
|
|
2310
|
+
const windows = transitions.filter((tr) => tr.t > 0 && tr.d > 0);
|
|
2311
|
+
if (!windows.length) return [...spans];
|
|
2312
|
+
const out = [];
|
|
2313
|
+
for (const span of spans) {
|
|
2314
|
+
const start = outputStartOf(rated, span.in, span.out, span.media);
|
|
2315
|
+
const win = start === null ? void 0 : windows.find((tr) => start >= tr.t - REST_LEAD && start < tr.t + tr.d);
|
|
2316
|
+
if (start === null || !win) {
|
|
2317
|
+
out.push(span);
|
|
2318
|
+
continue;
|
|
2319
|
+
}
|
|
2320
|
+
const after = win.t + win.d;
|
|
2321
|
+
if (mediaAtOutput(rated, after) !== (span.media ?? "")) continue;
|
|
2322
|
+
const head = sourceAt(rated, after);
|
|
2323
|
+
if (head === null || head > span.out - minLen) continue;
|
|
2324
|
+
out.push({ ...span, in: round32(head) });
|
|
2325
|
+
}
|
|
2326
|
+
return out;
|
|
2327
|
+
}
|
|
2328
|
+
function outputStartOf(rated, sIn, sOut, media) {
|
|
2329
|
+
let acc = 0;
|
|
2330
|
+
for (const p of rated) {
|
|
2331
|
+
const rate = p.rate ?? 1;
|
|
2332
|
+
const len = Math.max(0, p.out - p.in) / rate;
|
|
2333
|
+
if (sameMedia(p.media, media)) {
|
|
2334
|
+
const ovIn = Math.max(sIn, p.in);
|
|
2335
|
+
const ovOut = Math.min(sOut, p.out);
|
|
2336
|
+
if (ovOut > ovIn) return acc + (ovIn - p.in) / rate;
|
|
2337
|
+
}
|
|
2338
|
+
acc += len;
|
|
2339
|
+
}
|
|
2340
|
+
return null;
|
|
2341
|
+
}
|
|
2342
|
+
function sourceAt(rated, t) {
|
|
2343
|
+
let acc = 0;
|
|
2344
|
+
for (const p of rated) {
|
|
2345
|
+
const rate = p.rate ?? 1;
|
|
2346
|
+
const len = Math.max(0, p.out - p.in) / rate;
|
|
2347
|
+
if (t < acc + len) return p.in + (t - acc) * rate;
|
|
2348
|
+
acc += len;
|
|
2349
|
+
}
|
|
2350
|
+
return null;
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2191
2353
|
// src/lower/audioEnvelope.ts
|
|
2192
2354
|
function clipEnvelope(clip) {
|
|
2193
2355
|
const span = clipLength(clip);
|
|
@@ -3698,7 +3860,7 @@ function ratedSegments(doc) {
|
|
|
3698
3860
|
const freezes = isRecordingDoc(doc) ? docFreezes(doc) : [];
|
|
3699
3861
|
const speeds = doc.speed ?? [];
|
|
3700
3862
|
const pieces = segs.flatMap(
|
|
3701
|
-
(seg) =>
|
|
3863
|
+
(seg) => splitBySpeed3(
|
|
3702
3864
|
[seg],
|
|
3703
3865
|
speeds.filter(
|
|
3704
3866
|
(sp) => sameMedia(sp.media, seg.media)
|
|
@@ -3708,7 +3870,7 @@ function ratedSegments(doc) {
|
|
|
3708
3870
|
return withFreezes(pieces, freezes);
|
|
3709
3871
|
}
|
|
3710
3872
|
function durationSec(doc, rated) {
|
|
3711
|
-
const trimmed =
|
|
3873
|
+
const trimmed = totalDuration3(rated);
|
|
3712
3874
|
return trimmed > 0 ? trimmed : doc.source.meta.durationMs / 1e3;
|
|
3713
3875
|
}
|
|
3714
3876
|
function spanOutputExtent(segments, sIn, sOut, media) {
|
|
@@ -4036,6 +4198,52 @@ var FRAME_STEP_SRC = `
|
|
|
4036
4198
|
}
|
|
4037
4199
|
bgEl.currentTime = bgTarget
|
|
4038
4200
|
}
|
|
4201
|
+
}
|
|
4202
|
+
// A GHOST element: a second element on the same bytes, held on ONE frame
|
|
4203
|
+
// (a transition's outgoing snapshot), so one recording can be both cards
|
|
4204
|
+
// at a boundary. Cached by source. Its readiness rides pendingDecodes,
|
|
4205
|
+
// so an export's first paint inside a window waits for it, and a frame
|
|
4206
|
+
// asked before it could seek is sought the moment it can.
|
|
4207
|
+
function ghostEl(el) {
|
|
4208
|
+
var gc = ns.ghostCache || (ns.ghostCache = new Map())
|
|
4209
|
+
var src = el.currentSrc || el.src
|
|
4210
|
+
var g = gc.get(src)
|
|
4211
|
+
if (g) return g
|
|
4212
|
+
g = document.createElement('video')
|
|
4213
|
+
g.crossOrigin = 'anonymous'
|
|
4214
|
+
g.muted = true
|
|
4215
|
+
g.playsInline = true
|
|
4216
|
+
g.preload = 'auto'
|
|
4217
|
+
var gp = new Promise(function (resolve) {
|
|
4218
|
+
var fired = false
|
|
4219
|
+
var done = function () {
|
|
4220
|
+
if (fired) return
|
|
4221
|
+
fired = true
|
|
4222
|
+
g.removeEventListener('loadeddata', done)
|
|
4223
|
+
if (g.__voilaWant != null && g.readyState >= 1) {
|
|
4224
|
+
var want = g.__voilaWant
|
|
4225
|
+
g.__voilaWant = null
|
|
4226
|
+
var seeked = function () { g.removeEventListener('seeked', seeked); resolve() }
|
|
4227
|
+
g.addEventListener('seeked', seeked)
|
|
4228
|
+
setTimeout(seeked, 250)
|
|
4229
|
+
g.currentTime = Math.min(want, g.duration || want)
|
|
4230
|
+
} else resolve()
|
|
4231
|
+
}
|
|
4232
|
+
g.addEventListener('loadeddata', done)
|
|
4233
|
+
setTimeout(done, 2000)
|
|
4234
|
+
})
|
|
4235
|
+
if (ns.pendingDecodes) {
|
|
4236
|
+
ns.pendingDecodes.add(gp)
|
|
4237
|
+
gp.finally(function () { ns.pendingDecodes.delete(gp) })
|
|
4238
|
+
}
|
|
4239
|
+
g.src = src
|
|
4240
|
+
g.load()
|
|
4241
|
+
gc.set(src, g)
|
|
4242
|
+
return g
|
|
4243
|
+
}
|
|
4244
|
+
function stepGhost(g, at) {
|
|
4245
|
+
if (g.readyState >= 1) { g.__voilaWant = null; stepPaused(g, at); return }
|
|
4246
|
+
g.__voilaWant = at
|
|
4039
4247
|
}`;
|
|
4040
4248
|
var SETUP = `async (ctx) => {
|
|
4041
4249
|
;${timelineRuntimeCode2}
|
|
@@ -4302,6 +4510,15 @@ var SETUP = `async (ctx) => {
|
|
|
4302
4510
|
if (cam) stepPaused(cam, srcT)
|
|
4303
4511
|
if (mic) stepPaused(mic, srcT)
|
|
4304
4512
|
}
|
|
4513
|
+
// A boundary's outgoing snapshot: the ghost element on the clip's last
|
|
4514
|
+
// frame, stepped like the footage, and warmed a second ahead.
|
|
4515
|
+
const trs = d.transitions || []
|
|
4516
|
+
for (let i = 0; i < trs.length; i++) {
|
|
4517
|
+
const tr = trs[i]
|
|
4518
|
+
if (!tr.out || t < tr.t - 1 || t >= tr.t + tr.d) continue
|
|
4519
|
+
const live = tr.out.media ? mediaEls[tr.out.media] : video
|
|
4520
|
+
if (live && live.play) stepGhost(ghostEl(live), tr.out.at)
|
|
4521
|
+
}
|
|
4305
4522
|
const bg2 = d.frame && d.frame.backgroundMedia
|
|
4306
4523
|
if (bg2 && bg2.key && bg2.kind !== 'image') {
|
|
4307
4524
|
const bgEl2 = cache.get(bg2.key)
|
|
@@ -4579,6 +4796,87 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
4579
4796
|
if (video.play) syncVid(video) // stills (HTMLImageElement) have nothing to sync
|
|
4580
4797
|
if (r.cam && !amId) syncVid(r.cam)
|
|
4581
4798
|
if (r.mic && !amId) syncVid(r.mic)
|
|
4799
|
+
|
|
4800
|
+
// --- transitions at a boundary (data.transitions, output seconds): the
|
|
4801
|
+
// record whose window holds t, else the next one within a second (its
|
|
4802
|
+
// snapshot warms ahead, so playback never shows a blank ghost). Locals
|
|
4803
|
+
// are tr-prefixed (one var scope).
|
|
4804
|
+
var trs = d.transitions || [], trA = null, trW = null
|
|
4805
|
+
for (var trI = 0; trI < trs.length; trI++) {
|
|
4806
|
+
var trR = trs[trI]
|
|
4807
|
+
if (t >= trR.t && t < trR.t + trR.d) { trA = trR; break }
|
|
4808
|
+
if (trR.out && trR.t > t && trR.t <= t + 1) { trW = trR; break }
|
|
4809
|
+
}
|
|
4810
|
+
function trEase(p) { p = p < 0 ? 0 : p > 1 ? 1 : p; var q = 1 - p; return 1 - q * q * q }
|
|
4811
|
+
function trSide(side) { return side === 'left' ? [-1, 0] : side === 'right' ? [1, 0] : side === 'up' ? [0, 1] : [0, -1] }
|
|
4812
|
+
// A record's outgoing media: its live element, and the GHOST held on the
|
|
4813
|
+
// clip's last frame (a second element on the same bytes, so one
|
|
4814
|
+
// recording can be both cards: a page change inside a single take). A
|
|
4815
|
+
// still needs no ghost.
|
|
4816
|
+
function trLiveEl(o) { return o.media ? (r.media ? r.media[o.media] : null) : r.video }
|
|
4817
|
+
function trGhostEl(o) {
|
|
4818
|
+
var el = trLiveEl(o)
|
|
4819
|
+
if (!el) return null
|
|
4820
|
+
if (!el.play) return el
|
|
4821
|
+
var g = ghostEl(el)
|
|
4822
|
+
stepGhost(g, o.at)
|
|
4823
|
+
return g
|
|
4824
|
+
}
|
|
4825
|
+
if (trW) trGhostEl(trW.out)
|
|
4826
|
+
var trIn = trA && trA.in ? trA.in : null, trOut = trA && trA.out ? trA.out : null
|
|
4827
|
+
var trGEl = trOut ? trGhostEl(trOut) : null
|
|
4828
|
+
if (!trGEl) trOut = null
|
|
4829
|
+
// The live card's enter and the ghost's exit as plane-fraction offsets
|
|
4830
|
+
// (x in plane widths, y in plane heights; +y up), a scale and an opacity.
|
|
4831
|
+
var trOx = 0, trOy = 0, trSc = 1, trOp = 1
|
|
4832
|
+
if (trIn) {
|
|
4833
|
+
var trP = 1 - trEase((t - trA.t) / trIn.d)
|
|
4834
|
+
if (trIn.kind === 'slide') { var trV = trSide(trIn.side); trOx = trV[0] * trP * ${TRANSITION_TRAVEL}; trOy = trV[1] * trP * ${TRANSITION_TRAVEL} }
|
|
4835
|
+
else if (trIn.kind === 'fade') trOp = 1 - trP
|
|
4836
|
+
else { trSc = 1 - ${TRANSITION_SCALE_STEP} * trP; trOp = 1 - trP }
|
|
4837
|
+
}
|
|
4838
|
+
var trGox = 0, trGoy = 0, trGsc = 1, trGop = 1
|
|
4839
|
+
if (trOut) {
|
|
4840
|
+
var trQ = trEase((t - trA.t) / trOut.d)
|
|
4841
|
+
if (trOut.kind === 'slide') { var trV2 = trSide(trOut.side); trGox = trV2[0] * trQ * ${TRANSITION_TRAVEL}; trGoy = trV2[1] * trQ * ${TRANSITION_TRAVEL} }
|
|
4842
|
+
else if (trOut.kind === 'fade') trGop = 1 - trQ
|
|
4843
|
+
else { trGsc = 1 - ${TRANSITION_SCALE_STEP} * trQ; trGop = 1 - trQ }
|
|
4844
|
+
}
|
|
4845
|
+
var trGM = null
|
|
4846
|
+
if (trOut && trOut.media && d.media) for (var trJ = 0; trJ < d.media.length; trJ++) if (d.media[trJ].id === trOut.media) trGM = d.media[trJ]
|
|
4847
|
+
var trGFr = trOut && trOut.media && trGM && trGM.frame ? trGM.frame : frame
|
|
4848
|
+
// The ghost layer: made on first need (most documents never cross a
|
|
4849
|
+
// boundary), sized with the card layer, above it and below the stack's
|
|
4850
|
+
// card planes. The flat stub paints the ghost on the card's own canvas.
|
|
4851
|
+
var gh = null
|
|
4852
|
+
if (trOut) {
|
|
4853
|
+
if (!r.ghost && ctx.THREE && ctx.scene && card.mesh) {
|
|
4854
|
+
var gT = ctx.THREE
|
|
4855
|
+
var gCv = document.createElement('canvas')
|
|
4856
|
+
gCv.width = cW; gCv.height = cH
|
|
4857
|
+
var gTex = new gT.CanvasTexture(gCv)
|
|
4858
|
+
gTex.colorSpace = gT.SRGBColorSpace
|
|
4859
|
+
gTex.minFilter = gT.LinearFilter; gTex.magFilter = gT.LinearFilter; gTex.generateMipmaps = false
|
|
4860
|
+
var gPh = 2 * ${Math.abs(CARD_Z)} * Math.tan(${CARD_FOV} * Math.PI / 180 / 2)
|
|
4861
|
+
var gMesh = new gT.Mesh(new gT.PlaneGeometry(gPh * (W / H) * (cW / W), gPh * (cH / H)), new gT.MeshBasicMaterial({ map: gTex, transparent: true, depthTest: false, depthWrite: false }))
|
|
4862
|
+
gMesh.position.set(0, 0, ${CARD_Z + GHOST_Z_LIFT})
|
|
4863
|
+
gMesh.frustumCulled = false
|
|
4864
|
+
gMesh.renderOrder = ${GHOST_RENDER_ORDER}
|
|
4865
|
+
ctx.scene.add(gMesh)
|
|
4866
|
+
r.ghost = { canvas: gCv, c2d: gCv.getContext('2d'), texture: gTex, mesh: gMesh, w: cW, h: cH }
|
|
4867
|
+
}
|
|
4868
|
+
gh = r.ghost || { c2d: c, canvas: cv }
|
|
4869
|
+
if (r.ghost && (r.ghost.w !== cW || r.ghost.h !== cH)) {
|
|
4870
|
+
r.ghost.canvas.width = cW; r.ghost.canvas.height = cH; r.ghost.w = cW; r.ghost.h = cH
|
|
4871
|
+
if (r.ghost.texture.dispose) r.ghost.texture.dispose()
|
|
4872
|
+
if (r.ghost.mesh.geometry && r.ghost.mesh.geometry.dispose) r.ghost.mesh.geometry.dispose()
|
|
4873
|
+
var gPh2 = 2 * ${Math.abs(CARD_Z)} * Math.tan(${CARD_FOV} * Math.PI / 180 / 2)
|
|
4874
|
+
r.ghost.mesh.geometry = new ctx.THREE.PlaneGeometry(gPh2 * (W / H) * (cW / W), gPh2 * (cH / H))
|
|
4875
|
+
}
|
|
4876
|
+
}
|
|
4877
|
+
// The two cursor points a boundary lerps between (frame space), set by
|
|
4878
|
+
// the passes below: the ghost's last, the live card's current.
|
|
4879
|
+
var trQa = null, trQb = null, trQs = 0, trQw = 0
|
|
4582
4880
|
// Gain routing (live via SET_DATA). With a mic sidecar (AT split) the
|
|
4583
4881
|
// recording <video> carries SYSTEM audio \u2014 its volume is the system fader \u2014
|
|
4584
4882
|
// and the sidecar element is the voice (micGain). Legacy takes have one
|
|
@@ -4775,6 +5073,19 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
4775
5073
|
if (bg.texture) bg.texture.needsUpdate = true
|
|
4776
5074
|
}
|
|
4777
5075
|
|
|
5076
|
+
// The card paints in PASSES: the ghost first (the outgoing clip's last
|
|
5077
|
+
// frame on its own canvas, while a boundary is crossing), then the live
|
|
5078
|
+
// card. A pass swaps the canvas, the element, the media, the source
|
|
5079
|
+
// moment and the card fields it reads; the ghost reads the camera as it
|
|
5080
|
+
// stood at the boundary and paints no clicks and no dot. The live pass
|
|
5081
|
+
// runs last, so everything after the loop reads the live card.
|
|
5082
|
+
var lvVideo = video, lvActM = actM, lvAmId = amId, lvSrcT = srcT, lvCfr = cfr, lvC = c, lvCv = cv
|
|
5083
|
+
var trPasses = trOut ? [true, false] : [false]
|
|
5084
|
+
for (var tpI = 0; tpI < trPasses.length; tpI++) {
|
|
5085
|
+
var tpG = trPasses[tpI]
|
|
5086
|
+
var tpT = t
|
|
5087
|
+
if (tpG) { c = gh.c2d; cv = gh.canvas; video = trGEl; actM = trGM; amId = trOut.media; srcT = trOut.at; cfr = trGFr; tpT = trA.t - 0.001 }
|
|
5088
|
+
else { c = lvC; cv = lvCv; video = lvVideo; actM = lvActM; amId = lvAmId; srcT = lvSrcT; cfr = lvCfr }
|
|
4778
5089
|
// The CARD layer canvas starts transparent each frame \u2014 the padding around
|
|
4779
5090
|
// the contain-fit card shows the background layer through the plane's alpha.
|
|
4780
5091
|
// Under overscan the canvas is larger than the frame and every frame-space
|
|
@@ -4831,7 +5142,7 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
4831
5142
|
var lvl = 1, zx = 0.5, zy = 0.5
|
|
4832
5143
|
var zt = d.zoomTrack
|
|
4833
5144
|
if (zt && zt.keyframes && zt.keyframes.length) {
|
|
4834
|
-
var z = TL.sample(zt,
|
|
5145
|
+
var z = TL.sample(zt, tpT, TL.lerpArray)
|
|
4835
5146
|
lvl = z[0]; zx = z[1]; zy = z[2]
|
|
4836
5147
|
}
|
|
4837
5148
|
var barH = bar.kind && bar.kind !== 'none' ? (bar.height || 44) * s2 : 0
|
|
@@ -5034,7 +5345,7 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5034
5345
|
var cks = d.clicks || []
|
|
5035
5346
|
var ckF = d.clickFx || {}
|
|
5036
5347
|
var ckPress = 1
|
|
5037
|
-
if (cks.length) {
|
|
5348
|
+
if (cks.length && !tpG) {
|
|
5038
5349
|
var ckK = ckF.k || 1
|
|
5039
5350
|
var ckPre = ${CLICK_FX_PRE}
|
|
5040
5351
|
var ckRD = ${CLICK_RIPPLE_DUR} * (ckF.dur || 1)
|
|
@@ -5193,7 +5504,15 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5193
5504
|
}
|
|
5194
5505
|
}
|
|
5195
5506
|
}
|
|
5196
|
-
|
|
5507
|
+
// Across a boundary the dot is ONE, drawn in frame space on the
|
|
5508
|
+
// overlay layer (the lerp after the passes), never on a card that is
|
|
5509
|
+
// moving: each pass leaves its point, mapped out of the zoom.
|
|
5510
|
+
if (trOut) {
|
|
5511
|
+
var tqX = ax, tqY = ay
|
|
5512
|
+
if (lvl > 1.001 && !d.zoomSuppressed) { tqX = fx + (ax - fx) * lvl; tqY = fy + (ay - fy) * lvl }
|
|
5513
|
+
if (tpG) trQa = [tqX, tqY]
|
|
5514
|
+
else { trQb = [tqX, tqY]; trQs = curSize * ckPress; trQw = s2 }
|
|
5515
|
+
} else if (cuA > 0.01) {
|
|
5197
5516
|
c.save()
|
|
5198
5517
|
c.fillStyle = 'rgba(255,255,255,' + (0.95 * cuA) + ')'
|
|
5199
5518
|
c.strokeStyle = 'rgba(0,0,0,' + (0.4 * cuA) + ')'
|
|
@@ -5204,6 +5523,8 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5204
5523
|
}
|
|
5205
5524
|
if (fitCover) c.restore()
|
|
5206
5525
|
c.restore()
|
|
5526
|
+
if (tpG && gh.texture) gh.texture.needsUpdate = true
|
|
5527
|
+
}
|
|
5207
5528
|
|
|
5208
5529
|
// --- OVERLAY layer (screen-space plane, never tilts): the cam bubble, the
|
|
5209
5530
|
// recording's own footage. Text/image/video overlay CLIPS are the studio
|
|
@@ -5221,7 +5542,7 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5221
5542
|
if (camV && camV.readyState >= 2) r.camHasFrame = true
|
|
5222
5543
|
var camActiveNow = !!(camV && camOn && camS.visible !== false && r.camHasFrame)
|
|
5223
5544
|
var ovSig = W + 'x' + H
|
|
5224
|
-
var ovDirty = ov.sig !== ovSig || camActiveNow || ov.active
|
|
5545
|
+
var ovDirty = ov.sig !== ovSig || camActiveNow || ov.active || !!trOut
|
|
5225
5546
|
if (ovDirty) {
|
|
5226
5547
|
ovC.clearRect(0, 0, W, H)
|
|
5227
5548
|
// webcam bubble \u2014 pinned to the frame corner regardless of card tilt/zoom.
|
|
@@ -5271,8 +5592,26 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5271
5592
|
ovC.restore()
|
|
5272
5593
|
}
|
|
5273
5594
|
}
|
|
5595
|
+
// The boundary dot: the outgoing card's last cursor point and the
|
|
5596
|
+
// incoming's current one, each carried by its card's move, one dot on
|
|
5597
|
+
// the eased lerp between them (the idle fade never fires across a page
|
|
5598
|
+
// change). It lands on the incoming's own dot as the window closes.
|
|
5599
|
+
if (trOut && (trQa || trQb) && !(d.cursorStyle && d.cursorStyle.visible === false)) {
|
|
5600
|
+
var tqA = trQa || trQb, tqB = trQb || trQa
|
|
5601
|
+
var tqP = trEase((t - trA.t) / trA.d)
|
|
5602
|
+
var tqAx = (tqA[0] - W / 2) * trGsc + W / 2 + trGox * W, tqAy = (tqA[1] - H / 2) * trGsc + H / 2 - trGoy * H
|
|
5603
|
+
var tqBx = (tqB[0] - W / 2) * trSc + W / 2 + trOx * W, tqBy = (tqB[1] - H / 2) * trSc + H / 2 - trOy * H
|
|
5604
|
+
var tqX2 = tqAx + (tqBx - tqAx) * tqP, tqY2 = tqAy + (tqBy - tqAy) * tqP
|
|
5605
|
+
var tqR = trQs || (((d.cursorStyle && d.cursorStyle.size) || 24) * s * 0.5)
|
|
5606
|
+
ovC.save()
|
|
5607
|
+
ovC.fillStyle = 'rgba(255,255,255,0.95)'
|
|
5608
|
+
ovC.strokeStyle = 'rgba(0,0,0,0.4)'
|
|
5609
|
+
ovC.lineWidth = 2 * (trQw || s)
|
|
5610
|
+
ovC.beginPath(); ovC.arc(tqX2, tqY2, tqR, 0, Math.PI * 2); ovC.fill(); ovC.stroke()
|
|
5611
|
+
ovC.restore()
|
|
5612
|
+
}
|
|
5274
5613
|
ov.sig = ovSig
|
|
5275
|
-
ov.active = camActiveNow
|
|
5614
|
+
ov.active = camActiveNow || !!trOut
|
|
5276
5615
|
if (ov.texture) ov.texture.needsUpdate = true
|
|
5277
5616
|
}
|
|
5278
5617
|
|
|
@@ -5301,15 +5640,22 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5301
5640
|
// which the overscan grows).
|
|
5302
5641
|
var cpk = d.cardPoseTrack
|
|
5303
5642
|
var cpH = 2 * ${Math.abs(CARD_Z)} * Math.tan(${CARD_FOV} * Math.PI / 180 / 2)
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5643
|
+
var cpv = null
|
|
5644
|
+
if (cpk && cpk.keyframes && cpk.keyframes.length) cpv = TL.sample(cpk, t, TL.lerpArray)
|
|
5645
|
+
if ((cpv || trIn) && card.mesh.scale && card.mesh.position) {
|
|
5646
|
+
// The pose track, then a boundary's enter on top: a slide is an
|
|
5647
|
+
// offset in plane widths and heights, a fade an opacity, a scale both.
|
|
5648
|
+
var cpS = (cpv ? cpv[0] : 1) * trSc
|
|
5649
|
+
var cpO = (cpv ? cpv[2] : 1) * trOp
|
|
5650
|
+
card.mesh.scale.x = cpS; card.mesh.scale.y = cpS
|
|
5651
|
+
card.mesh.position.x = trOx * cpH * (W / H)
|
|
5652
|
+
card.mesh.position.y = ((cpv ? cpv[1] : 0) + trOy) * cpH
|
|
5653
|
+
if (card.mesh.material) card.mesh.material.opacity = cpO
|
|
5309
5654
|
// Gone past its clip: the mesh leaves the scene, not just its paint.
|
|
5310
|
-
card.mesh.visible =
|
|
5311
|
-
} else if (card.mesh.scale && card.mesh.position && ((card.mesh.scale.x !== undefined && card.mesh.scale.x !== 1) || (card.mesh.position.y !== undefined && card.mesh.position.y !== 0) || card.mesh.visible === false)) {
|
|
5655
|
+
card.mesh.visible = cpO > 0.001
|
|
5656
|
+
} else if (card.mesh.scale && card.mesh.position && ((card.mesh.scale.x !== undefined && card.mesh.scale.x !== 1) || (card.mesh.position.y !== undefined && card.mesh.position.y !== 0) || (card.mesh.position.x !== undefined && card.mesh.position.x !== 0) || card.mesh.visible === false)) {
|
|
5312
5657
|
card.mesh.scale.x = 1; card.mesh.scale.y = 1
|
|
5658
|
+
card.mesh.position.x = 0
|
|
5313
5659
|
card.mesh.position.y = 0
|
|
5314
5660
|
if (card.mesh.material) card.mesh.material.opacity = 1
|
|
5315
5661
|
card.mesh.visible = true
|
|
@@ -5326,6 +5672,29 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5326
5672
|
}
|
|
5327
5673
|
}
|
|
5328
5674
|
|
|
5675
|
+
// The ghost plane: posed as the card stood at the boundary (its lean, its
|
|
5676
|
+
// pose), then moved by its exit; hidden while no boundary is crossing.
|
|
5677
|
+
if (r.ghost && r.ghost.mesh) {
|
|
5678
|
+
var gm = r.ghost.mesh
|
|
5679
|
+
if (trOut && gh === r.ghost) {
|
|
5680
|
+
var gtT = trA.t - 0.001
|
|
5681
|
+
var grx = 0, gry = 0
|
|
5682
|
+
var gtk = d.tiltTrack
|
|
5683
|
+
if (gtk && gtk.keyframes && gtk.keyframes.length && !d.tiltSuppressed) { var gtv = TL.sample(gtk, gtT, TL.lerpArray); grx = gtv[0] * Math.PI / 180; gry = gtv[1] * Math.PI / 180 }
|
|
5684
|
+
gm.rotation.x = grx; gm.rotation.y = gry
|
|
5685
|
+
var gpv = [1, 0, 1]
|
|
5686
|
+
var gpk = d.cardPoseTrack
|
|
5687
|
+
if (gpk && gpk.keyframes && gpk.keyframes.length) gpv = TL.sample(gpk, gtT, TL.lerpArray)
|
|
5688
|
+
var gpH = 2 * ${Math.abs(CARD_Z)} * Math.tan(${CARD_FOV} * Math.PI / 180 / 2)
|
|
5689
|
+
var gS = gpv[0] * trGsc, gO = gpv[2] * trGop
|
|
5690
|
+
gm.scale.x = gS; gm.scale.y = gS
|
|
5691
|
+
gm.position.x = trGox * gpH * (W / H)
|
|
5692
|
+
gm.position.y = (gpv[1] + trGoy) * gpH
|
|
5693
|
+
if (gm.material) gm.material.opacity = gO
|
|
5694
|
+
gm.visible = gO > 0.001
|
|
5695
|
+
} else if (gm.visible !== false) gm.visible = false
|
|
5696
|
+
}
|
|
5697
|
+
|
|
5329
5698
|
// The card layer redraws every frame (dynamic content); bg/overlay uploads are
|
|
5330
5699
|
// gated inside their blocks (dirty-tracking above).
|
|
5331
5700
|
if (card.texture) card.texture.needsUpdate = true
|
|
@@ -5550,6 +5919,7 @@ function studioLayerData(layers, duration) {
|
|
|
5550
5919
|
function lowerToComposition(input) {
|
|
5551
5920
|
const doc = migrateMotion(input);
|
|
5552
5921
|
const rated = ratedSegments(doc);
|
|
5922
|
+
const transitions = docTransitions(doc);
|
|
5553
5923
|
const footageEnd = durationSec(doc, rated);
|
|
5554
5924
|
const duration = outputEnd(doc, footageEnd);
|
|
5555
5925
|
const fx = doc.cursor.clickFx;
|
|
@@ -5564,7 +5934,12 @@ function lowerToComposition(input) {
|
|
|
5564
5934
|
const layout = docCardLayout(doc);
|
|
5565
5935
|
const meta = doc.source.meta;
|
|
5566
5936
|
const zoomStyle = resolveZoomStyle(doc.zoomStyle, doc.zoomParams);
|
|
5567
|
-
const zoomSpans =
|
|
5937
|
+
const zoomSpans = restSpansThroughTransitions(
|
|
5938
|
+
doc.zoom,
|
|
5939
|
+
rated,
|
|
5940
|
+
transitions,
|
|
5941
|
+
ZOOM_SPAN_MIN
|
|
5942
|
+
).map((z) => {
|
|
5568
5943
|
if (z.focusMode === "auto") {
|
|
5569
5944
|
const f = followFocusEvents(
|
|
5570
5945
|
z,
|
|
@@ -5651,6 +6026,9 @@ function lowerToComposition(input) {
|
|
|
5651
6026
|
})
|
|
5652
6027
|
} : {},
|
|
5653
6028
|
...cursorFade.length ? { cursorFade } : {},
|
|
6029
|
+
// The boundaries that move: ON_FRAME reads them by output time and
|
|
6030
|
+
// needs no document. Absent when none moves.
|
|
6031
|
+
...transitions.length ? { transitions } : {},
|
|
5654
6032
|
// Click effects: OUTPUT-anchored click records + resolved styling (named
|
|
5655
6033
|
// intensities/colors become numbers HERE — ON_FRAME reads no registry).
|
|
5656
6034
|
...clickFxData(doc, rated),
|
|
@@ -5668,7 +6046,13 @@ function lowerToComposition(input) {
|
|
|
5668
6046
|
// producer of this track, never a second pose.
|
|
5669
6047
|
...(() => {
|
|
5670
6048
|
const head = entranceTiltKeyframes(cardEnter(doc.frame));
|
|
5671
|
-
const
|
|
6049
|
+
const tilts = doc.tilt ? restSpansThroughTransitions(
|
|
6050
|
+
doc.tilt,
|
|
6051
|
+
rated,
|
|
6052
|
+
transitions,
|
|
6053
|
+
TILT_SPAN_MIN
|
|
6054
|
+
) : [];
|
|
6055
|
+
const spans = tilts.length ? tiltTrackFromDoc(tilts, rated, zoomStyle.tilt) : void 0;
|
|
5672
6056
|
const track = prependEntrance(spans, head);
|
|
5673
6057
|
return track ? { tiltTrack: track } : {};
|
|
5674
6058
|
})(),
|
|
@@ -6145,18 +6529,18 @@ function zoomCoversRect(span, rect, layout, tol = 0.02) {
|
|
|
6145
6529
|
}
|
|
6146
6530
|
|
|
6147
6531
|
// src/digest/style.ts
|
|
6148
|
-
import { totalDuration as
|
|
6532
|
+
import { totalDuration as totalDuration5 } from "@vosjs/timeline";
|
|
6149
6533
|
|
|
6150
6534
|
// src/audioBeds.ts
|
|
6151
|
-
import { totalDuration as
|
|
6152
|
-
var
|
|
6535
|
+
import { totalDuration as totalDuration4 } from "@vosjs/timeline";
|
|
6536
|
+
var round33 = (v) => Math.round(v * 1e3) / 1e3;
|
|
6153
6537
|
function docOutputDuration(doc) {
|
|
6154
|
-
const footage =
|
|
6538
|
+
const footage = totalDuration4(ratedSegments(doc));
|
|
6155
6539
|
return isRecordingDoc(doc) ? outputEnd(migrateMotion(doc), footage) : footage;
|
|
6156
6540
|
}
|
|
6157
6541
|
function musicBedClip(input) {
|
|
6158
|
-
const track =
|
|
6159
|
-
const output =
|
|
6542
|
+
const track = round33(input.trackDuration);
|
|
6543
|
+
const output = round33(input.outputDuration);
|
|
6160
6544
|
const clip = {
|
|
6161
6545
|
id: input.id,
|
|
6162
6546
|
key: input.key,
|
|
@@ -6171,7 +6555,7 @@ function musicBedClip(input) {
|
|
|
6171
6555
|
fadeIn: 0,
|
|
6172
6556
|
// A bed that just stops reads as a glitch; clamp so a very short cut
|
|
6173
6557
|
// still spends most of its time at full level.
|
|
6174
|
-
fadeOut: output > 0 ? Math.min(1.5,
|
|
6558
|
+
fadeOut: output > 0 ? Math.min(1.5, round33(output / 4)) : 1.5,
|
|
6175
6559
|
duck: input.hasMic || void 0
|
|
6176
6560
|
};
|
|
6177
6561
|
if (output <= 0) return clip;
|
|
@@ -6200,18 +6584,18 @@ function refillAudioBeds(doc, prevDuration, nextDuration) {
|
|
|
6200
6584
|
const placed = clip.loop ? Math.max(clip.out - clip.in, clip.loopLen ?? clip.out - clip.in) : clip.out - clip.in;
|
|
6201
6585
|
if (Math.abs(clip.start + placed - prevDuration) > EPS2) continue;
|
|
6202
6586
|
const span = clip.out - clip.in;
|
|
6203
|
-
const nextLen =
|
|
6587
|
+
const nextLen = round33(nextDuration - clip.start);
|
|
6204
6588
|
if (clip.loop) {
|
|
6205
6589
|
if (nextLen >= span) {
|
|
6206
6590
|
clip.loopLen = nextLen;
|
|
6207
6591
|
} else {
|
|
6208
6592
|
clip.loop = void 0;
|
|
6209
6593
|
clip.loopLen = void 0;
|
|
6210
|
-
clip.out =
|
|
6594
|
+
clip.out = round33(clip.in + nextLen);
|
|
6211
6595
|
}
|
|
6212
6596
|
} else {
|
|
6213
6597
|
if (clip.in + nextLen <= clip.duration) {
|
|
6214
|
-
clip.out =
|
|
6598
|
+
clip.out = round33(clip.in + nextLen);
|
|
6215
6599
|
} else {
|
|
6216
6600
|
clip.out = clip.duration;
|
|
6217
6601
|
clip.loop = true;
|
|
@@ -6432,8 +6816,8 @@ function applyTemplate(template, take, opts) {
|
|
|
6432
6816
|
}
|
|
6433
6817
|
].sort((a, b) => a.at - b.at);
|
|
6434
6818
|
}
|
|
6435
|
-
const templateEnd =
|
|
6436
|
-
const takeEnd =
|
|
6819
|
+
const templateEnd = totalDuration5(ratedSegments(template));
|
|
6820
|
+
const takeEnd = totalDuration5(ratedSegments(doc));
|
|
6437
6821
|
const shift = shiftBy(opts.at, templateEnd, takeEnd);
|
|
6438
6822
|
const own = new Map((take.overlays ?? []).map((o) => [o.id, o]));
|
|
6439
6823
|
const overlays = (doc.overlays ?? []).filter(
|
|
@@ -6442,7 +6826,7 @@ function applyTemplate(template, take, opts) {
|
|
|
6442
6826
|
const skipped = [];
|
|
6443
6827
|
for (const clip of template.overlays ?? []) {
|
|
6444
6828
|
const next = structuredClone(clip);
|
|
6445
|
-
next.start = Math.max(0,
|
|
6829
|
+
next.start = Math.max(0, round34(clip.start + shift));
|
|
6446
6830
|
next.from = from;
|
|
6447
6831
|
const mine = own.get(clip.id);
|
|
6448
6832
|
if (next.kind === "text") {
|
|
@@ -6470,7 +6854,7 @@ function applyTemplate(template, take, opts) {
|
|
|
6470
6854
|
for (const clip of template.objects ?? []) {
|
|
6471
6855
|
const next = structuredClone(clip);
|
|
6472
6856
|
if (next.span)
|
|
6473
|
-
next.span.start = Math.max(0,
|
|
6857
|
+
next.span.start = Math.max(0, round34(next.span.start + shift));
|
|
6474
6858
|
next.from = from;
|
|
6475
6859
|
objects.push(next);
|
|
6476
6860
|
}
|
|
@@ -6480,7 +6864,7 @@ function applyTemplate(template, take, opts) {
|
|
|
6480
6864
|
);
|
|
6481
6865
|
for (const clip of template.audio) {
|
|
6482
6866
|
const next = structuredClone(clip);
|
|
6483
|
-
next.start = Math.max(0,
|
|
6867
|
+
next.start = Math.max(0, round34(clip.start + shift));
|
|
6484
6868
|
next.from = from;
|
|
6485
6869
|
audio.push(next);
|
|
6486
6870
|
}
|
|
@@ -6501,7 +6885,7 @@ function applyTemplate(template, take, opts) {
|
|
|
6501
6885
|
}
|
|
6502
6886
|
return { doc, notes };
|
|
6503
6887
|
}
|
|
6504
|
-
var
|
|
6888
|
+
var round34 = (v) => Math.round(v * 1e3) / 1e3;
|
|
6505
6889
|
|
|
6506
6890
|
// src/digest/geometry.ts
|
|
6507
6891
|
var CROP_MIN_FRAC = 0.25;
|
|
@@ -6743,7 +7127,7 @@ function round10(v) {
|
|
|
6743
7127
|
|
|
6744
7128
|
// src/lower/lowerStudioDoc.ts
|
|
6745
7129
|
import { applyTimelineEdits } from "@vosjs/shared/timelineEdits";
|
|
6746
|
-
import { totalDuration as
|
|
7130
|
+
import { totalDuration as totalDuration6 } from "@vosjs/timeline";
|
|
6747
7131
|
function lowerStudioDoc(doc, opts = {}) {
|
|
6748
7132
|
return isRecordingDoc(doc) ? lowerToComposition(doc) : lowerProgramDoc(doc, opts);
|
|
6749
7133
|
}
|
|
@@ -6774,7 +7158,7 @@ function lowerProgramDoc(doc, opts = {}) {
|
|
|
6774
7158
|
) : doc.program.config;
|
|
6775
7159
|
const own = programDuration(doc);
|
|
6776
7160
|
const rated = ratedSegments(doc);
|
|
6777
|
-
const duration = own > 0 ?
|
|
7161
|
+
const duration = own > 0 ? totalDuration6(rated) : 0;
|
|
6778
7162
|
const entryData = studioLayerData(doc, duration);
|
|
6779
7163
|
const baseData = anchor.data && typeof anchor.data === "object" ? anchor.data : {};
|
|
6780
7164
|
const retimed = !!doc.speed?.length && own > 0;
|
|
@@ -7372,31 +7756,10 @@ import {
|
|
|
7372
7756
|
removeSegment,
|
|
7373
7757
|
segmentRate as segmentRate3,
|
|
7374
7758
|
sourceToTimeline as sourceToTimeline2,
|
|
7375
|
-
splitBySpeed as
|
|
7376
|
-
totalDuration as
|
|
7759
|
+
splitBySpeed as splitBySpeed4,
|
|
7760
|
+
totalDuration as totalDuration7,
|
|
7377
7761
|
trimSegment
|
|
7378
7762
|
} from "@vosjs/timeline";
|
|
7379
|
-
function effectiveSegments(doc) {
|
|
7380
|
-
if (isRecordingDoc(doc) && doc.segments.length) return doc.segments;
|
|
7381
|
-
return [{ in: 0, out: anchorSourceDuration(doc) }];
|
|
7382
|
-
}
|
|
7383
|
-
var segmentPieces = (seg, speeds, freezes, leading) => {
|
|
7384
|
-
const own = freezes.filter(
|
|
7385
|
-
(f) => f.at <= seg.out + 1e-9 && (leading ? f.at >= seg.in - 1e-9 : f.at > seg.in + 1e-9)
|
|
7386
|
-
);
|
|
7387
|
-
return withFreezes(splitBySpeed3([seg], speeds), own);
|
|
7388
|
-
};
|
|
7389
|
-
var outputLen = (seg, speeds, freezes = [], leading = true) => totalDuration6(segmentPieces(seg, speeds, freezes, leading));
|
|
7390
|
-
var segmentStarts = (segments, speeds, freezes = []) => {
|
|
7391
|
-
const starts = [];
|
|
7392
|
-
let acc = 0;
|
|
7393
|
-
segments.forEach((s, i) => {
|
|
7394
|
-
starts.push(acc);
|
|
7395
|
-
acc += outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i));
|
|
7396
|
-
});
|
|
7397
|
-
return starts;
|
|
7398
|
-
};
|
|
7399
|
-
var contiguous = (segments, i) => i > 0 && Math.abs(segments[i - 1].out - segments[i].in) < 1e-9;
|
|
7400
7763
|
var videoLane = {
|
|
7401
7764
|
id: "video",
|
|
7402
7765
|
label: "Video",
|
|
@@ -7448,7 +7811,7 @@ var videoLane = {
|
|
|
7448
7811
|
if (index < 0 || index >= segments.length) return null;
|
|
7449
7812
|
const seg = segments[index];
|
|
7450
7813
|
const starts = segmentStarts(segments, speeds, freezes);
|
|
7451
|
-
const fullMap =
|
|
7814
|
+
const fullMap = splitBySpeed4([{ in: 0, out: sourceDuration }], speeds);
|
|
7452
7815
|
const edgeSrc = g.edge === "start" ? seg.in : seg.out;
|
|
7453
7816
|
const edgeOutNow = g.edge === "start" ? starts[index] : starts[index] + outputLen(
|
|
7454
7817
|
seg,
|
|
@@ -7484,7 +7847,7 @@ var videoLane = {
|
|
|
7484
7847
|
const leading = index === 0 || !contiguous(segments, index);
|
|
7485
7848
|
const local = g.t - starts[index];
|
|
7486
7849
|
for (const m of freezeOutputExtents(
|
|
7487
|
-
|
|
7850
|
+
splitBySpeed4([s], speeds),
|
|
7488
7851
|
freezes.filter(
|
|
7489
7852
|
(f) => f.at <= s.out + 1e-9 && (leading ? f.at >= s.in - 1e-9 : f.at > s.in + 1e-9)
|
|
7490
7853
|
)
|
|
@@ -7551,7 +7914,7 @@ var freezeLane = {
|
|
|
7551
7914
|
if (!freezes.length) return [];
|
|
7552
7915
|
const segs = effectiveSegments(doc);
|
|
7553
7916
|
const marks = freezeOutputExtents(
|
|
7554
|
-
|
|
7917
|
+
splitBySpeed4(segs, doc.speed ?? []),
|
|
7555
7918
|
freezes
|
|
7556
7919
|
);
|
|
7557
7920
|
return freezes.flatMap((f) => {
|
|
@@ -7577,12 +7940,12 @@ var freezeLane = {
|
|
|
7577
7940
|
if (g.type === "create") {
|
|
7578
7941
|
const rated = ratedSegments(doc);
|
|
7579
7942
|
for (const m of freezeOutputExtents(
|
|
7580
|
-
|
|
7943
|
+
splitBySpeed4(segs, speeds),
|
|
7581
7944
|
freezes
|
|
7582
7945
|
).values()) {
|
|
7583
7946
|
if (g.t >= m.t - 1e-9 && g.t <= m.t + m.duration + 1e-9) return null;
|
|
7584
7947
|
}
|
|
7585
|
-
if (g.t >
|
|
7948
|
+
if (g.t > totalDuration7(rated) + 1e-9) return null;
|
|
7586
7949
|
const srcT = round11(mapTime2(rated, Math.max(0, g.t)));
|
|
7587
7950
|
if (!kept(srcT) || !clear(srcT)) return null;
|
|
7588
7951
|
const id = nextFreezeId(doc);
|
|
@@ -7618,11 +7981,11 @@ var freezeLane = {
|
|
|
7618
7981
|
return mine;
|
|
7619
7982
|
};
|
|
7620
7983
|
const base = withFreezes(
|
|
7621
|
-
|
|
7984
|
+
splitBySpeed4(segs, speeds),
|
|
7622
7985
|
freezes.filter((x) => x.id !== f.id)
|
|
7623
7986
|
);
|
|
7624
7987
|
if (g.type === "move" || g.type === "resize" && g.edge === "start") {
|
|
7625
|
-
const marks = freezeOutputExtents(
|
|
7988
|
+
const marks = freezeOutputExtents(splitBySpeed4(segs, speeds), freezes);
|
|
7626
7989
|
const mine = marks.get(f.id);
|
|
7627
7990
|
const endOut = mine ? mine.t + mine.duration : null;
|
|
7628
7991
|
const srcT = round11(
|
|
@@ -7635,7 +7998,7 @@ var freezeLane = {
|
|
|
7635
7998
|
x.at = srcT;
|
|
7636
7999
|
if (g.type === "resize" && endOut !== null) {
|
|
7637
8000
|
const after = freezeOutputExtents(
|
|
7638
|
-
|
|
8001
|
+
splitBySpeed4(effectiveSegments(d), d.speed ?? []),
|
|
7639
8002
|
docFreezes({
|
|
7640
8003
|
...d,
|
|
7641
8004
|
freeze: (d.freeze ?? []).map(
|
|
@@ -7649,7 +8012,7 @@ var freezeLane = {
|
|
|
7649
8012
|
};
|
|
7650
8013
|
}
|
|
7651
8014
|
if (g.type === "resize") {
|
|
7652
|
-
const marks = freezeOutputExtents(
|
|
8015
|
+
const marks = freezeOutputExtents(splitBySpeed4(segs, speeds), freezes);
|
|
7653
8016
|
const mine = marks.get(f.id);
|
|
7654
8017
|
if (!mine) return null;
|
|
7655
8018
|
const seconds = clampFreezeSeconds(g.t - mine.t);
|
|
@@ -8210,7 +8573,7 @@ var speedLane = {
|
|
|
8210
8573
|
const sp = spans.find((s) => s.id === g.id);
|
|
8211
8574
|
if (!sp) return null;
|
|
8212
8575
|
const others = spans.filter((o) => o.id !== g.id);
|
|
8213
|
-
const base =
|
|
8576
|
+
const base = splitBySpeed4(effectiveSegments(doc), others);
|
|
8214
8577
|
if (g.type === "move") {
|
|
8215
8578
|
const len = sp.out - sp.in;
|
|
8216
8579
|
let newIn = clampToKept(
|
|
@@ -8822,6 +9185,8 @@ export {
|
|
|
8822
9185
|
REST_TILT_ID,
|
|
8823
9186
|
SCENE_MOTION,
|
|
8824
9187
|
SCENE_QUIET,
|
|
9188
|
+
SEGMENT_ENTER_KINDS,
|
|
9189
|
+
SEGMENT_EXIT_KINDS,
|
|
8825
9190
|
SPEED_RATE_MAX,
|
|
8826
9191
|
SPEED_RATE_MIN,
|
|
8827
9192
|
SPEED_SPAN_MIN,
|
|
@@ -8847,6 +9212,9 @@ export {
|
|
|
8847
9212
|
TILT_RAMP_OUT,
|
|
8848
9213
|
TILT_SPAN_MIN,
|
|
8849
9214
|
TILT_UI_DEG_MAX,
|
|
9215
|
+
TRANSITION_SECONDS,
|
|
9216
|
+
TRANSITION_SECONDS_MAX,
|
|
9217
|
+
TRANSITION_SECONDS_MIN,
|
|
8850
9218
|
TRANSITION_SPEED_MULT,
|
|
8851
9219
|
WINDOW_FOCUS_MIN,
|
|
8852
9220
|
ZOOM_CHAIN_GAP,
|
|
@@ -8912,6 +9280,7 @@ export {
|
|
|
8912
9280
|
docStillTime,
|
|
8913
9281
|
docToCropSpace,
|
|
8914
9282
|
docToFullSpace,
|
|
9283
|
+
docTransitions,
|
|
8915
9284
|
dropTemplate,
|
|
8916
9285
|
duckCurve,
|
|
8917
9286
|
effectiveSegments,
|
|
@@ -9011,10 +9380,15 @@ export {
|
|
|
9011
9380
|
resolveText3dAsset,
|
|
9012
9381
|
resolveZoomStyle,
|
|
9013
9382
|
restLeanOf,
|
|
9383
|
+
restSpansThroughTransitions,
|
|
9014
9384
|
sameMedia,
|
|
9015
9385
|
sceneChanges,
|
|
9016
9386
|
scrollRuns,
|
|
9387
|
+
segmentOutputExtents,
|
|
9388
|
+
outputLen as segmentOutputLen,
|
|
9389
|
+
segmentStarts,
|
|
9017
9390
|
setSpeedInRange,
|
|
9391
|
+
slideSide,
|
|
9018
9392
|
smoothCursor,
|
|
9019
9393
|
spanOutputExtent,
|
|
9020
9394
|
speedLane,
|
|
@@ -9024,6 +9398,7 @@ export {
|
|
|
9024
9398
|
tiltLane,
|
|
9025
9399
|
tiltTrackFromDoc,
|
|
9026
9400
|
transitionMult,
|
|
9401
|
+
transitionSeconds,
|
|
9027
9402
|
videoLane,
|
|
9028
9403
|
voiceKey,
|
|
9029
9404
|
withBackdrop,
|