Initial commit

This commit is contained in:
twotalesanimation
2026-05-19 22:20:29 +02:00
commit 0fbe856dce
173 changed files with 38316 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
/**
* Frame-accurate utilities for VFX review.
* All frame calculations use 0-based frame numbers
* unless documented otherwise.
*/
/** Convert frame number to video currentTime in seconds */
export function frameToTime(frame: number, fps: number): number {
return frame / fps;
}
/** Convert video currentTime to frame number (0-based) */
export function timeToFrame(time: number, fps: number): number {
return Math.floor(time * fps);
}
/** Format a frame number as a timecode string: MM:SS:FF */
export function frameToTimecode(frame: number, fps: number): string {
const totalSeconds = frame / fps;
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = Math.floor(totalSeconds % 60);
const frames = frame % Math.round(fps);
if (hours > 0) {
return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}:${String(frames).padStart(2, "0")}`;
}
return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}:${String(frames).padStart(2, "0")}`;
}
/** Format a raw time (seconds) as MM:SS.mmm */
export function timeToDisplay(time: number): string {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
const ms = Math.floor((time % 1) * 1000);
return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}.${String(ms).padStart(3, "0")}`;
}
/** Given duration and fps, compute total frame count */
export function durationToFrameCount(duration: number, fps: number): number {
return Math.floor(duration * fps);
}
/** Clamp a frame number to [0, totalFrames - 1] */
export function clampFrame(frame: number, totalFrames: number): number {
return Math.max(0, Math.min(totalFrames - 1, frame));
}
/** Step a single frame forward or backward */
export function stepFrame(
currentFrame: number,
delta: number,
totalFrames: number
): number {
return clampFrame(currentFrame + delta, totalFrames);
}
/** Convert a timeline position (0-1) to frame number */
export function positionToFrame(position: number, totalFrames: number): number {
return clampFrame(Math.floor(position * totalFrames), totalFrames);
}
/** Convert a frame number to timeline position (0-1) */
export function frameToPosition(frame: number, totalFrames: number): number {
if (totalFrames === 0) return 0;
return frame / totalFrames;
}
/** Common VFX frame rates */
export const COMMON_FPS = [23.976, 24, 25, 29.97, 30, 48, 50, 59.94, 60] as const;
export type CommonFPS = (typeof COMMON_FPS)[number];
/** Human-readable fps label */
export function fpsLabel(fps: number): string {
if (fps === 23.976) return "23.976";
return String(fps);
}