/** * The scene: its MATLAB source, and the parameters the host offers it. * * Same split as the model (src/mgpu/registry.ts): what the body *is* lives in * the .m, and the sliders around it live here. A scene also gets `x`, `y`, * `z` (metres), `Lx`, `Ly`, `Lz`, `h` (metres), `c0` (m/s), `Ls` (the * string's length, metres) and the grid's counts for free — see * src/scene/scene.ts. */ import type { ParamSpec, Params } from '../mgpu/registry.ts'; import boxSource from '../../scenes/box.m?raw'; export interface MScene { key: string; label: string; blurb: string; params: ParamSpec[]; /** The string's height above the top plate, from this scene's parameters — * the host needs it to draw the string and place defaults, and the scene * needs it to build the line profile, so the scene's `gap` parameter is * the one shared source. */ stringZ: (params: Params) => number; source: string; } export const boxScene: MScene = { key: 'box', label: 'Box', blurb: 'A rigid box with a round sound hole in its top plate, under the string.', params: [ { key: 'body', label: 'body (0 = removed)', value: 1, min: 0, max: 1, step: 1, hint: 'Set to 0 to take the box away entirely and hear the bare string in open air. The bridge patch stays, so the bridge-drive route still sounds.', }, { key: 'boxl', label: 'body length (m)', value: 0.66, min: 0.3, max: 0.9, step: 0.01, hint: 'Along the string. The default just outreaches the string, so the bridge end sits over the body.', }, { key: 'boxw', label: 'body width (m)', value: 0.16, min: 0.08, max: 0.3, step: 0.005 }, { key: 'boxd', label: 'body depth (m)', value: 0.08, min: 0.03, max: 0.2, step: 0.005, hint: 'How far the box hangs below its top plate. Deeper means a bigger cavity and a lower air resonance.', }, { key: 'thick', label: 'wall thickness (m)', value: 0.016, min: 0.008, max: 0.04, step: 0.002, hint: 'Below about two cells the plates start to leak — the grid cannot hold a thinner wall closed.', }, { key: 'holer', label: 'sound hole radius (m)', value: 0.028, min: 0, max: 0.08, step: 0.002, hint: 'The opening in the top plate, toward the string. At 0 the box is sealed; the hole’s size against the cavity’s volume is what sets the air resonance (the Helmholtz pitch).', }, { key: 'holex', label: 'sound hole x (m)', value: 0, min: -0.3, max: 0.3, step: 0.01, }, { key: 'gap', label: 'string height (m)', value: 0.02, min: 0.01, max: 0.06, step: 0.002, hint: 'How far above the top plate the string runs.', }, { key: 'patchr', label: 'bridge patch radius (m)', value: 0.05, min: 0.02, max: 0.12, step: 0.005, hint: 'Size of the top-plate region the bridge force drives (the stand-in for the moving plate).', }, { key: 'absorb', label: 'wall absorption (1/s)', value: 800, min: 0, max: 2500, step: 25, hint: 'A thin lossy skin on the shell’s surfaces. At 0 the rigid cavity rings almost forever; turn it up until the box stops sounding metallic.', }, ], stringZ: (params) => params.gap ?? 0.02, source: boxSource, }; export const mScenes: MScene[] = [boxScene]; export const defaultSceneParams = (s: MScene): Params => Object.fromEntries(s.params.map((p) => [p.key, p.value]));