/** * Timeline coordinate conversion and formatting utilities */ /** * Base pixels per second at zoom level 1 * zoom=1: 5 pixels per second * zoom=2: 10 pixels per second, etc. */ const PIXELS_PER_SECOND_BASE = 5; /** * Convert time (in seconds) to pixel position */ export function timeToPixel(time: number, duration: number, zoom: number): number { if (duration === 0) return 0; const totalWidth = duration * zoom * PIXELS_PER_SECOND_BASE; return (time / duration) * totalWidth; } /** * Convert pixel position to time (in seconds) */ export function pixelToTime(pixel: number, duration: number, zoom: number): number { if (duration === 0) return 0; const totalWidth = duration * zoom * PIXELS_PER_SECOND_BASE; return (pixel / totalWidth) * duration; } /** * Calculate appropriate tick interval based on visible duration * Returns interval in seconds */ export function calculateTickInterval(visibleDuration: number): { major: number; minor: number; } { // Very zoomed in: show sub-second intervals if (visibleDuration < 5) { return { major: 1, minor: 0.5 }; } // Zoomed in: show every second if (visibleDuration < 20) { return { major: 5, minor: 1 }; } // Medium zoom: show every 5 seconds if (visibleDuration < 60) { return { major: 10, minor: 5 }; } // Zoomed out: show every 10 seconds if (visibleDuration < 300) { return { major: 30, minor: 10 }; } // Very zoomed out: show every minute return { major: 60, minor: 30 }; } /** * Format time in seconds to display format * Returns format like "0:00", "1:23", "12:34.5" */ export function formatTimeLabel(seconds: number, showMillis: boolean = false): string { const mins = Math.floor(seconds / 60); const secs = seconds % 60; if (showMillis) { const wholeSecs = Math.floor(secs); const decimalPart = Math.floor((secs - wholeSecs) * 10); return `${mins}:${wholeSecs.toString().padStart(2, '0')}.${decimalPart}`; } return `${mins}:${Math.floor(secs).toString().padStart(2, '0')}`; } /** * Calculate visible time range based on scroll position */ export function getVisibleTimeRange( scrollLeft: number, viewportWidth: number, duration: number, zoom: number ): { start: number; end: number } { const totalWidth = duration * zoom * 100; const start = pixelToTime(scrollLeft, duration, zoom); const end = pixelToTime(scrollLeft + viewportWidth, duration, zoom); return { start: Math.max(0, start), end: Math.min(duration, end), }; }