// Generic bead-spring system in 3D. Instruments build one of these; the solver // integrates it. SI units throughout. export interface Anchor { pos: [number, number, number]; // -1: fixed point. Otherwise a massless ring on a rod: x fixed, y and z // follow this bead, so the spring to it stays parallel to x (no transverse force). slideBead: number; } export interface BeadSystem { n: number; // number of beads rest: Float64Array; // 3n equilibrium positions mass: Float64Array; // n pinned: Uint8Array; // n, 1 = held fixed at rest position anchors: Anchor[]; // node index n + a // springs: endpoints are node indices (bead < n, anchor >= n) si: Int32Array; sj: Int32Array; sk: Float64Array; // stiffness (N/m) sL0: Float64Array; // rest length (m) sc: Float64Array; // dashpot on relative velocity, all 3 components (N s/m) sigma0: number; // mass-proportional damping: force -2 sigma0 m v bending?: Bending; } // Plate bending (Poisson ratio built into M). Hinge e measures the dihedral // angle across edge (e0, e1) shared by triangles (e0, e1, w0) and (e1, e0, w1); // phi_e = sign_e (theta_e - theta0_e) is its change, positive when the surface // folds toward its upward normal. Triangle t holds up to three hinges // (-1 = free edge) and has energy 1/2 phi^T M phi; M is the plate energy // A D/2 [(tr S)^2 - 2 (1 - nu) det S] of the shape operator // S = sum_e phi_e l_e / (2A) m_e m_e^T, condensed over free edges (no moment there). // Damping: the moments use phi + 2 sigma1 dphi/dt (Rayleigh, sigma1 omega^2). export interface Bending { w0: Int32Array; w1: Int32Array; e0: Int32Array; e1: Int32Array; sign: Float64Array; theta0: Float64Array; tri: Int32Array; // 3 per triangle M: Float64Array; // 9 per triangle sigma1: number; } export interface Excitation { u0: Float64Array; // 3n initial displacement from rest v0: Float64Array; // 3n initial velocity // External force F(t) = shape * F0 * 0.5 (1 - cos(2 pi t / tau)), 0 <= t <= tau force?: { shape: Float64Array; F0: number; tau: number }; // A mallet or stick: point mass q in 3D touching a patch of beads. // Normal force K delta^p with delta = (q - sum_i w_i (x_i - rest_i)) . normal; // Coulomb friction mu * normal force against tangential slip. // Starts at q = 0 (just touching the undeformed patch) with velocity vel0. mallet?: Mallet; } export interface Mallet { weights: Float64Array; // n, sums to 1 (contact patch) normal: [number, number, number]; // unit, pointing into the surface vel0: [number, number, number]; // m/s; its component along normal must be > 0 mass: number; // kg K: number; // N / m^p p: number; mu: number; // friction coefficient (0 = frictionless) // no contact once the mallet has slid this far sideways from the patch (m); default unlimited reach?: number; } export interface SimOptions { duration: number; // s fs: number; // audio sample rate (Hz) cfl: number; // fraction of the leapfrog stability limit 2/omega_max frameRate: number; // displacement frames per second pickup: Float64Array; // 3n weights; audio = sum w . acceleration // optional per-bead delay in samples (a microphone): bead i's term arrives delay[i] later pickupDelay?: Float64Array; } export interface SimResult { audio: Float32Array; // pickup acceleration (m/s^2) at fs fs: number; frames: Float32Array; // nFrames x 3n displacement from rest frameTimes: Float64Array; energy: Float64Array; // mechanical energy above equilibrium at frame times (J) balance: Float64Array; // E + dissipated - work_in - E0 at frame times (J) dt: number; stepsPerSample: number; omegaMax: number; wallSeconds: number; maxStretch: number; // largest |l - L0| / L0 of bead-to-bead springs, at frame times contactForce?: Float32Array; // mallet contact force at audio rate (N) malletPos?: Float32Array; // mallet position q (3 per frame) at frame times (m) }