Skip to main content

sva_samples/physics/
hammer.rs

1// Concern: one Hertzian felt hammer sub-stepped against its anvils | Non-concern: what an anvil is (each model's grid) | IO: (displacements) -> forces
2
3const SUBSTEPS: usize = 16;
4
5#[derive(Clone)]
6pub struct Hammer {
7    pos: f64,
8    prev: f64,
9    mass: f64,
10    k: f64,
11    anvil_k: Vec<f64>,
12    p: f64,
13}
14
15impl Hammer {
16    /// Seeded at the substep timescale, so the hammer carries `vel` at t=0.
17    pub fn new(mass: f64, k: f64, p: f64, vel: f64, dt: f64) -> Hammer {
18        let m_dt = dt / SUBSTEPS as f64;
19        Hammer {
20            pos: vel * m_dt * 0.5,
21            prev: -vel * m_dt * 0.5,
22            mass,
23            k,
24            anvil_k: Vec::new(),
25            p,
26        }
27    }
28
29    /// Anvil `i` meets the felt at `k * ratios[i]`.
30    pub fn with_anvil_ratios(mut self, ratios: &[f64]) -> Hammer {
31        self.anvil_k = ratios.iter().map(|r| self.k * r).collect();
32        self
33    }
34
35    pub fn pos(&self) -> f64 {
36        self.pos
37    }
38
39    /// `detached` is sticky per anvil.
40    pub fn substeps(&mut self, dt: f64, anvils: &[f64], detached: &mut [bool], forces: &mut [f64]) {
41        let m_dt = dt / SUBSTEPS as f64;
42        let (mut hp, mut hpv) = (self.pos, self.prev);
43        forces.fill(0.0);
44        for _ in 0..SUBSTEPS {
45            let mut total_force = 0.0;
46            for i in 0..anvils.len() {
47                let compression = hp - anvils[i];
48                let force = if !detached[i] && compression > 0.0 {
49                    self.anvil_k.get(i).copied().unwrap_or(self.k) * compression.powf(self.p)
50                } else {
51                    detached[i] = true;
52                    0.0
53                };
54                forces[i] += force;
55                total_force += force;
56            }
57            let next = 2.0 * hp - hpv - (m_dt * m_dt / self.mass) * total_force;
58            hpv = hp;
59            hp = next;
60        }
61        self.pos = hp;
62        self.prev = hpv;
63        for f in forces.iter_mut() {
64            *f /= SUBSTEPS as f64;
65        }
66    }
67}