ncollide3d/shape/
capsule.rs

1//! Support mapping based Capsule shape.
2
3use na::{self, RealField, Unit};
4
5use crate::math::{Point, Vector};
6use crate::query::{Contact, ContactKinematic, ContactPreprocessor};
7use crate::shape::{FeatureId, Segment, SupportMap};
8
9/// SupportMap description of a capsule shape with its principal axis aligned with the `y` axis.
10#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11#[derive(PartialEq, Debug, Copy, Clone)]
12pub struct Capsule<N> {
13    /// The half-height of the capsule's cylindrical part.
14    pub half_height: N,
15    /// The radius of the capsule.
16    pub radius: N,
17}
18
19impl<N: RealField + Copy> Capsule<N> {
20    /// Creates a new capsule.
21    ///
22    /// # Arguments:
23    /// * `half_height` - the half length of the capsule along the `y` axis.
24    /// * `radius` - radius of the rounded part of the capsule.
25    pub fn new(half_height: N, radius: N) -> Capsule<N> {
26        Capsule {
27            half_height,
28            radius,
29        }
30    }
31
32    /// The capsule half length along its local `y` axis.
33    #[inline]
34    #[deprecated(note = "use the `self.half_height` public field directly.")]
35    pub fn half_height(&self) -> N {
36        self.half_height
37    }
38
39    /// The capsule height along its local `y` axis.
40    #[inline]
41    pub fn height(&self) -> N {
42        self.half_height * na::convert(2.0)
43    }
44
45    /// The radius of the capsule's rounded part.
46    #[inline]
47    #[deprecated(note = "use the `self.radius` public field directly.")]
48    pub fn radius(&self) -> N {
49        self.radius
50    }
51
52    /// The segment that, once dilated by `self.radius` yields this capsule.
53    #[inline]
54    pub fn segment(&self) -> Segment<N> {
55        let mut a = Point::origin();
56        let mut b = Point::origin();
57        a.y = -self.half_height;
58        b.y = self.half_height;
59
60        Segment::new(a, b)
61    }
62
63    /// The contact preprocessor to be used for contact determination with this capsule.
64    #[inline]
65    pub fn contact_preprocessor(&self) -> impl ContactPreprocessor<N> {
66        CapsuleContactPreprocessor {
67            radius: self.radius,
68        }
69    }
70}
71
72impl<N: RealField + Copy> SupportMap<N> for Capsule<N> {
73    #[inline]
74    fn local_support_point(&self, dir: &Vector<N>) -> Point<N> {
75        self.local_support_point_toward(&Unit::new_normalize(*dir))
76    }
77
78    #[inline]
79    fn local_support_point_toward(&self, dir: &Unit<Vector<N>>) -> Point<N> {
80        let mut res: Vector<N> = na::zero();
81        res[1] = self.half_height.copysign(dir[1]);
82        Point::from(res + **dir * self.radius)
83    }
84}
85
86struct CapsuleContactPreprocessor<N: RealField + Copy> {
87    radius: N,
88}
89
90impl<N: RealField + Copy> CapsuleContactPreprocessor<N> {
91    //    pub fn new(radius: N) -> Self {
92    //        CapsuleContactPreprocessor {
93    //            radius
94    //        }
95    //    }
96}
97
98impl<N: RealField + Copy> ContactPreprocessor<N> for CapsuleContactPreprocessor<N> {
99    fn process_contact(
100        &self,
101        c: &mut Contact<N>,
102        kinematic: &mut ContactKinematic<N>,
103        is_first: bool,
104    ) -> bool {
105        // Fix the feature ID.
106        let feature = if is_first {
107            kinematic.feature1()
108        } else {
109            kinematic.feature2()
110        };
111
112        let actual_feature = match feature {
113            FeatureId::Vertex(i) => FeatureId::Face(i),
114            #[cfg(feature = "dim3")]
115            FeatureId::Edge(_) => FeatureId::Face(2),
116            FeatureId::Face(i) => FeatureId::Face(2 + i),
117            FeatureId::Unknown => return false,
118        };
119
120        if is_first {
121            kinematic.set_feature1(actual_feature);
122            kinematic.set_dilation1(self.radius);
123            c.world1 += *c.normal * self.radius;
124            c.depth += self.radius;
125        } else {
126            kinematic.set_feature2(actual_feature);
127            kinematic.set_dilation2(self.radius);
128            c.world2 -= *c.normal * self.radius;
129            c.depth += self.radius;
130        }
131
132        true
133    }
134}