1/**
2 * Everything in SI: metres, seconds, hertz, metres per second.
3 *
4 * The domain is deliberately small. A three-dimensional grid costs the cube of
5 * its resolution, so where the flat sibling can afford 512 points across ten
6 * metres this one gets 128 across two, and the number that decides whether
7 * what is on screen is physics or grid dispersion is the same either way: how
8 * many cells fit in a wavelength. At 128 points across 2 m a 1.5 kHz tone is
9 * about fifteen cells per wavelength, which is comfortable; raise the
10 * frequency and the app says when it stops being so.
11 */
13/** Speed of sound in air at about 20 °C, m/s. */
14export const C_AIR = 343;
16/** Side of the cubic domain, metres. Room-corner sized rather than
17 * hall sized, for the reason above. */
18export const DOMAIN = 2;
20/** Fraction of the domain given to the absorbing layer at each face. */
21export const SPONGE_FRAC = 0.15;
23/** Absorption rate the sponge reaches at the wall, inverse seconds. */
24export const SPONGE_MAX = 9000;
26/** Cells per wavelength below which what is on screen is as much grid
27 * dispersion as it is sound. */
28export const POOR_RESOLUTION = 8;
30/** A length in metres, written the way a person would say it. */
31export const fmtLength = (m: number): string =>
32 Math.abs(m) < 1 ? `${(1000 * m).toPrecision(3)} mm` : `${m.toPrecision(3)} m`;
34/** A duration in seconds, likewise. */
35export const fmtTime = (s: number): string => {
36 const a = Math.abs(s);
37 if (a > 0 && a < 1e-3) return `${(1e6 * s).toPrecision(3)} µs`;
38 if (a < 1) return `${(1e3 * s).toPrecision(3)} ms`;
39 return `${s.toPrecision(3)} s`;
40};