// Generate the two built-in digital phantoms as genuine KomaMRI `.phantom` // (HDF5) files: a small cube and a small sphere, ~10 mm, densely and uniformly // sampled with spins. Structure mirrors ../KomaMRI.jl write_phantom: // root attrs: Version, Name, Ns, Dims // group "position": x, y, z (metres) // group "contrast": ρ, T1, T2, T2s, Δw (s, s, s, rad/s) // // Run: npm run gen-phantoms (writes src/phantom/data/{cube,sphere}.phantom) import * as h5 from 'h5wasm/node' import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const OUT_DIR = path.resolve(__dirname, '../src/phantom/data') // Geometry (metres) and uniform spin spacing. const SIZE_MM = 10 // cube side / sphere diameter const SPACING_MM = 0.8 // uniform grid spacing const MM = 1e-3 // Uniform tissue properties (a single "water-like" material for the whole object). const RHO = 1.0 const T1 = 1.0 // s const T2 = 0.1 // s const T2S = 0.05 // s const DW = 0.0 // rad/s /** Build a centred uniform grid of points, keeping those for which keep(x,y,z) (metres). */ function buildGrid(sizeMm, spacingMm, keep) { const n = Math.floor(sizeMm / spacingMm) + 1 // points per axis const span = (n - 1) * spacingMm // actual extent covered const start = -span / 2 // centre on origin const xs = [], ys = [], zs = [] for (let i = 0; i < n; i++) { const x = (start + i * spacingMm) * MM for (let j = 0; j < n; j++) { const y = (start + j * spacingMm) * MM for (let k = 0; k < n; k++) { const z = (start + k * spacingMm) * MM if (keep(x, y, z)) { xs.push(x) ys.push(y) zs.push(z) } } } } return { x: Float32Array.from(xs), y: Float32Array.from(ys), z: Float32Array.from(zs), } } function writePhantom(name, grid) { const ns = grid.x.length const fill = (v) => { const a = new Float32Array(ns) a.fill(v) return a } // h5wasm/node writes to the real working directory, so use a temp name and // unlink it after reading the bytes back out. const filename = `.gen-${name}.phantom.tmp` const f = new h5.File(filename, 'w') f.create_attribute('Version', '1.0.0') f.create_attribute('Name', name) f.create_attribute('Ns', ns, [], ' ${path.relative(process.cwd(), outPath)}`) } async function main() { await h5.ready fs.mkdirSync(OUT_DIR, { recursive: true }) const r = (SIZE_MM / 2) * MM console.log('Generating phantoms (size %d mm, spacing %d mm):', SIZE_MM, SPACING_MM) writePhantom( 'cube', buildGrid(SIZE_MM, SPACING_MM, () => true), ) writePhantom( 'sphere', buildGrid(SIZE_MM, SPACING_MM, (x, y, z) => x * x + y * y + z * z <= r * r + 1e-18), ) console.log('Done.') } main()