@vosjs/studio-core 0.23.1 → 0.24.1
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 +190 -2
- package/dist/index.js +820 -235
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1828,8 +1828,8 @@ function round(v) {
|
|
|
1828
1828
|
// src/lower/lowerToComposition.ts
|
|
1829
1829
|
import {
|
|
1830
1830
|
EASINGS,
|
|
1831
|
-
lerpArray,
|
|
1832
|
-
sample,
|
|
1831
|
+
lerpArray as lerpArray2,
|
|
1832
|
+
sample as sample2,
|
|
1833
1833
|
segmentRate as segmentRate2,
|
|
1834
1834
|
sortKeyframes as sortKeyframes2,
|
|
1835
1835
|
sourceToTimeline,
|
|
@@ -2661,14 +2661,14 @@ function resolveOverlayFx(clip, clipDuration) {
|
|
|
2661
2661
|
const maxTotal = Math.max(dur, clipDuration * 0.9);
|
|
2662
2662
|
st = Math.min(st, Math.max(0, (maxTotal - dur) / (upperN - 1)));
|
|
2663
2663
|
}
|
|
2664
|
-
const
|
|
2664
|
+
const round13 = (v) => Math.round(v * 1e4) / 1e4;
|
|
2665
2665
|
return {
|
|
2666
2666
|
k: spec.fx,
|
|
2667
2667
|
u: unit,
|
|
2668
2668
|
d: FX_DIR[spec.direction ?? "forward"] ?? 0,
|
|
2669
|
-
st:
|
|
2670
|
-
dur:
|
|
2671
|
-
tt:
|
|
2669
|
+
st: round13(st),
|
|
2670
|
+
dur: round13(dur),
|
|
2671
|
+
tt: round13(st * (upperN - 1) + dur),
|
|
2672
2672
|
units,
|
|
2673
2673
|
n
|
|
2674
2674
|
};
|
|
@@ -3057,12 +3057,340 @@ function round2(v) {
|
|
|
3057
3057
|
return Math.round(v * 1e3) / 1e3;
|
|
3058
3058
|
}
|
|
3059
3059
|
|
|
3060
|
+
// src/lower/pin.ts
|
|
3061
|
+
import { lerpArray, mapTime, sample } from "@vosjs/timeline";
|
|
3062
|
+
var PIN_GAP = 24;
|
|
3063
|
+
var PIN_MARGIN = 24;
|
|
3064
|
+
var PIN_SAMPLE_HZ = 30;
|
|
3065
|
+
var PIN_SIMPLIFY_PX = 0.5;
|
|
3066
|
+
var PIN_EDGE_S = 0.35;
|
|
3067
|
+
var PIN_PRESS_WINDOW_S = 0.5;
|
|
3068
|
+
var PIN_TEXT_ADVANCE = 0.55;
|
|
3069
|
+
var SIDE_ORDER = ["right", "left", "below", "above"];
|
|
3070
|
+
function pinReferent(doc, pin) {
|
|
3071
|
+
const meta = doc.source.meta;
|
|
3072
|
+
const w = meta.width || 0;
|
|
3073
|
+
const h = meta.height || 0;
|
|
3074
|
+
if (pin.rect && validRect(pin.rect)) {
|
|
3075
|
+
return { rect: { ...pin.rect }, by: "rect", at: null };
|
|
3076
|
+
}
|
|
3077
|
+
if (pin.step !== void 0) {
|
|
3078
|
+
const step = findStep(meta.steps ?? [], pin.step);
|
|
3079
|
+
if (!step || step.skipped) return null;
|
|
3080
|
+
const carry = dragCarry(doc, step.tStart - 0.05, step.tEnd + 0.05);
|
|
3081
|
+
if (step.rect && validRect(step.rect) && w > 0 && h > 0) {
|
|
3082
|
+
return {
|
|
3083
|
+
rect: normRect(step.rect, w, h),
|
|
3084
|
+
by: "step",
|
|
3085
|
+
step,
|
|
3086
|
+
at: step.tStart,
|
|
3087
|
+
...carry ? { carry } : {}
|
|
3088
|
+
};
|
|
3089
|
+
}
|
|
3090
|
+
const union = unionRects(
|
|
3091
|
+
pressRects(doc, step.tStart - 0.05, step.tEnd + 0.05)
|
|
3092
|
+
);
|
|
3093
|
+
if (!union || w <= 0 || h <= 0) return null;
|
|
3094
|
+
return {
|
|
3095
|
+
rect: normRect(union, w, h),
|
|
3096
|
+
by: "step",
|
|
3097
|
+
step,
|
|
3098
|
+
at: step.tStart,
|
|
3099
|
+
...carry ? { carry } : {}
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3102
|
+
if (pin.press !== void 0 && Number.isFinite(pin.press)) {
|
|
3103
|
+
let best = null;
|
|
3104
|
+
for (const e of doc.source.cursor) {
|
|
3105
|
+
if (e.type !== "down" || !e.rect || !validRect(e.rect)) continue;
|
|
3106
|
+
const t = e.t / 1e3;
|
|
3107
|
+
const d = Math.abs(t - pin.press);
|
|
3108
|
+
if (d > PIN_PRESS_WINDOW_S) continue;
|
|
3109
|
+
if (!best || d < Math.abs(best.t - pin.press)) best = { t, rect: e.rect };
|
|
3110
|
+
}
|
|
3111
|
+
if (!best || w <= 0 || h <= 0) return null;
|
|
3112
|
+
const carry = dragCarry(doc, best.t - 0.01, best.t + 0.01);
|
|
3113
|
+
return {
|
|
3114
|
+
rect: normRect(best.rect, w, h),
|
|
3115
|
+
by: "press",
|
|
3116
|
+
at: best.t,
|
|
3117
|
+
...carry ? { carry } : {}
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
3120
|
+
return null;
|
|
3121
|
+
}
|
|
3122
|
+
function dragCarry(doc, from, to) {
|
|
3123
|
+
const meta = doc.source.meta;
|
|
3124
|
+
if (!(meta.width > 0) || !(meta.height > 0)) return null;
|
|
3125
|
+
const d = dragsFromTrack(doc.source.cursor, meta.width, meta.height).find(
|
|
3126
|
+
(g) => g.t0 >= from && g.t0 <= to
|
|
3127
|
+
);
|
|
3128
|
+
if (!d) return null;
|
|
3129
|
+
return { t0: d.t0, t1: d.t1, x0: d.nx, y0: d.ny };
|
|
3130
|
+
}
|
|
3131
|
+
function cursorAt(cursor, t, space) {
|
|
3132
|
+
let prev = null;
|
|
3133
|
+
for (const e of cursor) {
|
|
3134
|
+
if (e.type !== "move" && e.type !== "down" && e.type !== "up") continue;
|
|
3135
|
+
const cur = { t: e.t / 1e3, nx: e.x / space.w, ny: e.y / space.h };
|
|
3136
|
+
if (cur.t >= t) {
|
|
3137
|
+
if (!prev) return { nx: cur.nx, ny: cur.ny };
|
|
3138
|
+
const k = cur.t > prev.t ? (t - prev.t) / (cur.t - prev.t) : 1;
|
|
3139
|
+
return {
|
|
3140
|
+
nx: prev.nx + (cur.nx - prev.nx) * k,
|
|
3141
|
+
ny: prev.ny + (cur.ny - prev.ny) * k
|
|
3142
|
+
};
|
|
3143
|
+
}
|
|
3144
|
+
prev = cur;
|
|
3145
|
+
}
|
|
3146
|
+
return prev ? { nx: prev.nx, ny: prev.ny } : null;
|
|
3147
|
+
}
|
|
3148
|
+
function findStep(steps, ref) {
|
|
3149
|
+
const byId = steps.find((s) => s.id !== void 0 && s.id === ref);
|
|
3150
|
+
if (byId) return byId;
|
|
3151
|
+
const idx = typeof ref === "number" ? ref : Number(ref);
|
|
3152
|
+
if (!Number.isInteger(idx)) return void 0;
|
|
3153
|
+
return steps.find((s) => s.step === idx);
|
|
3154
|
+
}
|
|
3155
|
+
function pressRects(doc, from, to) {
|
|
3156
|
+
const out = [];
|
|
3157
|
+
for (const e of doc.source.cursor) {
|
|
3158
|
+
if (e.type !== "down" && e.type !== "key") continue;
|
|
3159
|
+
if (!e.rect || !validRect(e.rect)) continue;
|
|
3160
|
+
const t = e.t / 1e3;
|
|
3161
|
+
if (t < from || t > to) continue;
|
|
3162
|
+
out.push(e.rect);
|
|
3163
|
+
}
|
|
3164
|
+
return out;
|
|
3165
|
+
}
|
|
3166
|
+
function unionRects(rects) {
|
|
3167
|
+
if (!rects.length) return null;
|
|
3168
|
+
let x0 = Infinity;
|
|
3169
|
+
let y0 = Infinity;
|
|
3170
|
+
let x1 = -Infinity;
|
|
3171
|
+
let y1 = -Infinity;
|
|
3172
|
+
for (const r of rects) {
|
|
3173
|
+
x0 = Math.min(x0, r.x);
|
|
3174
|
+
y0 = Math.min(y0, r.y);
|
|
3175
|
+
x1 = Math.max(x1, r.x + r.w);
|
|
3176
|
+
y1 = Math.max(y1, r.y + r.h);
|
|
3177
|
+
}
|
|
3178
|
+
return { x: x0, y: y0, w: x1 - x0, h: y1 - y0 };
|
|
3179
|
+
}
|
|
3180
|
+
function validRect(r) {
|
|
3181
|
+
return Number.isFinite(r.x) && Number.isFinite(r.y) && Number.isFinite(r.w) && Number.isFinite(r.h) && r.w > 0 && r.h > 0;
|
|
3182
|
+
}
|
|
3183
|
+
function normRect(r, w, h) {
|
|
3184
|
+
return { x: r.x / w, y: r.y / h, w: r.w / w, h: r.h / h };
|
|
3185
|
+
}
|
|
3186
|
+
function pinBox(clip, layout) {
|
|
3187
|
+
const scale = clip.transform.scale || 1;
|
|
3188
|
+
if (clip.kind === "html") {
|
|
3189
|
+
const pb = htmlLayerPictureBox(clip);
|
|
3190
|
+
const wPic = htmlLayerWidth(clip) * layout.W * scale;
|
|
3191
|
+
const unit = wPic / pb.width;
|
|
3192
|
+
return { w: clip.box.width * unit, h: clip.box.height * unit };
|
|
3193
|
+
}
|
|
3194
|
+
if (clip.kind !== "text") {
|
|
3195
|
+
const w = (clip.width ?? OVERLAY_MEDIA_DEFAULT_WIDTH) * layout.W * scale;
|
|
3196
|
+
return { w, h: w / (16 / 9) };
|
|
3197
|
+
}
|
|
3198
|
+
const r = overlayRect(clip, estimateMeasure, layout.W, layout.H);
|
|
3199
|
+
return { w: r.w, h: r.h };
|
|
3200
|
+
}
|
|
3201
|
+
function estimateMeasure(text, font, letterSpacingPx = 0) {
|
|
3202
|
+
const m = /(\d+(?:\.\d+)?)px/.exec(font);
|
|
3203
|
+
const px = m ? Number(m[1]) : 16;
|
|
3204
|
+
return text.length * (px * PIN_TEXT_ADVANCE + letterSpacingPx);
|
|
3205
|
+
}
|
|
3206
|
+
function pinRectOnScreen(rect, t, layout, camera, zoomTrack) {
|
|
3207
|
+
let level = 1;
|
|
3208
|
+
let cx = 0.5;
|
|
3209
|
+
let cy = 0.5;
|
|
3210
|
+
let centring;
|
|
3211
|
+
if (zoomTrack && zoomTrack.keyframes.length) {
|
|
3212
|
+
const z = sample(zoomTrack, t, lerpArray);
|
|
3213
|
+
level = z[0];
|
|
3214
|
+
cx = z[1];
|
|
3215
|
+
cy = z[2];
|
|
3216
|
+
if (z.length > 3) centring = z[3];
|
|
3217
|
+
}
|
|
3218
|
+
const v = zoomView(level, cx, cy, layout, camera, centring);
|
|
3219
|
+
const map = (nx, ny) => {
|
|
3220
|
+
const px = layout.dx + nx * layout.dw;
|
|
3221
|
+
const py = layout.dy + ny * layout.dh;
|
|
3222
|
+
return {
|
|
3223
|
+
x: v.ox + (px - v.wcx) * v.level,
|
|
3224
|
+
y: v.oy + (py - v.wcy) * v.level
|
|
3225
|
+
};
|
|
3226
|
+
};
|
|
3227
|
+
const a = map(rect.x, rect.y);
|
|
3228
|
+
const b = map(rect.x + rect.w, rect.y + rect.h);
|
|
3229
|
+
return { x: a.x, y: a.y, w: b.x - a.x, h: b.y - a.y };
|
|
3230
|
+
}
|
|
3231
|
+
function pinReferentAt(referent, t, input) {
|
|
3232
|
+
const { carry, cursor, space, rated } = input;
|
|
3233
|
+
if (!carry || !cursor || !space || !rated) return referent;
|
|
3234
|
+
const s = mapTime(rated, t);
|
|
3235
|
+
const sc = Math.max(carry.t0, Math.min(carry.t1, s));
|
|
3236
|
+
const p = cursorAt(cursor, sc, space);
|
|
3237
|
+
if (!p) return referent;
|
|
3238
|
+
return {
|
|
3239
|
+
x: referent.x + (p.nx - carry.x0),
|
|
3240
|
+
y: referent.y + (p.ny - carry.y0),
|
|
3241
|
+
w: referent.w,
|
|
3242
|
+
h: referent.h
|
|
3243
|
+
};
|
|
3244
|
+
}
|
|
3245
|
+
function pinPlacement(input) {
|
|
3246
|
+
const { clip, referent, layout, camera, zoomTrack, base, motion } = input;
|
|
3247
|
+
const dur = Math.max(1e-3, clip.duration);
|
|
3248
|
+
const gap = clip.pin?.gap ?? PIN_GAP;
|
|
3249
|
+
const box = pinBox(clip, layout);
|
|
3250
|
+
const W = layout.W;
|
|
3251
|
+
const H = layout.H;
|
|
3252
|
+
const refAt = (local) => pinRectOnScreen(
|
|
3253
|
+
pinReferentAt(referent, clip.start + local, input),
|
|
3254
|
+
clip.start + local,
|
|
3255
|
+
layout,
|
|
3256
|
+
camera,
|
|
3257
|
+
zoomTrack
|
|
3258
|
+
);
|
|
3259
|
+
const centreFor = (r, side2) => {
|
|
3260
|
+
switch (side2) {
|
|
3261
|
+
case "right":
|
|
3262
|
+
return { x: r.x + r.w + gap + box.w / 2, y: r.y + r.h / 2 };
|
|
3263
|
+
case "left":
|
|
3264
|
+
return { x: r.x - gap - box.w / 2, y: r.y + r.h / 2 };
|
|
3265
|
+
case "below":
|
|
3266
|
+
return { x: r.x + r.w / 2, y: r.y + r.h + gap + box.h / 2 };
|
|
3267
|
+
case "above":
|
|
3268
|
+
return { x: r.x + r.w / 2, y: r.y - gap - box.h / 2 };
|
|
3269
|
+
}
|
|
3270
|
+
};
|
|
3271
|
+
const fits = (c) => c.x - box.w / 2 >= PIN_MARGIN && c.x + box.w / 2 <= W - PIN_MARGIN && c.y - box.h / 2 >= PIN_MARGIN && c.y + box.h / 2 <= H - PIN_MARGIN;
|
|
3272
|
+
const room = (c) => {
|
|
3273
|
+
const x0 = Math.max(PIN_MARGIN, c.x - box.w / 2);
|
|
3274
|
+
const x1 = Math.min(W - PIN_MARGIN, c.x + box.w / 2);
|
|
3275
|
+
const y0 = Math.max(PIN_MARGIN, c.y - box.h / 2);
|
|
3276
|
+
const y1 = Math.min(H - PIN_MARGIN, c.y + box.h / 2);
|
|
3277
|
+
return Math.max(0, x1 - x0) * Math.max(0, y1 - y0);
|
|
3278
|
+
};
|
|
3279
|
+
let side;
|
|
3280
|
+
const asked = clip.pin?.side ?? "auto";
|
|
3281
|
+
if (asked !== "auto") side = asked;
|
|
3282
|
+
else {
|
|
3283
|
+
const edge = Math.min(PIN_EDGE_S, dur / 3);
|
|
3284
|
+
const probes = [edge, dur / 2, dur - edge].map(refAt);
|
|
3285
|
+
const fitting = SIDE_ORDER.find(
|
|
3286
|
+
(s) => probes.every((r) => fits(centreFor(r, s)))
|
|
3287
|
+
);
|
|
3288
|
+
if (fitting) side = fitting;
|
|
3289
|
+
else {
|
|
3290
|
+
let best = SIDE_ORDER[0];
|
|
3291
|
+
let bestRoom = -1;
|
|
3292
|
+
for (const s of SIDE_ORDER) {
|
|
3293
|
+
const total = probes.reduce((acc, r) => acc + room(centreFor(r, s)), 0);
|
|
3294
|
+
if (total > bestRoom) {
|
|
3295
|
+
bestRoom = total;
|
|
3296
|
+
best = s;
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
side = best;
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
const clampC = (c) => {
|
|
3303
|
+
const minX = Math.min(W / 2, PIN_MARGIN + box.w / 2);
|
|
3304
|
+
const maxX = Math.max(W / 2, W - PIN_MARGIN - box.w / 2);
|
|
3305
|
+
const minY = Math.min(H / 2, PIN_MARGIN + box.h / 2);
|
|
3306
|
+
const maxY = Math.max(H / 2, H - PIN_MARGIN - box.h / 2);
|
|
3307
|
+
return {
|
|
3308
|
+
x: Math.max(minX, Math.min(maxX, c.x)),
|
|
3309
|
+
y: Math.max(minY, Math.min(maxY, c.y))
|
|
3310
|
+
};
|
|
3311
|
+
};
|
|
3312
|
+
const tipFor = (c) => {
|
|
3313
|
+
switch (side) {
|
|
3314
|
+
case "right":
|
|
3315
|
+
return { x: c.x - box.w / 2, y: c.y };
|
|
3316
|
+
case "left":
|
|
3317
|
+
return { x: c.x + box.w / 2, y: c.y };
|
|
3318
|
+
case "below":
|
|
3319
|
+
return { x: c.x, y: c.y - box.h / 2 };
|
|
3320
|
+
case "above":
|
|
3321
|
+
return { x: c.x, y: c.y + box.h / 2 };
|
|
3322
|
+
}
|
|
3323
|
+
};
|
|
3324
|
+
const n = Math.max(1, Math.ceil(dur * PIN_SAMPLE_HZ));
|
|
3325
|
+
const poses = [];
|
|
3326
|
+
const tips = [];
|
|
3327
|
+
let clamped = 0;
|
|
3328
|
+
for (let i = 0; i <= n; i++) {
|
|
3329
|
+
const local = i === n ? dur : i / PIN_SAMPLE_HZ;
|
|
3330
|
+
const want = centreFor(refAt(local), side);
|
|
3331
|
+
const c = clampC(want);
|
|
3332
|
+
clamped = Math.max(clamped, Math.hypot(c.x - want.x, c.y - want.y));
|
|
3333
|
+
const pose = motion?.keyframes.length ? sample(motion, local, lerpArray) : base;
|
|
3334
|
+
poses.push({
|
|
3335
|
+
t: local,
|
|
3336
|
+
value: [c.x / W, c.y / H, pose[2], pose[3], pose[4]],
|
|
3337
|
+
ease: "linear"
|
|
3338
|
+
});
|
|
3339
|
+
const tp = tipFor(c);
|
|
3340
|
+
tips.push({ t: local, value: [tp.x / W, tp.y / H], ease: "linear" });
|
|
3341
|
+
}
|
|
3342
|
+
return {
|
|
3343
|
+
side,
|
|
3344
|
+
track: { keyframes: simplify(poses, [W, H, 1, 1, 1]) },
|
|
3345
|
+
tip: { keyframes: simplify(tips, [W, H]) },
|
|
3346
|
+
clamped: round4(clamped),
|
|
3347
|
+
box
|
|
3348
|
+
};
|
|
3349
|
+
}
|
|
3350
|
+
function simplify(keys, scalePx) {
|
|
3351
|
+
if (keys.length <= 2) return keys.map(roundKey);
|
|
3352
|
+
const out = [roundKey(keys[0])];
|
|
3353
|
+
let a = 0;
|
|
3354
|
+
for (let i = 2; i <= keys.length - 1; i++) {
|
|
3355
|
+
const ka = keys[a];
|
|
3356
|
+
const ki = keys[i];
|
|
3357
|
+
let ok = true;
|
|
3358
|
+
for (let j = a + 1; j < i && ok; j++) {
|
|
3359
|
+
const kj = keys[j];
|
|
3360
|
+
const u = (kj.t - ka.t) / (ki.t - ka.t || 1);
|
|
3361
|
+
for (let k = 0; k < kj.value.length; k++) {
|
|
3362
|
+
const pred = ka.value[k] + (ki.value[k] - ka.value[k]) * u;
|
|
3363
|
+
if (Math.abs(pred - kj.value[k]) * (scalePx[k] ?? 1) > PIN_SIMPLIFY_PX) {
|
|
3364
|
+
ok = false;
|
|
3365
|
+
break;
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
if (!ok) {
|
|
3370
|
+
a = i - 1;
|
|
3371
|
+
out.push(roundKey(keys[a]));
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
const last = keys[keys.length - 1];
|
|
3375
|
+
if (out[out.length - 1].t !== round4(last.t)) out.push(roundKey(last));
|
|
3376
|
+
return out;
|
|
3377
|
+
}
|
|
3378
|
+
function roundKey(k) {
|
|
3379
|
+
return { t: round4(k.t), value: k.value.map(round42), ease: k.ease };
|
|
3380
|
+
}
|
|
3381
|
+
function round4(v) {
|
|
3382
|
+
return Math.round(v * 1e3) / 1e3;
|
|
3383
|
+
}
|
|
3384
|
+
function round42(v) {
|
|
3385
|
+
return Math.round(v * 1e5) / 1e5;
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3060
3388
|
// src/lower/cursorIdle.ts
|
|
3061
3389
|
var CURSOR_IDLE_HOLD = 1;
|
|
3062
3390
|
var CURSOR_IDLE_FADE_OUT = 0.35;
|
|
3063
3391
|
var CURSOR_IDLE_FADE_IN = 0.18;
|
|
3064
3392
|
var CURSOR_IDLE_EPS_FRAC = 25e-4;
|
|
3065
|
-
var
|
|
3393
|
+
var round5 = (n) => Math.round(n * 1e3) / 1e3;
|
|
3066
3394
|
function cursorIdleFade(track, o) {
|
|
3067
3395
|
const moves = track.filter((e) => e.type === "move" || e.type === "down" || e.type === "up").map((e) => ({ t: e.t / 1e3, x: e.x, y: e.y }));
|
|
3068
3396
|
if (moves.length === 0) return [];
|
|
@@ -3114,7 +3442,7 @@ function emit(keys, s, e) {
|
|
|
3114
3442
|
push(keys, e + CURSOR_IDLE_FADE_IN, 1);
|
|
3115
3443
|
}
|
|
3116
3444
|
function push(keys, t, a) {
|
|
3117
|
-
const rt =
|
|
3445
|
+
const rt = round5(t);
|
|
3118
3446
|
if (keys.length > 0 && rt <= keys[keys.length - 1].t) {
|
|
3119
3447
|
keys[keys.length - 1].a = a;
|
|
3120
3448
|
return;
|
|
@@ -4622,7 +4950,7 @@ function gatedRect(rect, x, y, space) {
|
|
|
4622
4950
|
return null;
|
|
4623
4951
|
const m = RECT_CONTAIN_MARGIN;
|
|
4624
4952
|
const inside = x >= rect.x - m && x <= rect.x + rect.w + m && y >= rect.y - m && y <= rect.y + rect.h + m;
|
|
4625
|
-
return inside ? [
|
|
4953
|
+
return inside ? [round6(rect.x), round6(rect.y), round6(rect.w), round6(rect.h)] : null;
|
|
4626
4954
|
}
|
|
4627
4955
|
function extractClicks(track, segments, opts) {
|
|
4628
4956
|
const out = [];
|
|
@@ -4647,11 +4975,11 @@ function extractClicks(track, segments, opts) {
|
|
|
4647
4975
|
const upSrc = st + Math.max(pressLen, 0.02);
|
|
4648
4976
|
const up = keptOutputEnd(segments, st, upSrc, opts.media) ?? ot + CLICK_SYNTH_RELEASE;
|
|
4649
4977
|
const click = {
|
|
4650
|
-
ot:
|
|
4651
|
-
up:
|
|
4652
|
-
st:
|
|
4653
|
-
x:
|
|
4654
|
-
y:
|
|
4978
|
+
ot: round6(ot),
|
|
4979
|
+
up: round6(Math.max(up, ot + 0.02)),
|
|
4980
|
+
st: round6(st),
|
|
4981
|
+
x: round6(e.x),
|
|
4982
|
+
y: round6(e.y),
|
|
4655
4983
|
b: button
|
|
4656
4984
|
};
|
|
4657
4985
|
if (opts.rects && e.rect) {
|
|
@@ -4670,7 +4998,7 @@ function hexToRgbTriplet(hex) {
|
|
|
4670
4998
|
const n = parseInt(h, 16);
|
|
4671
4999
|
return [n >> 16, n >> 8 & 255, n & 255];
|
|
4672
5000
|
}
|
|
4673
|
-
function
|
|
5001
|
+
function round6(v) {
|
|
4674
5002
|
return Math.round(v * 1e3) / 1e3;
|
|
4675
5003
|
}
|
|
4676
5004
|
|
|
@@ -4746,7 +5074,7 @@ function trackEmitter() {
|
|
|
4746
5074
|
if (tt <= prev.t + 1e-6 && sameVec(prev.value, value)) return prev.t;
|
|
4747
5075
|
if (tt <= prev.t + 1e-6) tt = prev.t + 1e-3;
|
|
4748
5076
|
}
|
|
4749
|
-
keyframes.push({ t:
|
|
5077
|
+
keyframes.push({ t: round7(tt), value: value.map(round7), ease });
|
|
4750
5078
|
return tt;
|
|
4751
5079
|
};
|
|
4752
5080
|
return { keyframes, push: push2 };
|
|
@@ -4923,7 +5251,7 @@ function camBubbleRectAt(doc, t) {
|
|
|
4923
5251
|
H
|
|
4924
5252
|
);
|
|
4925
5253
|
if (!track.keyframes.length) return camBubbleRect(doc.cam, W, H);
|
|
4926
|
-
return camRectFromPose(
|
|
5254
|
+
return camRectFromPose(sample2(track, t, lerpArray2), doc.cam, W, H);
|
|
4927
5255
|
}
|
|
4928
5256
|
function motionTrack(base, keys, dur) {
|
|
4929
5257
|
const sorted = keys.filter((k) => Number.isFinite(k.at)).sort((a, b) => a.at - b.at);
|
|
@@ -4971,7 +5299,7 @@ function overlayMotionPoseAt(o, t) {
|
|
|
4971
5299
|
Math.max(OVERLAY_MIN_DURATION, o.duration)
|
|
4972
5300
|
);
|
|
4973
5301
|
if (!track.keyframes.length) return null;
|
|
4974
|
-
return [...
|
|
5302
|
+
return [...sample2(track, t, lerpArray2)];
|
|
4975
5303
|
}
|
|
4976
5304
|
function objectMotionBase(o) {
|
|
4977
5305
|
const t = o.transform3d;
|
|
@@ -5001,7 +5329,7 @@ function objectMotionPoseAt(o, t, clipDur) {
|
|
|
5001
5329
|
o.span?.duration ?? clipDur
|
|
5002
5330
|
);
|
|
5003
5331
|
if (!track.keyframes.length) return null;
|
|
5004
|
-
return [...
|
|
5332
|
+
return [...sample2(track, t, lerpArray2)];
|
|
5005
5333
|
}
|
|
5006
5334
|
var FRAME_STEP_SRC = `
|
|
5007
5335
|
function stepPaused(vid, srcT) {
|
|
@@ -6383,6 +6711,59 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
6383
6711
|
}
|
|
6384
6712
|
}
|
|
6385
6713
|
|
|
6714
|
+
// pinned layers' marks \u2014 a standing ring or underline on a layer's
|
|
6715
|
+
// REFERENT for the layer's life (d.pins: OUTPUT-anchored records baked at
|
|
6716
|
+
// lowering, the referent in normalised video fractions). Painted inside the
|
|
6717
|
+
// zoom transform so the mark sits on the element under any level or pose,
|
|
6718
|
+
// at a constant on-screen weight (\xF7 ceZs), with the layer's own entrance
|
|
6719
|
+
// and exit. Locals are pn-prefixed (one var scope). Skipped on the ghost
|
|
6720
|
+
// pass like the effects.
|
|
6721
|
+
var pns = d.pins || []
|
|
6722
|
+
if (pns.length && !tpG) {
|
|
6723
|
+
for (var pi = 0; pi < pns.length; pi++) {
|
|
6724
|
+
var pn = pns[pi]
|
|
6725
|
+
if (!pn.mark || pn.mark === 'none') continue
|
|
6726
|
+
var pnT = t - pn.start
|
|
6727
|
+
if (pnT < 0 || pnT > pn.dur) continue
|
|
6728
|
+
var pnA = 1
|
|
6729
|
+
if (pnT < 0.35) { var pnU = pnT / 0.35; pnA = 1 - Math.pow(1 - pnU, 3) }
|
|
6730
|
+
if (pn.dur - pnT < 0.35) { var pnV = (pn.dur - pnT) / 0.35; pnA = Math.min(pnA, 1 - Math.pow(1 - pnV, 3)) }
|
|
6731
|
+
if (pnA <= 0.004) continue
|
|
6732
|
+
var pnX = dx + pn.x * dw, pnY = dy + pn.y * dh, pnW = pn.w * dw, pnH = pn.h * dh
|
|
6733
|
+
// a carried referent (a drag): moved by where the pointer has taken
|
|
6734
|
+
// it at the on-screen source moment, held at the release
|
|
6735
|
+
if (pn.carry) {
|
|
6736
|
+
var pcC = (actM ? actM.cursor : d.cursor) || []
|
|
6737
|
+
var pcT = Math.max(pn.carry.t0, Math.min(pn.carry.t1, srcT))
|
|
6738
|
+
var pcx = -1, pcy = -1
|
|
6739
|
+
for (var pk = 0; pk < pcC.length; pk++) { if (pcC[pk].t <= pcT) { pcx = pcC[pk].x; pcy = pcC[pk].y } }
|
|
6740
|
+
if (pcx >= 0) { pnX += (pcx / (space.w || vw) - pn.carry.x0) * dw; pnY += (pcy / (space.h || vh) - pn.carry.y0) * dh }
|
|
6741
|
+
}
|
|
6742
|
+
var pnLw = 2 * s2 / ceZs, pnPad = 6 * s2 / ceZs
|
|
6743
|
+
var pnCol = pn.color || 'rgba(255,255,255,0.95)'
|
|
6744
|
+
c.save()
|
|
6745
|
+
c.globalAlpha = pnA
|
|
6746
|
+
if (pn.mark === 'underline') {
|
|
6747
|
+
// a bar under the referent: dark rim, then the ink
|
|
6748
|
+
var pnBy = pnY + pnH + pnPad * 0.5
|
|
6749
|
+
c.fillStyle = 'rgba(0,0,0,0.35)'
|
|
6750
|
+
c.fillRect(pnX - pnLw, pnBy - pnLw, pnW + pnLw * 2, pnLw * 1.5 + pnLw * 2)
|
|
6751
|
+
c.fillStyle = pnCol
|
|
6752
|
+
c.fillRect(pnX, pnBy, pnW, pnLw * 1.5)
|
|
6753
|
+
} else {
|
|
6754
|
+
// a ring around the referent: the cursor's dual stroke, a dark rim
|
|
6755
|
+
// under the ink, legible on any content
|
|
6756
|
+
c.lineWidth = pnLw + 2 * s2 / ceZs
|
|
6757
|
+
c.strokeStyle = 'rgba(0,0,0,0.35)'
|
|
6758
|
+
rr(pnX - pnPad, pnY - pnPad, pnW + pnPad * 2, pnH + pnPad * 2, pnPad, c); c.stroke()
|
|
6759
|
+
c.lineWidth = pnLw
|
|
6760
|
+
c.strokeStyle = pnCol
|
|
6761
|
+
rr(pnX - pnPad, pnY - pnPad, pnW + pnPad * 2, pnH + pnPad * 2, pnPad, c); c.stroke()
|
|
6762
|
+
}
|
|
6763
|
+
c.restore()
|
|
6764
|
+
}
|
|
6765
|
+
}
|
|
6766
|
+
|
|
6386
6767
|
// cursor (SOURCE-anchored samples, read at the on-screen source moment).
|
|
6387
6768
|
// The dot is the only thing cursorStyle.visible hides \u2014 the track still drives
|
|
6388
6769
|
// cursor-follow zoom, and click effects draw above on their own switch.
|
|
@@ -6445,8 +6826,15 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
6445
6826
|
// Wait only for the FIRST decoded frame, then keep drawing through seeks.
|
|
6446
6827
|
if (camV && camV.readyState >= 2) r.camHasFrame = true
|
|
6447
6828
|
var camActiveNow = !!(camV && camOn && camS.visible !== false && r.camHasFrame)
|
|
6829
|
+
// A pinned layer's leader is live for the layer's life: the overlay layer
|
|
6830
|
+
// repaints while one is up (the referent moves with the camera).
|
|
6831
|
+
var pqs = d.pins || [], pqLive = false
|
|
6832
|
+
for (var pl = 0; pl < pqs.length; pl++) {
|
|
6833
|
+
var pq0 = pqs[pl]
|
|
6834
|
+
if (pq0.leader && t >= pq0.start && t <= pq0.start + pq0.dur) { pqLive = true; break }
|
|
6835
|
+
}
|
|
6448
6836
|
var ovSig = W + 'x' + H
|
|
6449
|
-
var ovDirty = ov.sig !== ovSig || camActiveNow || ov.active || !!trOut
|
|
6837
|
+
var ovDirty = ov.sig !== ovSig || camActiveNow || ov.active || !!trOut || pqLive
|
|
6450
6838
|
if (ovDirty) {
|
|
6451
6839
|
ovC.clearRect(0, 0, W, H)
|
|
6452
6840
|
// webcam bubble \u2014 pinned to the frame corner regardless of card tilt/zoom.
|
|
@@ -6496,6 +6884,66 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
6496
6884
|
ovC.restore()
|
|
6497
6885
|
}
|
|
6498
6886
|
}
|
|
6887
|
+
// pinned layers' leaders \u2014 a hairline from the layer's near edge (the
|
|
6888
|
+
// baked tip track, clip-local frame fractions) to the referent's near
|
|
6889
|
+
// edge, the referent mapped through the camera the way the boundary dot
|
|
6890
|
+
// is, with a dot at the referent. Fades with the layer; rides the rise
|
|
6891
|
+
// entrance so the line meets the card as it lands.
|
|
6892
|
+
if (pqLive) {
|
|
6893
|
+
for (var pj = 0; pj < pqs.length; pj++) {
|
|
6894
|
+
var pq = pqs[pj]
|
|
6895
|
+
if (!pq.leader) continue
|
|
6896
|
+
var pqT = t - pq.start
|
|
6897
|
+
if (pqT < 0 || pqT > pq.dur) continue
|
|
6898
|
+
var pqA = 1, pqRise = 0
|
|
6899
|
+
if (pqT < 0.35) {
|
|
6900
|
+
var pqU = 1 - Math.pow(1 - pqT / 0.35, 3)
|
|
6901
|
+
pqA = pqU
|
|
6902
|
+
if (pq.enter === 'rise') pqRise = (1 - pqU) * 24 * s
|
|
6903
|
+
}
|
|
6904
|
+
if (pq.dur - pqT < 0.35) { var pqV = (pq.dur - pqT) / 0.35; pqA = Math.min(pqA, 1 - Math.pow(1 - pqV, 3)) }
|
|
6905
|
+
if (pqA <= 0.004 || !pq.tip || !pq.tip.keyframes || !pq.tip.keyframes.length) continue
|
|
6906
|
+
var pqTip = TL.sample(pq.tip, pqT, TL.lerpArray)
|
|
6907
|
+
var pqTx = pqTip[0] * W, pqTy = pqTip[1] * H + pqRise
|
|
6908
|
+
// the referent's corners in frame space under the live camera
|
|
6909
|
+
var pqAx = dx + pq.x * dw, pqAy = dy + pq.y * dh
|
|
6910
|
+
var pqBx = dx + (pq.x + pq.w) * dw, pqBy = dy + (pq.y + pq.h) * dh
|
|
6911
|
+
if (pq.carry) {
|
|
6912
|
+
var pqcT = Math.max(pq.carry.t0, Math.min(pq.carry.t1, srcT))
|
|
6913
|
+
var pqcx = -1, pqcy = -1
|
|
6914
|
+
for (var pqk = 0; pqk < cur.length; pqk++) { if (cur[pqk].t <= pqcT) { pqcx = cur[pqk].x; pqcy = cur[pqk].y } }
|
|
6915
|
+
if (pqcx >= 0) {
|
|
6916
|
+
var pqOx = (pqcx / (space.w || vw) - pq.carry.x0) * dw, pqOy = (pqcy / (space.h || vh) - pq.carry.y0) * dh
|
|
6917
|
+
pqAx += pqOx; pqBx += pqOx; pqAy += pqOy; pqBy += pqOy
|
|
6918
|
+
}
|
|
6919
|
+
}
|
|
6920
|
+
if (lvl > 1.001 && !d.zoomSuppressed) {
|
|
6921
|
+
if (camStage) {
|
|
6922
|
+
pqAx = W / 2 + (pqAx - wcx) * lvl; pqAy = H / 2 + (pqAy - wcy) * lvl
|
|
6923
|
+
pqBx = W / 2 + (pqBx - wcx) * lvl; pqBy = H / 2 + (pqBy - wcy) * lvl
|
|
6924
|
+
} else {
|
|
6925
|
+
pqAx = fx + (pqAx - fx) * lvl; pqAy = fy + (pqAy - fy) * lvl
|
|
6926
|
+
pqBx = fx + (pqBx - fx) * lvl; pqBy = fy + (pqBy - fy) * lvl
|
|
6927
|
+
}
|
|
6928
|
+
}
|
|
6929
|
+
var pqRx = pq.side === 'right' ? pqBx : pq.side === 'left' ? pqAx : (pqAx + pqBx) / 2
|
|
6930
|
+
var pqRy = pq.side === 'below' ? pqBy : pq.side === 'above' ? pqAy : (pqAy + pqBy) / 2
|
|
6931
|
+
var pqCol = pq.color || 'rgba(255,255,255,0.95)'
|
|
6932
|
+
var pqLw = 2 * s
|
|
6933
|
+
ovC.save()
|
|
6934
|
+
ovC.globalAlpha = pqA
|
|
6935
|
+
ovC.lineCap = 'round'
|
|
6936
|
+
ovC.strokeStyle = 'rgba(0,0,0,0.35)'; ovC.lineWidth = pqLw + 2 * s
|
|
6937
|
+
ovC.beginPath(); ovC.moveTo(pqTx, pqTy); ovC.lineTo(pqRx, pqRy); ovC.stroke()
|
|
6938
|
+
ovC.strokeStyle = pqCol; ovC.lineWidth = pqLw
|
|
6939
|
+
ovC.beginPath(); ovC.moveTo(pqTx, pqTy); ovC.lineTo(pqRx, pqRy); ovC.stroke()
|
|
6940
|
+
ovC.fillStyle = 'rgba(0,0,0,0.35)'
|
|
6941
|
+
ovC.beginPath(); ovC.arc(pqRx, pqRy, 5 * s, 0, Math.PI * 2); ovC.fill()
|
|
6942
|
+
ovC.fillStyle = pqCol
|
|
6943
|
+
ovC.beginPath(); ovC.arc(pqRx, pqRy, 4 * s, 0, Math.PI * 2); ovC.fill()
|
|
6944
|
+
ovC.restore()
|
|
6945
|
+
}
|
|
6946
|
+
}
|
|
6499
6947
|
// The boundary dot: the outgoing card's last cursor point and the
|
|
6500
6948
|
// incoming's current one, each carried by its card's move, one dot on
|
|
6501
6949
|
// the eased lerp between them (the idle fade never fires across a page
|
|
@@ -6510,7 +6958,7 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
6510
6958
|
drawCur(ovC, tqX2, tqY2, tqR, 1, 2 * (trQw || s))
|
|
6511
6959
|
}
|
|
6512
6960
|
ov.sig = ovSig
|
|
6513
|
-
ov.active = camActiveNow || !!trOut
|
|
6961
|
+
ov.active = camActiveNow || !!trOut || pqLive
|
|
6514
6962
|
if (ov.texture) ov.texture.needsUpdate = true
|
|
6515
6963
|
}
|
|
6516
6964
|
|
|
@@ -6604,7 +7052,7 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
6604
7052
|
window.__voilaDebug = { cv: cv, bgCv: bg.canvas, ovCv: ov.canvas, W: W, H: H, glW: gl && gl.width, glH: gl && gl.height, t: t, canvases: document.querySelectorAll('canvas').length }
|
|
6605
7053
|
}
|
|
6606
7054
|
}`;
|
|
6607
|
-
function studioLayerData(layers, duration) {
|
|
7055
|
+
function studioLayerData(layers, duration, pins) {
|
|
6608
7056
|
const rec = "source" in layers ? layers : null;
|
|
6609
7057
|
return {
|
|
6610
7058
|
// Music/SFX clips with their gain envelopes baked (shared truth for the
|
|
@@ -6613,14 +7061,14 @@ function studioLayerData(layers, duration) {
|
|
|
6613
7061
|
// needs a decoded recording, which a sync lowering can't produce.
|
|
6614
7062
|
audio: (layers.audio ?? []).map((c) => ({
|
|
6615
7063
|
key: c.key,
|
|
6616
|
-
start:
|
|
6617
|
-
in:
|
|
6618
|
-
out:
|
|
6619
|
-
gain:
|
|
7064
|
+
start: round7(c.start),
|
|
7065
|
+
in: round7(c.in),
|
|
7066
|
+
out: round7(c.out),
|
|
7067
|
+
gain: round7(c.gain),
|
|
6620
7068
|
loop: !!c.loop,
|
|
6621
|
-
len:
|
|
7069
|
+
len: round7(clipLength(c)),
|
|
6622
7070
|
duck: !!c.duck,
|
|
6623
|
-
env: clipEnvelope(c).map((p) => ({ t:
|
|
7071
|
+
env: clipEnvelope(c).map((p) => ({ t: round7(p.t), g: round7(p.g) }))
|
|
6624
7072
|
})),
|
|
6625
7073
|
// Object clips: numbers resolved HERE; shapes are the drafted engine spec.
|
|
6626
7074
|
...layers.objects && layers.objects.length ? {
|
|
@@ -6633,17 +7081,17 @@ function studioLayerData(layers, duration) {
|
|
|
6633
7081
|
} : o.asset.kind === "text3d" ? resolveText3dAsset(o.asset) : { kind: "gltf", key: o.asset.key },
|
|
6634
7082
|
...o.span ? {
|
|
6635
7083
|
span: {
|
|
6636
|
-
start:
|
|
6637
|
-
duration:
|
|
7084
|
+
start: round7(o.span.start),
|
|
7085
|
+
duration: round7(o.span.duration)
|
|
6638
7086
|
}
|
|
6639
7087
|
} : {},
|
|
6640
|
-
x:
|
|
6641
|
-
y:
|
|
6642
|
-
z:
|
|
6643
|
-
rx:
|
|
6644
|
-
ry:
|
|
6645
|
-
rz:
|
|
6646
|
-
scale:
|
|
7088
|
+
x: round7(o.transform3d.x),
|
|
7089
|
+
y: round7(o.transform3d.y),
|
|
7090
|
+
z: round7(o.transform3d.z),
|
|
7091
|
+
rx: round7(o.transform3d.rx),
|
|
7092
|
+
ry: round7(o.transform3d.ry),
|
|
7093
|
+
rz: round7(o.transform3d.rz),
|
|
7094
|
+
scale: round7(o.transform3d.scale || OBJECT_DEFAULT_SCALE),
|
|
6647
7095
|
anim: idleKey(o),
|
|
6648
7096
|
// Pose keyframes: clip-local [x,y,z,rx,ry,rz,scale] track,
|
|
6649
7097
|
// sampled at t − span.start. Omitted when absent — parity.
|
|
@@ -6670,18 +7118,20 @@ function studioLayerData(layers, duration) {
|
|
|
6670
7118
|
const base = {
|
|
6671
7119
|
id: o.id,
|
|
6672
7120
|
kind: o.kind,
|
|
6673
|
-
start:
|
|
6674
|
-
dur:
|
|
6675
|
-
x:
|
|
6676
|
-
y:
|
|
6677
|
-
scale:
|
|
6678
|
-
rot:
|
|
7121
|
+
start: round7(o.start),
|
|
7122
|
+
dur: round7(Math.max(OVERLAY_MIN_DURATION, o.duration)),
|
|
7123
|
+
x: round7(o.transform.x),
|
|
7124
|
+
y: round7(o.transform.y),
|
|
7125
|
+
scale: round7(o.transform.scale || 1),
|
|
7126
|
+
rot: round7(o.transform.rotation || 0),
|
|
6679
7127
|
enter: enterKey(o),
|
|
6680
7128
|
exit: exitKey(o),
|
|
6681
7129
|
// Pose keyframes: a CLIP-LOCAL [x, y, scale, rot, opacity]
|
|
6682
7130
|
// track, sampled in ON_FRAME at t − start. Omitted when the clip
|
|
6683
7131
|
// has no motion — data byte parity.
|
|
6684
7132
|
...(() => {
|
|
7133
|
+
const pinned = pins?.get(o.id);
|
|
7134
|
+
if (pinned) return { track: pinned.track };
|
|
6685
7135
|
if (!o.motion || !o.motion.length) return {};
|
|
6686
7136
|
const mb = overlayMotionBase(o);
|
|
6687
7137
|
const track = motionTrack(
|
|
@@ -6698,10 +7148,10 @@ function studioLayerData(layers, duration) {
|
|
|
6698
7148
|
...base,
|
|
6699
7149
|
kind: "image",
|
|
6700
7150
|
key: hl.key,
|
|
6701
|
-
w:
|
|
7151
|
+
w: round7(hl.width),
|
|
6702
7152
|
radius: 0,
|
|
6703
7153
|
shadow: "none",
|
|
6704
|
-
opacity:
|
|
7154
|
+
opacity: round7(o.opacity ?? 1),
|
|
6705
7155
|
html: hl.html
|
|
6706
7156
|
};
|
|
6707
7157
|
}
|
|
@@ -6718,8 +7168,8 @@ function studioLayerData(layers, duration) {
|
|
|
6718
7168
|
...o.frame.browserBar?.theme ? { theme: o.frame.browserBar.theme } : {}
|
|
6719
7169
|
},
|
|
6720
7170
|
lean: [
|
|
6721
|
-
|
|
6722
|
-
|
|
7171
|
+
round7(o.frame.lean?.rx ?? 0),
|
|
7172
|
+
round7(o.frame.lean?.ry ?? 0)
|
|
6723
7173
|
],
|
|
6724
7174
|
shadow: o.frame.shadow ?? LAYER_CARD_SHADOW,
|
|
6725
7175
|
shadowContact: o.frame.shadowContact ?? 0,
|
|
@@ -6735,17 +7185,17 @@ function studioLayerData(layers, duration) {
|
|
|
6735
7185
|
for (const ev of lm.cursor) {
|
|
6736
7186
|
if (ev.type === "down")
|
|
6737
7187
|
clicks.push({
|
|
6738
|
-
t:
|
|
6739
|
-
x:
|
|
6740
|
-
y:
|
|
7188
|
+
t: round7(ev.t / 1e3),
|
|
7189
|
+
x: round7(ev.x),
|
|
7190
|
+
y: round7(ev.y)
|
|
6741
7191
|
});
|
|
6742
7192
|
}
|
|
6743
7193
|
return {
|
|
6744
7194
|
cur: {
|
|
6745
7195
|
pts: sm.map((p) => ({
|
|
6746
|
-
t:
|
|
6747
|
-
x:
|
|
6748
|
-
y:
|
|
7196
|
+
t: round7(p.t),
|
|
7197
|
+
x: round7(p.x),
|
|
7198
|
+
y: round7(p.y)
|
|
6749
7199
|
})),
|
|
6750
7200
|
space: { w: lm.meta.width, h: lm.meta.height },
|
|
6751
7201
|
size: rec.cursor.size,
|
|
@@ -6760,7 +7210,7 @@ function studioLayerData(layers, duration) {
|
|
|
6760
7210
|
...lm && lm.crop ? { crop: lm.crop } : {},
|
|
6761
7211
|
...lmCard ? { card: lmCard } : {},
|
|
6762
7212
|
...lmFacts,
|
|
6763
|
-
w:
|
|
7213
|
+
w: round7(o.width ?? OVERLAY_MEDIA_DEFAULT_WIDTH),
|
|
6764
7214
|
radius: o.radius ?? OVERLAY_MEDIA_DEFAULT_RADIUS,
|
|
6765
7215
|
opacity: o.opacity ?? 1,
|
|
6766
7216
|
loop: !!o.loop,
|
|
@@ -6770,7 +7220,7 @@ function studioLayerData(layers, duration) {
|
|
|
6770
7220
|
...o.shadow ? { shadow: o.shadow } : {},
|
|
6771
7221
|
...o.border && o.border.width > 0 ? {
|
|
6772
7222
|
border: {
|
|
6773
|
-
width:
|
|
7223
|
+
width: round7(o.border.width),
|
|
6774
7224
|
color: o.border.color
|
|
6775
7225
|
}
|
|
6776
7226
|
} : {}
|
|
@@ -6790,11 +7240,11 @@ function studioLayerData(layers, duration) {
|
|
|
6790
7240
|
// Style-v2 fields bake only when non-default (byte parity for
|
|
6791
7241
|
// older docs); ON_FRAME reads them unconditionally.
|
|
6792
7242
|
...st.fontStyle === "italic" ? { sty: "italic" } : {},
|
|
6793
|
-
...o.maxWidth ? { mw:
|
|
6794
|
-
...st.letterSpacing ? { ls:
|
|
6795
|
-
...st.lineHeight !== OVERLAY_LINE_HEIGHT ? { lh:
|
|
7243
|
+
...o.maxWidth ? { mw: round7(o.maxWidth) } : {},
|
|
7244
|
+
...st.letterSpacing ? { ls: round7(st.letterSpacing) } : {},
|
|
7245
|
+
...st.lineHeight !== OVERLAY_LINE_HEIGHT ? { lh: round7(st.lineHeight) } : {},
|
|
6796
7246
|
...st.align !== "center" ? { align: st.align } : {},
|
|
6797
|
-
...st.stroke ? { stroke: { c: st.stroke.color, w:
|
|
7247
|
+
...st.stroke ? { stroke: { c: st.stroke.color, w: round7(st.stroke.width) } } : {},
|
|
6798
7248
|
// Hosted face behind an override: ON_FRAME lazy-loads it so a
|
|
6799
7249
|
// live family/weight edit paints without a LOAD (SETUP only
|
|
6800
7250
|
// runs on cold load).
|
|
@@ -6807,10 +7257,10 @@ function studioLayerData(layers, duration) {
|
|
|
6807
7257
|
...bx ? {
|
|
6808
7258
|
box: {
|
|
6809
7259
|
c: bx.color,
|
|
6810
|
-
o:
|
|
6811
|
-
px:
|
|
6812
|
-
py:
|
|
6813
|
-
r:
|
|
7260
|
+
o: round7(bx.opacity),
|
|
7261
|
+
px: round7(bx.padX),
|
|
7262
|
+
py: round7(bx.padY),
|
|
7263
|
+
r: round7(bx.radius)
|
|
6814
7264
|
}
|
|
6815
7265
|
} : {},
|
|
6816
7266
|
// Entrance animation: segmentation + timing normalized
|
|
@@ -6844,36 +7294,22 @@ function lowerToComposition(input) {
|
|
|
6844
7294
|
sourceDuration: (doc.source.meta.durationMs || 0) / 1e3
|
|
6845
7295
|
}) : [];
|
|
6846
7296
|
const layout = docCardLayout(doc);
|
|
6847
|
-
const meta = doc.source.meta;
|
|
6848
7297
|
const zoomStyle = resolveZoomStyle(doc.zoomStyle, doc.zoomParams);
|
|
6849
7298
|
const camera = cameraModel(doc.frame);
|
|
6850
|
-
const zoomSpans =
|
|
6851
|
-
doc
|
|
7299
|
+
const zoomSpans = loweredZoomSpans(
|
|
7300
|
+
doc,
|
|
6852
7301
|
rated,
|
|
6853
7302
|
transitions,
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
recenter: zoomStyle.followRecenter,
|
|
6865
|
-
lookahead: zoomStyle.followLookahead,
|
|
6866
|
-
camera
|
|
6867
|
-
}
|
|
6868
|
-
);
|
|
6869
|
-
if (f.entry)
|
|
6870
|
-
return { ...z, cx: f.entry.cx, cy: f.entry.cy, followEvents: f.events };
|
|
6871
|
-
}
|
|
6872
|
-
return {
|
|
6873
|
-
...z,
|
|
6874
|
-
...clampFocus(z.cx, z.cy, clampZoomLevel(z.level), layout, camera)
|
|
6875
|
-
};
|
|
6876
|
-
});
|
|
7303
|
+
layout,
|
|
7304
|
+
camera,
|
|
7305
|
+
zoomStyle
|
|
7306
|
+
);
|
|
7307
|
+
const zoomTrack = prependEntrance(
|
|
7308
|
+
zoomTrackFromDoc(zoomSpans, rated, zoomStyle),
|
|
7309
|
+
entranceZoomKeyframes(cardEnter(doc.frame))
|
|
7310
|
+
);
|
|
7311
|
+
const pins = resolvePins(doc, layout, camera, zoomTrack);
|
|
7312
|
+
const pinsData = pinsDataOf(doc, pins);
|
|
6877
7313
|
const data = {
|
|
6878
7314
|
videoSrc: doc.source.videoKey,
|
|
6879
7315
|
isImage: doc.source.sourceKind === "image",
|
|
@@ -6896,17 +7332,17 @@ function lowerToComposition(input) {
|
|
|
6896
7332
|
// Rated segments (speed spans pre-intersected): ON_FRAME's mapTime and the
|
|
6897
7333
|
// export's audio splice read the rate straight off each segment.
|
|
6898
7334
|
segments: rated.map((seg) => ({
|
|
6899
|
-
in:
|
|
6900
|
-
out:
|
|
6901
|
-
...seg.rate !== void 0 && seg.rate !== 1 ? { rate:
|
|
7335
|
+
in: round7(seg.in),
|
|
7336
|
+
out: round7(seg.out),
|
|
7337
|
+
...seg.rate !== void 0 && seg.rate !== 1 ? { rate: round7(seg.rate) } : {},
|
|
6902
7338
|
...seg.media ? { media: seg.media } : {}
|
|
6903
7339
|
})),
|
|
6904
7340
|
frame: doc.frame,
|
|
6905
7341
|
micGain: doc.micGain ?? 1,
|
|
6906
7342
|
cursor: smoothed.map((p) => ({
|
|
6907
|
-
t:
|
|
6908
|
-
x:
|
|
6909
|
-
y:
|
|
7343
|
+
t: round7(p.t),
|
|
7344
|
+
x: round7(p.x),
|
|
7345
|
+
y: round7(p.y)
|
|
6910
7346
|
})),
|
|
6911
7347
|
cursorStyle: doc.cursor,
|
|
6912
7348
|
cursorSpace: { w: doc.source.meta.width, h: doc.source.meta.height },
|
|
@@ -6929,9 +7365,9 @@ function lowerToComposition(input) {
|
|
|
6929
7365
|
crop: m.crop ?? null,
|
|
6930
7366
|
hasAudio: !!m.meta.hasAudio,
|
|
6931
7367
|
cursor: sm.map((p) => ({
|
|
6932
|
-
t:
|
|
6933
|
-
x:
|
|
6934
|
-
y:
|
|
7368
|
+
t: round7(p.t),
|
|
7369
|
+
x: round7(p.x),
|
|
7370
|
+
y: round7(p.y)
|
|
6935
7371
|
})),
|
|
6936
7372
|
cursorSpace: { w: m.meta.width, h: m.meta.height },
|
|
6937
7373
|
// The card this media wears (its own placement, bar, corner,
|
|
@@ -6950,11 +7386,11 @@ function lowerToComposition(input) {
|
|
|
6950
7386
|
// intensities/colors become numbers HERE — ON_FRAME reads no registry).
|
|
6951
7387
|
...clickFxData(doc, rated),
|
|
6952
7388
|
// Rated segments so zoom spans land at their speed-adjusted output times.
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
7389
|
+
zoomTrack,
|
|
7390
|
+
// Pinned layers' marks and leaders: the referent (normalised video
|
|
7391
|
+
// fractions) plus the leader's tip track, painted by ON_FRAME beside the
|
|
7392
|
+
// camera. Absent when no pinned layer asks for either — byte parity.
|
|
7393
|
+
...pinsData.length ? { pins: pinsData } : {},
|
|
6958
7394
|
// Tilt spans: OUTPUT-time [rx, ry] degree track. The rest pose is
|
|
6959
7395
|
// FLAT — there is no static card tilt any more — and the motion constants
|
|
6960
7396
|
// come from the camera style's tilt personality ('drift' slows its
|
|
@@ -6987,7 +7423,7 @@ function lowerToComposition(input) {
|
|
|
6987
7423
|
};
|
|
6988
7424
|
const entryData = {
|
|
6989
7425
|
lights: true,
|
|
6990
|
-
...studioLayerData(doc, duration)
|
|
7426
|
+
...studioLayerData(doc, duration, pins)
|
|
6991
7427
|
};
|
|
6992
7428
|
const config = {
|
|
6993
7429
|
version: 2,
|
|
@@ -7013,6 +7449,144 @@ function lowerToComposition(input) {
|
|
|
7013
7449
|
};
|
|
7014
7450
|
return { config, data, stack: { [STUDIO_ENTRY_ID]: entryData }, duration };
|
|
7015
7451
|
}
|
|
7452
|
+
function loweredZoomSpans(doc, rated, transitions, layout, camera, zoomStyle) {
|
|
7453
|
+
const meta = doc.source.meta;
|
|
7454
|
+
return restSpansThroughTransitions(
|
|
7455
|
+
doc.zoom,
|
|
7456
|
+
rated,
|
|
7457
|
+
transitions,
|
|
7458
|
+
ZOOM_SPAN_MIN
|
|
7459
|
+
).map((z) => {
|
|
7460
|
+
if (z.focusMode === "auto") {
|
|
7461
|
+
const f = followFocusEvents(
|
|
7462
|
+
z,
|
|
7463
|
+
doc.source.cursor,
|
|
7464
|
+
{ w: meta.width, h: meta.height },
|
|
7465
|
+
layout,
|
|
7466
|
+
{
|
|
7467
|
+
safeRatio: zoomStyle.followSafeRatio,
|
|
7468
|
+
recenter: zoomStyle.followRecenter,
|
|
7469
|
+
lookahead: zoomStyle.followLookahead,
|
|
7470
|
+
camera
|
|
7471
|
+
}
|
|
7472
|
+
);
|
|
7473
|
+
if (f.entry)
|
|
7474
|
+
return { ...z, cx: f.entry.cx, cy: f.entry.cy, followEvents: f.events };
|
|
7475
|
+
}
|
|
7476
|
+
return {
|
|
7477
|
+
...z,
|
|
7478
|
+
...clampFocus(z.cx, z.cy, clampZoomLevel(z.level), layout, camera)
|
|
7479
|
+
};
|
|
7480
|
+
});
|
|
7481
|
+
}
|
|
7482
|
+
function docZoomTrack(input) {
|
|
7483
|
+
const doc = migrateMotion(input);
|
|
7484
|
+
const rated = ratedSegments(doc);
|
|
7485
|
+
const transitions = docTransitions(doc);
|
|
7486
|
+
const layout = docCardLayout(doc);
|
|
7487
|
+
const camera = cameraModel(doc.frame);
|
|
7488
|
+
const zoomStyle = resolveZoomStyle(doc.zoomStyle, doc.zoomParams);
|
|
7489
|
+
const spans = loweredZoomSpans(
|
|
7490
|
+
doc,
|
|
7491
|
+
rated,
|
|
7492
|
+
transitions,
|
|
7493
|
+
layout,
|
|
7494
|
+
camera,
|
|
7495
|
+
zoomStyle
|
|
7496
|
+
);
|
|
7497
|
+
return prependEntrance(
|
|
7498
|
+
zoomTrackFromDoc(spans, rated, zoomStyle),
|
|
7499
|
+
entranceZoomKeyframes(cardEnter(doc.frame))
|
|
7500
|
+
);
|
|
7501
|
+
}
|
|
7502
|
+
function resolvePins(doc, layout, camera, zoomTrack) {
|
|
7503
|
+
const out = /* @__PURE__ */ new Map();
|
|
7504
|
+
const rated = ratedSegments(doc);
|
|
7505
|
+
const space = { w: doc.source.meta.width, h: doc.source.meta.height };
|
|
7506
|
+
for (const o of doc.overlays ?? []) {
|
|
7507
|
+
if (!o.pin) continue;
|
|
7508
|
+
const ref = pinReferent(doc, o.pin);
|
|
7509
|
+
if (!ref) continue;
|
|
7510
|
+
const base = overlayMotionBase(o);
|
|
7511
|
+
const motion = o.motion && o.motion.length ? motionTrack(
|
|
7512
|
+
base,
|
|
7513
|
+
overlayMotionKeys(o, base),
|
|
7514
|
+
Math.max(OVERLAY_MIN_DURATION, o.duration)
|
|
7515
|
+
) : null;
|
|
7516
|
+
out.set(
|
|
7517
|
+
o.id,
|
|
7518
|
+
pinPlacement({
|
|
7519
|
+
clip: o,
|
|
7520
|
+
referent: ref.rect,
|
|
7521
|
+
layout,
|
|
7522
|
+
camera,
|
|
7523
|
+
zoomTrack,
|
|
7524
|
+
base,
|
|
7525
|
+
motion,
|
|
7526
|
+
carry: ref.carry ?? null,
|
|
7527
|
+
cursor: doc.source.cursor,
|
|
7528
|
+
space,
|
|
7529
|
+
rated
|
|
7530
|
+
})
|
|
7531
|
+
);
|
|
7532
|
+
}
|
|
7533
|
+
return out;
|
|
7534
|
+
}
|
|
7535
|
+
function overlayPinPoseAt(doc, o, t) {
|
|
7536
|
+
if (!o.pin) return null;
|
|
7537
|
+
const layout = docCardLayout(doc);
|
|
7538
|
+
const camera = cameraModel(doc.frame);
|
|
7539
|
+
const pins = resolvePins(
|
|
7540
|
+
{ ...doc, overlays: [o] },
|
|
7541
|
+
layout,
|
|
7542
|
+
camera,
|
|
7543
|
+
docZoomTrack(doc)
|
|
7544
|
+
);
|
|
7545
|
+
const p = pins.get(o.id);
|
|
7546
|
+
if (!p) return null;
|
|
7547
|
+
return [...sample2(p.track, t, lerpArray2)];
|
|
7548
|
+
}
|
|
7549
|
+
function pinsDataOf(doc, pins) {
|
|
7550
|
+
const out = [];
|
|
7551
|
+
for (const o of doc.overlays ?? []) {
|
|
7552
|
+
const p = o.pin;
|
|
7553
|
+
const placed = p ? pins.get(o.id) : void 0;
|
|
7554
|
+
if (!p || !placed) continue;
|
|
7555
|
+
const mark = p.mark ?? "none";
|
|
7556
|
+
if (mark === "none" && !p.leader) continue;
|
|
7557
|
+
const ref = pinReferent(doc, p);
|
|
7558
|
+
if (!ref) continue;
|
|
7559
|
+
out.push({
|
|
7560
|
+
id: o.id,
|
|
7561
|
+
start: round7(o.start),
|
|
7562
|
+
dur: round7(Math.max(OVERLAY_MIN_DURATION, o.duration)),
|
|
7563
|
+
x: round43(ref.rect.x),
|
|
7564
|
+
y: round43(ref.rect.y),
|
|
7565
|
+
w: round43(ref.rect.w),
|
|
7566
|
+
h: round43(ref.rect.h),
|
|
7567
|
+
side: placed.side,
|
|
7568
|
+
mark,
|
|
7569
|
+
leader: !!p.leader,
|
|
7570
|
+
...p.color ? { color: p.color } : {},
|
|
7571
|
+
// A carried referent (a drag): ON_FRAME moves the mark and the
|
|
7572
|
+
// leader's end with the pointer through [t0, t1], source seconds.
|
|
7573
|
+
...ref.carry ? {
|
|
7574
|
+
carry: {
|
|
7575
|
+
t0: round7(ref.carry.t0),
|
|
7576
|
+
t1: round7(ref.carry.t1),
|
|
7577
|
+
x0: round43(ref.carry.x0),
|
|
7578
|
+
y0: round43(ref.carry.y0)
|
|
7579
|
+
}
|
|
7580
|
+
} : {},
|
|
7581
|
+
enter: enterKey(o),
|
|
7582
|
+
tip: placed.tip
|
|
7583
|
+
});
|
|
7584
|
+
}
|
|
7585
|
+
return out;
|
|
7586
|
+
}
|
|
7587
|
+
function round43(v) {
|
|
7588
|
+
return Math.round(v * 1e5) / 1e5;
|
|
7589
|
+
}
|
|
7016
7590
|
function clickFxData(doc, rated) {
|
|
7017
7591
|
const fx = doc.cursor.clickFx;
|
|
7018
7592
|
const on = fx.style !== "none" || fx.press;
|
|
@@ -7046,7 +7620,7 @@ function clickFxData(doc, rated) {
|
|
|
7046
7620
|
}
|
|
7047
7621
|
};
|
|
7048
7622
|
}
|
|
7049
|
-
function
|
|
7623
|
+
function round7(v) {
|
|
7050
7624
|
return Math.round(v * 1e3) / 1e3;
|
|
7051
7625
|
}
|
|
7052
7626
|
|
|
@@ -7136,8 +7710,8 @@ function planAutoSpeed(track, opts) {
|
|
|
7136
7710
|
accepted.sort((a, b) => a.in - b.in);
|
|
7137
7711
|
return accepted.map((c, i) => ({
|
|
7138
7712
|
id: `s${i}`,
|
|
7139
|
-
in:
|
|
7140
|
-
out:
|
|
7713
|
+
in: round8(c.in),
|
|
7714
|
+
out: round8(c.out),
|
|
7141
7715
|
rate: clampSpeedRate(c.rate),
|
|
7142
7716
|
source: "auto"
|
|
7143
7717
|
}));
|
|
@@ -7190,7 +7764,7 @@ function collectRuns(evs, gap, minLen, emit2) {
|
|
|
7190
7764
|
}
|
|
7191
7765
|
flush();
|
|
7192
7766
|
}
|
|
7193
|
-
function
|
|
7767
|
+
function round8(v) {
|
|
7194
7768
|
return Math.round(v * 1e3) / 1e3;
|
|
7195
7769
|
}
|
|
7196
7770
|
|
|
@@ -7371,16 +7945,16 @@ function momentsFromDoc(doc, plan, opts = {}) {
|
|
|
7371
7945
|
return {
|
|
7372
7946
|
id: `m${String(i + 1).padStart(2, "0")}`,
|
|
7373
7947
|
kind: d.kind,
|
|
7374
|
-
source: { in:
|
|
7375
|
-
output: output ? { in:
|
|
7376
|
-
at: d.at === null ? null :
|
|
7377
|
-
outputAt: outAt ?
|
|
7378
|
-
focus: d.focus ? { cx:
|
|
7948
|
+
source: { in: round9(d.in), out: round9(d.out) },
|
|
7949
|
+
output: output ? { in: round9(output.start), out: round9(output.end) } : null,
|
|
7950
|
+
at: d.at === null ? null : round9(d.at),
|
|
7951
|
+
outputAt: outAt ? round9(outAt.start) : null,
|
|
7952
|
+
focus: d.focus ? { cx: round9(d.focus.cx), cy: round9(d.focus.cy) } : null,
|
|
7379
7953
|
rect: d.rect ? {
|
|
7380
|
-
x:
|
|
7381
|
-
y:
|
|
7382
|
-
w:
|
|
7383
|
-
h:
|
|
7954
|
+
x: round9(d.rect.x),
|
|
7955
|
+
y: round9(d.rect.y),
|
|
7956
|
+
w: round9(d.rect.w),
|
|
7957
|
+
h: round9(d.rect.h)
|
|
7384
7958
|
} : null,
|
|
7385
7959
|
...d.clicks !== void 0 ? { clicks: d.clicks } : {},
|
|
7386
7960
|
...d.surface ? { surface: true } : {},
|
|
@@ -7401,7 +7975,7 @@ function activityOf(bins, d) {
|
|
|
7401
7975
|
sum += bins[i];
|
|
7402
7976
|
n++;
|
|
7403
7977
|
}
|
|
7404
|
-
return n ?
|
|
7978
|
+
return n ? round9(sum / n) : null;
|
|
7405
7979
|
}
|
|
7406
7980
|
function saidOver(transcript, d) {
|
|
7407
7981
|
if (!transcript?.length) return null;
|
|
@@ -7410,7 +7984,7 @@ function saidOver(transcript, d) {
|
|
|
7410
7984
|
const text = transcript.filter((s) => s.start < hi && s.end > lo).map((s) => s.text.trim()).filter(Boolean).join(" ");
|
|
7411
7985
|
return text || null;
|
|
7412
7986
|
}
|
|
7413
|
-
function
|
|
7987
|
+
function round9(v) {
|
|
7414
7988
|
return Math.round(v * 1e3) / 1e3;
|
|
7415
7989
|
}
|
|
7416
7990
|
|
|
@@ -7904,8 +8478,8 @@ function buildDigest(input) {
|
|
|
7904
8478
|
return {
|
|
7905
8479
|
digestVersion: DIGEST_VERSION,
|
|
7906
8480
|
take: {
|
|
7907
|
-
sourceDuration:
|
|
7908
|
-
outputDuration:
|
|
8481
|
+
sourceDuration: round10(meta.durationMs / 1e3),
|
|
8482
|
+
outputDuration: round10(input.outputDuration),
|
|
7909
8483
|
width: meta.width,
|
|
7910
8484
|
height: meta.height,
|
|
7911
8485
|
captureWidth: meta.captureWidth ?? null,
|
|
@@ -7954,17 +8528,17 @@ function buildDigest(input) {
|
|
|
7954
8528
|
function outputDurationOf(doc) {
|
|
7955
8529
|
return docOutputDuration(doc);
|
|
7956
8530
|
}
|
|
7957
|
-
function
|
|
8531
|
+
function round10(v) {
|
|
7958
8532
|
return Math.round(v * 1e3) / 1e3;
|
|
7959
8533
|
}
|
|
7960
8534
|
|
|
7961
8535
|
// src/timeline/rangeActions.ts
|
|
7962
|
-
import { mapTime } from "@vosjs/timeline";
|
|
8536
|
+
import { mapTime as mapTime2 } from "@vosjs/timeline";
|
|
7963
8537
|
function outputRangeToSource(doc, t0, t1) {
|
|
7964
8538
|
const rated = ratedSegments(doc);
|
|
7965
8539
|
return {
|
|
7966
|
-
srcIn:
|
|
7967
|
-
srcOut:
|
|
8540
|
+
srcIn: mapTime2(rated, Math.max(0, t0)),
|
|
8541
|
+
srcOut: mapTime2(rated, Math.max(0, t1))
|
|
7968
8542
|
};
|
|
7969
8543
|
}
|
|
7970
8544
|
var EPS = 1e-6;
|
|
@@ -7980,20 +8554,20 @@ function setSpeedInRange(spans, srcIn, srcOut, rate) {
|
|
|
7980
8554
|
continue;
|
|
7981
8555
|
}
|
|
7982
8556
|
if (srcIn - s.in >= MIN_REMAINDER)
|
|
7983
|
-
kept.push({ ...s, out:
|
|
8557
|
+
kept.push({ ...s, out: round11(srcIn), source: "manual" });
|
|
7984
8558
|
if (s.out - srcOut >= MIN_REMAINDER)
|
|
7985
8559
|
kept.push({
|
|
7986
8560
|
...s,
|
|
7987
8561
|
id: mintId(used),
|
|
7988
|
-
in:
|
|
8562
|
+
in: round11(srcOut),
|
|
7989
8563
|
source: "manual"
|
|
7990
8564
|
});
|
|
7991
8565
|
}
|
|
7992
8566
|
if (rate != null)
|
|
7993
8567
|
kept.push({
|
|
7994
8568
|
id: mintId(used),
|
|
7995
|
-
in:
|
|
7996
|
-
out:
|
|
8569
|
+
in: round11(srcIn),
|
|
8570
|
+
out: round11(srcOut),
|
|
7997
8571
|
rate: clampSpeedRate(rate),
|
|
7998
8572
|
source: "manual"
|
|
7999
8573
|
});
|
|
@@ -8009,9 +8583,9 @@ function removeSourceRange(segments, srcIn, srcOut) {
|
|
|
8009
8583
|
next.push(seg);
|
|
8010
8584
|
continue;
|
|
8011
8585
|
}
|
|
8012
|
-
if (srcIn - seg.in >= MIN_SEGMENT) next.push({ ...seg, out:
|
|
8586
|
+
if (srcIn - seg.in >= MIN_SEGMENT) next.push({ ...seg, out: round11(srcIn) });
|
|
8013
8587
|
if (seg.out - srcOut >= MIN_SEGMENT)
|
|
8014
|
-
next.push({ ...seg, in:
|
|
8588
|
+
next.push({ ...seg, in: round11(srcOut) });
|
|
8015
8589
|
}
|
|
8016
8590
|
return next.length ? next : [...segments];
|
|
8017
8591
|
}
|
|
@@ -8029,8 +8603,8 @@ function zoomSpanForRange(zoom, srcIn, srcOut) {
|
|
|
8029
8603
|
while (used.has(`u${n}`)) n++;
|
|
8030
8604
|
return {
|
|
8031
8605
|
id: `u${n}`,
|
|
8032
|
-
in:
|
|
8033
|
-
out:
|
|
8606
|
+
in: round11(start),
|
|
8607
|
+
out: round11(end),
|
|
8034
8608
|
level: DEFAULT_ZOOM_LEVEL,
|
|
8035
8609
|
cx: 0.5,
|
|
8036
8610
|
cy: 0.5,
|
|
@@ -8044,7 +8618,7 @@ function mintId(used) {
|
|
|
8044
8618
|
used.add(id);
|
|
8045
8619
|
return id;
|
|
8046
8620
|
}
|
|
8047
|
-
function
|
|
8621
|
+
function round11(v) {
|
|
8048
8622
|
return Math.round(v * 1e3) / 1e3;
|
|
8049
8623
|
}
|
|
8050
8624
|
|
|
@@ -8729,7 +9303,7 @@ function applyLook(frame, look, size, video, placement) {
|
|
|
8729
9303
|
|
|
8730
9304
|
// src/timeline/lanes.ts
|
|
8731
9305
|
import {
|
|
8732
|
-
mapTime as
|
|
9306
|
+
mapTime as mapTime3,
|
|
8733
9307
|
removeSegment,
|
|
8734
9308
|
segmentRate as segmentRate3,
|
|
8735
9309
|
sourceToTimeline as sourceToTimeline2,
|
|
@@ -8748,8 +9322,8 @@ var videoLane = {
|
|
|
8748
9322
|
return segments.map((s, i) => ({
|
|
8749
9323
|
id: `seg-${i}`,
|
|
8750
9324
|
kind: "clip",
|
|
8751
|
-
t:
|
|
8752
|
-
duration:
|
|
9325
|
+
t: round12(starts[i]),
|
|
9326
|
+
duration: round12(
|
|
8753
9327
|
outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i))
|
|
8754
9328
|
)
|
|
8755
9329
|
}));
|
|
@@ -8797,7 +9371,7 @@ var videoLane = {
|
|
|
8797
9371
|
index === 0 || !contiguous(segments, index)
|
|
8798
9372
|
);
|
|
8799
9373
|
const anchorOut = sourceToTimeline2(fullMap, Math.min(edgeSrc, sourceDuration)) ?? edgeSrc;
|
|
8800
|
-
const sourceT =
|
|
9374
|
+
const sourceT = mapTime3(fullMap, anchorOut + (g.t - edgeOutNow));
|
|
8801
9375
|
const next = trimSegment(
|
|
8802
9376
|
segments,
|
|
8803
9377
|
index,
|
|
@@ -8832,8 +9406,8 @@ var videoLane = {
|
|
|
8832
9406
|
if (local >= m.t - 1e-9 && local <= m.t + m.duration + 1e-9)
|
|
8833
9407
|
return null;
|
|
8834
9408
|
}
|
|
8835
|
-
const sourceT =
|
|
8836
|
-
|
|
9409
|
+
const sourceT = round12(
|
|
9410
|
+
mapTime3(segmentPieces(s, speeds, freezes, leading), local)
|
|
8837
9411
|
);
|
|
8838
9412
|
if (sourceT - s.in < 0.05 || s.out - sourceT < 0.05) return null;
|
|
8839
9413
|
const next = [
|
|
@@ -8873,7 +9447,7 @@ var videoLane = {
|
|
|
8873
9447
|
n === 1 || !contiguous(segments, n - 1)
|
|
8874
9448
|
)
|
|
8875
9449
|
] : []
|
|
8876
|
-
].map(
|
|
9450
|
+
].map(round12);
|
|
8877
9451
|
}
|
|
8878
9452
|
};
|
|
8879
9453
|
function nextFreezeId(doc) {
|
|
@@ -8900,8 +9474,8 @@ var freezeLane = {
|
|
|
8900
9474
|
{
|
|
8901
9475
|
id: f.id,
|
|
8902
9476
|
kind: "clip",
|
|
8903
|
-
t:
|
|
8904
|
-
duration:
|
|
9477
|
+
t: round12(m.t),
|
|
9478
|
+
duration: round12(m.duration),
|
|
8905
9479
|
label: `${trimZeros(f.seconds)}s`
|
|
8906
9480
|
}
|
|
8907
9481
|
] : [];
|
|
@@ -8923,7 +9497,7 @@ var freezeLane = {
|
|
|
8923
9497
|
if (g.t >= m.t - 1e-9 && g.t <= m.t + m.duration + 1e-9) return null;
|
|
8924
9498
|
}
|
|
8925
9499
|
if (g.t > totalDuration7(rated) + 1e-9) return null;
|
|
8926
|
-
const srcT =
|
|
9500
|
+
const srcT = round12(mapTime3(rated, Math.max(0, g.t)));
|
|
8927
9501
|
if (!kept(srcT) || !clear(srcT)) return null;
|
|
8928
9502
|
const id = nextFreezeId(doc);
|
|
8929
9503
|
const mediaHere = mediaAtOutput(rated, Math.max(0, g.t));
|
|
@@ -8965,8 +9539,8 @@ var freezeLane = {
|
|
|
8965
9539
|
const marks = freezeOutputExtents(splitBySpeed4(segs, speeds), freezes);
|
|
8966
9540
|
const mine = marks.get(f.id);
|
|
8967
9541
|
const endOut = mine ? mine.t + mine.duration : null;
|
|
8968
|
-
const srcT =
|
|
8969
|
-
Math.min(sourceDuration,
|
|
9542
|
+
const srcT = round12(
|
|
9543
|
+
Math.min(sourceDuration, mapTime3(base, Math.max(0, g.t)))
|
|
8970
9544
|
);
|
|
8971
9545
|
if (!kept(srcT) || !clear(srcT, f.id)) return null;
|
|
8972
9546
|
return (d) => {
|
|
@@ -9025,8 +9599,8 @@ var zoomLane = {
|
|
|
9025
9599
|
{
|
|
9026
9600
|
id: z.id,
|
|
9027
9601
|
kind: "clip",
|
|
9028
|
-
t:
|
|
9029
|
-
duration:
|
|
9602
|
+
t: round12(ext.start),
|
|
9603
|
+
duration: round12(ext.end - ext.start),
|
|
9030
9604
|
label: `${z.level.toFixed(2)}\xD7`
|
|
9031
9605
|
}
|
|
9032
9606
|
];
|
|
@@ -9037,7 +9611,7 @@ var zoomLane = {
|
|
|
9037
9611
|
const rated = ratedSegments(doc);
|
|
9038
9612
|
const sourceDuration = anchorSourceDuration(doc);
|
|
9039
9613
|
if (g.type === "create") {
|
|
9040
|
-
const srcT =
|
|
9614
|
+
const srcT = mapTime3(rated, Math.max(0, g.t));
|
|
9041
9615
|
const mediaHere = mediaAtOutput(rated, Math.max(0, g.t));
|
|
9042
9616
|
const here = spans.filter((z) => (z.media ?? "") === mediaHere);
|
|
9043
9617
|
const mediaLen = mediaDuration(mediaSource(doc, mediaHere) ?? doc.source);
|
|
@@ -9053,8 +9627,8 @@ var zoomLane = {
|
|
|
9053
9627
|
...d.zoom,
|
|
9054
9628
|
{
|
|
9055
9629
|
id,
|
|
9056
|
-
in:
|
|
9057
|
-
out:
|
|
9630
|
+
in: round12(srcT),
|
|
9631
|
+
out: round12(srcT + len),
|
|
9058
9632
|
level: DEFAULT_ZOOM_LEVEL,
|
|
9059
9633
|
cx,
|
|
9060
9634
|
cy,
|
|
@@ -9071,7 +9645,7 @@ var zoomLane = {
|
|
|
9071
9645
|
const len = sp.out - sp.in;
|
|
9072
9646
|
let newIn = clampToKept(
|
|
9073
9647
|
effectiveSegments(doc),
|
|
9074
|
-
|
|
9648
|
+
mapTime3(rated, Math.max(0, g.t)),
|
|
9075
9649
|
len
|
|
9076
9650
|
);
|
|
9077
9651
|
for (const o of others) {
|
|
@@ -9086,8 +9660,8 @@ var zoomLane = {
|
|
|
9086
9660
|
return (d) => {
|
|
9087
9661
|
const z = d.zoom.find((x) => x.id === g.id);
|
|
9088
9662
|
if (!z) return;
|
|
9089
|
-
z.in =
|
|
9090
|
-
z.out =
|
|
9663
|
+
z.in = round12(newIn);
|
|
9664
|
+
z.out = round12(newIn + len);
|
|
9091
9665
|
z.source = "manual";
|
|
9092
9666
|
d.zoom.sort((a, b) => a.in - b.in);
|
|
9093
9667
|
};
|
|
@@ -9101,7 +9675,7 @@ var zoomLane = {
|
|
|
9101
9675
|
sourceDuration,
|
|
9102
9676
|
...others.filter((o) => o.in >= sp.out).map((o) => o.in)
|
|
9103
9677
|
);
|
|
9104
|
-
const sourceT =
|
|
9678
|
+
const sourceT = mapTime3(rated, Math.max(0, g.t));
|
|
9105
9679
|
const minSrc = Math.min(
|
|
9106
9680
|
ZOOM_SPAN_MIN * rateAt(rated, sp.in),
|
|
9107
9681
|
sp.out - sp.in
|
|
@@ -9116,8 +9690,8 @@ var zoomLane = {
|
|
|
9116
9690
|
return (d) => {
|
|
9117
9691
|
const z = d.zoom.find((x) => x.id === g.id);
|
|
9118
9692
|
if (!z) return;
|
|
9119
|
-
z.in =
|
|
9120
|
-
z.out =
|
|
9693
|
+
z.in = round12(next.in);
|
|
9694
|
+
z.out = round12(next.out);
|
|
9121
9695
|
z.source = "manual";
|
|
9122
9696
|
};
|
|
9123
9697
|
}
|
|
@@ -9139,8 +9713,8 @@ function cursorFocusAt(doc, srcT) {
|
|
|
9139
9713
|
if (Math.abs(e.t - ms) < Math.abs(best.t - ms)) best = e;
|
|
9140
9714
|
const clamp015 = (v) => Math.max(0, Math.min(1, v));
|
|
9141
9715
|
return {
|
|
9142
|
-
cx:
|
|
9143
|
-
cy:
|
|
9716
|
+
cx: round12(clamp015(best.x / width)),
|
|
9717
|
+
cy: round12(clamp015(best.y / height))
|
|
9144
9718
|
};
|
|
9145
9719
|
}
|
|
9146
9720
|
var tiltLane = {
|
|
@@ -9154,8 +9728,8 @@ var tiltLane = {
|
|
|
9154
9728
|
{
|
|
9155
9729
|
id: z.id,
|
|
9156
9730
|
kind: "clip",
|
|
9157
|
-
t:
|
|
9158
|
-
duration:
|
|
9731
|
+
t: round12(ext.start),
|
|
9732
|
+
duration: round12(ext.end - ext.start),
|
|
9159
9733
|
label: `${formatDeg(z.rx)}\xB0/${formatDeg(z.ry)}\xB0`
|
|
9160
9734
|
}
|
|
9161
9735
|
];
|
|
@@ -9166,7 +9740,7 @@ var tiltLane = {
|
|
|
9166
9740
|
const rated = ratedSegments(doc);
|
|
9167
9741
|
const sourceDuration = anchorSourceDuration(doc);
|
|
9168
9742
|
if (g.type === "create") {
|
|
9169
|
-
const srcT =
|
|
9743
|
+
const srcT = mapTime3(rated, Math.max(0, g.t));
|
|
9170
9744
|
const mediaHere = mediaAtOutput(rated, Math.max(0, g.t));
|
|
9171
9745
|
const here = spans.filter((z) => (z.media ?? "") === mediaHere);
|
|
9172
9746
|
const mediaLen = mediaDuration(mediaSource(doc, mediaHere) ?? doc.source);
|
|
@@ -9181,8 +9755,8 @@ var tiltLane = {
|
|
|
9181
9755
|
...d.tilt ?? [],
|
|
9182
9756
|
{
|
|
9183
9757
|
id,
|
|
9184
|
-
in:
|
|
9185
|
-
out:
|
|
9758
|
+
in: round12(srcT),
|
|
9759
|
+
out: round12(srcT + len),
|
|
9186
9760
|
rx: DEFAULT_TILT_POSE.rx,
|
|
9187
9761
|
ry: DEFAULT_TILT_POSE.ry,
|
|
9188
9762
|
source: "manual",
|
|
@@ -9198,7 +9772,7 @@ var tiltLane = {
|
|
|
9198
9772
|
const len = sp.out - sp.in;
|
|
9199
9773
|
let newIn = clampToKept(
|
|
9200
9774
|
effectiveSegments(doc),
|
|
9201
|
-
|
|
9775
|
+
mapTime3(rated, Math.max(0, g.t)),
|
|
9202
9776
|
len
|
|
9203
9777
|
);
|
|
9204
9778
|
for (const o of others) {
|
|
@@ -9213,8 +9787,8 @@ var tiltLane = {
|
|
|
9213
9787
|
return (d) => {
|
|
9214
9788
|
const z = (d.tilt ?? []).find((x) => x.id === g.id);
|
|
9215
9789
|
if (!z || !d.tilt) return;
|
|
9216
|
-
z.in =
|
|
9217
|
-
z.out =
|
|
9790
|
+
z.in = round12(newIn);
|
|
9791
|
+
z.out = round12(newIn + len);
|
|
9218
9792
|
z.source = "manual";
|
|
9219
9793
|
d.tilt.sort((a, b) => a.in - b.in);
|
|
9220
9794
|
};
|
|
@@ -9228,7 +9802,7 @@ var tiltLane = {
|
|
|
9228
9802
|
sourceDuration,
|
|
9229
9803
|
...others.filter((o) => o.in >= sp.out).map((o) => o.in)
|
|
9230
9804
|
);
|
|
9231
|
-
const sourceT =
|
|
9805
|
+
const sourceT = mapTime3(rated, Math.max(0, g.t));
|
|
9232
9806
|
const minSrc = Math.min(
|
|
9233
9807
|
TILT_SPAN_MIN * rateAt(rated, sp.in),
|
|
9234
9808
|
sp.out - sp.in
|
|
@@ -9243,8 +9817,8 @@ var tiltLane = {
|
|
|
9243
9817
|
return (d) => {
|
|
9244
9818
|
const z = (d.tilt ?? []).find((x) => x.id === g.id);
|
|
9245
9819
|
if (!z) return;
|
|
9246
|
-
z.in =
|
|
9247
|
-
z.out =
|
|
9820
|
+
z.in = round12(next.in);
|
|
9821
|
+
z.out = round12(next.out);
|
|
9248
9822
|
z.source = "manual";
|
|
9249
9823
|
};
|
|
9250
9824
|
}
|
|
@@ -9271,8 +9845,8 @@ var camMoveLane = {
|
|
|
9271
9845
|
{
|
|
9272
9846
|
id: z.id,
|
|
9273
9847
|
kind: "clip",
|
|
9274
|
-
t:
|
|
9275
|
-
duration:
|
|
9848
|
+
t: round12(ext.start),
|
|
9849
|
+
duration: round12(ext.end - ext.start),
|
|
9276
9850
|
label: z.size != null ? `${Math.round(z.size * 100)}%` : "Move"
|
|
9277
9851
|
}
|
|
9278
9852
|
];
|
|
@@ -9284,7 +9858,7 @@ var camMoveLane = {
|
|
|
9284
9858
|
const sourceDuration = anchorSourceDuration(doc);
|
|
9285
9859
|
if (g.type === "create") {
|
|
9286
9860
|
if (!doc.source.camKey) return null;
|
|
9287
|
-
const srcT =
|
|
9861
|
+
const srcT = mapTime3(rated, Math.max(0, g.t));
|
|
9288
9862
|
if (spans.some((z) => srcT >= z.in && srcT < z.out)) return null;
|
|
9289
9863
|
const next = spans.filter((z) => z.in > srcT).sort((a, b) => a.in - b.in).at(0);
|
|
9290
9864
|
const limit = Math.min(sourceDuration, next ? next.in : sourceDuration);
|
|
@@ -9296,8 +9870,8 @@ var camMoveLane = {
|
|
|
9296
9870
|
...d.camMotion ?? [],
|
|
9297
9871
|
{
|
|
9298
9872
|
id,
|
|
9299
|
-
in:
|
|
9300
|
-
out:
|
|
9873
|
+
in: round12(srcT),
|
|
9874
|
+
out: round12(srcT + len),
|
|
9301
9875
|
...DEFAULT_CAM_POSE,
|
|
9302
9876
|
source: "manual"
|
|
9303
9877
|
}
|
|
@@ -9311,7 +9885,7 @@ var camMoveLane = {
|
|
|
9311
9885
|
const len = sp.out - sp.in;
|
|
9312
9886
|
let newIn = clampToKept(
|
|
9313
9887
|
effectiveSegments(doc),
|
|
9314
|
-
|
|
9888
|
+
mapTime3(rated, Math.max(0, g.t)),
|
|
9315
9889
|
len
|
|
9316
9890
|
);
|
|
9317
9891
|
for (const o of others) {
|
|
@@ -9326,8 +9900,8 @@ var camMoveLane = {
|
|
|
9326
9900
|
return (d) => {
|
|
9327
9901
|
const z = (d.camMotion ?? []).find((x) => x.id === g.id);
|
|
9328
9902
|
if (!z || !d.camMotion) return;
|
|
9329
|
-
z.in =
|
|
9330
|
-
z.out =
|
|
9903
|
+
z.in = round12(newIn);
|
|
9904
|
+
z.out = round12(newIn + len);
|
|
9331
9905
|
z.source = "manual";
|
|
9332
9906
|
d.camMotion.sort((a, b) => a.in - b.in);
|
|
9333
9907
|
};
|
|
@@ -9341,7 +9915,7 @@ var camMoveLane = {
|
|
|
9341
9915
|
sourceDuration,
|
|
9342
9916
|
...others.filter((o) => o.in >= sp.out).map((o) => o.in)
|
|
9343
9917
|
);
|
|
9344
|
-
const sourceT =
|
|
9918
|
+
const sourceT = mapTime3(rated, Math.max(0, g.t));
|
|
9345
9919
|
const minSrc = Math.min(
|
|
9346
9920
|
CAM_SPAN_MIN * rateAt(rated, sp.in),
|
|
9347
9921
|
sp.out - sp.in
|
|
@@ -9356,8 +9930,8 @@ var camMoveLane = {
|
|
|
9356
9930
|
return (d) => {
|
|
9357
9931
|
const z = (d.camMotion ?? []).find((x) => x.id === g.id);
|
|
9358
9932
|
if (!z) return;
|
|
9359
|
-
z.in =
|
|
9360
|
-
z.out =
|
|
9933
|
+
z.in = round12(next.in);
|
|
9934
|
+
z.out = round12(next.out);
|
|
9361
9935
|
z.source = "manual";
|
|
9362
9936
|
};
|
|
9363
9937
|
}
|
|
@@ -9390,10 +9964,10 @@ var camLane = {
|
|
|
9390
9964
|
items.push({
|
|
9391
9965
|
id: `cam-${i}`,
|
|
9392
9966
|
kind: "clip",
|
|
9393
|
-
t:
|
|
9967
|
+
t: round12(
|
|
9394
9968
|
starts[i] + outputLen({ in: s.in, out: a }, speeds, freezes, leading)
|
|
9395
9969
|
),
|
|
9396
|
-
duration:
|
|
9970
|
+
duration: round12(
|
|
9397
9971
|
outputLen({ in: a, out: b }, speeds, freezes, a === s.in && leading)
|
|
9398
9972
|
)
|
|
9399
9973
|
});
|
|
@@ -9415,14 +9989,14 @@ var camLane = {
|
|
|
9415
9989
|
if (!seg) return null;
|
|
9416
9990
|
const clipSrcStart = Math.max(seg.in, Math.max(current.in, first.in));
|
|
9417
9991
|
const span = Math.max(0.05, current.out - current.in);
|
|
9418
|
-
const delta =
|
|
9992
|
+
const delta = mapTime3(rated, Math.max(0, g.t)) - clipSrcStart;
|
|
9419
9993
|
const base = Math.max(first.in, current.in);
|
|
9420
9994
|
const newIn = Math.min(
|
|
9421
9995
|
Math.max(first.in, base + delta),
|
|
9422
9996
|
Math.max(first.in, last.out - span)
|
|
9423
9997
|
);
|
|
9424
9998
|
return (d) => {
|
|
9425
|
-
d.cam.window = { in:
|
|
9999
|
+
d.cam.window = { in: round12(newIn), out: round12(newIn + span) };
|
|
9426
10000
|
};
|
|
9427
10001
|
}
|
|
9428
10002
|
const overlapping = eff.map((s, i) => ({
|
|
@@ -9431,12 +10005,12 @@ var camLane = {
|
|
|
9431
10005
|
})).filter((x) => x.len > 1e-6);
|
|
9432
10006
|
const edgeIdx = g.edge === "start" ? overlapping.at(0)?.i : overlapping.at(-1)?.i;
|
|
9433
10007
|
if (edgeIdx === void 0 || g.id !== `cam-${edgeIdx}`) return null;
|
|
9434
|
-
const sourceT = Math.min(Math.max(
|
|
10008
|
+
const sourceT = Math.min(Math.max(mapTime3(rated, g.t), 0), sourceDuration);
|
|
9435
10009
|
const next = g.edge === "start" ? { in: Math.min(sourceT, current.out - 0.05), out: current.out } : { in: current.in, out: Math.max(sourceT, current.in + 0.05) };
|
|
9436
10010
|
return (d) => {
|
|
9437
10011
|
d.cam.window = {
|
|
9438
|
-
in:
|
|
9439
|
-
out:
|
|
10012
|
+
in: round12(Math.max(0, next.in)),
|
|
10013
|
+
out: round12(Math.min(sourceDuration, next.out))
|
|
9440
10014
|
};
|
|
9441
10015
|
};
|
|
9442
10016
|
},
|
|
@@ -9456,8 +10030,8 @@ var micLane = {
|
|
|
9456
10030
|
return segments.map((s, i) => ({
|
|
9457
10031
|
id: `mic-${i}`,
|
|
9458
10032
|
kind: "clip",
|
|
9459
|
-
t:
|
|
9460
|
-
duration:
|
|
10033
|
+
t: round12(starts[i]),
|
|
10034
|
+
duration: round12(
|
|
9461
10035
|
outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i))
|
|
9462
10036
|
)
|
|
9463
10037
|
}));
|
|
@@ -9513,8 +10087,8 @@ var speedLane = {
|
|
|
9513
10087
|
{
|
|
9514
10088
|
id: sp.id,
|
|
9515
10089
|
kind: "clip",
|
|
9516
|
-
t:
|
|
9517
|
-
duration:
|
|
10090
|
+
t: round12(start),
|
|
10091
|
+
duration: round12(end - start),
|
|
9518
10092
|
label: `${sp.rate}\xD7`
|
|
9519
10093
|
}
|
|
9520
10094
|
];
|
|
@@ -9525,7 +10099,7 @@ var speedLane = {
|
|
|
9525
10099
|
const rated = ratedSegments(doc);
|
|
9526
10100
|
const sourceDuration = anchorSourceDuration(doc);
|
|
9527
10101
|
if (g.type === "create") {
|
|
9528
|
-
const srcT =
|
|
10102
|
+
const srcT = mapTime3(rated, Math.max(0, g.t));
|
|
9529
10103
|
if (spans.some((s) => srcT >= s.in && srcT < s.out)) return null;
|
|
9530
10104
|
const next = spans.filter((s) => s.in > srcT).sort((a, b) => a.in - b.in).at(0);
|
|
9531
10105
|
const limit = Math.min(sourceDuration, next ? next.in : sourceDuration);
|
|
@@ -9538,8 +10112,8 @@ var speedLane = {
|
|
|
9538
10112
|
...d.speed ?? [],
|
|
9539
10113
|
{
|
|
9540
10114
|
id,
|
|
9541
|
-
in:
|
|
9542
|
-
out:
|
|
10115
|
+
in: round12(srcT),
|
|
10116
|
+
out: round12(srcT + len),
|
|
9543
10117
|
rate: DEFAULT_SPEED_RATE,
|
|
9544
10118
|
source: "manual",
|
|
9545
10119
|
...mediaHere ? { media: mediaHere } : {}
|
|
@@ -9555,7 +10129,7 @@ var speedLane = {
|
|
|
9555
10129
|
const len = sp.out - sp.in;
|
|
9556
10130
|
let newIn = clampToKept(
|
|
9557
10131
|
effectiveSegments(doc),
|
|
9558
|
-
|
|
10132
|
+
mapTime3(base, Math.max(0, g.t)),
|
|
9559
10133
|
len
|
|
9560
10134
|
);
|
|
9561
10135
|
for (const o of others) {
|
|
@@ -9570,8 +10144,8 @@ var speedLane = {
|
|
|
9570
10144
|
return (d) => {
|
|
9571
10145
|
const s = d.speed?.find((x) => x.id === g.id);
|
|
9572
10146
|
if (!s) return;
|
|
9573
|
-
s.in =
|
|
9574
|
-
s.out =
|
|
10147
|
+
s.in = round12(newIn);
|
|
10148
|
+
s.out = round12(newIn + len);
|
|
9575
10149
|
s.source = "manual";
|
|
9576
10150
|
d.speed.sort((a, b) => a.in - b.in);
|
|
9577
10151
|
};
|
|
@@ -9584,7 +10158,7 @@ var speedLane = {
|
|
|
9584
10158
|
const minSrc = Math.min(SPEED_SPAN_MIN * sp.rate, sp.out - sp.in);
|
|
9585
10159
|
let next;
|
|
9586
10160
|
if (g.edge === "start") {
|
|
9587
|
-
const sourceT =
|
|
10161
|
+
const sourceT = mapTime3(base, Math.max(0, g.t));
|
|
9588
10162
|
next = {
|
|
9589
10163
|
in: Math.min(Math.max(lo, sourceT), sp.out - minSrc),
|
|
9590
10164
|
out: sp.out
|
|
@@ -9600,8 +10174,8 @@ var speedLane = {
|
|
|
9600
10174
|
return (d) => {
|
|
9601
10175
|
const s = d.speed?.find((x) => x.id === g.id);
|
|
9602
10176
|
if (!s) return;
|
|
9603
|
-
s.in =
|
|
9604
|
-
s.out =
|
|
10177
|
+
s.in = round12(next.in);
|
|
10178
|
+
s.out = round12(next.out);
|
|
9605
10179
|
s.source = "manual";
|
|
9606
10180
|
};
|
|
9607
10181
|
}
|
|
@@ -9620,8 +10194,8 @@ var audioLane = {
|
|
|
9620
10194
|
return doc.audio.map((c) => ({
|
|
9621
10195
|
id: c.id,
|
|
9622
10196
|
kind: "clip",
|
|
9623
|
-
t:
|
|
9624
|
-
duration:
|
|
10197
|
+
t: round12(c.start),
|
|
10198
|
+
duration: round12(clipLength(c)),
|
|
9625
10199
|
label: c.name
|
|
9626
10200
|
}));
|
|
9627
10201
|
},
|
|
@@ -9632,7 +10206,7 @@ var audioLane = {
|
|
|
9632
10206
|
const start = Math.max(0, g.t);
|
|
9633
10207
|
return (d) => {
|
|
9634
10208
|
const c = d.audio.find((x) => x.id === g.id);
|
|
9635
|
-
if (c) c.start =
|
|
10209
|
+
if (c) c.start = round12(start);
|
|
9636
10210
|
};
|
|
9637
10211
|
}
|
|
9638
10212
|
if (g.type === "resize") {
|
|
@@ -9645,8 +10219,8 @@ var audioLane = {
|
|
|
9645
10219
|
return (d) => {
|
|
9646
10220
|
const c = d.audio.find((x) => x.id === g.id);
|
|
9647
10221
|
if (!c) return;
|
|
9648
|
-
c.start =
|
|
9649
|
-
c.loopLen =
|
|
10222
|
+
c.start = round12(newStart2);
|
|
10223
|
+
c.loopLen = round12(end - newStart2);
|
|
9650
10224
|
};
|
|
9651
10225
|
}
|
|
9652
10226
|
const delta = g.t - clip.start;
|
|
@@ -9655,22 +10229,22 @@ var audioLane = {
|
|
|
9655
10229
|
return (d) => {
|
|
9656
10230
|
const c = d.audio.find((x) => x.id === g.id);
|
|
9657
10231
|
if (!c) return;
|
|
9658
|
-
c.in =
|
|
9659
|
-
c.start =
|
|
10232
|
+
c.in = round12(newIn);
|
|
10233
|
+
c.start = round12(newStart);
|
|
9660
10234
|
};
|
|
9661
10235
|
}
|
|
9662
10236
|
if (clip.loop) {
|
|
9663
10237
|
const newLen = Math.max(0.1, g.t - clip.start);
|
|
9664
10238
|
return (d) => {
|
|
9665
10239
|
const c = d.audio.find((x) => x.id === g.id);
|
|
9666
|
-
if (c) c.loopLen =
|
|
10240
|
+
if (c) c.loopLen = round12(newLen);
|
|
9667
10241
|
};
|
|
9668
10242
|
}
|
|
9669
10243
|
const span = Math.max(0.05, g.t - clip.start);
|
|
9670
10244
|
const newOut = Math.min(clip.in + span, clip.duration);
|
|
9671
10245
|
return (d) => {
|
|
9672
10246
|
const c = d.audio.find((x) => x.id === g.id);
|
|
9673
|
-
if (c) c.out =
|
|
10247
|
+
if (c) c.out = round12(Math.max(c.in + 0.05, newOut));
|
|
9674
10248
|
};
|
|
9675
10249
|
}
|
|
9676
10250
|
if (g.type === "remove") {
|
|
@@ -9683,8 +10257,8 @@ var audioLane = {
|
|
|
9683
10257
|
},
|
|
9684
10258
|
magnets(doc) {
|
|
9685
10259
|
return doc.audio.flatMap((c) => [
|
|
9686
|
-
|
|
9687
|
-
|
|
10260
|
+
round12(c.start),
|
|
10261
|
+
round12(c.start + clipLength(c))
|
|
9688
10262
|
]);
|
|
9689
10263
|
}
|
|
9690
10264
|
};
|
|
@@ -9702,7 +10276,7 @@ function poseItems(clipId, start, duration, motion) {
|
|
|
9702
10276
|
return (motion ?? []).map((p, i) => ({
|
|
9703
10277
|
id: `${clipId}::k${i}`,
|
|
9704
10278
|
kind: "keyframe",
|
|
9705
|
-
t:
|
|
10279
|
+
t: round12(start + Math.min(Math.max(0, p.at), duration))
|
|
9706
10280
|
}));
|
|
9707
10281
|
}
|
|
9708
10282
|
var overlaysLane = {
|
|
@@ -9713,8 +10287,8 @@ var overlaysLane = {
|
|
|
9713
10287
|
{
|
|
9714
10288
|
id: o.id,
|
|
9715
10289
|
kind: "clip",
|
|
9716
|
-
t:
|
|
9717
|
-
duration:
|
|
10290
|
+
t: round12(o.start),
|
|
10291
|
+
duration: round12(o.duration),
|
|
9718
10292
|
label: o.kind === "text" ? o.text.split("\n")[0].slice(0, 24) || "Text" : o.kind === "html" ? htmlLayerLabel(o) : o.kind === "image" ? "Image" : "Video"
|
|
9719
10293
|
},
|
|
9720
10294
|
// Pose diamonds render after the clips, so they sit on top.
|
|
@@ -9734,7 +10308,7 @@ var overlaysLane = {
|
|
|
9734
10308
|
return (d) => {
|
|
9735
10309
|
const o = (d.overlays ?? []).find((x) => x.id === kf.clipId);
|
|
9736
10310
|
const p = o?.motion?.[kf.index];
|
|
9737
|
-
if (p) p.at =
|
|
10311
|
+
if (p) p.at = round12(at);
|
|
9738
10312
|
};
|
|
9739
10313
|
}
|
|
9740
10314
|
if (g.type === "remove") {
|
|
@@ -9763,8 +10337,8 @@ var overlaysLane = {
|
|
|
9763
10337
|
{
|
|
9764
10338
|
id,
|
|
9765
10339
|
kind: "text",
|
|
9766
|
-
start:
|
|
9767
|
-
duration:
|
|
10340
|
+
start: round12(start),
|
|
10341
|
+
duration: round12(len),
|
|
9768
10342
|
text: "Title",
|
|
9769
10343
|
preset: "title",
|
|
9770
10344
|
// Frame fractions: centered, lower-third — at any aspect ratio.
|
|
@@ -9779,7 +10353,7 @@ var overlaysLane = {
|
|
|
9779
10353
|
const start = Math.max(0, g.t);
|
|
9780
10354
|
return (d) => {
|
|
9781
10355
|
const o = (d.overlays ?? []).find((x) => x.id === g.id);
|
|
9782
|
-
if (o) o.start =
|
|
10356
|
+
if (o) o.start = round12(start);
|
|
9783
10357
|
};
|
|
9784
10358
|
}
|
|
9785
10359
|
if (g.type === "resize") {
|
|
@@ -9789,14 +10363,14 @@ var overlaysLane = {
|
|
|
9789
10363
|
return (d) => {
|
|
9790
10364
|
const o = (d.overlays ?? []).find((x) => x.id === g.id);
|
|
9791
10365
|
if (!o) return;
|
|
9792
|
-
o.start =
|
|
9793
|
-
o.duration =
|
|
10366
|
+
o.start = round12(newStart);
|
|
10367
|
+
o.duration = round12(end - newStart);
|
|
9794
10368
|
};
|
|
9795
10369
|
}
|
|
9796
10370
|
const newLen = Math.max(OVERLAY_MIN_DURATION, g.t - clip.start);
|
|
9797
10371
|
return (d) => {
|
|
9798
10372
|
const o = (d.overlays ?? []).find((x) => x.id === g.id);
|
|
9799
|
-
if (o) o.duration =
|
|
10373
|
+
if (o) o.duration = round12(newLen);
|
|
9800
10374
|
};
|
|
9801
10375
|
}
|
|
9802
10376
|
return (d) => {
|
|
@@ -9805,8 +10379,8 @@ var overlaysLane = {
|
|
|
9805
10379
|
},
|
|
9806
10380
|
magnets(doc) {
|
|
9807
10381
|
return (doc.overlays ?? []).flatMap((o) => [
|
|
9808
|
-
|
|
9809
|
-
|
|
10382
|
+
round12(o.start),
|
|
10383
|
+
round12(o.start + o.duration)
|
|
9810
10384
|
]);
|
|
9811
10385
|
}
|
|
9812
10386
|
};
|
|
@@ -9819,8 +10393,8 @@ var objectsLane = {
|
|
|
9819
10393
|
{
|
|
9820
10394
|
id: o.id,
|
|
9821
10395
|
kind: "clip",
|
|
9822
|
-
t:
|
|
9823
|
-
duration:
|
|
10396
|
+
t: round12(o.span?.start ?? 0),
|
|
10397
|
+
duration: round12(o.span?.duration ?? outDur),
|
|
9824
10398
|
label: o.asset.kind === "primitive" ? o.asset.shape : o.asset.kind === "text3d" ? o.asset.text : "model"
|
|
9825
10399
|
},
|
|
9826
10400
|
// Pose diamonds: clip-local `at` from the span start (0 span-less).
|
|
@@ -9848,7 +10422,7 @@ var objectsLane = {
|
|
|
9848
10422
|
return (d) => {
|
|
9849
10423
|
const o = (d.objects ?? []).find((x) => x.id === kf.clipId);
|
|
9850
10424
|
const p = o?.motion?.[kf.index];
|
|
9851
|
-
if (p) p.at =
|
|
10425
|
+
if (p) p.at = round12(at);
|
|
9852
10426
|
};
|
|
9853
10427
|
}
|
|
9854
10428
|
if (g.type === "remove") {
|
|
@@ -9869,7 +10443,7 @@ var objectsLane = {
|
|
|
9869
10443
|
const start = Math.max(0, g.t);
|
|
9870
10444
|
return (d) => {
|
|
9871
10445
|
const o = (d.objects ?? []).find((x) => x.id === g.id);
|
|
9872
|
-
if (o?.span) o.span.start =
|
|
10446
|
+
if (o?.span) o.span.start = round12(start);
|
|
9873
10447
|
};
|
|
9874
10448
|
}
|
|
9875
10449
|
if (g.type === "resize") {
|
|
@@ -9880,14 +10454,14 @@ var objectsLane = {
|
|
|
9880
10454
|
return (d) => {
|
|
9881
10455
|
const o = (d.objects ?? []).find((x) => x.id === g.id);
|
|
9882
10456
|
if (!o?.span) return;
|
|
9883
|
-
o.span.start =
|
|
9884
|
-
o.span.duration =
|
|
10457
|
+
o.span.start = round12(newStart);
|
|
10458
|
+
o.span.duration = round12(end - newStart);
|
|
9885
10459
|
};
|
|
9886
10460
|
}
|
|
9887
10461
|
const newLen = Math.max(OVERLAY_MIN_DURATION, g.t - clip.span.start);
|
|
9888
10462
|
return (d) => {
|
|
9889
10463
|
const o = (d.objects ?? []).find((x) => x.id === g.id);
|
|
9890
|
-
if (o?.span) o.span.duration =
|
|
10464
|
+
if (o?.span) o.span.duration = round12(newLen);
|
|
9891
10465
|
};
|
|
9892
10466
|
}
|
|
9893
10467
|
return (d) => {
|
|
@@ -9896,7 +10470,7 @@ var objectsLane = {
|
|
|
9896
10470
|
},
|
|
9897
10471
|
magnets(doc) {
|
|
9898
10472
|
return (doc.objects ?? []).flatMap(
|
|
9899
|
-
(o) => o.span ? [
|
|
10473
|
+
(o) => o.span ? [round12(o.span.start), round12(o.span.start + o.span.duration)] : []
|
|
9900
10474
|
);
|
|
9901
10475
|
}
|
|
9902
10476
|
};
|
|
@@ -9923,7 +10497,7 @@ function nextSpeedId(doc) {
|
|
|
9923
10497
|
function segIndex(id) {
|
|
9924
10498
|
return Number(id.replace("seg-", ""));
|
|
9925
10499
|
}
|
|
9926
|
-
function
|
|
10500
|
+
function round12(v) {
|
|
9927
10501
|
return Math.round(v * 1e3) / 1e3;
|
|
9928
10502
|
}
|
|
9929
10503
|
|
|
@@ -9952,7 +10526,7 @@ function computePeaks(channels, buckets) {
|
|
|
9952
10526
|
}
|
|
9953
10527
|
|
|
9954
10528
|
// src/lower/duckCurve.ts
|
|
9955
|
-
import { mapTime as
|
|
10529
|
+
import { mapTime as mapTime4 } from "@vosjs/timeline";
|
|
9956
10530
|
var DEFAULT_DUCK = {
|
|
9957
10531
|
threshold: 0.02,
|
|
9958
10532
|
duckTo: 0.25,
|
|
@@ -9988,7 +10562,7 @@ function duckCurve(rms, segments, durationSec2, opts = DEFAULT_DUCK) {
|
|
|
9988
10562
|
const steps = Math.ceil(durationSec2 * opts.gridHz);
|
|
9989
10563
|
for (let i = 0; i <= steps; i++) {
|
|
9990
10564
|
const t = Math.min(durationSec2, i * dt);
|
|
9991
|
-
const srcT =
|
|
10565
|
+
const srcT = mapTime4(segments, t);
|
|
9992
10566
|
const w = Math.min(
|
|
9993
10567
|
rms.values.length - 1,
|
|
9994
10568
|
Math.max(0, Math.floor(srcT * rms.rate))
|
|
@@ -10164,6 +10738,8 @@ export {
|
|
|
10164
10738
|
OVERLAY_SIZE_MIN,
|
|
10165
10739
|
OVERLAY_TRANSITION_DUR,
|
|
10166
10740
|
OVERLAY_Z,
|
|
10741
|
+
PIN_GAP,
|
|
10742
|
+
PIN_MARGIN,
|
|
10167
10743
|
PLATE_GROUND,
|
|
10168
10744
|
PLAYBACK_ACTIVITY,
|
|
10169
10745
|
PRIMARY_MEDIA,
|
|
@@ -10272,6 +10848,7 @@ export {
|
|
|
10272
10848
|
docToCropSpace,
|
|
10273
10849
|
docToFullSpace,
|
|
10274
10850
|
docTransitions,
|
|
10851
|
+
docZoomTrack,
|
|
10275
10852
|
dragsFromTrack,
|
|
10276
10853
|
dropTemplate,
|
|
10277
10854
|
duckCurve,
|
|
@@ -10289,6 +10866,7 @@ export {
|
|
|
10289
10866
|
expectedFrameSize,
|
|
10290
10867
|
exportSizeFor,
|
|
10291
10868
|
extractClicks,
|
|
10869
|
+
findStep,
|
|
10292
10870
|
focusBounds,
|
|
10293
10871
|
focusForViewportCentre,
|
|
10294
10872
|
followFocusEvents,
|
|
@@ -10360,12 +10938,18 @@ export {
|
|
|
10360
10938
|
overlayLines,
|
|
10361
10939
|
overlayMotionBase,
|
|
10362
10940
|
overlayMotionPoseAt,
|
|
10941
|
+
overlayPinPoseAt,
|
|
10363
10942
|
overlayRect,
|
|
10364
10943
|
overlaysLane,
|
|
10365
10944
|
ownFreezes,
|
|
10366
10945
|
pageDisplayUrl,
|
|
10367
10946
|
parsePoseId,
|
|
10368
10947
|
pickStyle,
|
|
10948
|
+
pinBox,
|
|
10949
|
+
pinPlacement,
|
|
10950
|
+
pinRectOnScreen,
|
|
10951
|
+
pinReferent,
|
|
10952
|
+
pinReferentAt,
|
|
10369
10953
|
placeFreezes,
|
|
10370
10954
|
planAutoSpeed,
|
|
10371
10955
|
planAutoTilt,
|
|
@@ -10384,6 +10968,7 @@ export {
|
|
|
10384
10968
|
resolveExportSize,
|
|
10385
10969
|
resolveOverlayBox,
|
|
10386
10970
|
resolveOverlayStyle,
|
|
10971
|
+
resolvePins,
|
|
10387
10972
|
resolveText3dAsset,
|
|
10388
10973
|
resolveZoomStyle,
|
|
10389
10974
|
restLeanOf,
|