70 lines
2.6 KiB
TypeScript
70 lines
2.6 KiB
TypeScript
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,
|
|
};
|
|
}
|