/** * Regenerates realisticToothAssets.ts from FDI_*.svg files in src/assets/fdi/. * Usage (from frontend/): node scripts/extract-tooth-svgs.mjs */ import fs from 'node:fs'; import path from 'node:path'; const dir = path.resolve('src/assets/fdi'); const files = fs.readdirSync(dir).filter((f) => /^FDI_\d{2}\.svg$/i.test(f)); const out = {}; for (const file of files.sort()) { const id = file.match(/FDI_(\d{2})/i)?.[1]; if (!id) continue; const xml = fs.readFileSync(path.join(dir, file), 'utf8'); const vb = xml.match(/viewBox="([^"]+)"/)?.[1]; const toothG = xml.match(/]*>/)?.[0] ?? ''; const transform = toothG.match(/transform="([^"]+)"/)?.[1]; const rootD = xml.match(/id="root-path"[\s\S]*?d="([^"]+)"/)?.[1]; const crownD = xml.match(/id="crown-path"[\s\S]*?d="([^"]+)"/)?.[1]; if (!vb || !rootD || !crownD) throw new Error(`Failed to parse ${file}`); out[id] = { viewBox: vb, ...(transform ? { groupTransform: transform } : {}), crown: crownD, roots: [rootD], }; console.log(id, transform ? 'mirrored' : 'plain'); } const ts = `import type { FdiToothId } from '@/types/treatment'; /** * Realistic buccal-view assets from src/assets/fdi/FDI_*.svg. * Upper (1x/2x): root-up. Lower (3x/4x): crown-up. * Left chart side (Q1/Q4) includes horizontal flip via groupTransform. */ export interface RealisticToothAsset { viewBox: string; /** Applied inside the svg (e.g. Q1/Q4 mirror baked in source files). */ groupTransform?: string; crown: string; roots: string[]; } export const REALISTIC_TOOTH_ASSETS: Partial> = ${JSON.stringify(out, null, 2)}; export function getRealisticToothAsset(fdi: FdiToothId): RealisticToothAsset | null { return REALISTIC_TOOTH_ASSETS[fdi] ?? null; } `; fs.writeFileSync(path.resolve('src/components/treatment/realisticToothAssets.ts'), ts); console.log('Wrote realisticToothAssets.ts for', Object.keys(out).length, 'teeth');