hoomd_geometry/shape/sphero.rs
1// Copyright (c) 2024-2026 The Regents of the University of Michigan.
2// Part of hoomd-rs, released under the BSD 3-Clause License.
3
4//! Implement [`Sphero`]
5
6use serde::{Deserialize, Serialize};
7
8use crate::{BoundingSphereRadius, SupportMapping};
9use hoomd_utility::valid::PositiveReal;
10use hoomd_vector::InnerProduct;
11
12/// Round a shape with a given radius.
13///
14/// [`Sphero`] modifies a given shape by sweeping it with a hypersphere of the
15/// given radius. The resulting [`Sphero<S>`] type is a shape itself. If `S`
16/// implements [`crate::SupportMapping`], then [`Sphero<S>`] can be used in
17/// [`IntersectsAt`](crate::IntersectsAt) tests with other convex shapes. See
18/// the full list of implementations below to see what other traits [`Sphero<S>`]
19/// implements for a given `S`.
20///
21/// # Example
22///
23/// Test if a circle overlaps with a rounded rectangle:
24/// ```
25/// use hoomd_geometry::{
26/// Convex, IntersectsAt,
27/// shape::{Circle, Rectangle, Sphero},
28/// };
29/// use hoomd_vector::{Angle, Cartesian};
30/// use std::f64::consts::PI;
31///
32/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
33/// let circle = Convex(Circle {
34/// radius: 0.5.try_into()?,
35/// });
36/// let rectangle = Rectangle {
37/// edge_lengths: [3.0.try_into()?, 2.0.try_into()?],
38/// };
39/// let rounded_rectangle = Convex(Sphero {
40/// shape: rectangle,
41/// rounding_radius: 0.5.try_into()?,
42/// });
43///
44/// assert!(rounded_rectangle.intersects_at(
45/// &circle,
46/// &[2.4, 0.0].into(),
47/// &Angle::default()
48/// ));
49/// assert!(!rounded_rectangle.intersects_at(
50/// &circle,
51/// &[0.0, 2.4].into(),
52/// &Angle::default()
53/// ));
54/// assert!(circle.intersects_at(
55/// &rounded_rectangle,
56/// &[0.0, 2.4].into(),
57/// &Angle::from(PI / 2.0)
58/// ));
59/// # Ok(())
60/// # }
61/// ```
62#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
63pub struct Sphero<S> {
64 /// The shape to round.
65 pub shape: S,
66 /// The radius of the rounding hypersphere.
67 pub rounding_radius: PositiveReal,
68}
69
70impl<S, V> SupportMapping<V> for Sphero<S>
71where
72 S: SupportMapping<V>,
73 V: InnerProduct,
74{
75 #[inline]
76 fn support_mapping(&self, n: &V) -> V {
77 self.shape.support_mapping(n) + *n / n.norm() * self.rounding_radius.get()
78 }
79}
80
81impl<S> BoundingSphereRadius for Sphero<S>
82where
83 S: BoundingSphereRadius,
84{
85 #[inline]
86 fn bounding_sphere_radius(&self) -> PositiveReal {
87 (self.shape.bounding_sphere_radius().get() + self.rounding_radius.get())
88 .try_into()
89 .expect("expression should evaluate to a positive real")
90 }
91}