mock: 選択枠を太くし、最前面の描画層に移す(T-30 単位1)
選択枠と長押し候補の枠を NodeSvg の再帰から外し、ノードの描画より後に描く PhySelectionLayer に移した。枠はワールド変換とローカル矩形から純粋関数 selectionFrames で求め、画面上3.5pxで描く。部品の選択表示は形状上に残す。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TDkA8qMQ3Uf1eY3Ar19vjy
This commit is contained in:
@@ -229,6 +229,61 @@ export function localBounds(nodes: Node[], variants: Record<string, string> = {}
|
||||
visit(nodes, { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 });
|
||||
return obbAabb(points);
|
||||
}
|
||||
|
||||
export type SelectionFrame = {
|
||||
id: string;
|
||||
type: Node["type"];
|
||||
transform: Affine;
|
||||
box: Box;
|
||||
candidate: boolean;
|
||||
};
|
||||
/** 可視ノードの選択枠をワールド行列とローカル矩形で返す。 */
|
||||
export function selectionFrames(
|
||||
roots: Node[],
|
||||
selected: string[],
|
||||
variants: Record<string, string>,
|
||||
candidate: string | null,
|
||||
parent: Affine = { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
|
||||
): SelectionFrame[] {
|
||||
const frames: SelectionFrame[] = [];
|
||||
const visit = (nodes: Node[], ancestor: Affine) => {
|
||||
for (const node of nodes) {
|
||||
const transform = compose(ancestor, matrix(node.transform));
|
||||
if (node.type === "key" && (selected.includes(node.id) || candidate === node.id)) {
|
||||
frames.push({
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
transform,
|
||||
box: { x: 0, y: 0, width: node.size.w, height: node.size.h },
|
||||
candidate: candidate === node.id,
|
||||
});
|
||||
} else if (node.type === "cluster") {
|
||||
if (selected.includes(node.id) || candidate === node.id)
|
||||
frames.push({
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
transform,
|
||||
box: localBounds(node.children, variants),
|
||||
candidate: candidate === node.id,
|
||||
});
|
||||
visit(node.children, transform);
|
||||
} else if (node.type === "variantGroup") {
|
||||
const variant = node.children.find((item) => item.id === (variants[node.id] ?? node.default));
|
||||
if (selected.includes(node.id) || candidate === node.id)
|
||||
frames.push({
|
||||
id: node.id,
|
||||
type: node.type,
|
||||
transform,
|
||||
box: localBounds(variant?.children ?? [], variants),
|
||||
candidate: candidate === node.id,
|
||||
});
|
||||
if (variant) visit(variant.children, transform);
|
||||
}
|
||||
}
|
||||
};
|
||||
visit(roots, parent);
|
||||
return frames;
|
||||
}
|
||||
/** ワールド位置を貼り先の親ローカルへ戻し、親軸で1刻みずらす。T-11/T-27。 */
|
||||
export function pasteLocalTransform(sourceWorld: Affine, parentWorld: Affine, step: number): Transform {
|
||||
const local = compose(inverse(parentWorld), sourceWorld);
|
||||
|
||||
Reference in New Issue
Block a user