import { getPenControlPattern, PEN_CONTROL_PATTERN_IDS, } from '../data/penControlPatterns'; import type { PenControlCellStyle } from '../shared/penControlDrawUtils'; export const PEN_CONTROL_MODE = 'pen-control-mix' as const; export type PenControlMode = typeof PEN_CONTROL_MODE; export const ROWS_PER_PATTERN = 2; export const MIN_PATTERNS = 0; export const MAX_PATTERNS = 5; export const DEFAULT_PATTERN_COUNT = 5; export interface PenControlRowSpec { role: 'pair-first' | 'pair-second'; cells: PenControlCellStyle[]; } export interface PenControlPatternBlock { patternId: string; patternName: string; rows: [PenControlRowSpec, PenControlRowSpec]; } export interface PenControlSheetLayout { maxRow: number; maxCol: number; cellSize: number; startX: number; startY: number; colGap: number; rowGap: number; } export interface PenControlSheetData { mode: PenControlMode; patternCount: number; blocks: PenControlPatternBlock[]; layout: PenControlSheetLayout; } export function clampPatternCount( requested: number, maxRow: number, ): number { const capByRows = Math.floor(maxRow / ROWS_PER_PATTERN); const capped = Math.min( MAX_PATTERNS, Math.max(MIN_PATTERNS, requested), Math.max(MIN_PATTERNS, capByRows), ); return capped; } function buildRowCells( maxCol: number, _role: 'pair-first' | 'pair-second', ): PenControlCellStyle[] { const cells: PenControlCellStyle[] = []; for (let c = 0; c < maxCol; c++) { cells.push(c === 0 ? 'reference' : 'guide'); } return cells; } function normalizePatternIds( patternIds: string[], patternCount: number, ): string[] { const valid = patternIds.filter((id) => PEN_CONTROL_PATTERN_IDS.includes(id), ); const unique: string[] = []; for (const id of valid) { if (!unique.includes(id)) unique.push(id); } if (unique.length >= patternCount) { return unique.slice(0, patternCount); } const rest = PEN_CONTROL_PATTERN_IDS.filter((id) => !unique.includes(id)); const shuffled = [...rest].sort(() => Math.random() - 0.5); while (unique.length < patternCount && shuffled.length > 0) { unique.push(shuffled.shift()!); } return unique.slice(0, patternCount); } export function generatePenControlMix(options: { patternIds: string[]; patternCount: number; layout: PenControlSheetLayout; }): PenControlSheetData { const { layout } = options; const patternCount = clampPatternCount( options.patternCount, layout.maxRow, ); const ids = normalizePatternIds(options.patternIds, patternCount); const blocks: PenControlPatternBlock[] = ids.map((patternId) => { const pattern = getPenControlPattern(patternId); const pairFirst = buildRowCells(layout.maxCol, 'pair-first'); const pairSecond = buildRowCells(layout.maxCol, 'pair-second'); return { patternId, patternName: pattern?.name ?? patternId, rows: [ { role: 'pair-first', cells: pairFirst }, { role: 'pair-second', cells: pairSecond }, ], }; }); return { mode: PEN_CONTROL_MODE, patternCount: blocks.length, blocks, layout, }; }