Files
Beyond-KLE/mock/src/ui/phy/viewport.ts
T
ewdernaandClaude Opus 5.5 4d1eb23cf8 mock: PHY Editorを作り直す(T-29)
provisional/phy-editor-feedback.md の合意事項と phy-editor-keybindings.md の割り当て案に沿って、PHY 画面を一から書き直した。
App.vue は共通のシェル(入出力・履歴・タブ)だけにし、PHY は src/ui/phy/ に、コンポーネント・composable・純粋モジュール(logic.ts, viewport.ts)を分けて置いた。

- 画面確認で見つかった不具合(読み込み時の全体表示、Shift+クリック、削除の確認の大きさ、Alt+↑、フォーカス中の二重描画、長押し中の選択など)を直し、キャンバス上の実際の操作で確かめる e2e を足した
- 階層パネルでクラスターの子を余分な折りたたみで包まず、件数を配下のキー・部品の数にそろえた
- 実装は Codex(Luna)。検証: vue-tsc 0件、Vitest 64件、Playwright 30件、prettier --check

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VbfgHT848s3JbuFw8MKFF8
2026-10-07 20:54:37 +09:00

47 lines
1.3 KiB
TypeScript

export type Viewport = { zoom: number; pan: { x: number; y: number } };
export type ViewBounds = { x: number; y: number; width: number; height: number };
/** 境界を余白付きで中央へ収める。座標・倍率だけを扱う純粋関数。 */
export function fitViewport(
bounds: ViewBounds,
width: number,
height: number,
padding = 40,
minZoom = 8,
maxZoom = 160,
): Viewport {
const zoom = Math.max(
minZoom,
Math.min(
maxZoom,
Math.min(
(width - 2 * padding) / Math.max(bounds.width, 1),
(height - 2 * padding) / Math.max(bounds.height, 1),
),
),
);
return {
zoom,
pan: {
x: (width - (2 * bounds.x + bounds.width) * zoom) / 2,
y: (height - (2 * bounds.y + bounds.height) * zoom) / 2,
},
};
}
/** 指定した画面座標の下にある点を保ったまま倍率を変える。 */
export function zoomAround(
point: { x: number; y: number },
viewport: Viewport,
factor: number,
minZoom = 5,
maxZoom = 200,
): Viewport {
const zoom = Math.max(minZoom, Math.min(maxZoom, viewport.zoom * factor));
const world = {
x: (point.x - viewport.pan.x) / viewport.zoom,
y: (point.y - viewport.pan.y) / viewport.zoom,
};
return { zoom, pan: { x: point.x - world.x * zoom, y: point.y - world.y * zoom } };
}