export interface VisualLayout { name: string; col: VisualLayoutRow[]; } interface Positionable { offset: [number, number]; rotate: number; } export interface VisualLayoutRow extends Positionable { row: Array; } export interface VisualLayoutKey extends Positionable { key: number; size?: [number, number]; } export interface VisualLayoutSwitch extends Positionable { switch: { n: number; e: number; w: number; s: number; d: number; }; } export interface CompiledLayout { name: string; size: [number, number]; keys: CompiledLayoutKey[]; } export interface CompiledLayoutKey { id: number; shape: "quarter-circle" | "square"; cornerRadius: number; size: [number, number]; pos: [number, number]; rotate: number; } export function compileLayout(layout: VisualLayout): CompiledLayout { const compiled: CompiledLayout = { name: layout.name, size: [0, 0], keys: [], }; let y = 0; for (const { row, offset } of layout.col) { let x = offset?.[0] ?? 0; y += offset?.[1] ?? 0; let maxHeight = 0; for (const info of row) { const [ox, oy] = info.offset || [0, 0]; const rotate = info.rotate || 0; if ("key" in info) { const [width, height] = info.size ?? [1, 1]; compiled.keys.push({ id: info.key, shape: "square", size: [width, height], pos: [x + ox, y + oy], cornerRadius: 0.1, rotate, }); x += width + ox; maxHeight = Math.max(maxHeight, height + oy); } else if ("switch" in info) { const cx = x + ox + 1; const cy = y + oy + 1; for (const [i, id] of [ info.switch.s, info.switch.w, info.switch.n, info.switch.e, ].entries()) { compiled.keys.push({ id, shape: "quarter-circle", cornerRadius: 0, size: [2, 0.6], pos: [cx, cy], rotate: 90 * i + 45, }); } compiled.keys.push({ id: info.switch.d, shape: "square", cornerRadius: 0.5, size: [0.8, 0.8], pos: [x + 0.6 + ox, y + 0.6 + oy], rotate: 0, }); x += 2 + ox; maxHeight = Math.max(maxHeight, 2 + oy); } } y += maxHeight; compiled.size[0] = Math.max(compiled.size[0], x); } compiled.size[1] = y; return compiled; }