import { AffineTransform, getRegionDelta, Point, Rect, Region, Size, unionRects, } from "@noya-app/noya-geometry"; import { getBestSnapDelta, getElementSnapDelta, getGridSnapDelta, getValuesOnAxis, } from "./snapUtils"; export type MovingOptions = { constrainToSingleAxis?: boolean; ignoreSnapping?: boolean; }; export function getMovingOptionsForEvent({ shiftKey, ctrlKey, }: { shiftKey: boolean; ctrlKey: boolean; }): MovingOptions { return { constrainToSingleAxis: shiftKey, ignoreSnapping: ctrlKey, }; } export function getMoveTransform( delta: Point | Region, { constrainToSingleAxis }: MovingOptions ) { if ("anchor" in delta) { delta = getRegionDelta(delta); } if (constrainToSingleAxis) { if (Math.abs(delta.x) > Math.abs(delta.y)) { return AffineTransform.translate(delta.x, 0); } else { return AffineTransform.translate(0, delta.y); } } else { return AffineTransform.translate(delta); } } export function getMoveSnapDelta({ movingRects, stationaryRects, delta, gridSize, options, }: { movingRects: Rect[]; stationaryRects: Rect[]; delta: Point; gridSize: Size; options: MovingOptions; }) { const movingBoundingBox = unionRects(...movingRects); const transform = getMoveTransform(delta, options); const transformedMovingRect = transform.applyTo(movingBoundingBox); const possibleSnapsX = [ getElementSnapDelta({ movingValues: getValuesOnAxis(transformedMovingRect, "x"), stationaryValues: getValuesOnAxis(stationaryRects, "x"), }), getGridSnapDelta({ value: transformedMovingRect.x, gridSize: gridSize.width, }), ]; const possibleSnapsY = [ getElementSnapDelta({ movingValues: getValuesOnAxis(transformedMovingRect, "y"), stationaryValues: getValuesOnAxis(stationaryRects, "y"), }), getGridSnapDelta({ value: transformedMovingRect.y, gridSize: gridSize.height, }), ]; const snapDelta = { x: getBestSnapDelta(possibleSnapsX), y: getBestSnapDelta(possibleSnapsY), }; return { x: delta.x + snapDelta.x, y: delta.y + snapDelta.y, }; }