import { AffineTransform, Point, Rect } from "@noya-app/noya-geometry"; import type { EventWithPointAndTarget } from "../plugins/contextProperties"; export type IdentifiablePoint = Point & { id: string }; // Event coordinates are relative to (0,0), but we want them to include // the current page's zoom and offset from the origin export function convertPoint( origin: Point, zoom: number, point: Point, targetCoordinateSystem: "screen" | "canvas" ): Point { const transform = AffineTransform.scale(1 / zoom).translate( -origin.x, -origin.y ); switch (targetCoordinateSystem) { case "canvas": return transform.applyTo(point); case "screen": return transform.invert().applyTo(point); } } export function convertOffsetPoint(event: EventWithPointAndTarget): Point { // If the event is not targetting the canvas element, find the ancestor canvas element // by looking for data-infinite-canvas-root attribute const canvas = (event.target as HTMLElement | undefined)?.closest( "[data-infinite-canvas-root]" ) as HTMLDivElement; if (!canvas) { return { x: 0, y: 0 }; } const rect = canvas.getBoundingClientRect(); return { x: event.clientX - rect.left, y: event.clientY - rect.top, }; } export const DEFAULT_POINT_HANDLE_SIZE = 8; /** * Transform points from the coordinate space of the rect to the coordinate space of the canvas. */ export function getAbsolutizeTransform(rect: Rect): AffineTransform { return AffineTransform.translate(rect.x, rect.y).scale( rect.width, rect.height ); } /* * Transform points from the coordinate space of the drawn points to the coordinate space of the rect. * As a result, all points will be in the range [0, 1]. */ export function getNormalizeTransform(rect: Rect) { const scaleX = rect.width === 0 ? 0 : 1 / rect.width; const scaleY = rect.height === 0 ? 0 : 1 / rect.height; return AffineTransform.scale(scaleX, scaleY).translate(-rect.x, -rect.y); }