parry3d/shape/
cylinder.rs

1//! Support mapping based Cylinder shape.
2
3use crate::math::{Point, Real, Vector};
4use crate::shape::SupportMap;
5use na;
6use num::Zero;
7
8#[cfg(feature = "alloc")]
9use either::Either;
10
11#[cfg(feature = "rkyv")]
12use rkyv::{bytecheck, CheckBytes};
13
14/// Cylinder shape with its principal axis aligned with the `y` axis.
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
17#[cfg_attr(
18    feature = "rkyv",
19    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
20    archive(as = "Self")
21)]
22#[derive(PartialEq, Debug, Copy, Clone)]
23#[repr(C)]
24pub struct Cylinder {
25    /// The half-height of the cylinder.
26    pub half_height: Real,
27    /// The radius of the cylinder.
28    pub radius: Real,
29}
30
31impl Cylinder {
32    /// Creates a new cylinder.
33    ///
34    /// # Arguments:
35    /// * `half_height` - the half length of the cylinder along the `y` axis.
36    /// * `radius` - the length of the cylinder along all other axis.
37    pub fn new(half_height: Real, radius: Real) -> Cylinder {
38        assert!(half_height.is_sign_positive() && radius.is_sign_positive());
39
40        Cylinder {
41            half_height,
42            radius,
43        }
44    }
45
46    /// Computes a scaled version of this cylinder.
47    ///
48    /// If the scaling factor is non-uniform, then it can’t be represented as
49    /// cylinder. Instead, a convex polyhedral approximation (with `nsubdivs`
50    /// subdivisions) is returned. Returns `None` if that approximation had degenerate
51    /// normals (for example if the scaling factor along one axis is zero).
52    #[cfg(feature = "alloc")]
53    #[inline]
54    pub fn scaled(
55        self,
56        scale: &Vector<Real>,
57        nsubdivs: u32,
58    ) -> Option<Either<Self, super::ConvexPolyhedron>> {
59        if scale.x != scale.z {
60            // The scaled shape isn’t a cylinder.
61            let (mut vtx, idx) = self.to_trimesh(nsubdivs);
62            vtx.iter_mut()
63                .for_each(|pt| pt.coords = pt.coords.component_mul(scale));
64            Some(Either::Right(super::ConvexPolyhedron::from_convex_mesh(
65                vtx, &idx,
66            )?))
67        } else {
68            Some(Either::Left(Self::new(
69                self.half_height * scale.y,
70                self.radius * scale.x,
71            )))
72        }
73    }
74}
75
76impl SupportMap for Cylinder {
77    fn local_support_point(&self, dir: &Vector<Real>) -> Point<Real> {
78        let mut vres = *dir;
79
80        vres[1] = 0.0;
81
82        if vres.normalize_mut().is_zero() {
83            vres = na::zero()
84        } else {
85            vres *= self.radius;
86        }
87
88        vres[1] = self.half_height.copysign(dir[1]);
89
90        Point::from(vres)
91    }
92}