parry3d/shape/
ball.rs

1use either::Either;
2use na::Unit;
3
4use crate::math::{Isometry, Point, Real, Vector};
5use crate::shape::SupportMap;
6
7/// A Ball shape.
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
9#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
10#[cfg_attr(
11    feature = "rkyv",
12    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
13    archive(check_bytes)
14)]
15#[derive(PartialEq, Debug, Copy, Clone)]
16#[repr(C)]
17pub struct Ball {
18    /// The radius of the ball.
19    pub radius: Real,
20}
21
22impl Ball {
23    /// Creates a new ball with the given radius.
24    #[inline]
25    pub fn new(radius: Real) -> Ball {
26        Ball { radius }
27    }
28
29    /// Computes a scaled version of this ball.
30    ///
31    /// If the scaling factor is non-uniform, then it can’t be represented as
32    /// ball. Instead, a convex polygon approximation (with `nsubdivs`
33    /// subdivisions) is returned. Returns `None` if that approximation had degenerate
34    /// normals (for example if the scaling factor along one axis is zero).
35    #[cfg(all(feature = "dim2", feature = "alloc"))]
36    #[inline]
37    pub fn scaled(
38        self,
39        scale: &Vector<Real>,
40        nsubdivs: u32,
41    ) -> Option<Either<Self, super::ConvexPolygon>> {
42        if scale.x != scale.y {
43            // The scaled shape isn’t a ball.
44            let mut vtx = self.to_polyline(nsubdivs);
45            vtx.iter_mut()
46                .for_each(|pt| pt.coords = pt.coords.component_mul(scale));
47            Some(Either::Right(super::ConvexPolygon::from_convex_polyline(
48                vtx,
49            )?))
50        } else {
51            let uniform_scale = scale.x;
52            Some(Either::Left(Self::new(self.radius * uniform_scale.abs())))
53        }
54    }
55
56    /// Computes a scaled version of this ball.
57    ///
58    /// If the scaling factor is non-uniform, then it can’t be represented as
59    /// ball. Instead, a convex polygon approximation (with `nsubdivs`
60    /// subdivisions) is returned. Returns `None` if that approximation had degenerate
61    /// normals (for example if the scaling factor along one axis is zero).
62    #[cfg(all(feature = "dim3", feature = "alloc"))]
63    #[inline]
64    pub fn scaled(
65        self,
66        scale: &Vector<Real>,
67        nsubdivs: u32,
68    ) -> Option<Either<Self, super::ConvexPolyhedron>> {
69        if scale.x != scale.y || scale.x != scale.z || scale.y != scale.z {
70            // The scaled shape isn’t a ball.
71            let (mut vtx, idx) = self.to_trimesh(nsubdivs, nsubdivs);
72            vtx.iter_mut()
73                .for_each(|pt| pt.coords = pt.coords.component_mul(scale));
74            Some(Either::Right(super::ConvexPolyhedron::from_convex_mesh(
75                vtx, &idx,
76            )?))
77        } else {
78            let uniform_scale = scale.x;
79            Some(Either::Left(Self::new(self.radius * uniform_scale.abs())))
80        }
81    }
82}
83
84impl SupportMap for Ball {
85    #[inline]
86    fn support_point(&self, m: &Isometry<Real>, dir: &Vector<Real>) -> Point<Real> {
87        self.support_point_toward(m, &Unit::new_normalize(*dir))
88    }
89
90    #[inline]
91    fn support_point_toward(&self, m: &Isometry<Real>, dir: &Unit<Vector<Real>>) -> Point<Real> {
92        Point::from(m.translation.vector) + **dir * self.radius
93    }
94
95    #[inline]
96    fn local_support_point(&self, dir: &Vector<Real>) -> Point<Real> {
97        self.local_support_point_toward(&Unit::new_normalize(*dir))
98    }
99
100    #[inline]
101    fn local_support_point_toward(&self, dir: &Unit<Vector<Real>>) -> Point<Real> {
102        Point::from(**dir * self.radius)
103    }
104}