selection bounds
도형을 선택하면 에디터는 선택 영역을 보여줍니다. 하나만 선택했을 때도 필요하고, 여러 개를 선택했을 때도 필요합니다.
selection bounds는 선택된 node들이 화면에서 차지하는 범위입니다.
bounds를 계산하는 실제 코드
node의 네 모서리를 world로 보낸 뒤, 모든 점을 포함하는 axis-aligned bounds를 만듭니다.
function transformPoint(m, p) {
return {
x: m[0] * p.x + m[3] * p.y + m[6],
y: m[1] * p.x + m[4] * p.y + m[7]
};
}
function nodeWorldCorners(node) {
const corners = [
{ x: 0, y: 0 },
{ x: node.width, y: 0 },
{ x: node.width, y: node.height },
{ x: 0, y: node.height }
];
return corners.map((p) => transformPoint(node.worldMatrix, p));
}
function boundsFromPoints(points) {
const xs = points.map((p) => p.x);
const ys = points.map((p) => p.y);
const minX = Math.min(...xs);
const minY = Math.min(...ys);
const maxX = Math.max(...xs);
const maxY = Math.max(...ys);
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
}
function selectionBounds(nodes) {
return boundsFromPoints(nodes.flatMap(nodeWorldCorners));
}
이 결과를 camera로 screen 좌표에 투영하면 selection outline과 handle 위치를 그릴 수 있습니다.
node bounds를 world로 보낸다
node의 local rectangle은 단순합니다.
(0,0), (w,0), (w,h), (0,h)
하지만 node transform을 적용하면 world에서 회전되거나 scale될 수 있습니다. 따라서 네 모서리를 world로 변환합니다.
worldCorners = nodeWorldMatrix * localCorners
여러 node는 합집합을 만든다
선택된 node가 여러 개라면 각 node의 world bounds를 구한 뒤 합집합을 만듭니다.
selectionBounds = union(boundsA, boundsB, boundsC)
이 bounds는 resize handle, marquee result, inspector의 위치 표시 등에 쓰입니다.
overlay는 screen bounds로 그린다
selection bounds 자체는 world 기준으로 계산할 수 있지만, 화면에 그릴 때는 camera를 적용합니다.
world bounds -> screen bounds -> overlay draw
handle 크기와 outline 두께는 screen pixel 기준으로 유지하는 것이 자연스럽습니다.
오늘의 핵심
selection bounds는 선택된 node들의 편집용 대표 geometry입니다.
node local corners
-> world corners
-> union
-> screen overlay