import type { CanvasNode } from "~/types/canvas"; import { clamp } from "#shared/general.util"; export type Direction = 'bottom' | 'top' | 'left' | 'right'; export type Position = { x: number, y: number }; export type Box = Position & { w: number, h: number }; export type Path = { path: string; from: Position; to: Position; side: Direction; } export const rotation: Record = { top: "180", bottom: "0", left: "90", right: "270" }; export const opposite: Record = { top: "bottom", bottom: "top", left: "right", right: "left" } export function edgePos(side: Direction, pos: Position, offset: number): Position { switch (side) { case "left": return { x: pos.x - offset, y: pos.y }; case "right": return { x: pos.x + offset, y: pos.y }; case "top": return { x: pos.x, y: pos.y - offset }; case "bottom": return { x: pos.x, y: pos.y + offset } } } export function getNode(nodes: CanvasNode[], id: string): CanvasNode | undefined { return nodes.find(e => e.id === id); } export function posFromDir(e: { minX: number, minY: number, maxX: number, maxY: number }, t: Direction): Position { switch (t) { case "top": return { x: (e.minX + e.maxX) / 2, y: e.minY }; case "right": return { x: e.maxX, y: (e.minY + e.maxY) / 2 }; case "bottom": return { x: (e.minX + e.maxX) / 2, y: e.maxY }; case "left": return { x: e.minX, y: (e.minY + e.maxY) / 2 }; } } export function getBbox(node: CanvasNode): { minX: number, minY: number, maxX: number, maxY: number } { return { minX: node.x, minY: node.y, maxX: node.x + node.width, maxY: node.y + node.height }; } export function getPath(from: CanvasNode, fromSide: Direction, to: CanvasNode, toSide: Direction): Path | undefined { if(from === undefined || to === undefined) return; const start = posFromDir(getBbox(from), fromSide), end = posFromDir(getBbox(to), toSide); return bezier(start, fromSide, end, toSide); } export function bezier(from: Position, fromSide: Direction, to: Position, toSide: Direction): Path { const r = Math.hypot(from.x - to.x, from.y - to.y), o = clamp(r / 2, 70, 150), a = edgePos(fromSide, from, o), s = edgePos(toSide, to, o); return { path: `M${from.x},${from.y} C${a.x},${a.y} ${s.x},${s.y} ${to.x},${to.y}`, from: from, to: to, side: toSide, }; } export function labelCenter(from: CanvasNode, fromSide: Direction, to: CanvasNode, toSide: Direction): string { const start = posFromDir(getBbox(from), fromSide), end = posFromDir(getBbox(to), toSide); const len = Math.hypot(start.x - end.x, start.y - end.y), offset = clamp(len / 2, 70, 150), b = edgePos(fromSide, start, offset), s = edgePos(toSide, end, offset); const center = getCenter(start, end, b, s, 0.5); return `translate(${center.x}px, ${center.y}px)`; } export function getCenter(n: Position, i: Position, r: Position, o: Position, e: number): Position { const a = 1 - e, s = a * a * a, l = 3 * e * a * a, c = 3 * e * e * a, u = e * e * e; return { x: s * n.x + l * r.x + c * o.x + u * i.x, y: s * n.y + l * r.y + c * o.y + u * i.y }; } export function gridSnap(value: number, grid: number): number { return Math.round(value / grid) * grid; }