diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-20 11:31:17 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-20 11:31:17 +0200 |
| commit | 3e89d2998588f8e35c524e523ba2ee853f0f6475 (patch) | |
| tree | 57221f96c1259b3b07e3ddf8c1adefc502b9d8eb /apps/web/src/timeline/group-move/snap.ts | |
| parent | 729d10592f4e802f91c555c309ff3ba4b119f843 (diff) | |
| download | opencut-3e89d2998588f8e35c524e523ba2ee853f0f6475.tar.gz | |
refactor: restructure files to their domains, new preview overlay system, and dep graph
Diffstat (limited to 'apps/web/src/timeline/group-move/snap.ts')
| -rw-r--r-- | apps/web/src/timeline/group-move/snap.ts | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/apps/web/src/timeline/group-move/snap.ts b/apps/web/src/timeline/group-move/snap.ts new file mode 100644 index 00000000..32c0a2f1 --- /dev/null +++ b/apps/web/src/timeline/group-move/snap.ts @@ -0,0 +1,85 @@ +import type { SceneTracks } from "@/timeline"; +import { + buildTimelineSnapPoints, + getTimelineSnapThresholdInTicks, + resolveTimelineSnap, + type SnapPoint, +} from "@/timeline/snapping"; +import { getElementEdgeSnapPoints } from "@/timeline/element-snap-source"; +import { getPlayheadSnapPoints } from "@/timeline/playhead-snap-source"; +import { getAnimationKeyframeSnapPointsForTimeline } from "@/animation/timeline-snap-points"; +import type { MoveGroup } from "./types"; + +export function snapGroupEdges({ + group, + anchorStartTime, + tracks, + playheadTime, + zoomLevel, +}: { + group: MoveGroup; + anchorStartTime: number; + tracks: SceneTracks; + playheadTime: number; + zoomLevel: number; +}): { + snappedAnchorStartTime: number; + snapPoint: SnapPoint | null; +} { + const excludeElementIds = new Set( + group.members.map((member) => member.elementId), + ); + const snapPoints = buildTimelineSnapPoints({ + sources: [ + () => getElementEdgeSnapPoints({ tracks, excludeElementIds }), + () => getPlayheadSnapPoints({ playheadTime }), + () => + getAnimationKeyframeSnapPointsForTimeline({ + tracks, + excludeElementIds, + }), + ], + }); + const maxSnapDistance = getTimelineSnapThresholdInTicks({ zoomLevel }); + + let closestSnapDistance = Infinity; + let snappedAnchorStartTime = anchorStartTime; + let snapPoint: SnapPoint | null = null; + + for (const member of group.members) { + const memberStartTime = anchorStartTime + member.timeOffset; + const memberStartSnap = resolveTimelineSnap({ + targetTime: memberStartTime, + snapPoints, + maxSnapDistance, + }); + if ( + memberStartSnap.snapPoint && + memberStartSnap.snapDistance < closestSnapDistance + ) { + closestSnapDistance = memberStartSnap.snapDistance; + snappedAnchorStartTime = memberStartSnap.snappedTime - member.timeOffset; + snapPoint = memberStartSnap.snapPoint; + } + + const memberEndSnap = resolveTimelineSnap({ + targetTime: memberStartTime + member.duration, + snapPoints, + maxSnapDistance, + }); + if ( + memberEndSnap.snapPoint && + memberEndSnap.snapDistance < closestSnapDistance + ) { + closestSnapDistance = memberEndSnap.snapDistance; + snappedAnchorStartTime = + memberEndSnap.snappedTime - member.duration - member.timeOffset; + snapPoint = memberEndSnap.snapPoint; + } + } + + return { + snappedAnchorStartTime, + snapPoint, + }; +} |
