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:
@@ -12,6 +12,7 @@ import {
|
||||
pasteParent,
|
||||
resolveKey,
|
||||
snap,
|
||||
selectionFrames,
|
||||
tabOrder,
|
||||
treeIndex,
|
||||
visibleNodeIds,
|
||||
@@ -34,6 +35,37 @@ const phy: PhyFile = {
|
||||
],
|
||||
};
|
||||
describe("PHY pure interaction rules", () => {
|
||||
it("computes selected frame world transforms through nested rotations", () => {
|
||||
const roots: PhyNode[] = [
|
||||
{
|
||||
type: "cluster",
|
||||
id: "outer",
|
||||
transform: { x: 10, y: 20, rotation: 90 },
|
||||
children: [
|
||||
{
|
||||
type: "cluster",
|
||||
id: "inner",
|
||||
transform: { x: 2, y: 3, rotation: -90 },
|
||||
children: [
|
||||
{
|
||||
type: "key",
|
||||
id: "target",
|
||||
profile: "p",
|
||||
transform: { x: 4, y: 5, rotation: 30 },
|
||||
size: { w: 2, h: 1 },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const frame = selectionFrames(roots, ["target"], {}, null)[0]!;
|
||||
expect(frame.id).toBe("target");
|
||||
expect(frame.transform.e).toBeCloseTo(11);
|
||||
expect(frame.transform.f).toBeCloseTo(27);
|
||||
expect(frame.transform.a).toBeCloseTo(Math.cos(Math.PI / 6));
|
||||
expect(frame.transform.b).toBeCloseTo(Math.sin(Math.PI / 6));
|
||||
});
|
||||
it("removes ancestors and descendants from mixed selections", () => {
|
||||
expect(normalizeSelection(phy, ["c", "k"])).toEqual(["k"]);
|
||||
expect(normalizeSelection(phy, ["k", "c"])).toEqual(["c"]);
|
||||
|
||||
Reference in New Issue
Block a user