@vosjs/studio-core 0.23.0 → 0.24.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 +158 -2
- package/dist/index.js +712 -219
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1164,18 +1164,21 @@ var ZOOM_STYLES = {
|
|
|
1164
1164
|
typingGap: 2.5,
|
|
1165
1165
|
typingHold: 1.1,
|
|
1166
1166
|
typingMinLevel: 1.4,
|
|
1167
|
-
rampIn: 0.
|
|
1167
|
+
rampIn: 0.6,
|
|
1168
1168
|
rampInOverlap: 0.2,
|
|
1169
1169
|
rampOut: 0.5,
|
|
1170
1170
|
chainGap: 3,
|
|
1171
1171
|
pan: 0.55,
|
|
1172
1172
|
// The reference's corner trace at 60 fps: a quarter of the move by a
|
|
1173
|
-
// fifth of the time, two thirds by half,
|
|
1174
|
-
//
|
|
1175
|
-
// (0.
|
|
1176
|
-
// and
|
|
1177
|
-
ease
|
|
1178
|
-
|
|
1173
|
+
// fifth of the time, two thirds by half, nine tenths by three
|
|
1174
|
+
// quarters, over ~0.6 s. Fitted against those three points,
|
|
1175
|
+
// (0.36, 0, 0.4, 1) is the closest cubic (15 / 64 / 93 %; half the
|
|
1176
|
+
// move at ~245 ms and 90 % at ~425 ms at 0.6 s): a gentle start and a
|
|
1177
|
+
// long settle. CSS ease (0.25, 0.1, 0.25, 1) reached the midpoint
|
|
1178
|
+
// 70 ms earlier and read a touch fast; (0.16, 1, 0.3, 1) put half the
|
|
1179
|
+
// move in the first sixty milliseconds and read as a twitch.
|
|
1180
|
+
ease: "css-bezier(0.36, 0, 0.4, 1)",
|
|
1181
|
+
panEase: "css-bezier(0.36, 0, 0.4, 1)",
|
|
1179
1182
|
followSafeRatio: 0.45,
|
|
1180
1183
|
followRecenter: 0.55,
|
|
1181
1184
|
followLookahead: 0.4,
|
|
@@ -1825,8 +1828,8 @@ function round(v) {
|
|
|
1825
1828
|
// src/lower/lowerToComposition.ts
|
|
1826
1829
|
import {
|
|
1827
1830
|
EASINGS,
|
|
1828
|
-
lerpArray,
|
|
1829
|
-
sample,
|
|
1831
|
+
lerpArray as lerpArray2,
|
|
1832
|
+
sample as sample2,
|
|
1830
1833
|
segmentRate as segmentRate2,
|
|
1831
1834
|
sortKeyframes as sortKeyframes2,
|
|
1832
1835
|
sourceToTimeline,
|
|
@@ -2658,14 +2661,14 @@ function resolveOverlayFx(clip, clipDuration) {
|
|
|
2658
2661
|
const maxTotal = Math.max(dur, clipDuration * 0.9);
|
|
2659
2662
|
st = Math.min(st, Math.max(0, (maxTotal - dur) / (upperN - 1)));
|
|
2660
2663
|
}
|
|
2661
|
-
const
|
|
2664
|
+
const round13 = (v) => Math.round(v * 1e4) / 1e4;
|
|
2662
2665
|
return {
|
|
2663
2666
|
k: spec.fx,
|
|
2664
2667
|
u: unit,
|
|
2665
2668
|
d: FX_DIR[spec.direction ?? "forward"] ?? 0,
|
|
2666
|
-
st:
|
|
2667
|
-
dur:
|
|
2668
|
-
tt:
|
|
2669
|
+
st: round13(st),
|
|
2670
|
+
dur: round13(dur),
|
|
2671
|
+
tt: round13(st * (upperN - 1) + dur),
|
|
2669
2672
|
units,
|
|
2670
2673
|
n
|
|
2671
2674
|
};
|
|
@@ -3054,12 +3057,280 @@ function round2(v) {
|
|
|
3054
3057
|
return Math.round(v * 1e3) / 1e3;
|
|
3055
3058
|
}
|
|
3056
3059
|
|
|
3060
|
+
// src/lower/pin.ts
|
|
3061
|
+
import { lerpArray, 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
|
+
if (step.rect && validRect(step.rect) && w > 0 && h > 0) {
|
|
3081
|
+
return {
|
|
3082
|
+
rect: normRect(step.rect, w, h),
|
|
3083
|
+
by: "step",
|
|
3084
|
+
step,
|
|
3085
|
+
at: step.tStart
|
|
3086
|
+
};
|
|
3087
|
+
}
|
|
3088
|
+
const union = unionRects(
|
|
3089
|
+
pressRects(doc, step.tStart - 0.05, step.tEnd + 0.05)
|
|
3090
|
+
);
|
|
3091
|
+
if (!union || w <= 0 || h <= 0) return null;
|
|
3092
|
+
return { rect: normRect(union, w, h), by: "step", step, at: step.tStart };
|
|
3093
|
+
}
|
|
3094
|
+
if (pin.press !== void 0 && Number.isFinite(pin.press)) {
|
|
3095
|
+
let best = null;
|
|
3096
|
+
for (const e of doc.source.cursor) {
|
|
3097
|
+
if (e.type !== "down" || !e.rect || !validRect(e.rect)) continue;
|
|
3098
|
+
const t = e.t / 1e3;
|
|
3099
|
+
const d = Math.abs(t - pin.press);
|
|
3100
|
+
if (d > PIN_PRESS_WINDOW_S) continue;
|
|
3101
|
+
if (!best || d < Math.abs(best.t - pin.press)) best = { t, rect: e.rect };
|
|
3102
|
+
}
|
|
3103
|
+
if (!best || w <= 0 || h <= 0) return null;
|
|
3104
|
+
return { rect: normRect(best.rect, w, h), by: "press", at: best.t };
|
|
3105
|
+
}
|
|
3106
|
+
return null;
|
|
3107
|
+
}
|
|
3108
|
+
function findStep(steps, ref) {
|
|
3109
|
+
const byId = steps.find((s) => s.id !== void 0 && s.id === ref);
|
|
3110
|
+
if (byId) return byId;
|
|
3111
|
+
const idx = typeof ref === "number" ? ref : Number(ref);
|
|
3112
|
+
if (!Number.isInteger(idx)) return void 0;
|
|
3113
|
+
return steps.find((s) => s.step === idx);
|
|
3114
|
+
}
|
|
3115
|
+
function pressRects(doc, from, to) {
|
|
3116
|
+
const out = [];
|
|
3117
|
+
for (const e of doc.source.cursor) {
|
|
3118
|
+
if (e.type !== "down" && e.type !== "key") continue;
|
|
3119
|
+
if (!e.rect || !validRect(e.rect)) continue;
|
|
3120
|
+
const t = e.t / 1e3;
|
|
3121
|
+
if (t < from || t > to) continue;
|
|
3122
|
+
out.push(e.rect);
|
|
3123
|
+
}
|
|
3124
|
+
return out;
|
|
3125
|
+
}
|
|
3126
|
+
function unionRects(rects) {
|
|
3127
|
+
if (!rects.length) return null;
|
|
3128
|
+
let x0 = Infinity;
|
|
3129
|
+
let y0 = Infinity;
|
|
3130
|
+
let x1 = -Infinity;
|
|
3131
|
+
let y1 = -Infinity;
|
|
3132
|
+
for (const r of rects) {
|
|
3133
|
+
x0 = Math.min(x0, r.x);
|
|
3134
|
+
y0 = Math.min(y0, r.y);
|
|
3135
|
+
x1 = Math.max(x1, r.x + r.w);
|
|
3136
|
+
y1 = Math.max(y1, r.y + r.h);
|
|
3137
|
+
}
|
|
3138
|
+
return { x: x0, y: y0, w: x1 - x0, h: y1 - y0 };
|
|
3139
|
+
}
|
|
3140
|
+
function validRect(r) {
|
|
3141
|
+
return Number.isFinite(r.x) && Number.isFinite(r.y) && Number.isFinite(r.w) && Number.isFinite(r.h) && r.w > 0 && r.h > 0;
|
|
3142
|
+
}
|
|
3143
|
+
function normRect(r, w, h) {
|
|
3144
|
+
return { x: r.x / w, y: r.y / h, w: r.w / w, h: r.h / h };
|
|
3145
|
+
}
|
|
3146
|
+
function pinBox(clip, layout) {
|
|
3147
|
+
const scale = clip.transform.scale || 1;
|
|
3148
|
+
if (clip.kind === "html") {
|
|
3149
|
+
const pb = htmlLayerPictureBox(clip);
|
|
3150
|
+
const wPic = htmlLayerWidth(clip) * layout.W * scale;
|
|
3151
|
+
const unit = wPic / pb.width;
|
|
3152
|
+
return { w: clip.box.width * unit, h: clip.box.height * unit };
|
|
3153
|
+
}
|
|
3154
|
+
if (clip.kind !== "text") {
|
|
3155
|
+
const w = (clip.width ?? OVERLAY_MEDIA_DEFAULT_WIDTH) * layout.W * scale;
|
|
3156
|
+
return { w, h: w / (16 / 9) };
|
|
3157
|
+
}
|
|
3158
|
+
const r = overlayRect(clip, estimateMeasure, layout.W, layout.H);
|
|
3159
|
+
return { w: r.w, h: r.h };
|
|
3160
|
+
}
|
|
3161
|
+
function estimateMeasure(text, font, letterSpacingPx = 0) {
|
|
3162
|
+
const m = /(\d+(?:\.\d+)?)px/.exec(font);
|
|
3163
|
+
const px = m ? Number(m[1]) : 16;
|
|
3164
|
+
return text.length * (px * PIN_TEXT_ADVANCE + letterSpacingPx);
|
|
3165
|
+
}
|
|
3166
|
+
function pinRectOnScreen(rect, t, layout, camera, zoomTrack) {
|
|
3167
|
+
let level = 1;
|
|
3168
|
+
let cx = 0.5;
|
|
3169
|
+
let cy = 0.5;
|
|
3170
|
+
let centring;
|
|
3171
|
+
if (zoomTrack && zoomTrack.keyframes.length) {
|
|
3172
|
+
const z = sample(zoomTrack, t, lerpArray);
|
|
3173
|
+
level = z[0];
|
|
3174
|
+
cx = z[1];
|
|
3175
|
+
cy = z[2];
|
|
3176
|
+
if (z.length > 3) centring = z[3];
|
|
3177
|
+
}
|
|
3178
|
+
const v = zoomView(level, cx, cy, layout, camera, centring);
|
|
3179
|
+
const map = (nx, ny) => {
|
|
3180
|
+
const px = layout.dx + nx * layout.dw;
|
|
3181
|
+
const py = layout.dy + ny * layout.dh;
|
|
3182
|
+
return {
|
|
3183
|
+
x: v.ox + (px - v.wcx) * v.level,
|
|
3184
|
+
y: v.oy + (py - v.wcy) * v.level
|
|
3185
|
+
};
|
|
3186
|
+
};
|
|
3187
|
+
const a = map(rect.x, rect.y);
|
|
3188
|
+
const b = map(rect.x + rect.w, rect.y + rect.h);
|
|
3189
|
+
return { x: a.x, y: a.y, w: b.x - a.x, h: b.y - a.y };
|
|
3190
|
+
}
|
|
3191
|
+
function pinPlacement(input) {
|
|
3192
|
+
const { clip, referent, layout, camera, zoomTrack, base, motion } = input;
|
|
3193
|
+
const dur = Math.max(1e-3, clip.duration);
|
|
3194
|
+
const gap = clip.pin?.gap ?? PIN_GAP;
|
|
3195
|
+
const box = pinBox(clip, layout);
|
|
3196
|
+
const W = layout.W;
|
|
3197
|
+
const H = layout.H;
|
|
3198
|
+
const refAt = (local) => pinRectOnScreen(referent, clip.start + local, layout, camera, zoomTrack);
|
|
3199
|
+
const centreFor = (r, side2) => {
|
|
3200
|
+
switch (side2) {
|
|
3201
|
+
case "right":
|
|
3202
|
+
return { x: r.x + r.w + gap + box.w / 2, y: r.y + r.h / 2 };
|
|
3203
|
+
case "left":
|
|
3204
|
+
return { x: r.x - gap - box.w / 2, y: r.y + r.h / 2 };
|
|
3205
|
+
case "below":
|
|
3206
|
+
return { x: r.x + r.w / 2, y: r.y + r.h + gap + box.h / 2 };
|
|
3207
|
+
case "above":
|
|
3208
|
+
return { x: r.x + r.w / 2, y: r.y - gap - box.h / 2 };
|
|
3209
|
+
}
|
|
3210
|
+
};
|
|
3211
|
+
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;
|
|
3212
|
+
const room = (c) => {
|
|
3213
|
+
const x0 = Math.max(PIN_MARGIN, c.x - box.w / 2);
|
|
3214
|
+
const x1 = Math.min(W - PIN_MARGIN, c.x + box.w / 2);
|
|
3215
|
+
const y0 = Math.max(PIN_MARGIN, c.y - box.h / 2);
|
|
3216
|
+
const y1 = Math.min(H - PIN_MARGIN, c.y + box.h / 2);
|
|
3217
|
+
return Math.max(0, x1 - x0) * Math.max(0, y1 - y0);
|
|
3218
|
+
};
|
|
3219
|
+
let side;
|
|
3220
|
+
const asked = clip.pin?.side ?? "auto";
|
|
3221
|
+
if (asked !== "auto") side = asked;
|
|
3222
|
+
else {
|
|
3223
|
+
const edge = Math.min(PIN_EDGE_S, dur / 3);
|
|
3224
|
+
const probes = [edge, dur / 2, dur - edge].map(refAt);
|
|
3225
|
+
const fitting = SIDE_ORDER.find(
|
|
3226
|
+
(s) => probes.every((r) => fits(centreFor(r, s)))
|
|
3227
|
+
);
|
|
3228
|
+
if (fitting) side = fitting;
|
|
3229
|
+
else {
|
|
3230
|
+
let best = SIDE_ORDER[0];
|
|
3231
|
+
let bestRoom = -1;
|
|
3232
|
+
for (const s of SIDE_ORDER) {
|
|
3233
|
+
const total = probes.reduce((acc, r) => acc + room(centreFor(r, s)), 0);
|
|
3234
|
+
if (total > bestRoom) {
|
|
3235
|
+
bestRoom = total;
|
|
3236
|
+
best = s;
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
side = best;
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
const clampC = (c) => {
|
|
3243
|
+
const minX = Math.min(W / 2, PIN_MARGIN + box.w / 2);
|
|
3244
|
+
const maxX = Math.max(W / 2, W - PIN_MARGIN - box.w / 2);
|
|
3245
|
+
const minY = Math.min(H / 2, PIN_MARGIN + box.h / 2);
|
|
3246
|
+
const maxY = Math.max(H / 2, H - PIN_MARGIN - box.h / 2);
|
|
3247
|
+
return {
|
|
3248
|
+
x: Math.max(minX, Math.min(maxX, c.x)),
|
|
3249
|
+
y: Math.max(minY, Math.min(maxY, c.y))
|
|
3250
|
+
};
|
|
3251
|
+
};
|
|
3252
|
+
const tipFor = (c) => {
|
|
3253
|
+
switch (side) {
|
|
3254
|
+
case "right":
|
|
3255
|
+
return { x: c.x - box.w / 2, y: c.y };
|
|
3256
|
+
case "left":
|
|
3257
|
+
return { x: c.x + box.w / 2, y: c.y };
|
|
3258
|
+
case "below":
|
|
3259
|
+
return { x: c.x, y: c.y - box.h / 2 };
|
|
3260
|
+
case "above":
|
|
3261
|
+
return { x: c.x, y: c.y + box.h / 2 };
|
|
3262
|
+
}
|
|
3263
|
+
};
|
|
3264
|
+
const n = Math.max(1, Math.ceil(dur * PIN_SAMPLE_HZ));
|
|
3265
|
+
const poses = [];
|
|
3266
|
+
const tips = [];
|
|
3267
|
+
let clamped = 0;
|
|
3268
|
+
for (let i = 0; i <= n; i++) {
|
|
3269
|
+
const local = i === n ? dur : i / PIN_SAMPLE_HZ;
|
|
3270
|
+
const want = centreFor(refAt(local), side);
|
|
3271
|
+
const c = clampC(want);
|
|
3272
|
+
clamped = Math.max(clamped, Math.hypot(c.x - want.x, c.y - want.y));
|
|
3273
|
+
const pose = motion?.keyframes.length ? sample(motion, local, lerpArray) : base;
|
|
3274
|
+
poses.push({
|
|
3275
|
+
t: local,
|
|
3276
|
+
value: [c.x / W, c.y / H, pose[2], pose[3], pose[4]],
|
|
3277
|
+
ease: "linear"
|
|
3278
|
+
});
|
|
3279
|
+
const tp = tipFor(c);
|
|
3280
|
+
tips.push({ t: local, value: [tp.x / W, tp.y / H], ease: "linear" });
|
|
3281
|
+
}
|
|
3282
|
+
return {
|
|
3283
|
+
side,
|
|
3284
|
+
track: { keyframes: simplify(poses, [W, H, 1, 1, 1]) },
|
|
3285
|
+
tip: { keyframes: simplify(tips, [W, H]) },
|
|
3286
|
+
clamped: round4(clamped),
|
|
3287
|
+
box
|
|
3288
|
+
};
|
|
3289
|
+
}
|
|
3290
|
+
function simplify(keys, scalePx) {
|
|
3291
|
+
if (keys.length <= 2) return keys.map(roundKey);
|
|
3292
|
+
const out = [roundKey(keys[0])];
|
|
3293
|
+
let a = 0;
|
|
3294
|
+
for (let i = 2; i <= keys.length - 1; i++) {
|
|
3295
|
+
const ka = keys[a];
|
|
3296
|
+
const ki = keys[i];
|
|
3297
|
+
let ok = true;
|
|
3298
|
+
for (let j = a + 1; j < i && ok; j++) {
|
|
3299
|
+
const kj = keys[j];
|
|
3300
|
+
const u = (kj.t - ka.t) / (ki.t - ka.t || 1);
|
|
3301
|
+
for (let k = 0; k < kj.value.length; k++) {
|
|
3302
|
+
const pred = ka.value[k] + (ki.value[k] - ka.value[k]) * u;
|
|
3303
|
+
if (Math.abs(pred - kj.value[k]) * (scalePx[k] ?? 1) > PIN_SIMPLIFY_PX) {
|
|
3304
|
+
ok = false;
|
|
3305
|
+
break;
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
if (!ok) {
|
|
3310
|
+
a = i - 1;
|
|
3311
|
+
out.push(roundKey(keys[a]));
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
const last = keys[keys.length - 1];
|
|
3315
|
+
if (out[out.length - 1].t !== round4(last.t)) out.push(roundKey(last));
|
|
3316
|
+
return out;
|
|
3317
|
+
}
|
|
3318
|
+
function roundKey(k) {
|
|
3319
|
+
return { t: round4(k.t), value: k.value.map(round42), ease: k.ease };
|
|
3320
|
+
}
|
|
3321
|
+
function round4(v) {
|
|
3322
|
+
return Math.round(v * 1e3) / 1e3;
|
|
3323
|
+
}
|
|
3324
|
+
function round42(v) {
|
|
3325
|
+
return Math.round(v * 1e5) / 1e5;
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3057
3328
|
// src/lower/cursorIdle.ts
|
|
3058
3329
|
var CURSOR_IDLE_HOLD = 1;
|
|
3059
3330
|
var CURSOR_IDLE_FADE_OUT = 0.35;
|
|
3060
3331
|
var CURSOR_IDLE_FADE_IN = 0.18;
|
|
3061
3332
|
var CURSOR_IDLE_EPS_FRAC = 25e-4;
|
|
3062
|
-
var
|
|
3333
|
+
var round5 = (n) => Math.round(n * 1e3) / 1e3;
|
|
3063
3334
|
function cursorIdleFade(track, o) {
|
|
3064
3335
|
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 }));
|
|
3065
3336
|
if (moves.length === 0) return [];
|
|
@@ -3111,7 +3382,7 @@ function emit(keys, s, e) {
|
|
|
3111
3382
|
push(keys, e + CURSOR_IDLE_FADE_IN, 1);
|
|
3112
3383
|
}
|
|
3113
3384
|
function push(keys, t, a) {
|
|
3114
|
-
const rt =
|
|
3385
|
+
const rt = round5(t);
|
|
3115
3386
|
if (keys.length > 0 && rt <= keys[keys.length - 1].t) {
|
|
3116
3387
|
keys[keys.length - 1].a = a;
|
|
3117
3388
|
return;
|
|
@@ -4619,7 +4890,7 @@ function gatedRect(rect, x, y, space) {
|
|
|
4619
4890
|
return null;
|
|
4620
4891
|
const m = RECT_CONTAIN_MARGIN;
|
|
4621
4892
|
const inside = x >= rect.x - m && x <= rect.x + rect.w + m && y >= rect.y - m && y <= rect.y + rect.h + m;
|
|
4622
|
-
return inside ? [
|
|
4893
|
+
return inside ? [round6(rect.x), round6(rect.y), round6(rect.w), round6(rect.h)] : null;
|
|
4623
4894
|
}
|
|
4624
4895
|
function extractClicks(track, segments, opts) {
|
|
4625
4896
|
const out = [];
|
|
@@ -4644,11 +4915,11 @@ function extractClicks(track, segments, opts) {
|
|
|
4644
4915
|
const upSrc = st + Math.max(pressLen, 0.02);
|
|
4645
4916
|
const up = keptOutputEnd(segments, st, upSrc, opts.media) ?? ot + CLICK_SYNTH_RELEASE;
|
|
4646
4917
|
const click = {
|
|
4647
|
-
ot:
|
|
4648
|
-
up:
|
|
4649
|
-
st:
|
|
4650
|
-
x:
|
|
4651
|
-
y:
|
|
4918
|
+
ot: round6(ot),
|
|
4919
|
+
up: round6(Math.max(up, ot + 0.02)),
|
|
4920
|
+
st: round6(st),
|
|
4921
|
+
x: round6(e.x),
|
|
4922
|
+
y: round6(e.y),
|
|
4652
4923
|
b: button
|
|
4653
4924
|
};
|
|
4654
4925
|
if (opts.rects && e.rect) {
|
|
@@ -4667,7 +4938,7 @@ function hexToRgbTriplet(hex) {
|
|
|
4667
4938
|
const n = parseInt(h, 16);
|
|
4668
4939
|
return [n >> 16, n >> 8 & 255, n & 255];
|
|
4669
4940
|
}
|
|
4670
|
-
function
|
|
4941
|
+
function round6(v) {
|
|
4671
4942
|
return Math.round(v * 1e3) / 1e3;
|
|
4672
4943
|
}
|
|
4673
4944
|
|
|
@@ -4743,7 +5014,7 @@ function trackEmitter() {
|
|
|
4743
5014
|
if (tt <= prev.t + 1e-6 && sameVec(prev.value, value)) return prev.t;
|
|
4744
5015
|
if (tt <= prev.t + 1e-6) tt = prev.t + 1e-3;
|
|
4745
5016
|
}
|
|
4746
|
-
keyframes.push({ t:
|
|
5017
|
+
keyframes.push({ t: round7(tt), value: value.map(round7), ease });
|
|
4747
5018
|
return tt;
|
|
4748
5019
|
};
|
|
4749
5020
|
return { keyframes, push: push2 };
|
|
@@ -4920,7 +5191,7 @@ function camBubbleRectAt(doc, t) {
|
|
|
4920
5191
|
H
|
|
4921
5192
|
);
|
|
4922
5193
|
if (!track.keyframes.length) return camBubbleRect(doc.cam, W, H);
|
|
4923
|
-
return camRectFromPose(
|
|
5194
|
+
return camRectFromPose(sample2(track, t, lerpArray2), doc.cam, W, H);
|
|
4924
5195
|
}
|
|
4925
5196
|
function motionTrack(base, keys, dur) {
|
|
4926
5197
|
const sorted = keys.filter((k) => Number.isFinite(k.at)).sort((a, b) => a.at - b.at);
|
|
@@ -4968,7 +5239,7 @@ function overlayMotionPoseAt(o, t) {
|
|
|
4968
5239
|
Math.max(OVERLAY_MIN_DURATION, o.duration)
|
|
4969
5240
|
);
|
|
4970
5241
|
if (!track.keyframes.length) return null;
|
|
4971
|
-
return [...
|
|
5242
|
+
return [...sample2(track, t, lerpArray2)];
|
|
4972
5243
|
}
|
|
4973
5244
|
function objectMotionBase(o) {
|
|
4974
5245
|
const t = o.transform3d;
|
|
@@ -4998,7 +5269,7 @@ function objectMotionPoseAt(o, t, clipDur) {
|
|
|
4998
5269
|
o.span?.duration ?? clipDur
|
|
4999
5270
|
);
|
|
5000
5271
|
if (!track.keyframes.length) return null;
|
|
5001
|
-
return [...
|
|
5272
|
+
return [...sample2(track, t, lerpArray2)];
|
|
5002
5273
|
}
|
|
5003
5274
|
var FRAME_STEP_SRC = `
|
|
5004
5275
|
function stepPaused(vid, srcT) {
|
|
@@ -6380,6 +6651,50 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
6380
6651
|
}
|
|
6381
6652
|
}
|
|
6382
6653
|
|
|
6654
|
+
// pinned layers' marks \u2014 a standing ring or underline on a layer's
|
|
6655
|
+
// REFERENT for the layer's life (d.pins: OUTPUT-anchored records baked at
|
|
6656
|
+
// lowering, the referent in normalised video fractions). Painted inside the
|
|
6657
|
+
// zoom transform so the mark sits on the element under any level or pose,
|
|
6658
|
+
// at a constant on-screen weight (\xF7 ceZs), with the layer's own entrance
|
|
6659
|
+
// and exit. Locals are pn-prefixed (one var scope). Skipped on the ghost
|
|
6660
|
+
// pass like the effects.
|
|
6661
|
+
var pns = d.pins || []
|
|
6662
|
+
if (pns.length && !tpG) {
|
|
6663
|
+
for (var pi = 0; pi < pns.length; pi++) {
|
|
6664
|
+
var pn = pns[pi]
|
|
6665
|
+
if (!pn.mark || pn.mark === 'none') continue
|
|
6666
|
+
var pnT = t - pn.start
|
|
6667
|
+
if (pnT < 0 || pnT > pn.dur) continue
|
|
6668
|
+
var pnA = 1
|
|
6669
|
+
if (pnT < 0.35) { var pnU = pnT / 0.35; pnA = 1 - Math.pow(1 - pnU, 3) }
|
|
6670
|
+
if (pn.dur - pnT < 0.35) { var pnV = (pn.dur - pnT) / 0.35; pnA = Math.min(pnA, 1 - Math.pow(1 - pnV, 3)) }
|
|
6671
|
+
if (pnA <= 0.004) continue
|
|
6672
|
+
var pnX = dx + pn.x * dw, pnY = dy + pn.y * dh, pnW = pn.w * dw, pnH = pn.h * dh
|
|
6673
|
+
var pnLw = 2 * s2 / ceZs, pnPad = 6 * s2 / ceZs
|
|
6674
|
+
var pnCol = pn.color || 'rgba(255,255,255,0.95)'
|
|
6675
|
+
c.save()
|
|
6676
|
+
c.globalAlpha = pnA
|
|
6677
|
+
if (pn.mark === 'underline') {
|
|
6678
|
+
// a bar under the referent: dark rim, then the ink
|
|
6679
|
+
var pnBy = pnY + pnH + pnPad * 0.5
|
|
6680
|
+
c.fillStyle = 'rgba(0,0,0,0.35)'
|
|
6681
|
+
c.fillRect(pnX - pnLw, pnBy - pnLw, pnW + pnLw * 2, pnLw * 1.5 + pnLw * 2)
|
|
6682
|
+
c.fillStyle = pnCol
|
|
6683
|
+
c.fillRect(pnX, pnBy, pnW, pnLw * 1.5)
|
|
6684
|
+
} else {
|
|
6685
|
+
// a ring around the referent: the cursor's dual stroke, a dark rim
|
|
6686
|
+
// under the ink, legible on any content
|
|
6687
|
+
c.lineWidth = pnLw + 2 * s2 / ceZs
|
|
6688
|
+
c.strokeStyle = 'rgba(0,0,0,0.35)'
|
|
6689
|
+
rr(pnX - pnPad, pnY - pnPad, pnW + pnPad * 2, pnH + pnPad * 2, pnPad, c); c.stroke()
|
|
6690
|
+
c.lineWidth = pnLw
|
|
6691
|
+
c.strokeStyle = pnCol
|
|
6692
|
+
rr(pnX - pnPad, pnY - pnPad, pnW + pnPad * 2, pnH + pnPad * 2, pnPad, c); c.stroke()
|
|
6693
|
+
}
|
|
6694
|
+
c.restore()
|
|
6695
|
+
}
|
|
6696
|
+
}
|
|
6697
|
+
|
|
6383
6698
|
// cursor (SOURCE-anchored samples, read at the on-screen source moment).
|
|
6384
6699
|
// The dot is the only thing cursorStyle.visible hides \u2014 the track still drives
|
|
6385
6700
|
// cursor-follow zoom, and click effects draw above on their own switch.
|
|
@@ -6442,8 +6757,15 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
6442
6757
|
// Wait only for the FIRST decoded frame, then keep drawing through seeks.
|
|
6443
6758
|
if (camV && camV.readyState >= 2) r.camHasFrame = true
|
|
6444
6759
|
var camActiveNow = !!(camV && camOn && camS.visible !== false && r.camHasFrame)
|
|
6760
|
+
// A pinned layer's leader is live for the layer's life: the overlay layer
|
|
6761
|
+
// repaints while one is up (the referent moves with the camera).
|
|
6762
|
+
var pqs = d.pins || [], pqLive = false
|
|
6763
|
+
for (var pl = 0; pl < pqs.length; pl++) {
|
|
6764
|
+
var pq0 = pqs[pl]
|
|
6765
|
+
if (pq0.leader && t >= pq0.start && t <= pq0.start + pq0.dur) { pqLive = true; break }
|
|
6766
|
+
}
|
|
6445
6767
|
var ovSig = W + 'x' + H
|
|
6446
|
-
var ovDirty = ov.sig !== ovSig || camActiveNow || ov.active || !!trOut
|
|
6768
|
+
var ovDirty = ov.sig !== ovSig || camActiveNow || ov.active || !!trOut || pqLive
|
|
6447
6769
|
if (ovDirty) {
|
|
6448
6770
|
ovC.clearRect(0, 0, W, H)
|
|
6449
6771
|
// webcam bubble \u2014 pinned to the frame corner regardless of card tilt/zoom.
|
|
@@ -6493,6 +6815,57 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
6493
6815
|
ovC.restore()
|
|
6494
6816
|
}
|
|
6495
6817
|
}
|
|
6818
|
+
// pinned layers' leaders \u2014 a hairline from the layer's near edge (the
|
|
6819
|
+
// baked tip track, clip-local frame fractions) to the referent's near
|
|
6820
|
+
// edge, the referent mapped through the camera the way the boundary dot
|
|
6821
|
+
// is, with a dot at the referent. Fades with the layer; rides the rise
|
|
6822
|
+
// entrance so the line meets the card as it lands.
|
|
6823
|
+
if (pqLive) {
|
|
6824
|
+
for (var pj = 0; pj < pqs.length; pj++) {
|
|
6825
|
+
var pq = pqs[pj]
|
|
6826
|
+
if (!pq.leader) continue
|
|
6827
|
+
var pqT = t - pq.start
|
|
6828
|
+
if (pqT < 0 || pqT > pq.dur) continue
|
|
6829
|
+
var pqA = 1, pqRise = 0
|
|
6830
|
+
if (pqT < 0.35) {
|
|
6831
|
+
var pqU = 1 - Math.pow(1 - pqT / 0.35, 3)
|
|
6832
|
+
pqA = pqU
|
|
6833
|
+
if (pq.enter === 'rise') pqRise = (1 - pqU) * 24 * s
|
|
6834
|
+
}
|
|
6835
|
+
if (pq.dur - pqT < 0.35) { var pqV = (pq.dur - pqT) / 0.35; pqA = Math.min(pqA, 1 - Math.pow(1 - pqV, 3)) }
|
|
6836
|
+
if (pqA <= 0.004 || !pq.tip || !pq.tip.keyframes || !pq.tip.keyframes.length) continue
|
|
6837
|
+
var pqTip = TL.sample(pq.tip, pqT, TL.lerpArray)
|
|
6838
|
+
var pqTx = pqTip[0] * W, pqTy = pqTip[1] * H + pqRise
|
|
6839
|
+
// the referent's corners in frame space under the live camera
|
|
6840
|
+
var pqAx = dx + pq.x * dw, pqAy = dy + pq.y * dh
|
|
6841
|
+
var pqBx = dx + (pq.x + pq.w) * dw, pqBy = dy + (pq.y + pq.h) * dh
|
|
6842
|
+
if (lvl > 1.001 && !d.zoomSuppressed) {
|
|
6843
|
+
if (camStage) {
|
|
6844
|
+
pqAx = W / 2 + (pqAx - wcx) * lvl; pqAy = H / 2 + (pqAy - wcy) * lvl
|
|
6845
|
+
pqBx = W / 2 + (pqBx - wcx) * lvl; pqBy = H / 2 + (pqBy - wcy) * lvl
|
|
6846
|
+
} else {
|
|
6847
|
+
pqAx = fx + (pqAx - fx) * lvl; pqAy = fy + (pqAy - fy) * lvl
|
|
6848
|
+
pqBx = fx + (pqBx - fx) * lvl; pqBy = fy + (pqBy - fy) * lvl
|
|
6849
|
+
}
|
|
6850
|
+
}
|
|
6851
|
+
var pqRx = pq.side === 'right' ? pqBx : pq.side === 'left' ? pqAx : (pqAx + pqBx) / 2
|
|
6852
|
+
var pqRy = pq.side === 'below' ? pqBy : pq.side === 'above' ? pqAy : (pqAy + pqBy) / 2
|
|
6853
|
+
var pqCol = pq.color || 'rgba(255,255,255,0.95)'
|
|
6854
|
+
var pqLw = 2 * s
|
|
6855
|
+
ovC.save()
|
|
6856
|
+
ovC.globalAlpha = pqA
|
|
6857
|
+
ovC.lineCap = 'round'
|
|
6858
|
+
ovC.strokeStyle = 'rgba(0,0,0,0.35)'; ovC.lineWidth = pqLw + 2 * s
|
|
6859
|
+
ovC.beginPath(); ovC.moveTo(pqTx, pqTy); ovC.lineTo(pqRx, pqRy); ovC.stroke()
|
|
6860
|
+
ovC.strokeStyle = pqCol; ovC.lineWidth = pqLw
|
|
6861
|
+
ovC.beginPath(); ovC.moveTo(pqTx, pqTy); ovC.lineTo(pqRx, pqRy); ovC.stroke()
|
|
6862
|
+
ovC.fillStyle = 'rgba(0,0,0,0.35)'
|
|
6863
|
+
ovC.beginPath(); ovC.arc(pqRx, pqRy, 5 * s, 0, Math.PI * 2); ovC.fill()
|
|
6864
|
+
ovC.fillStyle = pqCol
|
|
6865
|
+
ovC.beginPath(); ovC.arc(pqRx, pqRy, 4 * s, 0, Math.PI * 2); ovC.fill()
|
|
6866
|
+
ovC.restore()
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6496
6869
|
// The boundary dot: the outgoing card's last cursor point and the
|
|
6497
6870
|
// incoming's current one, each carried by its card's move, one dot on
|
|
6498
6871
|
// the eased lerp between them (the idle fade never fires across a page
|
|
@@ -6507,7 +6880,7 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
6507
6880
|
drawCur(ovC, tqX2, tqY2, tqR, 1, 2 * (trQw || s))
|
|
6508
6881
|
}
|
|
6509
6882
|
ov.sig = ovSig
|
|
6510
|
-
ov.active = camActiveNow || !!trOut
|
|
6883
|
+
ov.active = camActiveNow || !!trOut || pqLive
|
|
6511
6884
|
if (ov.texture) ov.texture.needsUpdate = true
|
|
6512
6885
|
}
|
|
6513
6886
|
|
|
@@ -6601,7 +6974,7 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
6601
6974
|
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 }
|
|
6602
6975
|
}
|
|
6603
6976
|
}`;
|
|
6604
|
-
function studioLayerData(layers, duration) {
|
|
6977
|
+
function studioLayerData(layers, duration, pins) {
|
|
6605
6978
|
const rec = "source" in layers ? layers : null;
|
|
6606
6979
|
return {
|
|
6607
6980
|
// Music/SFX clips with their gain envelopes baked (shared truth for the
|
|
@@ -6610,14 +6983,14 @@ function studioLayerData(layers, duration) {
|
|
|
6610
6983
|
// needs a decoded recording, which a sync lowering can't produce.
|
|
6611
6984
|
audio: (layers.audio ?? []).map((c) => ({
|
|
6612
6985
|
key: c.key,
|
|
6613
|
-
start:
|
|
6614
|
-
in:
|
|
6615
|
-
out:
|
|
6616
|
-
gain:
|
|
6986
|
+
start: round7(c.start),
|
|
6987
|
+
in: round7(c.in),
|
|
6988
|
+
out: round7(c.out),
|
|
6989
|
+
gain: round7(c.gain),
|
|
6617
6990
|
loop: !!c.loop,
|
|
6618
|
-
len:
|
|
6991
|
+
len: round7(clipLength(c)),
|
|
6619
6992
|
duck: !!c.duck,
|
|
6620
|
-
env: clipEnvelope(c).map((p) => ({ t:
|
|
6993
|
+
env: clipEnvelope(c).map((p) => ({ t: round7(p.t), g: round7(p.g) }))
|
|
6621
6994
|
})),
|
|
6622
6995
|
// Object clips: numbers resolved HERE; shapes are the drafted engine spec.
|
|
6623
6996
|
...layers.objects && layers.objects.length ? {
|
|
@@ -6630,17 +7003,17 @@ function studioLayerData(layers, duration) {
|
|
|
6630
7003
|
} : o.asset.kind === "text3d" ? resolveText3dAsset(o.asset) : { kind: "gltf", key: o.asset.key },
|
|
6631
7004
|
...o.span ? {
|
|
6632
7005
|
span: {
|
|
6633
|
-
start:
|
|
6634
|
-
duration:
|
|
7006
|
+
start: round7(o.span.start),
|
|
7007
|
+
duration: round7(o.span.duration)
|
|
6635
7008
|
}
|
|
6636
7009
|
} : {},
|
|
6637
|
-
x:
|
|
6638
|
-
y:
|
|
6639
|
-
z:
|
|
6640
|
-
rx:
|
|
6641
|
-
ry:
|
|
6642
|
-
rz:
|
|
6643
|
-
scale:
|
|
7010
|
+
x: round7(o.transform3d.x),
|
|
7011
|
+
y: round7(o.transform3d.y),
|
|
7012
|
+
z: round7(o.transform3d.z),
|
|
7013
|
+
rx: round7(o.transform3d.rx),
|
|
7014
|
+
ry: round7(o.transform3d.ry),
|
|
7015
|
+
rz: round7(o.transform3d.rz),
|
|
7016
|
+
scale: round7(o.transform3d.scale || OBJECT_DEFAULT_SCALE),
|
|
6644
7017
|
anim: idleKey(o),
|
|
6645
7018
|
// Pose keyframes: clip-local [x,y,z,rx,ry,rz,scale] track,
|
|
6646
7019
|
// sampled at t − span.start. Omitted when absent — parity.
|
|
@@ -6667,18 +7040,20 @@ function studioLayerData(layers, duration) {
|
|
|
6667
7040
|
const base = {
|
|
6668
7041
|
id: o.id,
|
|
6669
7042
|
kind: o.kind,
|
|
6670
|
-
start:
|
|
6671
|
-
dur:
|
|
6672
|
-
x:
|
|
6673
|
-
y:
|
|
6674
|
-
scale:
|
|
6675
|
-
rot:
|
|
7043
|
+
start: round7(o.start),
|
|
7044
|
+
dur: round7(Math.max(OVERLAY_MIN_DURATION, o.duration)),
|
|
7045
|
+
x: round7(o.transform.x),
|
|
7046
|
+
y: round7(o.transform.y),
|
|
7047
|
+
scale: round7(o.transform.scale || 1),
|
|
7048
|
+
rot: round7(o.transform.rotation || 0),
|
|
6676
7049
|
enter: enterKey(o),
|
|
6677
7050
|
exit: exitKey(o),
|
|
6678
7051
|
// Pose keyframes: a CLIP-LOCAL [x, y, scale, rot, opacity]
|
|
6679
7052
|
// track, sampled in ON_FRAME at t − start. Omitted when the clip
|
|
6680
7053
|
// has no motion — data byte parity.
|
|
6681
7054
|
...(() => {
|
|
7055
|
+
const pinned = pins?.get(o.id);
|
|
7056
|
+
if (pinned) return { track: pinned.track };
|
|
6682
7057
|
if (!o.motion || !o.motion.length) return {};
|
|
6683
7058
|
const mb = overlayMotionBase(o);
|
|
6684
7059
|
const track = motionTrack(
|
|
@@ -6695,10 +7070,10 @@ function studioLayerData(layers, duration) {
|
|
|
6695
7070
|
...base,
|
|
6696
7071
|
kind: "image",
|
|
6697
7072
|
key: hl.key,
|
|
6698
|
-
w:
|
|
7073
|
+
w: round7(hl.width),
|
|
6699
7074
|
radius: 0,
|
|
6700
7075
|
shadow: "none",
|
|
6701
|
-
opacity:
|
|
7076
|
+
opacity: round7(o.opacity ?? 1),
|
|
6702
7077
|
html: hl.html
|
|
6703
7078
|
};
|
|
6704
7079
|
}
|
|
@@ -6715,8 +7090,8 @@ function studioLayerData(layers, duration) {
|
|
|
6715
7090
|
...o.frame.browserBar?.theme ? { theme: o.frame.browserBar.theme } : {}
|
|
6716
7091
|
},
|
|
6717
7092
|
lean: [
|
|
6718
|
-
|
|
6719
|
-
|
|
7093
|
+
round7(o.frame.lean?.rx ?? 0),
|
|
7094
|
+
round7(o.frame.lean?.ry ?? 0)
|
|
6720
7095
|
],
|
|
6721
7096
|
shadow: o.frame.shadow ?? LAYER_CARD_SHADOW,
|
|
6722
7097
|
shadowContact: o.frame.shadowContact ?? 0,
|
|
@@ -6732,17 +7107,17 @@ function studioLayerData(layers, duration) {
|
|
|
6732
7107
|
for (const ev of lm.cursor) {
|
|
6733
7108
|
if (ev.type === "down")
|
|
6734
7109
|
clicks.push({
|
|
6735
|
-
t:
|
|
6736
|
-
x:
|
|
6737
|
-
y:
|
|
7110
|
+
t: round7(ev.t / 1e3),
|
|
7111
|
+
x: round7(ev.x),
|
|
7112
|
+
y: round7(ev.y)
|
|
6738
7113
|
});
|
|
6739
7114
|
}
|
|
6740
7115
|
return {
|
|
6741
7116
|
cur: {
|
|
6742
7117
|
pts: sm.map((p) => ({
|
|
6743
|
-
t:
|
|
6744
|
-
x:
|
|
6745
|
-
y:
|
|
7118
|
+
t: round7(p.t),
|
|
7119
|
+
x: round7(p.x),
|
|
7120
|
+
y: round7(p.y)
|
|
6746
7121
|
})),
|
|
6747
7122
|
space: { w: lm.meta.width, h: lm.meta.height },
|
|
6748
7123
|
size: rec.cursor.size,
|
|
@@ -6757,7 +7132,7 @@ function studioLayerData(layers, duration) {
|
|
|
6757
7132
|
...lm && lm.crop ? { crop: lm.crop } : {},
|
|
6758
7133
|
...lmCard ? { card: lmCard } : {},
|
|
6759
7134
|
...lmFacts,
|
|
6760
|
-
w:
|
|
7135
|
+
w: round7(o.width ?? OVERLAY_MEDIA_DEFAULT_WIDTH),
|
|
6761
7136
|
radius: o.radius ?? OVERLAY_MEDIA_DEFAULT_RADIUS,
|
|
6762
7137
|
opacity: o.opacity ?? 1,
|
|
6763
7138
|
loop: !!o.loop,
|
|
@@ -6767,7 +7142,7 @@ function studioLayerData(layers, duration) {
|
|
|
6767
7142
|
...o.shadow ? { shadow: o.shadow } : {},
|
|
6768
7143
|
...o.border && o.border.width > 0 ? {
|
|
6769
7144
|
border: {
|
|
6770
|
-
width:
|
|
7145
|
+
width: round7(o.border.width),
|
|
6771
7146
|
color: o.border.color
|
|
6772
7147
|
}
|
|
6773
7148
|
} : {}
|
|
@@ -6787,11 +7162,11 @@ function studioLayerData(layers, duration) {
|
|
|
6787
7162
|
// Style-v2 fields bake only when non-default (byte parity for
|
|
6788
7163
|
// older docs); ON_FRAME reads them unconditionally.
|
|
6789
7164
|
...st.fontStyle === "italic" ? { sty: "italic" } : {},
|
|
6790
|
-
...o.maxWidth ? { mw:
|
|
6791
|
-
...st.letterSpacing ? { ls:
|
|
6792
|
-
...st.lineHeight !== OVERLAY_LINE_HEIGHT ? { lh:
|
|
7165
|
+
...o.maxWidth ? { mw: round7(o.maxWidth) } : {},
|
|
7166
|
+
...st.letterSpacing ? { ls: round7(st.letterSpacing) } : {},
|
|
7167
|
+
...st.lineHeight !== OVERLAY_LINE_HEIGHT ? { lh: round7(st.lineHeight) } : {},
|
|
6793
7168
|
...st.align !== "center" ? { align: st.align } : {},
|
|
6794
|
-
...st.stroke ? { stroke: { c: st.stroke.color, w:
|
|
7169
|
+
...st.stroke ? { stroke: { c: st.stroke.color, w: round7(st.stroke.width) } } : {},
|
|
6795
7170
|
// Hosted face behind an override: ON_FRAME lazy-loads it so a
|
|
6796
7171
|
// live family/weight edit paints without a LOAD (SETUP only
|
|
6797
7172
|
// runs on cold load).
|
|
@@ -6804,10 +7179,10 @@ function studioLayerData(layers, duration) {
|
|
|
6804
7179
|
...bx ? {
|
|
6805
7180
|
box: {
|
|
6806
7181
|
c: bx.color,
|
|
6807
|
-
o:
|
|
6808
|
-
px:
|
|
6809
|
-
py:
|
|
6810
|
-
r:
|
|
7182
|
+
o: round7(bx.opacity),
|
|
7183
|
+
px: round7(bx.padX),
|
|
7184
|
+
py: round7(bx.padY),
|
|
7185
|
+
r: round7(bx.radius)
|
|
6811
7186
|
}
|
|
6812
7187
|
} : {},
|
|
6813
7188
|
// Entrance animation: segmentation + timing normalized
|
|
@@ -6841,36 +7216,22 @@ function lowerToComposition(input) {
|
|
|
6841
7216
|
sourceDuration: (doc.source.meta.durationMs || 0) / 1e3
|
|
6842
7217
|
}) : [];
|
|
6843
7218
|
const layout = docCardLayout(doc);
|
|
6844
|
-
const meta = doc.source.meta;
|
|
6845
7219
|
const zoomStyle = resolveZoomStyle(doc.zoomStyle, doc.zoomParams);
|
|
6846
7220
|
const camera = cameraModel(doc.frame);
|
|
6847
|
-
const zoomSpans =
|
|
6848
|
-
doc
|
|
7221
|
+
const zoomSpans = loweredZoomSpans(
|
|
7222
|
+
doc,
|
|
6849
7223
|
rated,
|
|
6850
7224
|
transitions,
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
recenter: zoomStyle.followRecenter,
|
|
6862
|
-
lookahead: zoomStyle.followLookahead,
|
|
6863
|
-
camera
|
|
6864
|
-
}
|
|
6865
|
-
);
|
|
6866
|
-
if (f.entry)
|
|
6867
|
-
return { ...z, cx: f.entry.cx, cy: f.entry.cy, followEvents: f.events };
|
|
6868
|
-
}
|
|
6869
|
-
return {
|
|
6870
|
-
...z,
|
|
6871
|
-
...clampFocus(z.cx, z.cy, clampZoomLevel(z.level), layout, camera)
|
|
6872
|
-
};
|
|
6873
|
-
});
|
|
7225
|
+
layout,
|
|
7226
|
+
camera,
|
|
7227
|
+
zoomStyle
|
|
7228
|
+
);
|
|
7229
|
+
const zoomTrack = prependEntrance(
|
|
7230
|
+
zoomTrackFromDoc(zoomSpans, rated, zoomStyle),
|
|
7231
|
+
entranceZoomKeyframes(cardEnter(doc.frame))
|
|
7232
|
+
);
|
|
7233
|
+
const pins = resolvePins(doc, layout, camera, zoomTrack);
|
|
7234
|
+
const pinsData = pinsDataOf(doc, pins);
|
|
6874
7235
|
const data = {
|
|
6875
7236
|
videoSrc: doc.source.videoKey,
|
|
6876
7237
|
isImage: doc.source.sourceKind === "image",
|
|
@@ -6893,17 +7254,17 @@ function lowerToComposition(input) {
|
|
|
6893
7254
|
// Rated segments (speed spans pre-intersected): ON_FRAME's mapTime and the
|
|
6894
7255
|
// export's audio splice read the rate straight off each segment.
|
|
6895
7256
|
segments: rated.map((seg) => ({
|
|
6896
|
-
in:
|
|
6897
|
-
out:
|
|
6898
|
-
...seg.rate !== void 0 && seg.rate !== 1 ? { rate:
|
|
7257
|
+
in: round7(seg.in),
|
|
7258
|
+
out: round7(seg.out),
|
|
7259
|
+
...seg.rate !== void 0 && seg.rate !== 1 ? { rate: round7(seg.rate) } : {},
|
|
6899
7260
|
...seg.media ? { media: seg.media } : {}
|
|
6900
7261
|
})),
|
|
6901
7262
|
frame: doc.frame,
|
|
6902
7263
|
micGain: doc.micGain ?? 1,
|
|
6903
7264
|
cursor: smoothed.map((p) => ({
|
|
6904
|
-
t:
|
|
6905
|
-
x:
|
|
6906
|
-
y:
|
|
7265
|
+
t: round7(p.t),
|
|
7266
|
+
x: round7(p.x),
|
|
7267
|
+
y: round7(p.y)
|
|
6907
7268
|
})),
|
|
6908
7269
|
cursorStyle: doc.cursor,
|
|
6909
7270
|
cursorSpace: { w: doc.source.meta.width, h: doc.source.meta.height },
|
|
@@ -6926,9 +7287,9 @@ function lowerToComposition(input) {
|
|
|
6926
7287
|
crop: m.crop ?? null,
|
|
6927
7288
|
hasAudio: !!m.meta.hasAudio,
|
|
6928
7289
|
cursor: sm.map((p) => ({
|
|
6929
|
-
t:
|
|
6930
|
-
x:
|
|
6931
|
-
y:
|
|
7290
|
+
t: round7(p.t),
|
|
7291
|
+
x: round7(p.x),
|
|
7292
|
+
y: round7(p.y)
|
|
6932
7293
|
})),
|
|
6933
7294
|
cursorSpace: { w: m.meta.width, h: m.meta.height },
|
|
6934
7295
|
// The card this media wears (its own placement, bar, corner,
|
|
@@ -6947,11 +7308,11 @@ function lowerToComposition(input) {
|
|
|
6947
7308
|
// intensities/colors become numbers HERE — ON_FRAME reads no registry).
|
|
6948
7309
|
...clickFxData(doc, rated),
|
|
6949
7310
|
// Rated segments so zoom spans land at their speed-adjusted output times.
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
7311
|
+
zoomTrack,
|
|
7312
|
+
// Pinned layers' marks and leaders: the referent (normalised video
|
|
7313
|
+
// fractions) plus the leader's tip track, painted by ON_FRAME beside the
|
|
7314
|
+
// camera. Absent when no pinned layer asks for either — byte parity.
|
|
7315
|
+
...pinsData.length ? { pins: pinsData } : {},
|
|
6955
7316
|
// Tilt spans: OUTPUT-time [rx, ry] degree track. The rest pose is
|
|
6956
7317
|
// FLAT — there is no static card tilt any more — and the motion constants
|
|
6957
7318
|
// come from the camera style's tilt personality ('drift' slows its
|
|
@@ -6984,7 +7345,7 @@ function lowerToComposition(input) {
|
|
|
6984
7345
|
};
|
|
6985
7346
|
const entryData = {
|
|
6986
7347
|
lights: true,
|
|
6987
|
-
...studioLayerData(doc, duration)
|
|
7348
|
+
...studioLayerData(doc, duration, pins)
|
|
6988
7349
|
};
|
|
6989
7350
|
const config = {
|
|
6990
7351
|
version: 2,
|
|
@@ -7010,6 +7371,128 @@ function lowerToComposition(input) {
|
|
|
7010
7371
|
};
|
|
7011
7372
|
return { config, data, stack: { [STUDIO_ENTRY_ID]: entryData }, duration };
|
|
7012
7373
|
}
|
|
7374
|
+
function loweredZoomSpans(doc, rated, transitions, layout, camera, zoomStyle) {
|
|
7375
|
+
const meta = doc.source.meta;
|
|
7376
|
+
return restSpansThroughTransitions(
|
|
7377
|
+
doc.zoom,
|
|
7378
|
+
rated,
|
|
7379
|
+
transitions,
|
|
7380
|
+
ZOOM_SPAN_MIN
|
|
7381
|
+
).map((z) => {
|
|
7382
|
+
if (z.focusMode === "auto") {
|
|
7383
|
+
const f = followFocusEvents(
|
|
7384
|
+
z,
|
|
7385
|
+
doc.source.cursor,
|
|
7386
|
+
{ w: meta.width, h: meta.height },
|
|
7387
|
+
layout,
|
|
7388
|
+
{
|
|
7389
|
+
safeRatio: zoomStyle.followSafeRatio,
|
|
7390
|
+
recenter: zoomStyle.followRecenter,
|
|
7391
|
+
lookahead: zoomStyle.followLookahead,
|
|
7392
|
+
camera
|
|
7393
|
+
}
|
|
7394
|
+
);
|
|
7395
|
+
if (f.entry)
|
|
7396
|
+
return { ...z, cx: f.entry.cx, cy: f.entry.cy, followEvents: f.events };
|
|
7397
|
+
}
|
|
7398
|
+
return {
|
|
7399
|
+
...z,
|
|
7400
|
+
...clampFocus(z.cx, z.cy, clampZoomLevel(z.level), layout, camera)
|
|
7401
|
+
};
|
|
7402
|
+
});
|
|
7403
|
+
}
|
|
7404
|
+
function docZoomTrack(input) {
|
|
7405
|
+
const doc = migrateMotion(input);
|
|
7406
|
+
const rated = ratedSegments(doc);
|
|
7407
|
+
const transitions = docTransitions(doc);
|
|
7408
|
+
const layout = docCardLayout(doc);
|
|
7409
|
+
const camera = cameraModel(doc.frame);
|
|
7410
|
+
const zoomStyle = resolveZoomStyle(doc.zoomStyle, doc.zoomParams);
|
|
7411
|
+
const spans = loweredZoomSpans(
|
|
7412
|
+
doc,
|
|
7413
|
+
rated,
|
|
7414
|
+
transitions,
|
|
7415
|
+
layout,
|
|
7416
|
+
camera,
|
|
7417
|
+
zoomStyle
|
|
7418
|
+
);
|
|
7419
|
+
return prependEntrance(
|
|
7420
|
+
zoomTrackFromDoc(spans, rated, zoomStyle),
|
|
7421
|
+
entranceZoomKeyframes(cardEnter(doc.frame))
|
|
7422
|
+
);
|
|
7423
|
+
}
|
|
7424
|
+
function resolvePins(doc, layout, camera, zoomTrack) {
|
|
7425
|
+
const out = /* @__PURE__ */ new Map();
|
|
7426
|
+
for (const o of doc.overlays ?? []) {
|
|
7427
|
+
if (!o.pin) continue;
|
|
7428
|
+
const ref = pinReferent(doc, o.pin);
|
|
7429
|
+
if (!ref) continue;
|
|
7430
|
+
const base = overlayMotionBase(o);
|
|
7431
|
+
const motion = o.motion && o.motion.length ? motionTrack(
|
|
7432
|
+
base,
|
|
7433
|
+
overlayMotionKeys(o, base),
|
|
7434
|
+
Math.max(OVERLAY_MIN_DURATION, o.duration)
|
|
7435
|
+
) : null;
|
|
7436
|
+
out.set(
|
|
7437
|
+
o.id,
|
|
7438
|
+
pinPlacement({
|
|
7439
|
+
clip: o,
|
|
7440
|
+
referent: ref.rect,
|
|
7441
|
+
layout,
|
|
7442
|
+
camera,
|
|
7443
|
+
zoomTrack,
|
|
7444
|
+
base,
|
|
7445
|
+
motion
|
|
7446
|
+
})
|
|
7447
|
+
);
|
|
7448
|
+
}
|
|
7449
|
+
return out;
|
|
7450
|
+
}
|
|
7451
|
+
function overlayPinPoseAt(doc, o, t) {
|
|
7452
|
+
if (!o.pin) return null;
|
|
7453
|
+
const layout = docCardLayout(doc);
|
|
7454
|
+
const camera = cameraModel(doc.frame);
|
|
7455
|
+
const pins = resolvePins(
|
|
7456
|
+
{ ...doc, overlays: [o] },
|
|
7457
|
+
layout,
|
|
7458
|
+
camera,
|
|
7459
|
+
docZoomTrack(doc)
|
|
7460
|
+
);
|
|
7461
|
+
const p = pins.get(o.id);
|
|
7462
|
+
if (!p) return null;
|
|
7463
|
+
return [...sample2(p.track, t, lerpArray2)];
|
|
7464
|
+
}
|
|
7465
|
+
function pinsDataOf(doc, pins) {
|
|
7466
|
+
const out = [];
|
|
7467
|
+
for (const o of doc.overlays ?? []) {
|
|
7468
|
+
const p = o.pin;
|
|
7469
|
+
const placed = p ? pins.get(o.id) : void 0;
|
|
7470
|
+
if (!p || !placed) continue;
|
|
7471
|
+
const mark = p.mark ?? "none";
|
|
7472
|
+
if (mark === "none" && !p.leader) continue;
|
|
7473
|
+
const ref = pinReferent(doc, p);
|
|
7474
|
+
if (!ref) continue;
|
|
7475
|
+
out.push({
|
|
7476
|
+
id: o.id,
|
|
7477
|
+
start: round7(o.start),
|
|
7478
|
+
dur: round7(Math.max(OVERLAY_MIN_DURATION, o.duration)),
|
|
7479
|
+
x: round43(ref.rect.x),
|
|
7480
|
+
y: round43(ref.rect.y),
|
|
7481
|
+
w: round43(ref.rect.w),
|
|
7482
|
+
h: round43(ref.rect.h),
|
|
7483
|
+
side: placed.side,
|
|
7484
|
+
mark,
|
|
7485
|
+
leader: !!p.leader,
|
|
7486
|
+
...p.color ? { color: p.color } : {},
|
|
7487
|
+
enter: enterKey(o),
|
|
7488
|
+
tip: placed.tip
|
|
7489
|
+
});
|
|
7490
|
+
}
|
|
7491
|
+
return out;
|
|
7492
|
+
}
|
|
7493
|
+
function round43(v) {
|
|
7494
|
+
return Math.round(v * 1e5) / 1e5;
|
|
7495
|
+
}
|
|
7013
7496
|
function clickFxData(doc, rated) {
|
|
7014
7497
|
const fx = doc.cursor.clickFx;
|
|
7015
7498
|
const on = fx.style !== "none" || fx.press;
|
|
@@ -7043,7 +7526,7 @@ function clickFxData(doc, rated) {
|
|
|
7043
7526
|
}
|
|
7044
7527
|
};
|
|
7045
7528
|
}
|
|
7046
|
-
function
|
|
7529
|
+
function round7(v) {
|
|
7047
7530
|
return Math.round(v * 1e3) / 1e3;
|
|
7048
7531
|
}
|
|
7049
7532
|
|
|
@@ -7133,8 +7616,8 @@ function planAutoSpeed(track, opts) {
|
|
|
7133
7616
|
accepted.sort((a, b) => a.in - b.in);
|
|
7134
7617
|
return accepted.map((c, i) => ({
|
|
7135
7618
|
id: `s${i}`,
|
|
7136
|
-
in:
|
|
7137
|
-
out:
|
|
7619
|
+
in: round8(c.in),
|
|
7620
|
+
out: round8(c.out),
|
|
7138
7621
|
rate: clampSpeedRate(c.rate),
|
|
7139
7622
|
source: "auto"
|
|
7140
7623
|
}));
|
|
@@ -7187,7 +7670,7 @@ function collectRuns(evs, gap, minLen, emit2) {
|
|
|
7187
7670
|
}
|
|
7188
7671
|
flush();
|
|
7189
7672
|
}
|
|
7190
|
-
function
|
|
7673
|
+
function round8(v) {
|
|
7191
7674
|
return Math.round(v * 1e3) / 1e3;
|
|
7192
7675
|
}
|
|
7193
7676
|
|
|
@@ -7368,16 +7851,16 @@ function momentsFromDoc(doc, plan, opts = {}) {
|
|
|
7368
7851
|
return {
|
|
7369
7852
|
id: `m${String(i + 1).padStart(2, "0")}`,
|
|
7370
7853
|
kind: d.kind,
|
|
7371
|
-
source: { in:
|
|
7372
|
-
output: output ? { in:
|
|
7373
|
-
at: d.at === null ? null :
|
|
7374
|
-
outputAt: outAt ?
|
|
7375
|
-
focus: d.focus ? { cx:
|
|
7854
|
+
source: { in: round9(d.in), out: round9(d.out) },
|
|
7855
|
+
output: output ? { in: round9(output.start), out: round9(output.end) } : null,
|
|
7856
|
+
at: d.at === null ? null : round9(d.at),
|
|
7857
|
+
outputAt: outAt ? round9(outAt.start) : null,
|
|
7858
|
+
focus: d.focus ? { cx: round9(d.focus.cx), cy: round9(d.focus.cy) } : null,
|
|
7376
7859
|
rect: d.rect ? {
|
|
7377
|
-
x:
|
|
7378
|
-
y:
|
|
7379
|
-
w:
|
|
7380
|
-
h:
|
|
7860
|
+
x: round9(d.rect.x),
|
|
7861
|
+
y: round9(d.rect.y),
|
|
7862
|
+
w: round9(d.rect.w),
|
|
7863
|
+
h: round9(d.rect.h)
|
|
7381
7864
|
} : null,
|
|
7382
7865
|
...d.clicks !== void 0 ? { clicks: d.clicks } : {},
|
|
7383
7866
|
...d.surface ? { surface: true } : {},
|
|
@@ -7398,7 +7881,7 @@ function activityOf(bins, d) {
|
|
|
7398
7881
|
sum += bins[i];
|
|
7399
7882
|
n++;
|
|
7400
7883
|
}
|
|
7401
|
-
return n ?
|
|
7884
|
+
return n ? round9(sum / n) : null;
|
|
7402
7885
|
}
|
|
7403
7886
|
function saidOver(transcript, d) {
|
|
7404
7887
|
if (!transcript?.length) return null;
|
|
@@ -7407,7 +7890,7 @@ function saidOver(transcript, d) {
|
|
|
7407
7890
|
const text = transcript.filter((s) => s.start < hi && s.end > lo).map((s) => s.text.trim()).filter(Boolean).join(" ");
|
|
7408
7891
|
return text || null;
|
|
7409
7892
|
}
|
|
7410
|
-
function
|
|
7893
|
+
function round9(v) {
|
|
7411
7894
|
return Math.round(v * 1e3) / 1e3;
|
|
7412
7895
|
}
|
|
7413
7896
|
|
|
@@ -7901,8 +8384,8 @@ function buildDigest(input) {
|
|
|
7901
8384
|
return {
|
|
7902
8385
|
digestVersion: DIGEST_VERSION,
|
|
7903
8386
|
take: {
|
|
7904
|
-
sourceDuration:
|
|
7905
|
-
outputDuration:
|
|
8387
|
+
sourceDuration: round10(meta.durationMs / 1e3),
|
|
8388
|
+
outputDuration: round10(input.outputDuration),
|
|
7906
8389
|
width: meta.width,
|
|
7907
8390
|
height: meta.height,
|
|
7908
8391
|
captureWidth: meta.captureWidth ?? null,
|
|
@@ -7951,7 +8434,7 @@ function buildDigest(input) {
|
|
|
7951
8434
|
function outputDurationOf(doc) {
|
|
7952
8435
|
return docOutputDuration(doc);
|
|
7953
8436
|
}
|
|
7954
|
-
function
|
|
8437
|
+
function round10(v) {
|
|
7955
8438
|
return Math.round(v * 1e3) / 1e3;
|
|
7956
8439
|
}
|
|
7957
8440
|
|
|
@@ -7977,20 +8460,20 @@ function setSpeedInRange(spans, srcIn, srcOut, rate) {
|
|
|
7977
8460
|
continue;
|
|
7978
8461
|
}
|
|
7979
8462
|
if (srcIn - s.in >= MIN_REMAINDER)
|
|
7980
|
-
kept.push({ ...s, out:
|
|
8463
|
+
kept.push({ ...s, out: round11(srcIn), source: "manual" });
|
|
7981
8464
|
if (s.out - srcOut >= MIN_REMAINDER)
|
|
7982
8465
|
kept.push({
|
|
7983
8466
|
...s,
|
|
7984
8467
|
id: mintId(used),
|
|
7985
|
-
in:
|
|
8468
|
+
in: round11(srcOut),
|
|
7986
8469
|
source: "manual"
|
|
7987
8470
|
});
|
|
7988
8471
|
}
|
|
7989
8472
|
if (rate != null)
|
|
7990
8473
|
kept.push({
|
|
7991
8474
|
id: mintId(used),
|
|
7992
|
-
in:
|
|
7993
|
-
out:
|
|
8475
|
+
in: round11(srcIn),
|
|
8476
|
+
out: round11(srcOut),
|
|
7994
8477
|
rate: clampSpeedRate(rate),
|
|
7995
8478
|
source: "manual"
|
|
7996
8479
|
});
|
|
@@ -8006,9 +8489,9 @@ function removeSourceRange(segments, srcIn, srcOut) {
|
|
|
8006
8489
|
next.push(seg);
|
|
8007
8490
|
continue;
|
|
8008
8491
|
}
|
|
8009
|
-
if (srcIn - seg.in >= MIN_SEGMENT) next.push({ ...seg, out:
|
|
8492
|
+
if (srcIn - seg.in >= MIN_SEGMENT) next.push({ ...seg, out: round11(srcIn) });
|
|
8010
8493
|
if (seg.out - srcOut >= MIN_SEGMENT)
|
|
8011
|
-
next.push({ ...seg, in:
|
|
8494
|
+
next.push({ ...seg, in: round11(srcOut) });
|
|
8012
8495
|
}
|
|
8013
8496
|
return next.length ? next : [...segments];
|
|
8014
8497
|
}
|
|
@@ -8026,8 +8509,8 @@ function zoomSpanForRange(zoom, srcIn, srcOut) {
|
|
|
8026
8509
|
while (used.has(`u${n}`)) n++;
|
|
8027
8510
|
return {
|
|
8028
8511
|
id: `u${n}`,
|
|
8029
|
-
in:
|
|
8030
|
-
out:
|
|
8512
|
+
in: round11(start),
|
|
8513
|
+
out: round11(end),
|
|
8031
8514
|
level: DEFAULT_ZOOM_LEVEL,
|
|
8032
8515
|
cx: 0.5,
|
|
8033
8516
|
cy: 0.5,
|
|
@@ -8041,7 +8524,7 @@ function mintId(used) {
|
|
|
8041
8524
|
used.add(id);
|
|
8042
8525
|
return id;
|
|
8043
8526
|
}
|
|
8044
|
-
function
|
|
8527
|
+
function round11(v) {
|
|
8045
8528
|
return Math.round(v * 1e3) / 1e3;
|
|
8046
8529
|
}
|
|
8047
8530
|
|
|
@@ -8745,8 +9228,8 @@ var videoLane = {
|
|
|
8745
9228
|
return segments.map((s, i) => ({
|
|
8746
9229
|
id: `seg-${i}`,
|
|
8747
9230
|
kind: "clip",
|
|
8748
|
-
t:
|
|
8749
|
-
duration:
|
|
9231
|
+
t: round12(starts[i]),
|
|
9232
|
+
duration: round12(
|
|
8750
9233
|
outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i))
|
|
8751
9234
|
)
|
|
8752
9235
|
}));
|
|
@@ -8829,7 +9312,7 @@ var videoLane = {
|
|
|
8829
9312
|
if (local >= m.t - 1e-9 && local <= m.t + m.duration + 1e-9)
|
|
8830
9313
|
return null;
|
|
8831
9314
|
}
|
|
8832
|
-
const sourceT =
|
|
9315
|
+
const sourceT = round12(
|
|
8833
9316
|
mapTime2(segmentPieces(s, speeds, freezes, leading), local)
|
|
8834
9317
|
);
|
|
8835
9318
|
if (sourceT - s.in < 0.05 || s.out - sourceT < 0.05) return null;
|
|
@@ -8870,7 +9353,7 @@ var videoLane = {
|
|
|
8870
9353
|
n === 1 || !contiguous(segments, n - 1)
|
|
8871
9354
|
)
|
|
8872
9355
|
] : []
|
|
8873
|
-
].map(
|
|
9356
|
+
].map(round12);
|
|
8874
9357
|
}
|
|
8875
9358
|
};
|
|
8876
9359
|
function nextFreezeId(doc) {
|
|
@@ -8897,8 +9380,8 @@ var freezeLane = {
|
|
|
8897
9380
|
{
|
|
8898
9381
|
id: f.id,
|
|
8899
9382
|
kind: "clip",
|
|
8900
|
-
t:
|
|
8901
|
-
duration:
|
|
9383
|
+
t: round12(m.t),
|
|
9384
|
+
duration: round12(m.duration),
|
|
8902
9385
|
label: `${trimZeros(f.seconds)}s`
|
|
8903
9386
|
}
|
|
8904
9387
|
] : [];
|
|
@@ -8920,7 +9403,7 @@ var freezeLane = {
|
|
|
8920
9403
|
if (g.t >= m.t - 1e-9 && g.t <= m.t + m.duration + 1e-9) return null;
|
|
8921
9404
|
}
|
|
8922
9405
|
if (g.t > totalDuration7(rated) + 1e-9) return null;
|
|
8923
|
-
const srcT =
|
|
9406
|
+
const srcT = round12(mapTime2(rated, Math.max(0, g.t)));
|
|
8924
9407
|
if (!kept(srcT) || !clear(srcT)) return null;
|
|
8925
9408
|
const id = nextFreezeId(doc);
|
|
8926
9409
|
const mediaHere = mediaAtOutput(rated, Math.max(0, g.t));
|
|
@@ -8962,7 +9445,7 @@ var freezeLane = {
|
|
|
8962
9445
|
const marks = freezeOutputExtents(splitBySpeed4(segs, speeds), freezes);
|
|
8963
9446
|
const mine = marks.get(f.id);
|
|
8964
9447
|
const endOut = mine ? mine.t + mine.duration : null;
|
|
8965
|
-
const srcT =
|
|
9448
|
+
const srcT = round12(
|
|
8966
9449
|
Math.min(sourceDuration, mapTime2(base, Math.max(0, g.t)))
|
|
8967
9450
|
);
|
|
8968
9451
|
if (!kept(srcT) || !clear(srcT, f.id)) return null;
|
|
@@ -9022,8 +9505,8 @@ var zoomLane = {
|
|
|
9022
9505
|
{
|
|
9023
9506
|
id: z.id,
|
|
9024
9507
|
kind: "clip",
|
|
9025
|
-
t:
|
|
9026
|
-
duration:
|
|
9508
|
+
t: round12(ext.start),
|
|
9509
|
+
duration: round12(ext.end - ext.start),
|
|
9027
9510
|
label: `${z.level.toFixed(2)}\xD7`
|
|
9028
9511
|
}
|
|
9029
9512
|
];
|
|
@@ -9050,8 +9533,8 @@ var zoomLane = {
|
|
|
9050
9533
|
...d.zoom,
|
|
9051
9534
|
{
|
|
9052
9535
|
id,
|
|
9053
|
-
in:
|
|
9054
|
-
out:
|
|
9536
|
+
in: round12(srcT),
|
|
9537
|
+
out: round12(srcT + len),
|
|
9055
9538
|
level: DEFAULT_ZOOM_LEVEL,
|
|
9056
9539
|
cx,
|
|
9057
9540
|
cy,
|
|
@@ -9083,8 +9566,8 @@ var zoomLane = {
|
|
|
9083
9566
|
return (d) => {
|
|
9084
9567
|
const z = d.zoom.find((x) => x.id === g.id);
|
|
9085
9568
|
if (!z) return;
|
|
9086
|
-
z.in =
|
|
9087
|
-
z.out =
|
|
9569
|
+
z.in = round12(newIn);
|
|
9570
|
+
z.out = round12(newIn + len);
|
|
9088
9571
|
z.source = "manual";
|
|
9089
9572
|
d.zoom.sort((a, b) => a.in - b.in);
|
|
9090
9573
|
};
|
|
@@ -9113,8 +9596,8 @@ var zoomLane = {
|
|
|
9113
9596
|
return (d) => {
|
|
9114
9597
|
const z = d.zoom.find((x) => x.id === g.id);
|
|
9115
9598
|
if (!z) return;
|
|
9116
|
-
z.in =
|
|
9117
|
-
z.out =
|
|
9599
|
+
z.in = round12(next.in);
|
|
9600
|
+
z.out = round12(next.out);
|
|
9118
9601
|
z.source = "manual";
|
|
9119
9602
|
};
|
|
9120
9603
|
}
|
|
@@ -9136,8 +9619,8 @@ function cursorFocusAt(doc, srcT) {
|
|
|
9136
9619
|
if (Math.abs(e.t - ms) < Math.abs(best.t - ms)) best = e;
|
|
9137
9620
|
const clamp015 = (v) => Math.max(0, Math.min(1, v));
|
|
9138
9621
|
return {
|
|
9139
|
-
cx:
|
|
9140
|
-
cy:
|
|
9622
|
+
cx: round12(clamp015(best.x / width)),
|
|
9623
|
+
cy: round12(clamp015(best.y / height))
|
|
9141
9624
|
};
|
|
9142
9625
|
}
|
|
9143
9626
|
var tiltLane = {
|
|
@@ -9151,8 +9634,8 @@ var tiltLane = {
|
|
|
9151
9634
|
{
|
|
9152
9635
|
id: z.id,
|
|
9153
9636
|
kind: "clip",
|
|
9154
|
-
t:
|
|
9155
|
-
duration:
|
|
9637
|
+
t: round12(ext.start),
|
|
9638
|
+
duration: round12(ext.end - ext.start),
|
|
9156
9639
|
label: `${formatDeg(z.rx)}\xB0/${formatDeg(z.ry)}\xB0`
|
|
9157
9640
|
}
|
|
9158
9641
|
];
|
|
@@ -9178,8 +9661,8 @@ var tiltLane = {
|
|
|
9178
9661
|
...d.tilt ?? [],
|
|
9179
9662
|
{
|
|
9180
9663
|
id,
|
|
9181
|
-
in:
|
|
9182
|
-
out:
|
|
9664
|
+
in: round12(srcT),
|
|
9665
|
+
out: round12(srcT + len),
|
|
9183
9666
|
rx: DEFAULT_TILT_POSE.rx,
|
|
9184
9667
|
ry: DEFAULT_TILT_POSE.ry,
|
|
9185
9668
|
source: "manual",
|
|
@@ -9210,8 +9693,8 @@ var tiltLane = {
|
|
|
9210
9693
|
return (d) => {
|
|
9211
9694
|
const z = (d.tilt ?? []).find((x) => x.id === g.id);
|
|
9212
9695
|
if (!z || !d.tilt) return;
|
|
9213
|
-
z.in =
|
|
9214
|
-
z.out =
|
|
9696
|
+
z.in = round12(newIn);
|
|
9697
|
+
z.out = round12(newIn + len);
|
|
9215
9698
|
z.source = "manual";
|
|
9216
9699
|
d.tilt.sort((a, b) => a.in - b.in);
|
|
9217
9700
|
};
|
|
@@ -9240,8 +9723,8 @@ var tiltLane = {
|
|
|
9240
9723
|
return (d) => {
|
|
9241
9724
|
const z = (d.tilt ?? []).find((x) => x.id === g.id);
|
|
9242
9725
|
if (!z) return;
|
|
9243
|
-
z.in =
|
|
9244
|
-
z.out =
|
|
9726
|
+
z.in = round12(next.in);
|
|
9727
|
+
z.out = round12(next.out);
|
|
9245
9728
|
z.source = "manual";
|
|
9246
9729
|
};
|
|
9247
9730
|
}
|
|
@@ -9268,8 +9751,8 @@ var camMoveLane = {
|
|
|
9268
9751
|
{
|
|
9269
9752
|
id: z.id,
|
|
9270
9753
|
kind: "clip",
|
|
9271
|
-
t:
|
|
9272
|
-
duration:
|
|
9754
|
+
t: round12(ext.start),
|
|
9755
|
+
duration: round12(ext.end - ext.start),
|
|
9273
9756
|
label: z.size != null ? `${Math.round(z.size * 100)}%` : "Move"
|
|
9274
9757
|
}
|
|
9275
9758
|
];
|
|
@@ -9293,8 +9776,8 @@ var camMoveLane = {
|
|
|
9293
9776
|
...d.camMotion ?? [],
|
|
9294
9777
|
{
|
|
9295
9778
|
id,
|
|
9296
|
-
in:
|
|
9297
|
-
out:
|
|
9779
|
+
in: round12(srcT),
|
|
9780
|
+
out: round12(srcT + len),
|
|
9298
9781
|
...DEFAULT_CAM_POSE,
|
|
9299
9782
|
source: "manual"
|
|
9300
9783
|
}
|
|
@@ -9323,8 +9806,8 @@ var camMoveLane = {
|
|
|
9323
9806
|
return (d) => {
|
|
9324
9807
|
const z = (d.camMotion ?? []).find((x) => x.id === g.id);
|
|
9325
9808
|
if (!z || !d.camMotion) return;
|
|
9326
|
-
z.in =
|
|
9327
|
-
z.out =
|
|
9809
|
+
z.in = round12(newIn);
|
|
9810
|
+
z.out = round12(newIn + len);
|
|
9328
9811
|
z.source = "manual";
|
|
9329
9812
|
d.camMotion.sort((a, b) => a.in - b.in);
|
|
9330
9813
|
};
|
|
@@ -9353,8 +9836,8 @@ var camMoveLane = {
|
|
|
9353
9836
|
return (d) => {
|
|
9354
9837
|
const z = (d.camMotion ?? []).find((x) => x.id === g.id);
|
|
9355
9838
|
if (!z) return;
|
|
9356
|
-
z.in =
|
|
9357
|
-
z.out =
|
|
9839
|
+
z.in = round12(next.in);
|
|
9840
|
+
z.out = round12(next.out);
|
|
9358
9841
|
z.source = "manual";
|
|
9359
9842
|
};
|
|
9360
9843
|
}
|
|
@@ -9387,10 +9870,10 @@ var camLane = {
|
|
|
9387
9870
|
items.push({
|
|
9388
9871
|
id: `cam-${i}`,
|
|
9389
9872
|
kind: "clip",
|
|
9390
|
-
t:
|
|
9873
|
+
t: round12(
|
|
9391
9874
|
starts[i] + outputLen({ in: s.in, out: a }, speeds, freezes, leading)
|
|
9392
9875
|
),
|
|
9393
|
-
duration:
|
|
9876
|
+
duration: round12(
|
|
9394
9877
|
outputLen({ in: a, out: b }, speeds, freezes, a === s.in && leading)
|
|
9395
9878
|
)
|
|
9396
9879
|
});
|
|
@@ -9419,7 +9902,7 @@ var camLane = {
|
|
|
9419
9902
|
Math.max(first.in, last.out - span)
|
|
9420
9903
|
);
|
|
9421
9904
|
return (d) => {
|
|
9422
|
-
d.cam.window = { in:
|
|
9905
|
+
d.cam.window = { in: round12(newIn), out: round12(newIn + span) };
|
|
9423
9906
|
};
|
|
9424
9907
|
}
|
|
9425
9908
|
const overlapping = eff.map((s, i) => ({
|
|
@@ -9432,8 +9915,8 @@ var camLane = {
|
|
|
9432
9915
|
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) };
|
|
9433
9916
|
return (d) => {
|
|
9434
9917
|
d.cam.window = {
|
|
9435
|
-
in:
|
|
9436
|
-
out:
|
|
9918
|
+
in: round12(Math.max(0, next.in)),
|
|
9919
|
+
out: round12(Math.min(sourceDuration, next.out))
|
|
9437
9920
|
};
|
|
9438
9921
|
};
|
|
9439
9922
|
},
|
|
@@ -9453,8 +9936,8 @@ var micLane = {
|
|
|
9453
9936
|
return segments.map((s, i) => ({
|
|
9454
9937
|
id: `mic-${i}`,
|
|
9455
9938
|
kind: "clip",
|
|
9456
|
-
t:
|
|
9457
|
-
duration:
|
|
9939
|
+
t: round12(starts[i]),
|
|
9940
|
+
duration: round12(
|
|
9458
9941
|
outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i))
|
|
9459
9942
|
)
|
|
9460
9943
|
}));
|
|
@@ -9510,8 +9993,8 @@ var speedLane = {
|
|
|
9510
9993
|
{
|
|
9511
9994
|
id: sp.id,
|
|
9512
9995
|
kind: "clip",
|
|
9513
|
-
t:
|
|
9514
|
-
duration:
|
|
9996
|
+
t: round12(start),
|
|
9997
|
+
duration: round12(end - start),
|
|
9515
9998
|
label: `${sp.rate}\xD7`
|
|
9516
9999
|
}
|
|
9517
10000
|
];
|
|
@@ -9535,8 +10018,8 @@ var speedLane = {
|
|
|
9535
10018
|
...d.speed ?? [],
|
|
9536
10019
|
{
|
|
9537
10020
|
id,
|
|
9538
|
-
in:
|
|
9539
|
-
out:
|
|
10021
|
+
in: round12(srcT),
|
|
10022
|
+
out: round12(srcT + len),
|
|
9540
10023
|
rate: DEFAULT_SPEED_RATE,
|
|
9541
10024
|
source: "manual",
|
|
9542
10025
|
...mediaHere ? { media: mediaHere } : {}
|
|
@@ -9567,8 +10050,8 @@ var speedLane = {
|
|
|
9567
10050
|
return (d) => {
|
|
9568
10051
|
const s = d.speed?.find((x) => x.id === g.id);
|
|
9569
10052
|
if (!s) return;
|
|
9570
|
-
s.in =
|
|
9571
|
-
s.out =
|
|
10053
|
+
s.in = round12(newIn);
|
|
10054
|
+
s.out = round12(newIn + len);
|
|
9572
10055
|
s.source = "manual";
|
|
9573
10056
|
d.speed.sort((a, b) => a.in - b.in);
|
|
9574
10057
|
};
|
|
@@ -9597,8 +10080,8 @@ var speedLane = {
|
|
|
9597
10080
|
return (d) => {
|
|
9598
10081
|
const s = d.speed?.find((x) => x.id === g.id);
|
|
9599
10082
|
if (!s) return;
|
|
9600
|
-
s.in =
|
|
9601
|
-
s.out =
|
|
10083
|
+
s.in = round12(next.in);
|
|
10084
|
+
s.out = round12(next.out);
|
|
9602
10085
|
s.source = "manual";
|
|
9603
10086
|
};
|
|
9604
10087
|
}
|
|
@@ -9617,8 +10100,8 @@ var audioLane = {
|
|
|
9617
10100
|
return doc.audio.map((c) => ({
|
|
9618
10101
|
id: c.id,
|
|
9619
10102
|
kind: "clip",
|
|
9620
|
-
t:
|
|
9621
|
-
duration:
|
|
10103
|
+
t: round12(c.start),
|
|
10104
|
+
duration: round12(clipLength(c)),
|
|
9622
10105
|
label: c.name
|
|
9623
10106
|
}));
|
|
9624
10107
|
},
|
|
@@ -9629,7 +10112,7 @@ var audioLane = {
|
|
|
9629
10112
|
const start = Math.max(0, g.t);
|
|
9630
10113
|
return (d) => {
|
|
9631
10114
|
const c = d.audio.find((x) => x.id === g.id);
|
|
9632
|
-
if (c) c.start =
|
|
10115
|
+
if (c) c.start = round12(start);
|
|
9633
10116
|
};
|
|
9634
10117
|
}
|
|
9635
10118
|
if (g.type === "resize") {
|
|
@@ -9642,8 +10125,8 @@ var audioLane = {
|
|
|
9642
10125
|
return (d) => {
|
|
9643
10126
|
const c = d.audio.find((x) => x.id === g.id);
|
|
9644
10127
|
if (!c) return;
|
|
9645
|
-
c.start =
|
|
9646
|
-
c.loopLen =
|
|
10128
|
+
c.start = round12(newStart2);
|
|
10129
|
+
c.loopLen = round12(end - newStart2);
|
|
9647
10130
|
};
|
|
9648
10131
|
}
|
|
9649
10132
|
const delta = g.t - clip.start;
|
|
@@ -9652,22 +10135,22 @@ var audioLane = {
|
|
|
9652
10135
|
return (d) => {
|
|
9653
10136
|
const c = d.audio.find((x) => x.id === g.id);
|
|
9654
10137
|
if (!c) return;
|
|
9655
|
-
c.in =
|
|
9656
|
-
c.start =
|
|
10138
|
+
c.in = round12(newIn);
|
|
10139
|
+
c.start = round12(newStart);
|
|
9657
10140
|
};
|
|
9658
10141
|
}
|
|
9659
10142
|
if (clip.loop) {
|
|
9660
10143
|
const newLen = Math.max(0.1, g.t - clip.start);
|
|
9661
10144
|
return (d) => {
|
|
9662
10145
|
const c = d.audio.find((x) => x.id === g.id);
|
|
9663
|
-
if (c) c.loopLen =
|
|
10146
|
+
if (c) c.loopLen = round12(newLen);
|
|
9664
10147
|
};
|
|
9665
10148
|
}
|
|
9666
10149
|
const span = Math.max(0.05, g.t - clip.start);
|
|
9667
10150
|
const newOut = Math.min(clip.in + span, clip.duration);
|
|
9668
10151
|
return (d) => {
|
|
9669
10152
|
const c = d.audio.find((x) => x.id === g.id);
|
|
9670
|
-
if (c) c.out =
|
|
10153
|
+
if (c) c.out = round12(Math.max(c.in + 0.05, newOut));
|
|
9671
10154
|
};
|
|
9672
10155
|
}
|
|
9673
10156
|
if (g.type === "remove") {
|
|
@@ -9680,8 +10163,8 @@ var audioLane = {
|
|
|
9680
10163
|
},
|
|
9681
10164
|
magnets(doc) {
|
|
9682
10165
|
return doc.audio.flatMap((c) => [
|
|
9683
|
-
|
|
9684
|
-
|
|
10166
|
+
round12(c.start),
|
|
10167
|
+
round12(c.start + clipLength(c))
|
|
9685
10168
|
]);
|
|
9686
10169
|
}
|
|
9687
10170
|
};
|
|
@@ -9699,7 +10182,7 @@ function poseItems(clipId, start, duration, motion) {
|
|
|
9699
10182
|
return (motion ?? []).map((p, i) => ({
|
|
9700
10183
|
id: `${clipId}::k${i}`,
|
|
9701
10184
|
kind: "keyframe",
|
|
9702
|
-
t:
|
|
10185
|
+
t: round12(start + Math.min(Math.max(0, p.at), duration))
|
|
9703
10186
|
}));
|
|
9704
10187
|
}
|
|
9705
10188
|
var overlaysLane = {
|
|
@@ -9710,8 +10193,8 @@ var overlaysLane = {
|
|
|
9710
10193
|
{
|
|
9711
10194
|
id: o.id,
|
|
9712
10195
|
kind: "clip",
|
|
9713
|
-
t:
|
|
9714
|
-
duration:
|
|
10196
|
+
t: round12(o.start),
|
|
10197
|
+
duration: round12(o.duration),
|
|
9715
10198
|
label: o.kind === "text" ? o.text.split("\n")[0].slice(0, 24) || "Text" : o.kind === "html" ? htmlLayerLabel(o) : o.kind === "image" ? "Image" : "Video"
|
|
9716
10199
|
},
|
|
9717
10200
|
// Pose diamonds render after the clips, so they sit on top.
|
|
@@ -9731,7 +10214,7 @@ var overlaysLane = {
|
|
|
9731
10214
|
return (d) => {
|
|
9732
10215
|
const o = (d.overlays ?? []).find((x) => x.id === kf.clipId);
|
|
9733
10216
|
const p = o?.motion?.[kf.index];
|
|
9734
|
-
if (p) p.at =
|
|
10217
|
+
if (p) p.at = round12(at);
|
|
9735
10218
|
};
|
|
9736
10219
|
}
|
|
9737
10220
|
if (g.type === "remove") {
|
|
@@ -9760,8 +10243,8 @@ var overlaysLane = {
|
|
|
9760
10243
|
{
|
|
9761
10244
|
id,
|
|
9762
10245
|
kind: "text",
|
|
9763
|
-
start:
|
|
9764
|
-
duration:
|
|
10246
|
+
start: round12(start),
|
|
10247
|
+
duration: round12(len),
|
|
9765
10248
|
text: "Title",
|
|
9766
10249
|
preset: "title",
|
|
9767
10250
|
// Frame fractions: centered, lower-third — at any aspect ratio.
|
|
@@ -9776,7 +10259,7 @@ var overlaysLane = {
|
|
|
9776
10259
|
const start = Math.max(0, g.t);
|
|
9777
10260
|
return (d) => {
|
|
9778
10261
|
const o = (d.overlays ?? []).find((x) => x.id === g.id);
|
|
9779
|
-
if (o) o.start =
|
|
10262
|
+
if (o) o.start = round12(start);
|
|
9780
10263
|
};
|
|
9781
10264
|
}
|
|
9782
10265
|
if (g.type === "resize") {
|
|
@@ -9786,14 +10269,14 @@ var overlaysLane = {
|
|
|
9786
10269
|
return (d) => {
|
|
9787
10270
|
const o = (d.overlays ?? []).find((x) => x.id === g.id);
|
|
9788
10271
|
if (!o) return;
|
|
9789
|
-
o.start =
|
|
9790
|
-
o.duration =
|
|
10272
|
+
o.start = round12(newStart);
|
|
10273
|
+
o.duration = round12(end - newStart);
|
|
9791
10274
|
};
|
|
9792
10275
|
}
|
|
9793
10276
|
const newLen = Math.max(OVERLAY_MIN_DURATION, g.t - clip.start);
|
|
9794
10277
|
return (d) => {
|
|
9795
10278
|
const o = (d.overlays ?? []).find((x) => x.id === g.id);
|
|
9796
|
-
if (o) o.duration =
|
|
10279
|
+
if (o) o.duration = round12(newLen);
|
|
9797
10280
|
};
|
|
9798
10281
|
}
|
|
9799
10282
|
return (d) => {
|
|
@@ -9802,8 +10285,8 @@ var overlaysLane = {
|
|
|
9802
10285
|
},
|
|
9803
10286
|
magnets(doc) {
|
|
9804
10287
|
return (doc.overlays ?? []).flatMap((o) => [
|
|
9805
|
-
|
|
9806
|
-
|
|
10288
|
+
round12(o.start),
|
|
10289
|
+
round12(o.start + o.duration)
|
|
9807
10290
|
]);
|
|
9808
10291
|
}
|
|
9809
10292
|
};
|
|
@@ -9816,8 +10299,8 @@ var objectsLane = {
|
|
|
9816
10299
|
{
|
|
9817
10300
|
id: o.id,
|
|
9818
10301
|
kind: "clip",
|
|
9819
|
-
t:
|
|
9820
|
-
duration:
|
|
10302
|
+
t: round12(o.span?.start ?? 0),
|
|
10303
|
+
duration: round12(o.span?.duration ?? outDur),
|
|
9821
10304
|
label: o.asset.kind === "primitive" ? o.asset.shape : o.asset.kind === "text3d" ? o.asset.text : "model"
|
|
9822
10305
|
},
|
|
9823
10306
|
// Pose diamonds: clip-local `at` from the span start (0 span-less).
|
|
@@ -9845,7 +10328,7 @@ var objectsLane = {
|
|
|
9845
10328
|
return (d) => {
|
|
9846
10329
|
const o = (d.objects ?? []).find((x) => x.id === kf.clipId);
|
|
9847
10330
|
const p = o?.motion?.[kf.index];
|
|
9848
|
-
if (p) p.at =
|
|
10331
|
+
if (p) p.at = round12(at);
|
|
9849
10332
|
};
|
|
9850
10333
|
}
|
|
9851
10334
|
if (g.type === "remove") {
|
|
@@ -9866,7 +10349,7 @@ var objectsLane = {
|
|
|
9866
10349
|
const start = Math.max(0, g.t);
|
|
9867
10350
|
return (d) => {
|
|
9868
10351
|
const o = (d.objects ?? []).find((x) => x.id === g.id);
|
|
9869
|
-
if (o?.span) o.span.start =
|
|
10352
|
+
if (o?.span) o.span.start = round12(start);
|
|
9870
10353
|
};
|
|
9871
10354
|
}
|
|
9872
10355
|
if (g.type === "resize") {
|
|
@@ -9877,14 +10360,14 @@ var objectsLane = {
|
|
|
9877
10360
|
return (d) => {
|
|
9878
10361
|
const o = (d.objects ?? []).find((x) => x.id === g.id);
|
|
9879
10362
|
if (!o?.span) return;
|
|
9880
|
-
o.span.start =
|
|
9881
|
-
o.span.duration =
|
|
10363
|
+
o.span.start = round12(newStart);
|
|
10364
|
+
o.span.duration = round12(end - newStart);
|
|
9882
10365
|
};
|
|
9883
10366
|
}
|
|
9884
10367
|
const newLen = Math.max(OVERLAY_MIN_DURATION, g.t - clip.span.start);
|
|
9885
10368
|
return (d) => {
|
|
9886
10369
|
const o = (d.objects ?? []).find((x) => x.id === g.id);
|
|
9887
|
-
if (o?.span) o.span.duration =
|
|
10370
|
+
if (o?.span) o.span.duration = round12(newLen);
|
|
9888
10371
|
};
|
|
9889
10372
|
}
|
|
9890
10373
|
return (d) => {
|
|
@@ -9893,7 +10376,7 @@ var objectsLane = {
|
|
|
9893
10376
|
},
|
|
9894
10377
|
magnets(doc) {
|
|
9895
10378
|
return (doc.objects ?? []).flatMap(
|
|
9896
|
-
(o) => o.span ? [
|
|
10379
|
+
(o) => o.span ? [round12(o.span.start), round12(o.span.start + o.span.duration)] : []
|
|
9897
10380
|
);
|
|
9898
10381
|
}
|
|
9899
10382
|
};
|
|
@@ -9920,7 +10403,7 @@ function nextSpeedId(doc) {
|
|
|
9920
10403
|
function segIndex(id) {
|
|
9921
10404
|
return Number(id.replace("seg-", ""));
|
|
9922
10405
|
}
|
|
9923
|
-
function
|
|
10406
|
+
function round12(v) {
|
|
9924
10407
|
return Math.round(v * 1e3) / 1e3;
|
|
9925
10408
|
}
|
|
9926
10409
|
|
|
@@ -10161,6 +10644,8 @@ export {
|
|
|
10161
10644
|
OVERLAY_SIZE_MIN,
|
|
10162
10645
|
OVERLAY_TRANSITION_DUR,
|
|
10163
10646
|
OVERLAY_Z,
|
|
10647
|
+
PIN_GAP,
|
|
10648
|
+
PIN_MARGIN,
|
|
10164
10649
|
PLATE_GROUND,
|
|
10165
10650
|
PLAYBACK_ACTIVITY,
|
|
10166
10651
|
PRIMARY_MEDIA,
|
|
@@ -10269,6 +10754,7 @@ export {
|
|
|
10269
10754
|
docToCropSpace,
|
|
10270
10755
|
docToFullSpace,
|
|
10271
10756
|
docTransitions,
|
|
10757
|
+
docZoomTrack,
|
|
10272
10758
|
dragsFromTrack,
|
|
10273
10759
|
dropTemplate,
|
|
10274
10760
|
duckCurve,
|
|
@@ -10286,6 +10772,7 @@ export {
|
|
|
10286
10772
|
expectedFrameSize,
|
|
10287
10773
|
exportSizeFor,
|
|
10288
10774
|
extractClicks,
|
|
10775
|
+
findStep,
|
|
10289
10776
|
focusBounds,
|
|
10290
10777
|
focusForViewportCentre,
|
|
10291
10778
|
followFocusEvents,
|
|
@@ -10357,12 +10844,17 @@ export {
|
|
|
10357
10844
|
overlayLines,
|
|
10358
10845
|
overlayMotionBase,
|
|
10359
10846
|
overlayMotionPoseAt,
|
|
10847
|
+
overlayPinPoseAt,
|
|
10360
10848
|
overlayRect,
|
|
10361
10849
|
overlaysLane,
|
|
10362
10850
|
ownFreezes,
|
|
10363
10851
|
pageDisplayUrl,
|
|
10364
10852
|
parsePoseId,
|
|
10365
10853
|
pickStyle,
|
|
10854
|
+
pinBox,
|
|
10855
|
+
pinPlacement,
|
|
10856
|
+
pinRectOnScreen,
|
|
10857
|
+
pinReferent,
|
|
10366
10858
|
placeFreezes,
|
|
10367
10859
|
planAutoSpeed,
|
|
10368
10860
|
planAutoTilt,
|
|
@@ -10381,6 +10873,7 @@ export {
|
|
|
10381
10873
|
resolveExportSize,
|
|
10382
10874
|
resolveOverlayBox,
|
|
10383
10875
|
resolveOverlayStyle,
|
|
10876
|
+
resolvePins,
|
|
10384
10877
|
resolveText3dAsset,
|
|
10385
10878
|
resolveZoomStyle,
|
|
10386
10879
|
restLeanOf,
|