1// Generate the gallery's pulseq example sources from a demoSeq batch-test run.
2// Reads scripts/demo-results.json (written by test-demos.mjs), and for every
3// demo that ran cleanly AND has a CATALOG entry that isn't already curated,
4// writes an adapted .m into src/examples/pulseq/<id>.m. Also writes
5// src/examples/generated-timings.ts with a rough run-time label per example.
6//
7// node scripts/test-demos.mjs && node scripts/gen-examples.mjs
8import fs from 'node:fs'
9import path from 'node:path'
10import { fileURLToPath } from 'node:url'
11import { adaptDemo } from './test-demos.mjs'
12import { CATALOG } from '../src/examples/catalog.ts'
14const here = path.dirname(fileURLToPath(import.meta.url))
15const repoRoot = path.join(here, '..')
16const demoDir = process.env.PULSEQ_DEMO_DIR ?? path.resolve(repoRoot, '../../pulseq/matlab/demoSeq')
17const outDir = path.join(repoRoot, 'src', 'examples', 'pulseq')
19// Sequences hand-curated in src/examples/*.m — never auto-generate these.
20const CURATED = new Set(['fid', 'epi', 'gre'])
22function demoToId(file) {
23 return file.replace(/^write_?/, '').replace(/\.m$/, '')
24}
26function approxLabel(ms) {
27 const s = ms / 1000
28 if (s < 1.5) return '~1 s'
29 if (s < 3) return '~2 s'
30 if (s < 8) return `~${Math.round(s)} s`
31 if (s < 45) return `~${Math.round(s / 5) * 5} s`
32 return '~1 min+'
33}
35const results = JSON.parse(fs.readFileSync(path.join(here, 'demo-results.json'), 'utf8'))
37fs.rmSync(outDir, { recursive: true, force: true })
38fs.mkdirSync(outDir, { recursive: true })
40// Timings for the curated examples (measured separately; gre has autoLabel off)
41const timings = { fid: '~1 s', epi: '~2 s', gre: '~5 s' }
42const written = []
44for (const r of results) {
45 const ok = r.ok && r.seqFiles?.length > 0 && r.seqFiles.every((s) => s.parseOk)
46 if (!ok) continue
47 const id = demoToId(r.demo)
48 if (CURATED.has(id) || !(id in CATALOG)) continue
49 const src = adaptDemo(fs.readFileSync(path.join(demoDir, r.demo), 'utf8'))
50 fs.writeFileSync(path.join(outDir, `${id}.m`), src)
51 timings[id] = approxLabel(r.elapsedMs)
52 written.push(id)
53}
55const tsBody =
56 '// AUTO-GENERATED by scripts/gen-examples.mjs — do not edit.\n' +
57 '// Rough in-browser run-time labels per example id.\n' +
58 'export const TIMINGS: Record<string, string> = ' +
59 JSON.stringify(timings, null, 2) +
60 '\n'
61fs.writeFileSync(path.join(repoRoot, 'src', 'examples', 'generated-timings.ts'), tsBody)
63console.log(`generated ${written.length} example sources:`, written.join(', '))
64console.log('timings ->', JSON.stringify(timings))