// Shared overlay-transform geometry — used by BOTH the preview gizmo (Preview.tsx) and the // render-time transform (EditorComposition.tsx) so the on-screen move/resize handles and the // composited result agree exactly. An overlay scene draws its element anchored to one of these // approximate frame points; a drag writes `ox`/`oy` (composition px offset) and a corner-resize // writes `scale` (a size multiplier). Both translate FROM and pivot ABOUT the same anchor. export const COMP_W = 1920; export const COMP_H = 1080; // approximate anchor point (fraction of the frame) for each overlay `pos` value — roughly where // its element sits. The move handle starts here (+ ox/oy); scale pivots here so resizing keeps the // element planted where it is instead of sliding toward the frame centre. export const OV_ANCHORS: Record = { tl: { x: 0.14, y: 0.17 }, tc: { x: 0.5, y: 0.14 }, tr: { x: 0.86, y: 0.17 }, bl: { x: 0.14, y: 0.83 }, bc: { x: 0.5, y: 0.85 }, br: { x: 0.86, y: 0.83 }, center: { x: 0.5, y: 0.5 }, left: { x: 0.09, y: 0.5 }, right: { x: 0.91, y: 0.5 }, }; export const overlayAnchor = (pos?: unknown): { x: number; y: number } => OV_ANCHORS[String(pos ?? "bc")] || OV_ANCHORS.bc; // CSS transform-origin (composition px) for an overlay's anchor — the pivot scale() rotates about. export const overlayOrigin = (pos?: unknown): string => { const a = overlayAnchor(pos); return `${Math.round(a.x * COMP_W)}px ${Math.round(a.y * COMP_H)}px`; }; export const OV_SCALE_MIN = 0.4; export const OV_SCALE_MAX = 3; export const clampOverlayScale = (s: number): number => Math.max(OV_SCALE_MIN, Math.min(OV_SCALE_MAX, s));