summaryrefslogtreecommitdiff
path: root/apps/web/src/ripple/apply.ts
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-20 11:31:17 +0200
committerMaze Winther <mazewinther@gmail.com>2026-04-20 11:31:17 +0200
commit3e89d2998588f8e35c524e523ba2ee853f0f6475 (patch)
tree57221f96c1259b3b07e3ddf8c1adefc502b9d8eb /apps/web/src/ripple/apply.ts
parent729d10592f4e802f91c555c309ff3ba4b119f843 (diff)
downloadopencut-3e89d2998588f8e35c524e523ba2ee853f0f6475.tar.gz
refactor: restructure files to their domains, new preview overlay system, and dep graph
Diffstat (limited to 'apps/web/src/ripple/apply.ts')
-rw-r--r--apps/web/src/ripple/apply.ts77
1 files changed, 77 insertions, 0 deletions
diff --git a/apps/web/src/ripple/apply.ts b/apps/web/src/ripple/apply.ts
new file mode 100644
index 00000000..4ad83529
--- /dev/null
+++ b/apps/web/src/ripple/apply.ts
@@ -0,0 +1,77 @@
+import type { SceneTracks, TimelineTrack } from "@/timeline/types";
+import { rippleShiftElements } from "./shift";
+
+export interface RippleAdjustment {
+ trackId: string;
+ afterTime: number;
+ shiftAmount: number;
+}
+
+export function applyRippleAdjustments({
+ tracks,
+ adjustments,
+}: {
+ tracks: SceneTracks;
+ adjustments: RippleAdjustment[];
+}): SceneTracks {
+ if (adjustments.length === 0) {
+ return tracks;
+ }
+
+ const adjustmentsByTrack = new Map<string, RippleAdjustment[]>();
+ for (const adjustment of adjustments) {
+ const trackAdjustments = adjustmentsByTrack.get(adjustment.trackId) ?? [];
+ trackAdjustments.push(adjustment);
+ adjustmentsByTrack.set(adjustment.trackId, trackAdjustments);
+ }
+
+ return {
+ overlay: tracks.overlay.map((track) =>
+ applyTrackRippleAdjustments({
+ track,
+ adjustments: adjustmentsByTrack.get(track.id) ?? [],
+ }),
+ ),
+ main: applyTrackRippleAdjustments({
+ track: tracks.main,
+ adjustments: adjustmentsByTrack.get(tracks.main.id) ?? [],
+ }),
+ audio: tracks.audio.map((track) =>
+ applyTrackRippleAdjustments({
+ track,
+ adjustments: adjustmentsByTrack.get(track.id) ?? [],
+ }),
+ ),
+ };
+}
+
+function applyTrackRippleAdjustments<
+ TElement extends TimelineTrack["elements"][number],
+ TTrack extends TimelineTrack & { elements: TElement[] },
+>({
+ track,
+ adjustments,
+}: {
+ track: TTrack;
+ adjustments: RippleAdjustment[];
+}): TTrack {
+ if (adjustments.length === 0) {
+ return track;
+ }
+
+ const sortedAdjustments = [...adjustments].sort(
+ (firstAdjustment, secondAdjustment) =>
+ secondAdjustment.afterTime - firstAdjustment.afterTime,
+ );
+
+ let elements: TElement[] = track.elements;
+ for (const adjustment of sortedAdjustments) {
+ elements = rippleShiftElements({
+ elements,
+ afterTime: adjustment.afterTime,
+ shiftAmount: adjustment.shiftAmount,
+ });
+ }
+
+ return { ...track, elements };
+}