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