import type { Mount, ProblemSpec } from "../spec/types"; import { formatNumber as n, formatString as s } from "./format"; function mountLine(m: Mount): string { switch (m.type) { case "thread": return `struct('type', 'thread', 'x', ${n(m.x)}, 'y', ${n(m.y)}, 'length', ${n(m.length)}, 'thread', struct('name', ${s(m.thread.name)}, 'diameter', ${n(m.thread.diameter)}, 'G', ${n(m.thread.G)})); % one thread: hole [m], length [m], thread diameter [m] and shear modulus [Pa] for its torsion`; case "threads": return `struct('type', 'threads', 'x1', ${n(m.x1)}, 'y1', ${n(m.y1)}, 'x2', ${n(m.x2)}, 'y2', ${n(m.y2)}, 'length', ${n(m.length)}); % two threads of one length from hooks at one height: the two holes [m]`; case "pads": { const pads = m.pads .map((p) => `struct('x', ${n(p.x)}, 'y', ${n(p.y)}, 'rx', ${n(p.rx)}, 'ry', ${n(p.ry)})`) .join(", "); return `struct('type', 'pads', 'pads', [${pads}], 'k', ${n(m.k)}, 'c', ${n(m.c)}); % pads under a horizontal plate: centre and half-widths [m]; stiffness [N/m^3] and damping [N s/m^3] per area, one-sided`; } case "cymbal": return `struct('type', 'cymbal', 'r_rod', ${n(m.r_rod)}, 'r_felt', ${n(m.r_felt)}, 'k', ${n(m.k)}, 'c', ${n(m.c)}, 'preload', ${n(m.preload)}); % stand: rod radius, felt radius [m], felt stiffness [N/m^3] and damping [N s/m^3], preload compression [m]`; case "vise": return `struct('type', 'vise', 'edge', ${s(m.edge)}, 'depth', ${n(m.depth)}, 'tilt', ${n(m.tilt)}); % one edge in a vise: which edge, jaw depth [m], tilt [deg] (0 hangs down, 90 horizontal, 180 stands up)`; case "pinched": return `struct('type', 'pinched', 'x', ${n(m.x)}, 'y', ${n(m.y)}, 'radius', ${n(m.radius)}, 'k', ${n(m.k)}, 'c', ${n(m.c)}, 'tilt', ${n(m.tilt)}); % between finger and thumb: patch centre and radius [m], grip stiffness [N/m] and damping [N s/m], tilt [deg]`; } } /** * Render the specification as the MATLAB `params` struct. The controls edit the * spec; this block is what the solver actually reads, so the two cannot drift. */ export function generateParamsBlock(spec: ProblemSpec): string { const e = spec.excitation; const excitation = e.type === "impulse" ? `struct('type', 'impulse', 'x', ${n(e.x)}, 'y', ${n(e.y)}, 'force', ${n(e.force)}, 'duration', ${n(e.duration)}, 'radius', ${n(e.radius)}); % half-sine hammer pulse: strike point [m], peak force [N], contact time [s], footprint radius [m]` : `struct('type', 'release', 'x', ${n(e.x)}, 'y', ${n(e.y)}, 'displacement', ${n(e.displacement)}, 'radius', ${n(e.radius)}); % pull aside and let go: pull point [m], displacement there at release [m], footprint radius [m]`; const lines = [ "% Problem specification, generated from the controls. SI units: m, s, N, Pa, kg/m^3.", "% Edit anything here or in the library files; the controls regenerate only this block.", "params = struct();", `params.plate = struct('shape', ${s(spec.plate.shape)}, 'Lx', ${n(spec.plate.Lx)}, 'Ly', ${n(spec.plate.Ly)}, 'h', ${n(spec.plate.h)}); % 'rectangle' or 'ellipse' in the box Lx x Ly, thickness h [m]`, `params.material = struct('name', ${s(spec.material.name)}, 'E', ${n(spec.material.E)}, 'nu', ${n(spec.material.nu)}, 'rho', ${n(spec.material.rho)}); % Young's modulus [Pa], Poisson's ratio, density [kg/m^3]`, `params.gravity = struct('g', ${n(spec.gravity.g)}); % [m/s^2]; 0 switches gravity off`, `params.mount = ${mountLine(spec.mount)}`, `params.damping = struct('sigma0', ${n(spec.damping.sigma0)}, 'sigma1', ${n(spec.damping.sigma1)}); % loss rates: frequency-independent [1/s], frequency-dependent [m^2/s]`, `params.excitation = ${excitation}`, `params.probe = struct('x', ${n(spec.probe.x)}, 'y', ${n(spec.probe.y)}); % where the time series are recorded [m]`, `params.listener = struct('x', ${n(spec.listener.x)}, 'y', ${n(spec.listener.y)}, 'z', ${n(spec.listener.z)}, 'c', ${n(spec.listener.c)}, 'rho_air', ${n(spec.listener.rho_air)}, 'model', ${s(spec.listener.model)}); % microphone in plate coordinates [m], sound speed [m/s], air density [kg/m^3], 'baffled' or 'unbaffled'`, `params.output = struct('fs', ${n(spec.output.fs)}, 'nframes', ${n(spec.output.nframes)}); % sample rate of the series and the audio [Hz], animation frames`, `params.fd = struct('nx', ${n(spec.fd.nx)}, 'ny', ${n(spec.fd.ny)}, 'cfl', ${n(spec.fd.cfl)}, 'duration', ${n(spec.fd.duration)}); % grid nodes, fraction of the stable time step (> 1 blows up), simulated time [s]`, ]; return lines.join("\n"); }