From bd107634d597966064ceca98438aac43b23dc98a Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Sat, 28 Feb 2026 01:24:23 +0300 Subject: [PATCH] Rotation-aware snap guides and selection bounds Both snap targets and moving selection bounds now compute AABB from rotated corners, so guides align to the visual edges of rotated shapes. --- src/engine/snap.ts | 55 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/src/engine/snap.ts b/src/engine/snap.ts index 22ea37091..b42a254b3 100644 --- a/src/engine/snap.ts +++ b/src/engine/snap.ts @@ -16,13 +16,47 @@ export interface SnapResult { } function getEdges(node: SceneNode) { + if (node.rotation === 0) { + return { + left: node.x, + right: node.x + node.width, + centerX: node.x + node.width / 2, + top: node.y, + bottom: node.y + node.height, + centerY: node.y + node.height / 2 + } + } + + const cx = node.x + node.width / 2 + const cy = node.y + node.height / 2 + const rad = (node.rotation * Math.PI) / 180 + const cos = Math.cos(rad) + const sin = Math.sin(rad) + const hw = node.width / 2 + const hh = node.height / 2 + + const corners = [ + { x: cx + (-hw) * cos - (-hh) * sin, y: cy + (-hw) * sin + (-hh) * cos }, + { x: cx + hw * cos - (-hh) * sin, y: cy + hw * sin + (-hh) * cos }, + { x: cx + hw * cos - hh * sin, y: cy + hw * sin + hh * cos }, + { x: cx + (-hw) * cos - hh * sin, y: cy + (-hw) * sin + hh * cos }, + ] + + let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity + for (const c of corners) { + left = Math.min(left, c.x) + right = Math.max(right, c.x) + top = Math.min(top, c.y) + bottom = Math.max(bottom, c.y) + } + return { - left: node.x, - right: node.x + node.width, - centerX: node.x + node.width / 2, - top: node.y, - bottom: node.y + node.height, - centerY: node.y + node.height / 2 + left, + right, + centerX: (left + right) / 2, + top, + bottom, + centerY: (top + bottom) / 2 } } @@ -123,10 +157,11 @@ export function computeSelectionBounds( let maxX = -Infinity let maxY = -Infinity for (const n of nodes) { - minX = Math.min(minX, n.x) - minY = Math.min(minY, n.y) - maxX = Math.max(maxX, n.x + n.width) - maxY = Math.max(maxY, n.y + n.height) + const edges = getEdges(n) + minX = Math.min(minX, edges.left) + minY = Math.min(minY, edges.top) + maxX = Math.max(maxX, edges.right) + maxY = Math.max(maxY, edges.bottom) } return { x: minX, y: minY, width: maxX - minX, height: maxY - minY } }