diff options
Diffstat (limited to 'apps/web/src/lib/ripple/diff.ts')
| -rw-r--r-- | apps/web/src/lib/ripple/diff.ts | 279 |
1 files changed, 0 insertions, 279 deletions
diff --git a/apps/web/src/lib/ripple/diff.ts b/apps/web/src/lib/ripple/diff.ts deleted file mode 100644 index 49238738..00000000 --- a/apps/web/src/lib/ripple/diff.ts +++ /dev/null @@ -1,279 +0,0 @@ -import type { SceneTracks, TimelineElement, TimelineTrack } from "@/lib/timeline/types"; -import type { RippleAdjustment } from "./apply"; - -interface Interval { - startTime: number; - endTime: number; -} - -interface ElementSpan extends Interval { - id: string; -} - -export function computeRippleAdjustments({ - beforeTracks, - afterTracks, -}: { - beforeTracks: SceneTracks; - afterTracks: SceneTracks; -}): RippleAdjustment[] { - const beforeTrackList = [ - ...beforeTracks.overlay, - beforeTracks.main, - ...beforeTracks.audio, - ]; - const afterTrackList = [ - ...afterTracks.overlay, - afterTracks.main, - ...afterTracks.audio, - ]; - const afterTracksById = new Map(afterTrackList.map((track) => [track.id, track])); - const allAfterElementIds = new Set( - afterTrackList.flatMap((track) => track.elements.map((element) => element.id)), - ); - - return beforeTrackList.flatMap((beforeTrack): RippleAdjustment[] => - computeTrackRippleAdjustments({ - trackId: beforeTrack.id, - beforeElements: beforeTrack.elements, - afterElements: afterTracksById.get(beforeTrack.id)?.elements ?? [], - allAfterElementIds, - }), - ); -} - -function computeTrackRippleAdjustments({ - trackId, - beforeElements, - afterElements, - allAfterElementIds, -}: { - trackId: string; - beforeElements: TimelineElement[]; - afterElements: TimelineElement[]; - allAfterElementIds: Set<string>; -}): RippleAdjustment[] { - const beforeElementsById = buildElementSpanMap({ elements: beforeElements }); - const afterElementsById = buildElementSpanMap({ elements: afterElements }); - const { vacatedIntervals, joinedIntervals } = collectTrackIntervals({ - beforeElementsById, - afterElementsById, - allAfterElementIds, - }); - const freedIntervals = subtractIntervalSets({ - sourceIntervals: vacatedIntervals, - overlappingIntervals: joinedIntervals, - }); - - return buildAdjustments({ trackId, intervals: freedIntervals }); -} - -function buildElementSpanMap({ - elements, -}: { - elements: TimelineElement[]; -}): Map<string, ElementSpan> { - return new Map( - elements.map((element) => [ - element.id, - { - id: element.id, - startTime: element.startTime, - endTime: element.startTime + element.duration, - }, - ]), - ); -} - -function collectTrackIntervals({ - beforeElementsById, - afterElementsById, - allAfterElementIds, -}: { - beforeElementsById: Map<string, ElementSpan>; - afterElementsById: Map<string, ElementSpan>; - allAfterElementIds: Set<string>; -}): { - vacatedIntervals: Interval[]; - joinedIntervals: Interval[]; -} { - const vacatedIntervals: Interval[] = []; - const joinedIntervals: Interval[] = []; - - for (const beforeElement of beforeElementsById.values()) { - const afterElement = afterElementsById.get(beforeElement.id); - if (!afterElement) { - const wasMovedToAnotherTrack = allAfterElementIds.has(beforeElement.id); - if (!wasMovedToAnotherTrack) { - pushInterval({ - intervals: vacatedIntervals, - startTime: beforeElement.startTime, - endTime: beforeElement.endTime, - }); - } - continue; - } - - if (beforeElement.endTime > afterElement.endTime) { - pushInterval({ - intervals: vacatedIntervals, - startTime: afterElement.endTime, - endTime: beforeElement.endTime, - }); - } - } - - for (const afterElement of afterElementsById.values()) { - if (beforeElementsById.has(afterElement.id)) { - continue; - } - - pushInterval({ - intervals: joinedIntervals, - startTime: afterElement.startTime, - endTime: afterElement.endTime, - }); - } - - return { - vacatedIntervals: normalizeIntervals({ intervals: vacatedIntervals }), - joinedIntervals: normalizeIntervals({ intervals: joinedIntervals }), - }; -} - -function buildAdjustments({ - trackId, - intervals, -}: { - trackId: string; - intervals: Interval[]; -}): RippleAdjustment[] { - return intervals.flatMap((interval): RippleAdjustment[] => { - const shiftAmount = interval.endTime - interval.startTime; - if (shiftAmount <= 0) { - return []; - } - - return [ - { - trackId, - afterTime: interval.endTime, - shiftAmount, - }, - ]; - }); -} - -function subtractIntervalSets({ - sourceIntervals, - overlappingIntervals, -}: { - sourceIntervals: Interval[]; - overlappingIntervals: Interval[]; -}): Interval[] { - const normalizedSourceIntervals = normalizeIntervals({ - intervals: sourceIntervals, - }); - const normalizedOverlappingIntervals = normalizeIntervals({ - intervals: overlappingIntervals, - }); - - return normalizedSourceIntervals.flatMap((sourceInterval) => - subtractSingleInterval({ - sourceInterval, - overlappingIntervals: normalizedOverlappingIntervals, - }), - ); -} - -function normalizeIntervals({ - intervals, -}: { - intervals: Interval[]; -}): Interval[] { - const validIntervals: Interval[] = []; - for (const interval of intervals) { - pushInterval({ - intervals: validIntervals, - startTime: interval.startTime, - endTime: interval.endTime, - }); - } - - const sortedIntervals = validIntervals.sort( - (leftInterval, rightInterval) => - leftInterval.startTime - rightInterval.startTime, - ); - - if (sortedIntervals.length === 0) { - return []; - } - - const mergedIntervals: Interval[] = [{ ...sortedIntervals[0] }]; - for (const interval of sortedIntervals.slice(1)) { - const previousInterval = mergedIntervals[mergedIntervals.length - 1]; - if (interval.startTime <= previousInterval.endTime) { - previousInterval.endTime = Math.max( - previousInterval.endTime, - interval.endTime, - ); - continue; - } - - mergedIntervals.push({ ...interval }); - } - - return mergedIntervals; -} - -function subtractSingleInterval({ - sourceInterval, - overlappingIntervals, -}: { - sourceInterval: Interval; - overlappingIntervals: Interval[]; -}): Interval[] { - let remainingIntervals: Interval[] = [{ ...sourceInterval }]; - - for (const overlappingInterval of overlappingIntervals) { - remainingIntervals = remainingIntervals.flatMap((remainingInterval) => { - if ( - overlappingInterval.endTime <= remainingInterval.startTime || - overlappingInterval.startTime >= remainingInterval.endTime - ) { - return [remainingInterval]; - } - - const nextIntervals: Interval[] = []; - pushInterval({ - intervals: nextIntervals, - startTime: remainingInterval.startTime, - endTime: overlappingInterval.startTime, - }); - pushInterval({ - intervals: nextIntervals, - startTime: overlappingInterval.endTime, - endTime: remainingInterval.endTime, - }); - return nextIntervals; - }); - - if (remainingIntervals.length === 0) { - return []; - } - } - - return remainingIntervals; -} - -function pushInterval({ - intervals, - startTime, - endTime, -}: { intervals: Interval[]; startTime: number; endTime: number }): void { - if (endTime <= startTime) { - return; - } - - intervals.push({ startTime, endTime }); -} |
