Skip to main content

concinnity_physics/sim/character/
capsule.rs

1// concinnity-physics/src/sim/character/capsule.rs
2//
3// The capsule a character move is resolved against, held by the caller across
4// the fixed ticks rather than rebuilt per move. Here that cache buys nothing --
5// the shape is two floats and building one allocates nothing -- but the shape
6// is the one thing a simulation genuinely owns, so it is the caller that keeps
7// it and a simulation whose shape does cost something needs no other change.
8
9use crate::ColliderShape;
10
11/// A character's collision capsule: a cylinder of `2 * half_height` capped by
12/// hemispheres of `radius`, standing on the y axis.
13#[derive(Debug, Clone, Copy, PartialEq)]
14pub struct CharacterCapsule {
15    half_height: f32,
16    radius: f32,
17}
18
19impl CharacterCapsule {
20    /// Build the capsule for a cylinder of `2 * half_height` capped by
21    /// hemispheres of `radius`.
22    pub fn new(half_height: f32, radius: f32) -> Self {
23        CharacterCapsule {
24            half_height,
25            radius,
26        }
27    }
28
29    /// Adopt new dimensions, for a character whose capsule was re-authored.
30    pub fn resize(&mut self, half_height: f32, radius: f32) {
31        self.half_height = half_height;
32        self.radius = radius;
33    }
34
35    /// Half the cylindrical section's height, excluding the caps.
36    pub fn half_height(&self) -> f32 {
37        self.half_height
38    }
39
40    /// Cap and cylinder radius.
41    pub fn radius(&self) -> f32 {
42        self.radius
43    }
44
45    /// The capsule as a collider shape, which is what a sweep is asked in
46    /// terms of.
47    pub(crate) fn shape(&self) -> ColliderShape {
48        ColliderShape::Capsule {
49            half_height: self.half_height,
50            radius: self.radius,
51        }
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn the_capsule_carries_the_dimensions_it_was_built_from() {
61        let capsule = CharacterCapsule::new(0.6, 0.3);
62        assert_eq!(capsule.half_height(), 0.6);
63        assert_eq!(capsule.radius(), 0.3);
64        assert_eq!(
65            capsule.shape(),
66            ColliderShape::Capsule {
67                half_height: 0.6,
68                radius: 0.3,
69            }
70        );
71    }
72
73    #[test]
74    fn resizing_replaces_both_dimensions() {
75        let mut capsule = CharacterCapsule::new(0.6, 0.3);
76        capsule.resize(0.6, 0.3);
77        assert_eq!(capsule, CharacterCapsule::new(0.6, 0.3));
78        capsule.resize(0.9, 0.4);
79        assert_eq!(capsule.half_height(), 0.9);
80        assert_eq!(capsule.radius(), 0.4);
81    }
82}