mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
136 lines
3.4 KiB
TypeScript
136 lines
3.4 KiB
TypeScript
import type { TimelineTrack } from "@/timeline";
|
|
import { timelineTimeToPixels } from "@/timeline/pixel-utils";
|
|
import {
|
|
TIMELINE_CONTENT_TOP_PADDING_PX,
|
|
} from "./layout";
|
|
import { getCumulativeHeightBefore, getTrackHeight } from "./track-layout";
|
|
|
|
type TimelineElementRef = { trackId: string; elementId: string };
|
|
|
|
interface SelectionRectangle {
|
|
left: number;
|
|
top: number;
|
|
right: number;
|
|
bottom: number;
|
|
}
|
|
|
|
function getNormalizedRectangle({
|
|
startPos,
|
|
endPos,
|
|
}: {
|
|
startPos: { x: number; y: number };
|
|
endPos: { x: number; y: number };
|
|
}): SelectionRectangle {
|
|
return {
|
|
left: Math.min(startPos.x, endPos.x),
|
|
top: Math.min(startPos.y, endPos.y),
|
|
right: Math.max(startPos.x, endPos.x),
|
|
bottom: Math.max(startPos.y, endPos.y),
|
|
};
|
|
}
|
|
|
|
function getSelectionRectangleInContent({
|
|
container,
|
|
scrollContainer,
|
|
startPos,
|
|
endPos,
|
|
}: {
|
|
container: HTMLElement;
|
|
scrollContainer: HTMLDivElement | null;
|
|
startPos: { x: number; y: number };
|
|
endPos: { x: number; y: number };
|
|
}): SelectionRectangle {
|
|
const containerRect = container.getBoundingClientRect();
|
|
const scrollRect = scrollContainer?.getBoundingClientRect() ?? containerRect;
|
|
const scrollLeft = scrollContainer?.scrollLeft ?? 0;
|
|
const scrollTop = scrollContainer?.scrollTop ?? 0;
|
|
|
|
const adjustedStart = {
|
|
x: startPos.x - containerRect.left + scrollLeft,
|
|
y: startPos.y - scrollRect.top + scrollTop,
|
|
};
|
|
const adjustedEnd = {
|
|
x: endPos.x - containerRect.left + scrollLeft,
|
|
y: endPos.y - scrollRect.top + scrollTop,
|
|
};
|
|
|
|
return getNormalizedRectangle({
|
|
startPos: adjustedStart,
|
|
endPos: adjustedEnd,
|
|
});
|
|
}
|
|
|
|
function isRectangleIntersecting({
|
|
elementRectangle,
|
|
selectionRectangle,
|
|
}: {
|
|
elementRectangle: SelectionRectangle;
|
|
selectionRectangle: SelectionRectangle;
|
|
}): boolean {
|
|
return !(
|
|
elementRectangle.right < selectionRectangle.left ||
|
|
elementRectangle.left > selectionRectangle.right ||
|
|
elementRectangle.bottom < selectionRectangle.top ||
|
|
elementRectangle.top > selectionRectangle.bottom
|
|
);
|
|
}
|
|
|
|
export function resolveTimelineElementIntersections({
|
|
container,
|
|
scrollContainer,
|
|
tracks,
|
|
zoomLevel,
|
|
startPos,
|
|
currentPos,
|
|
}: {
|
|
container: HTMLElement;
|
|
scrollContainer: HTMLDivElement | null;
|
|
tracks: TimelineTrack[];
|
|
zoomLevel: number;
|
|
startPos: { x: number; y: number };
|
|
currentPos: { x: number; y: number };
|
|
}): TimelineElementRef[] {
|
|
const selectionRectangle = getSelectionRectangleInContent({
|
|
container,
|
|
scrollContainer,
|
|
startPos,
|
|
endPos: currentPos,
|
|
});
|
|
const selectedElements: TimelineElementRef[] = [];
|
|
|
|
for (const [trackIndex, track] of tracks.entries()) {
|
|
const trackTop = getCumulativeHeightBefore({
|
|
tracks,
|
|
trackIndex,
|
|
});
|
|
const trackHeight = getTrackHeight({ type: track.type });
|
|
const elementTop = TIMELINE_CONTENT_TOP_PADDING_PX + trackTop;
|
|
const elementBottom = elementTop + trackHeight;
|
|
|
|
for (const element of track.elements) {
|
|
const elementLeft = timelineTimeToPixels({ time: element.startTime, zoomLevel });
|
|
const elementRight = timelineTimeToPixels({ time: element.startTime + element.duration, zoomLevel });
|
|
const elementRectangle = {
|
|
left: elementLeft,
|
|
top: elementTop,
|
|
right: elementRight,
|
|
bottom: elementBottom,
|
|
};
|
|
|
|
if (
|
|
isRectangleIntersecting({
|
|
elementRectangle,
|
|
selectionRectangle,
|
|
})
|
|
) {
|
|
selectedElements.push({
|
|
trackId: track.id,
|
|
elementId: element.id,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return selectedElements;
|
|
}
|