Files
Beyond-KLE/mock/tests/keyShape.test.ts
T
ewdernaandClaude Opus 5.5 afd9af1914 mock: PHY Editorで異形キーを描き・編集し・追加できるようにする(T-32)
- domain/keyShape.ts に、キーの矩形・外接矩形・和の外形の計算と、異形キーのプリセット表(今はISO Enterだけ)を置いた
- キャンバスと選択枠は、2つ目の矩形を持つキーを和の外形のpathで描く。範囲選択と囲み矩形も両方の矩形で取る
- プロパティ欄に「異形キー」のチェックと、2つ目の矩形の欄(X2・Y2・Width2・Height2)を置いた
- ツールバーに「異形キーを足す」selectを置いた。1Uと同じ規則の位置に、外接矩形の左上を合わせて足す
- 検証: vue-tsc 0件、Vitest 106件(うち新規5件)、Playwright PHY 31件(うち新規1件)、prettier --check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xmqw9x7EoryY6zf116i3yM
2026-10-26 08:55:20 +09:00

63 lines
2.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
IRREGULAR_KEY_PRESETS,
hasSecondRect,
keyOrigin,
keyOutline,
keyRects,
} from "../src/domain/keyShape";
import { keyPath } from "../src/ui/phy/logic";
const isoEnter = IRREGULAR_KEY_PRESETS.find((p) => p.id === "iso-enter")!.size;
const asSet = (loop: { x: number; y: number }[]) => loop.map((p) => `${p.x},${p.y}`).sort();
describe("key shape", () => {
it("fills missing second-rectangle fields with KLE's defaults", () => {
expect(hasSecondRect({ w: 1, h: 1 })).toBe(false);
expect(keyRects({ w: 1, h: 1 })).toEqual([{ x: 0, y: 0, w: 1, h: 1 }]);
expect(keyRects({ w: 1.25, h: 1, w2: 1.75 })).toEqual([
{ x: 0, y: 0, w: 1.25, h: 1 },
{ x: 0, y: 0, w: 1.75, h: 1 },
]);
});
it("traces the ISO Enter as one clockwise loop of six corners", () => {
const loops = keyOutline(isoEnter);
expect(loops).toHaveLength(1);
expect(asSet(loops[0]!)).toEqual(
asSet([
{ x: -0.25, y: 0 },
{ x: 1.25, y: 0 },
{ x: 1.25, y: 2 },
{ x: 0, y: 2 },
{ x: 0, y: 1 },
{ x: -0.25, y: 1 },
]),
);
// Shoelace area is positive for a clockwise loop on a y-down screen.
const loop = loops[0]!;
const area = loop.reduce((sum, p, i) => {
const q = loop[(i + 1) % loop.length]!;
return sum + (p.x * q.y - q.x * p.y) / 2;
}, 0);
expect(area).toBeCloseTo(1.25 * 2 + 0.25);
});
it("traces a rectangle-in-rectangle as the outer rectangle", () => {
expect(keyOutline({ w: 1, h: 1, w2: 2 }).map(asSet)).toEqual([
asSet([
{ x: 0, y: 0 },
{ x: 2, y: 0 },
{ x: 2, y: 1 },
{ x: 0, y: 1 },
]),
]);
});
it("puts the bounding box's top-left where the key is placed", () => {
expect(keyOrigin(isoEnter)).toEqual({ x: -0.25, y: 0 });
expect(keyOrigin({ w: 1, h: 1 })).toEqual({ x: 0, y: 0 });
});
it("draws plain keys as rects and irregular ones as a path", () => {
expect(keyPath({ w: 1, h: 1 })).toBeUndefined();
expect(keyPath(isoEnter)).toMatch(/^M .* Z$/);
});
});