import type { CanvasNodeData, ViewportTransform } from "@/types/canvas"; export type CanvasBounds = { left: number; top: number; right: number; bottom: number; }; export type CanvasViewportSize = { width: number; height: number; }; export function getCanvasNodesBounds(nodes: CanvasNodeData[]): CanvasBounds | null { if (!nodes.length) return null; let left = Number.POSITIVE_INFINITY; let top = Number.POSITIVE_INFINITY; let right = Number.NEGATIVE_INFINITY; let bottom = Number.NEGATIVE_INFINITY; nodes.forEach((node) => { left = Math.min(left, node.position.x); top = Math.min(top, node.position.y); right = Math.max(right, node.position.x + node.width); bottom = Math.max(bottom, node.position.y + node.height); }); return { left, top, right, bottom }; } export function viewportForBounds(bounds: CanvasBounds, viewportSize: CanvasViewportSize, options: { padding?: number; minScale?: number; maxScale?: number } = {}): ViewportTransform { const padding = options.padding ?? 96; const minScale = options.minScale ?? 0.05; const maxScale = options.maxScale ?? 1; const boundsWidth = Math.max(1, bounds.right - bounds.left); const boundsHeight = Math.max(1, bounds.bottom - bounds.top); const availableWidth = Math.max(1, viewportSize.width - padding * 2); const availableHeight = Math.max(1, viewportSize.height - padding * 2); const k = Math.min(maxScale, Math.max(minScale, Math.min(availableWidth / boundsWidth, availableHeight / boundsHeight))); const centerX = (bounds.left + bounds.right) / 2; const centerY = (bounds.top + bounds.bottom) / 2; return { x: viewportSize.width / 2 - centerX * k, y: viewportSize.height / 2 - centerY * k, k, }; } export function viewportAtScale(viewport: ViewportTransform, viewportSize: CanvasViewportSize, scale: number): ViewportTransform { const k = Math.min(2, Math.max(0.05, scale)); const centerWorldX = (viewportSize.width / 2 - viewport.x) / viewport.k; const centerWorldY = (viewportSize.height / 2 - viewport.y) / viewport.k; return { x: viewportSize.width / 2 - centerWorldX * k, y: viewportSize.height / 2 - centerWorldY * k, k, }; } export function interpolateViewport(from: ViewportTransform, to: ViewportTransform, progress: number): ViewportTransform { const t = 1 - Math.pow(1 - Math.min(1, Math.max(0, progress)), 3); return { x: from.x + (to.x - from.x) * t, y: from.y + (to.y - from.y) * t, k: from.k + (to.k - from.k) * t, }; }