@vosjs/studio-core 0.11.1 → 0.12.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 +106 -25
- package/dist/index.js +366 -57
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,6 +8,13 @@ var TRANSITION_SPEED_MULT = {
|
|
|
8
8
|
function transitionMult(t) {
|
|
9
9
|
return t !== void 0 && t in TRANSITION_SPEED_MULT ? TRANSITION_SPEED_MULT[t] : 1;
|
|
10
10
|
}
|
|
11
|
+
var FREEZE_SECONDS_MIN = 0.25;
|
|
12
|
+
var FREEZE_SECONDS_MAX = 30;
|
|
13
|
+
var FREEZE_DEFAULT_SECONDS = 2;
|
|
14
|
+
function clampFreezeSeconds(seconds) {
|
|
15
|
+
const s = Math.min(FREEZE_SECONDS_MAX, Math.max(FREEZE_SECONDS_MIN, seconds));
|
|
16
|
+
return Math.round(s * 1e3) / 1e3;
|
|
17
|
+
}
|
|
11
18
|
var SPEED_RATE_MIN = 0.1;
|
|
12
19
|
var SPEED_RATE_MAX = 16;
|
|
13
20
|
var SPEED_SPAN_MIN = 0.25;
|
|
@@ -506,17 +513,43 @@ function lastLayerEnd(doc) {
|
|
|
506
513
|
import { splitBySpeed, totalDuration } from "@vosjs/timeline";
|
|
507
514
|
function docRestTime(doc) {
|
|
508
515
|
if (doc.endCard) return null;
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
516
|
+
const freezes = docFreezes(doc);
|
|
517
|
+
if (!freezes.length) return null;
|
|
518
|
+
const segs = docSegmentsOf(doc);
|
|
519
|
+
const marks = freezeOutputExtents(
|
|
520
|
+
splitBySpeed(segs, doc.speed ?? []),
|
|
521
|
+
freezes
|
|
515
522
|
);
|
|
516
|
-
|
|
517
|
-
if (
|
|
523
|
+
let rest = null;
|
|
524
|
+
for (const m of marks.values()) if (rest === null || m.t > rest) rest = m.t;
|
|
525
|
+
if (rest === null || !(rest >= 0)) return null;
|
|
518
526
|
return Math.round(rest * 1e3) / 1e3;
|
|
519
527
|
}
|
|
528
|
+
function docSegmentsOf(doc) {
|
|
529
|
+
const segs = Array.isArray(doc.segments) ? doc.segments : [];
|
|
530
|
+
if (segs.length) return segs;
|
|
531
|
+
const ms = doc.source?.meta?.durationMs;
|
|
532
|
+
return [{ in: 0, out: typeof ms === "number" ? ms / 1e3 : 0 }];
|
|
533
|
+
}
|
|
534
|
+
function docFreezes(doc) {
|
|
535
|
+
const own = Array.isArray(doc.freeze) ? doc.freeze : [];
|
|
536
|
+
const legacy = legacyHoldFreezes(
|
|
537
|
+
Array.isArray(doc.segments) ? doc.segments : []
|
|
538
|
+
);
|
|
539
|
+
const all = [...own, ...legacy].filter(
|
|
540
|
+
(f) => typeof f.at === "number" && Number.isFinite(f.at) && typeof f.seconds === "number" && f.seconds > 0
|
|
541
|
+
);
|
|
542
|
+
return all.sort((a, b) => a.at - b.at);
|
|
543
|
+
}
|
|
544
|
+
function legacyHoldFreezes(segments) {
|
|
545
|
+
const out = [];
|
|
546
|
+
segments.forEach((s, i) => {
|
|
547
|
+
const hold = s.hold;
|
|
548
|
+
if (typeof hold === "number" && hold > 0)
|
|
549
|
+
out.push({ id: `h${i}`, at: s.out, seconds: hold });
|
|
550
|
+
});
|
|
551
|
+
return out;
|
|
552
|
+
}
|
|
520
553
|
var ENTRANCE_SECONDS = 1.2;
|
|
521
554
|
var END_CARD_SECONDS = 2.5;
|
|
522
555
|
var END_CARD_RECEDE = 0.7;
|
|
@@ -540,28 +573,67 @@ function exitSeconds(e) {
|
|
|
540
573
|
const house = e.kind === "fade" ? CARD_FADE_SECONDS : END_CARD_RECEDE;
|
|
541
574
|
return Math.max(0.1, Math.min(3, e.seconds ?? house));
|
|
542
575
|
}
|
|
543
|
-
function
|
|
544
|
-
const
|
|
545
|
-
const pending =
|
|
576
|
+
function placeFreezes(rated, freezes) {
|
|
577
|
+
const marks = /* @__PURE__ */ new Map();
|
|
578
|
+
const pending = [...freezes].filter((f) => f.seconds > 0).sort((a, b) => a.at - b.at);
|
|
579
|
+
const pieces = [];
|
|
580
|
+
let acc = 0;
|
|
581
|
+
const freezePiece = (f) => {
|
|
582
|
+
const at = Math.max(HOLD_SOURCE_SPAN, f.at);
|
|
583
|
+
marks.set(f.id, { t: acc, duration: f.seconds });
|
|
584
|
+
pieces.push({
|
|
585
|
+
in: at - HOLD_SOURCE_SPAN,
|
|
586
|
+
out: at,
|
|
587
|
+
rate: HOLD_SOURCE_SPAN / f.seconds
|
|
588
|
+
});
|
|
589
|
+
acc += f.seconds;
|
|
590
|
+
};
|
|
591
|
+
const push2 = (p) => {
|
|
592
|
+
pieces.push(p);
|
|
593
|
+
acc += Math.max(0, p.out - p.in) / (p.rate ?? 1);
|
|
594
|
+
};
|
|
546
595
|
for (let i = 0; i < rated.length; i++) {
|
|
547
|
-
|
|
548
|
-
out.push(piece);
|
|
596
|
+
let piece = rated[i];
|
|
549
597
|
const next = rated[i + 1];
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
598
|
+
while (pending.length) {
|
|
599
|
+
const f = pending[0];
|
|
600
|
+
if (f.at > piece.out + 1e-9) break;
|
|
601
|
+
if (f.at < piece.in - 1e-9) {
|
|
602
|
+
pending.shift();
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
if (f.at <= piece.in + 1e-9) {
|
|
606
|
+
pending.shift();
|
|
607
|
+
freezePiece(f);
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (f.at < piece.out - 1e-9) {
|
|
611
|
+
push2({ ...piece, out: f.at });
|
|
612
|
+
pending.shift();
|
|
613
|
+
freezePiece(f);
|
|
614
|
+
piece = { ...piece, in: f.at };
|
|
615
|
+
continue;
|
|
561
616
|
}
|
|
617
|
+
const continues = next && Math.abs(next.in - piece.out) < 1e-9 && next.out > piece.out;
|
|
618
|
+
if (continues) break;
|
|
619
|
+
push2(piece);
|
|
620
|
+
piece = null;
|
|
621
|
+
pending.shift();
|
|
622
|
+
freezePiece(f);
|
|
623
|
+
break;
|
|
562
624
|
}
|
|
625
|
+
if (piece) push2(piece);
|
|
563
626
|
}
|
|
564
|
-
return
|
|
627
|
+
return { pieces, marks };
|
|
628
|
+
}
|
|
629
|
+
function withFreezes(rated, freezes) {
|
|
630
|
+
return placeFreezes(rated, freezes).pieces;
|
|
631
|
+
}
|
|
632
|
+
function freezeOutputExtents(rated, freezes) {
|
|
633
|
+
return placeFreezes(rated, freezes).marks;
|
|
634
|
+
}
|
|
635
|
+
function withHolds(docSegments, rated) {
|
|
636
|
+
return withFreezes(rated, legacyHoldFreezes(docSegments));
|
|
565
637
|
}
|
|
566
638
|
function entranceTiltKeyframes(e) {
|
|
567
639
|
const s = entranceSeconds(e);
|
|
@@ -704,9 +776,29 @@ function migrateMotion(doc) {
|
|
|
704
776
|
const segments = Array.isArray(doc.segments) ? doc.segments : [];
|
|
705
777
|
const overlays = Array.isArray(doc.overlays) ? doc.overlays : [];
|
|
706
778
|
const objects = Array.isArray(doc.objects) ? doc.objects : [];
|
|
707
|
-
const
|
|
779
|
+
const heldSegments = segments.some((s) => s.hold !== void 0);
|
|
780
|
+
const legacy = frame.entrance !== void 0 || doc.endCard !== void 0 || heldSegments || overlays.some(hasLegacyClipMotion) || objects.some((o) => o.animation !== void 0);
|
|
708
781
|
if (!legacy) return doc;
|
|
709
782
|
const out = { ...doc };
|
|
783
|
+
if (heldSegments) {
|
|
784
|
+
const holds = legacyHoldFreezes(segments);
|
|
785
|
+
const own = Array.isArray(doc.freeze) ? doc.freeze : [];
|
|
786
|
+
const taken = new Set(own.map((f) => f.id));
|
|
787
|
+
let n = own.length;
|
|
788
|
+
const migrated = holds.map((h) => {
|
|
789
|
+
let id = `f${n++}`;
|
|
790
|
+
while (taken.has(id)) id = `f${n++}`;
|
|
791
|
+
taken.add(id);
|
|
792
|
+
return { id, at: h.at, seconds: h.seconds };
|
|
793
|
+
});
|
|
794
|
+
out.freeze = [...own, ...migrated].sort((a, b) => a.at - b.at);
|
|
795
|
+
out.segments = segments.map((s) => {
|
|
796
|
+
if (s.hold === void 0) return s;
|
|
797
|
+
const next = { ...s };
|
|
798
|
+
delete next.hold;
|
|
799
|
+
return next;
|
|
800
|
+
});
|
|
801
|
+
}
|
|
710
802
|
const nextFrame = { ...frame };
|
|
711
803
|
if (frame.entrance !== void 0) {
|
|
712
804
|
const e = frame.entrance;
|
|
@@ -730,7 +822,10 @@ function migrateMotion(doc) {
|
|
|
730
822
|
const card = doc.endCard;
|
|
731
823
|
if (card && segments.length) {
|
|
732
824
|
const seconds = Math.max(1, Math.min(8, card.seconds ?? END_CARD_SECONDS));
|
|
733
|
-
const rated =
|
|
825
|
+
const rated = withFreezes(
|
|
826
|
+
splitBySpeed(segments, doc.speed ?? []),
|
|
827
|
+
docFreezes(doc)
|
|
828
|
+
);
|
|
734
829
|
const endStart = totalDuration(rated);
|
|
735
830
|
nextOverlays = [...nextOverlays, ...endCardClips(card, doc, endStart)];
|
|
736
831
|
nextFrame.anim = {
|
|
@@ -767,12 +862,12 @@ function outputEnd(doc, footageEnd) {
|
|
|
767
862
|
}
|
|
768
863
|
|
|
769
864
|
// src/docVersion.ts
|
|
770
|
-
var DOC_SCHEMA_VERSION =
|
|
865
|
+
var DOC_SCHEMA_VERSION = 4;
|
|
771
866
|
function migrateHostedDoc(raw) {
|
|
772
867
|
const version = typeof raw.docSchemaVersion === "number" ? raw.docSchemaVersion : 0;
|
|
773
868
|
if (version >= DOC_SCHEMA_VERSION) return raw;
|
|
774
869
|
let doc = raw;
|
|
775
|
-
if (version <
|
|
870
|
+
if (version < 4 && doc.source && typeof doc.source === "object") {
|
|
776
871
|
doc = migrateMotion(doc);
|
|
777
872
|
}
|
|
778
873
|
return { ...doc, docSchemaVersion: DOC_SCHEMA_VERSION };
|
|
@@ -3222,7 +3317,8 @@ var CAM_PAN_EASE = ZOOM_PAN_EASE;
|
|
|
3222
3317
|
var PROGRAM_DURATION = 1;
|
|
3223
3318
|
function ratedSegments(doc) {
|
|
3224
3319
|
const segs = isRecordingDoc(doc) ? doc.segments.length ? doc.segments : [{ in: 0, out: doc.source.meta.durationMs / 1e3 }] : [{ in: 0, out: programDuration(doc) }];
|
|
3225
|
-
|
|
3320
|
+
const freezes = isRecordingDoc(doc) ? docFreezes(doc) : [];
|
|
3321
|
+
return withFreezes(splitBySpeed2(segs, doc.speed ?? []), freezes);
|
|
3226
3322
|
}
|
|
3227
3323
|
function durationSec(doc, rated) {
|
|
3228
3324
|
const trimmed = totalDuration2(rated);
|
|
@@ -5638,7 +5734,7 @@ function layoutOf(doc) {
|
|
|
5638
5734
|
frame: LAYOUT_FRAME_FIELDS.filter((k) => frame[k] !== void 0),
|
|
5639
5735
|
clips: (doc.overlays ?? []).filter(isStageClip).map((o) => o.id),
|
|
5640
5736
|
lean: restLeanOf(doc) !== null,
|
|
5641
|
-
|
|
5737
|
+
freeze: trailingFreeze(doc) !== null
|
|
5642
5738
|
};
|
|
5643
5739
|
}
|
|
5644
5740
|
function copyLayout(from, to, opts = {}) {
|
|
@@ -5700,11 +5796,33 @@ function copyLayout(from, to, opts = {}) {
|
|
|
5700
5796
|
notes.push("lean kept: this take has tilt spans of its own");
|
|
5701
5797
|
}
|
|
5702
5798
|
}
|
|
5703
|
-
const
|
|
5799
|
+
const freeze = trailingFreeze(from);
|
|
5704
5800
|
const last = doc.segments.at(-1);
|
|
5705
|
-
if (
|
|
5801
|
+
if (freeze && last) {
|
|
5802
|
+
const own2 = (doc.freeze ?? []).filter(
|
|
5803
|
+
(f) => Math.abs(f.at - last.out) > 1e-9
|
|
5804
|
+
);
|
|
5805
|
+
const taken = new Set(own2.map((f) => f.id));
|
|
5806
|
+
let n = 0;
|
|
5807
|
+
while (taken.has(`f${n}`)) n++;
|
|
5808
|
+
doc.freeze = [
|
|
5809
|
+
...own2,
|
|
5810
|
+
{ id: `f${n}`, at: last.out, seconds: freeze.seconds }
|
|
5811
|
+
].sort((a, b) => a.at - b.at);
|
|
5812
|
+
doc.segments = doc.segments.map((s) => {
|
|
5813
|
+
if (s.hold === void 0) return s;
|
|
5814
|
+
const next = { ...s };
|
|
5815
|
+
delete next.hold;
|
|
5816
|
+
return next;
|
|
5817
|
+
});
|
|
5818
|
+
}
|
|
5706
5819
|
return { doc, notes };
|
|
5707
5820
|
}
|
|
5821
|
+
function trailingFreeze(doc) {
|
|
5822
|
+
const last = doc.segments.at(-1);
|
|
5823
|
+
if (!last) return null;
|
|
5824
|
+
return docFreezes(doc).find((f) => Math.abs(f.at - last.out) < 1e-9) ?? null;
|
|
5825
|
+
}
|
|
5708
5826
|
var shiftBy = (at, templateEnd, takeEnd) => at === "start" ? 0 : at === "end" ? takeEnd - templateEnd : at;
|
|
5709
5827
|
function clipsFrom(doc, from) {
|
|
5710
5828
|
return {
|
|
@@ -6676,33 +6794,44 @@ function effectiveSegments(doc) {
|
|
|
6676
6794
|
if (isRecordingDoc(doc) && doc.segments.length) return doc.segments;
|
|
6677
6795
|
return [{ in: 0, out: anchorSourceDuration(doc) }];
|
|
6678
6796
|
}
|
|
6679
|
-
var
|
|
6680
|
-
|
|
6797
|
+
var segmentPieces = (seg, speeds, freezes, leading) => {
|
|
6798
|
+
const own = freezes.filter(
|
|
6799
|
+
(f) => f.at <= seg.out + 1e-9 && (leading ? f.at >= seg.in - 1e-9 : f.at > seg.in + 1e-9)
|
|
6800
|
+
);
|
|
6801
|
+
return withFreezes(splitBySpeed3([seg], speeds), own);
|
|
6802
|
+
};
|
|
6803
|
+
var outputLen = (seg, speeds, freezes = [], leading = true) => totalDuration6(segmentPieces(seg, speeds, freezes, leading));
|
|
6804
|
+
var segmentStarts = (segments, speeds, freezes = []) => {
|
|
6681
6805
|
const starts = [];
|
|
6682
6806
|
let acc = 0;
|
|
6683
|
-
|
|
6807
|
+
segments.forEach((s, i) => {
|
|
6684
6808
|
starts.push(acc);
|
|
6685
|
-
acc += outputLen(s, speeds);
|
|
6686
|
-
}
|
|
6809
|
+
acc += outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i));
|
|
6810
|
+
});
|
|
6687
6811
|
return starts;
|
|
6688
6812
|
};
|
|
6813
|
+
var contiguous = (segments, i) => i > 0 && Math.abs(segments[i - 1].out - segments[i].in) < 1e-9;
|
|
6689
6814
|
var videoLane = {
|
|
6690
6815
|
id: "video",
|
|
6691
6816
|
label: "Video",
|
|
6692
6817
|
items(doc) {
|
|
6693
6818
|
const segments = effectiveSegments(doc);
|
|
6694
6819
|
const speeds = doc.speed ?? [];
|
|
6695
|
-
const
|
|
6820
|
+
const freezes = docFreezes(doc);
|
|
6821
|
+
const starts = segmentStarts(segments, speeds, freezes);
|
|
6696
6822
|
return segments.map((s, i) => ({
|
|
6697
6823
|
id: `seg-${i}`,
|
|
6698
6824
|
kind: "clip",
|
|
6699
|
-
t: starts[i],
|
|
6700
|
-
duration:
|
|
6825
|
+
t: round10(starts[i]),
|
|
6826
|
+
duration: round10(
|
|
6827
|
+
outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i))
|
|
6828
|
+
)
|
|
6701
6829
|
}));
|
|
6702
6830
|
},
|
|
6703
6831
|
gesture(doc, g) {
|
|
6704
6832
|
const segments = effectiveSegments(doc);
|
|
6705
6833
|
const speeds = doc.speed ?? [];
|
|
6834
|
+
const freezes = docFreezes(doc);
|
|
6706
6835
|
const sourceDuration = anchorSourceDuration(doc);
|
|
6707
6836
|
switch (g.type) {
|
|
6708
6837
|
case "move": {
|
|
@@ -6714,7 +6843,7 @@ var videoLane = {
|
|
|
6714
6843
|
let acc = 0;
|
|
6715
6844
|
let insert = others.length;
|
|
6716
6845
|
for (let i = 0; i < others.length; i++) {
|
|
6717
|
-
const dur = outputLen(others[i], speeds);
|
|
6846
|
+
const dur = outputLen(others[i], speeds, freezes);
|
|
6718
6847
|
if (g.t < acc + dur / 2) {
|
|
6719
6848
|
insert = i;
|
|
6720
6849
|
break;
|
|
@@ -6732,10 +6861,15 @@ var videoLane = {
|
|
|
6732
6861
|
const index = segIndex(g.id);
|
|
6733
6862
|
if (index < 0 || index >= segments.length) return null;
|
|
6734
6863
|
const seg = segments[index];
|
|
6735
|
-
const starts = segmentStarts(segments, speeds);
|
|
6864
|
+
const starts = segmentStarts(segments, speeds, freezes);
|
|
6736
6865
|
const fullMap = splitBySpeed3([{ in: 0, out: sourceDuration }], speeds);
|
|
6737
6866
|
const edgeSrc = g.edge === "start" ? seg.in : seg.out;
|
|
6738
|
-
const edgeOutNow = g.edge === "start" ? starts[index] : starts[index] + outputLen(
|
|
6867
|
+
const edgeOutNow = g.edge === "start" ? starts[index] : starts[index] + outputLen(
|
|
6868
|
+
seg,
|
|
6869
|
+
speeds,
|
|
6870
|
+
freezes.filter((f) => f.at < seg.out - 1e-9),
|
|
6871
|
+
index === 0 || !contiguous(segments, index)
|
|
6872
|
+
);
|
|
6739
6873
|
const anchorOut = sourceToTimeline3(fullMap, Math.min(edgeSrc, sourceDuration)) ?? edgeSrc;
|
|
6740
6874
|
const sourceT = mapTime2(fullMap, anchorOut + (g.t - edgeOutNow));
|
|
6741
6875
|
const next = trimSegment(
|
|
@@ -6750,13 +6884,31 @@ var videoLane = {
|
|
|
6750
6884
|
};
|
|
6751
6885
|
}
|
|
6752
6886
|
case "create": {
|
|
6753
|
-
const starts = segmentStarts(segments, speeds);
|
|
6887
|
+
const starts = segmentStarts(segments, speeds, freezes);
|
|
6754
6888
|
const index = segments.findIndex(
|
|
6755
|
-
(s2, i) => g.t >= starts[i] && g.t < starts[i] + outputLen(
|
|
6889
|
+
(s2, i) => g.t >= starts[i] && g.t < starts[i] + outputLen(
|
|
6890
|
+
s2,
|
|
6891
|
+
speeds,
|
|
6892
|
+
freezes,
|
|
6893
|
+
i === 0 || !contiguous(segments, i)
|
|
6894
|
+
)
|
|
6756
6895
|
);
|
|
6757
6896
|
if (index < 0) return null;
|
|
6758
6897
|
const s = segments[index];
|
|
6759
|
-
const
|
|
6898
|
+
const leading = index === 0 || !contiguous(segments, index);
|
|
6899
|
+
const local = g.t - starts[index];
|
|
6900
|
+
for (const m of freezeOutputExtents(
|
|
6901
|
+
splitBySpeed3([s], speeds),
|
|
6902
|
+
freezes.filter(
|
|
6903
|
+
(f) => f.at <= s.out + 1e-9 && (leading ? f.at >= s.in - 1e-9 : f.at > s.in + 1e-9)
|
|
6904
|
+
)
|
|
6905
|
+
).values()) {
|
|
6906
|
+
if (local >= m.t - 1e-9 && local <= m.t + m.duration + 1e-9)
|
|
6907
|
+
return null;
|
|
6908
|
+
}
|
|
6909
|
+
const sourceT = round10(
|
|
6910
|
+
mapTime2(segmentPieces(s, speeds, freezes, leading), local)
|
|
6911
|
+
);
|
|
6760
6912
|
if (sourceT - s.in < 0.05 || s.out - sourceT < 0.05) return null;
|
|
6761
6913
|
const next = [
|
|
6762
6914
|
...segments.slice(0, index),
|
|
@@ -6782,15 +6934,154 @@ var videoLane = {
|
|
|
6782
6934
|
magnets(doc) {
|
|
6783
6935
|
const segments = effectiveSegments(doc);
|
|
6784
6936
|
const speeds = doc.speed ?? [];
|
|
6785
|
-
const
|
|
6937
|
+
const freezes = docFreezes(doc);
|
|
6938
|
+
const starts = segmentStarts(segments, speeds, freezes);
|
|
6939
|
+
const n = segments.length;
|
|
6786
6940
|
return [
|
|
6787
6941
|
...starts,
|
|
6788
|
-
...
|
|
6789
|
-
starts[
|
|
6942
|
+
...n ? [
|
|
6943
|
+
starts[n - 1] + outputLen(
|
|
6944
|
+
segments[n - 1],
|
|
6945
|
+
speeds,
|
|
6946
|
+
freezes,
|
|
6947
|
+
n === 1 || !contiguous(segments, n - 1)
|
|
6948
|
+
)
|
|
6790
6949
|
] : []
|
|
6791
|
-
];
|
|
6950
|
+
].map(round10);
|
|
6951
|
+
}
|
|
6952
|
+
};
|
|
6953
|
+
function nextFreezeId(doc) {
|
|
6954
|
+
const taken = new Set((doc.freeze ?? []).map((f) => f.id));
|
|
6955
|
+
let n = 0;
|
|
6956
|
+
while (taken.has(`f${n}`)) n++;
|
|
6957
|
+
return `f${n}`;
|
|
6958
|
+
}
|
|
6959
|
+
var FREEZE_GAP = 0.05;
|
|
6960
|
+
var freezeLane = {
|
|
6961
|
+
id: "freeze",
|
|
6962
|
+
label: "Freeze",
|
|
6963
|
+
items(doc) {
|
|
6964
|
+
const freezes = docFreezes(doc);
|
|
6965
|
+
if (!freezes.length) return [];
|
|
6966
|
+
const segs = effectiveSegments(doc);
|
|
6967
|
+
const marks = freezeOutputExtents(
|
|
6968
|
+
splitBySpeed3(segs, doc.speed ?? []),
|
|
6969
|
+
freezes
|
|
6970
|
+
);
|
|
6971
|
+
return freezes.flatMap((f) => {
|
|
6972
|
+
const m = marks.get(f.id);
|
|
6973
|
+
return m ? [
|
|
6974
|
+
{
|
|
6975
|
+
id: f.id,
|
|
6976
|
+
kind: "clip",
|
|
6977
|
+
t: round10(m.t),
|
|
6978
|
+
duration: round10(m.duration),
|
|
6979
|
+
label: `${trimZeros(f.seconds)}s`
|
|
6980
|
+
}
|
|
6981
|
+
] : [];
|
|
6982
|
+
});
|
|
6983
|
+
},
|
|
6984
|
+
gesture(doc, g) {
|
|
6985
|
+
const freezes = docFreezes(doc);
|
|
6986
|
+
const segs = effectiveSegments(doc);
|
|
6987
|
+
const speeds = doc.speed ?? [];
|
|
6988
|
+
const sourceDuration = anchorSourceDuration(doc);
|
|
6989
|
+
const kept = (t) => segs.some((s) => t >= s.in - 1e-9 && t <= s.out + 1e-9);
|
|
6990
|
+
const clear = (t, except) => !freezes.some((f2) => f2.id !== except && Math.abs(f2.at - t) < FREEZE_GAP);
|
|
6991
|
+
if (g.type === "create") {
|
|
6992
|
+
const rated = ratedSegments(doc);
|
|
6993
|
+
for (const m of freezeOutputExtents(
|
|
6994
|
+
splitBySpeed3(segs, speeds),
|
|
6995
|
+
freezes
|
|
6996
|
+
).values()) {
|
|
6997
|
+
if (g.t >= m.t - 1e-9 && g.t <= m.t + m.duration + 1e-9) return null;
|
|
6998
|
+
}
|
|
6999
|
+
if (g.t > totalDuration6(rated) + 1e-9) return null;
|
|
7000
|
+
const srcT = round10(mapTime2(rated, Math.max(0, g.t)));
|
|
7001
|
+
if (!kept(srcT) || !clear(srcT)) return null;
|
|
7002
|
+
const id = nextFreezeId(doc);
|
|
7003
|
+
return (d) => {
|
|
7004
|
+
d.freeze = [
|
|
7005
|
+
...d.freeze ?? [],
|
|
7006
|
+
{ id, at: srcT, seconds: FREEZE_DEFAULT_SECONDS }
|
|
7007
|
+
].sort((a, b) => a.at - b.at);
|
|
7008
|
+
};
|
|
7009
|
+
}
|
|
7010
|
+
const f = freezes.find((x) => x.id === g.id);
|
|
7011
|
+
if (!f) return null;
|
|
7012
|
+
const own = (d) => {
|
|
7013
|
+
const list = d.freeze ?? [];
|
|
7014
|
+
let mine = list.find((x) => x.id === f.id);
|
|
7015
|
+
if (!mine) {
|
|
7016
|
+
const id = nextFreezeId(d);
|
|
7017
|
+
mine = { id, at: f.at, seconds: f.seconds };
|
|
7018
|
+
d.freeze = [...list, mine];
|
|
7019
|
+
d.segments = d.segments.map((sg) => {
|
|
7020
|
+
if (sg.hold === void 0 || Math.abs(sg.out - f.at) > 1e-9) return sg;
|
|
7021
|
+
const next = { ...sg };
|
|
7022
|
+
delete next.hold;
|
|
7023
|
+
return next;
|
|
7024
|
+
});
|
|
7025
|
+
}
|
|
7026
|
+
return mine;
|
|
7027
|
+
};
|
|
7028
|
+
const base = withFreezes(
|
|
7029
|
+
splitBySpeed3(segs, speeds),
|
|
7030
|
+
freezes.filter((x) => x.id !== f.id)
|
|
7031
|
+
);
|
|
7032
|
+
if (g.type === "move" || g.type === "resize" && g.edge === "start") {
|
|
7033
|
+
const marks = freezeOutputExtents(splitBySpeed3(segs, speeds), freezes);
|
|
7034
|
+
const mine = marks.get(f.id);
|
|
7035
|
+
const endOut = mine ? mine.t + mine.duration : null;
|
|
7036
|
+
const srcT = round10(
|
|
7037
|
+
Math.min(sourceDuration, mapTime2(base, Math.max(0, g.t)))
|
|
7038
|
+
);
|
|
7039
|
+
if (!kept(srcT) || !clear(srcT, f.id)) return null;
|
|
7040
|
+
return (d) => {
|
|
7041
|
+
const x = own(d);
|
|
7042
|
+
if (!x) return;
|
|
7043
|
+
x.at = srcT;
|
|
7044
|
+
if (g.type === "resize" && endOut !== null) {
|
|
7045
|
+
const after = freezeOutputExtents(
|
|
7046
|
+
splitBySpeed3(effectiveSegments(d), d.speed ?? []),
|
|
7047
|
+
docFreezes({
|
|
7048
|
+
...d,
|
|
7049
|
+
freeze: (d.freeze ?? []).map(
|
|
7050
|
+
(y) => y.id === x.id ? { ...y, seconds: FREEZE_SECONDS_MIN } : y
|
|
7051
|
+
)
|
|
7052
|
+
})
|
|
7053
|
+
).get(x.id);
|
|
7054
|
+
if (after) x.seconds = clampFreezeSeconds(endOut - after.t);
|
|
7055
|
+
}
|
|
7056
|
+
d.freeze.sort((a, b) => a.at - b.at);
|
|
7057
|
+
};
|
|
7058
|
+
}
|
|
7059
|
+
if (g.type === "resize") {
|
|
7060
|
+
const marks = freezeOutputExtents(splitBySpeed3(segs, speeds), freezes);
|
|
7061
|
+
const mine = marks.get(f.id);
|
|
7062
|
+
if (!mine) return null;
|
|
7063
|
+
const seconds = clampFreezeSeconds(g.t - mine.t);
|
|
7064
|
+
if (Math.abs(seconds - f.seconds) < 1e-6) return null;
|
|
7065
|
+
return (d) => {
|
|
7066
|
+
const x = own(d);
|
|
7067
|
+
if (x) x.seconds = seconds;
|
|
7068
|
+
};
|
|
7069
|
+
}
|
|
7070
|
+
if (g.type === "remove") {
|
|
7071
|
+
return (d) => {
|
|
7072
|
+
const x = own(d);
|
|
7073
|
+
d.freeze = (d.freeze ?? []).filter((y) => y !== x);
|
|
7074
|
+
};
|
|
7075
|
+
}
|
|
7076
|
+
return null;
|
|
7077
|
+
},
|
|
7078
|
+
magnets(doc) {
|
|
7079
|
+
return freezeLane.items(doc).flatMap((it) => [it.t, it.t + (it.duration ?? 0)]);
|
|
6792
7080
|
}
|
|
6793
7081
|
};
|
|
7082
|
+
function trimZeros(n) {
|
|
7083
|
+
return String(Math.round(n * 100) / 100);
|
|
7084
|
+
}
|
|
6794
7085
|
var zoomLane = {
|
|
6795
7086
|
id: "zoom",
|
|
6796
7087
|
label: "Zoom",
|
|
@@ -7145,7 +7436,8 @@ var camLane = {
|
|
|
7145
7436
|
if (!doc.source.camKey || !doc.cam.visible) return [];
|
|
7146
7437
|
const segments = effectiveSegments(doc);
|
|
7147
7438
|
const speeds = doc.speed ?? [];
|
|
7148
|
-
const
|
|
7439
|
+
const freezes = docFreezes(doc);
|
|
7440
|
+
const starts = segmentStarts(segments, speeds, freezes);
|
|
7149
7441
|
const first = segments[0];
|
|
7150
7442
|
const last = segments[segments.length - 1];
|
|
7151
7443
|
const win = doc.cam.window ?? { in: first.in, out: last.out };
|
|
@@ -7154,11 +7446,16 @@ var camLane = {
|
|
|
7154
7446
|
const a = Math.max(s.in, win.in);
|
|
7155
7447
|
const b = Math.min(s.out, win.out);
|
|
7156
7448
|
if (b - a <= 1e-6) return;
|
|
7449
|
+
const leading = i === 0 || !contiguous(segments, i);
|
|
7157
7450
|
items.push({
|
|
7158
7451
|
id: `cam-${i}`,
|
|
7159
7452
|
kind: "clip",
|
|
7160
|
-
t: round10(
|
|
7161
|
-
|
|
7453
|
+
t: round10(
|
|
7454
|
+
starts[i] + outputLen({ in: s.in, out: a }, speeds, freezes, leading)
|
|
7455
|
+
),
|
|
7456
|
+
duration: round10(
|
|
7457
|
+
outputLen({ in: a, out: b }, speeds, freezes, a === s.in && leading)
|
|
7458
|
+
)
|
|
7162
7459
|
});
|
|
7163
7460
|
});
|
|
7164
7461
|
return items;
|
|
@@ -7214,12 +7511,15 @@ var micLane = {
|
|
|
7214
7511
|
if (!voiceKey(doc)) return [];
|
|
7215
7512
|
const segments = effectiveSegments(doc);
|
|
7216
7513
|
const speeds = doc.speed ?? [];
|
|
7217
|
-
const
|
|
7514
|
+
const freezes = docFreezes(doc);
|
|
7515
|
+
const starts = segmentStarts(segments, speeds, freezes);
|
|
7218
7516
|
return segments.map((s, i) => ({
|
|
7219
7517
|
id: `mic-${i}`,
|
|
7220
7518
|
kind: "clip",
|
|
7221
|
-
t: starts[i],
|
|
7222
|
-
duration:
|
|
7519
|
+
t: round10(starts[i]),
|
|
7520
|
+
duration: round10(
|
|
7521
|
+
outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i))
|
|
7522
|
+
)
|
|
7223
7523
|
}));
|
|
7224
7524
|
},
|
|
7225
7525
|
gesture() {
|
|
@@ -7886,6 +8186,9 @@ export {
|
|
|
7886
8186
|
FRAME_BORDER_COLOR_DEFAULT,
|
|
7887
8187
|
FRAME_BORDER_DEFAULT,
|
|
7888
8188
|
FRAME_BORDER_WIDTH_DEFAULT,
|
|
8189
|
+
FREEZE_DEFAULT_SECONDS,
|
|
8190
|
+
FREEZE_SECONDS_MAX,
|
|
8191
|
+
FREEZE_SECONDS_MIN,
|
|
7889
8192
|
HOUSE_GRADIENT,
|
|
7890
8193
|
IDLE_KINDS,
|
|
7891
8194
|
LAYOUT_FRAME_FIELDS,
|
|
@@ -7976,6 +8279,7 @@ export {
|
|
|
7976
8279
|
clampCamFrac,
|
|
7977
8280
|
clampCamSize,
|
|
7978
8281
|
clampFocus,
|
|
8282
|
+
clampFreezeSeconds,
|
|
7979
8283
|
clampSpeedRate,
|
|
7980
8284
|
clampTiltDeg,
|
|
7981
8285
|
clampZoomLevel,
|
|
@@ -7993,6 +8297,7 @@ export {
|
|
|
7993
8297
|
destinationById,
|
|
7994
8298
|
destinationsForChannel,
|
|
7995
8299
|
docCardLayout,
|
|
8300
|
+
docFreezes,
|
|
7996
8301
|
docOutputDuration,
|
|
7997
8302
|
docRestTime,
|
|
7998
8303
|
docToCropSpace,
|
|
@@ -8016,6 +8321,8 @@ export {
|
|
|
8016
8321
|
focusBounds,
|
|
8017
8322
|
followFocusEvents,
|
|
8018
8323
|
frameGeometry,
|
|
8324
|
+
freezeLane,
|
|
8325
|
+
freezeOutputExtents,
|
|
8019
8326
|
hexToRgbTriplet,
|
|
8020
8327
|
houseLook,
|
|
8021
8328
|
idleGaps,
|
|
@@ -8063,6 +8370,7 @@ export {
|
|
|
8063
8370
|
pageDisplayUrl,
|
|
8064
8371
|
parsePoseId,
|
|
8065
8372
|
pickStyle,
|
|
8373
|
+
placeFreezes,
|
|
8066
8374
|
planAutoSpeed,
|
|
8067
8375
|
planAutoTilt,
|
|
8068
8376
|
planAutoZoom,
|
|
@@ -8098,6 +8406,7 @@ export {
|
|
|
8098
8406
|
videoLane,
|
|
8099
8407
|
voiceKey,
|
|
8100
8408
|
withBackdrop,
|
|
8409
|
+
withFreezes,
|
|
8101
8410
|
withHolds,
|
|
8102
8411
|
withoutRejected,
|
|
8103
8412
|
wrapProgramLength,
|