import type { PlateResult } from "@plate-dyn/core"; export interface ModeTableProps { result: PlateResult; selected: number | null; onSelect: (k: number) => void; } /** * One row per mode: frequency, quality, the shape at the strike point (what * the hammer can put into it), at the hole (what the thread's dashpot takes * out) and its volume participation (what radiates). Click a row to see the * shape. */ export function ModeTable({ result, selected, onSelect }: ModeTableProps) { const m = result.modes; const hitPeak = Math.max(...Array.from(m.hit).map(Math.abs), 1e-300); return (
Modes
{Array.from(m.freq).map((f, k) => { const rigid = !m.audible[k]; const rel = Math.abs(m.hit[k]) / hitPeak; return ( onSelect(k)} style={{ cursor: "pointer", opacity: rigid ? 0.6 : 1 }} title="Show this mode's shape" > ); })}
# f [Hz] Q at the strike at the hole radiation excited
{k + 1} {rigid ? `${f.toFixed(2)} (rigid)` : f.toFixed(1)} {rigid ? "–" : m.q[k].toFixed(0)} {fmt(m.hit[k])} {fmt(m.support[k])} {rigid ? "–" : fmt(m.radiation[k])}
Shapes are mass-normalized (units of 1/√kg), so "at the strike" times the impulse is the modal momentum the hammer delivers; "radiation" is the integral of the shape over the plate (m²/√kg), zero for antisymmetric modes, which a microphone on the normal cannot hear. {result.info.n_rigid > 0 ? " The rigid-body modes take part in the collision and are left out of the sound." : ""}
); } function fmt(x: number): string { if (!Number.isFinite(x)) return "–"; const a = Math.abs(x); if (a === 0) return "0"; if (a < 1e-3 || a >= 1e4) return x.toExponential(2); return x.toPrecision(3); }