parry3d/shape/
cone.rs

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