1// The bead system as the solver sees it. Indices are 0-based here; the
2// script uses 1-based indices and parse.ts converts them.
4export type Method = "verlet" | "yoshida4";
5export type ListenQuantity = "acceleration" | "velocity" | "displacement";
7export interface Hit {
8 t: number; // start time (s)
9 beads: Int32Array;
10 // Impulse: velocity added to each bead at time t (m/s). Used when duration == 0.
11 velocity: [number, number, number];
12 // Force pulse: raised-cosine force on each bead, peak `force` (N), lasting `duration` (s).
13 force: [number, number, number];
14 duration: number;
15}
17export interface BeadSystem {
18 n: number;
19 pos0: Float64Array; // 3n, interleaved x,y,z (m)
20 mass: Float64Array; // n (kg)
21 pinned: Uint8Array; // n, 1 = held fixed
22 // Springs: f = k e + k3 e^3, e = L - L0
23 si: Int32Array;
24 sj: Int32Array;
25 sk: Float64Array;
26 sL0: Float64Array;
27 sk3: Float64Array;
28 // Bending triplets (j the middle bead): energy kb/2 |x_i - 2 x_j + x_k|^2,
29 // with damping force -cb (v_i - 2 v_j + v_k) (1, -2, 1) on (i, j, k)
30 bi: Int32Array;
31 bj: Int32Array;
32 bk: Int32Array;
33 bkb: Float64Array;
34 bcb: Float64Array;
35 // Dashpots on relative velocity: F_i = c (v_j - v_i)
36 di: Int32Array;
37 dj: Int32Array;
38 dc: Float64Array;
39 friction: Float64Array; // n, F_i = -c0_i v_i (N s/m)
40 hits: Hit[];
41 listenBeads: Int32Array;
42 listenDir: [number, number, number]; // unit vector
43 listenQuantity: ListenQuantity;
44 duration: number; // s
45 sampleRate: number; // Hz, audio output
46 dt: number; // s; 0 = automatic
47 safety: number; // fraction of the stability limit used when dt is automatic
48 method: Method;
49 frameRate: number; // Hz, snapshots for animation and mode energies
50 modes: boolean; // compute linear transverse modes and track their energies
51}
53export interface SimProgress {
54 t: number;
55 duration: number;
56}
58export interface Modes {
59 freqs: Float64Array; // Hz, ascending
60 // psi: orthonormal eigenvectors of M^{-1/2} K M^{-1/2} over free beads,
61 // column-major (nFree x nModes)
62 psi: Float64Array;
63 free: Int32Array; // bead index of each row of psi
64}
66export interface SimResult {
67 audio: Float32Array; // listener displacement at sampleRate (m)
68 sampleRate: number;
69 dt: number;
70 substeps: number; // solver steps per audio sample
71 omegaMax: number; // rad/s, highest linearized frequency
72 frameTimes: Float64Array; // times of the energy records (s)
73 energy: Float64Array; // mechanical energy above the rest state at each frame (J)
74 animTimes: Float64Array; // times of the stored displacement snapshots (s)
75 animFrames: Float32Array[]; // displacement (3n) at each snapshot (m)
76 dissipated: Float64Array; // cumulative energy removed by damping (J)
77 // mode energies (nModes per frame, frame-major), present when modes were computed
78 modeEnergy: Float32Array | null;
79 residualForce: number; // max |F| on free beads at rest (N)
80 maxTension: number; // max spring force at rest (N)
81 energyDrift: number; // max relative change in E + dissipated after the last excitation
82 freqErrors: { hz: number; cents: number }[]; // integrator frequency error at a few frequencies
83 elapsedMs: number;
84 aborted: string | null; // reason if the run stopped early
85 warnings: string[];
86}