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
| # |
f [Hz] |
Q |
at the strike |
at the hole |
radiation |
excited |
{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"
>
| {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);
}