Skip to main content

hoomd_geometry/
convex.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 `Convex`.
5
6use serde::{Deserialize, Serialize};
7
8use crate::{
9    BoundingSphereRadius, IntersectsAt, IntersectsAtGlobal, SupportMapping,
10    shape::{Circle, Sphere},
11    xenocollide::{collide2d, collide3d},
12};
13use hoomd_utility::valid::PositiveReal;
14use hoomd_vector::{Cartesian, Metric, Rotate, Rotation, RotationMatrix};
15
16/// A newtype that checks for intersections using [`xenocollide`](crate::xenocollide).
17///
18/// Use [`Convex`] to check for intersections between two convex shapes (possibly
19/// with different types).
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 Convex<S>(pub S);
64
65impl<V, S> SupportMapping<V> for Convex<S>
66where
67    S: SupportMapping<V>,
68{
69    // Forward the call to the inner type.
70    #[inline]
71    fn support_mapping(&self, n: &V) -> V {
72        self.0.support_mapping(n)
73    }
74}
75
76impl<S> BoundingSphereRadius for Convex<S>
77where
78    S: BoundingSphereRadius,
79{
80    // Forward the call to the inner type.
81    #[inline]
82    fn bounding_sphere_radius(&self) -> PositiveReal {
83        self.0.bounding_sphere_radius()
84    }
85}
86
87impl<A, B, R> IntersectsAt<Convex<A>, Cartesian<2>, R> for Convex<B>
88where
89    A: SupportMapping<Cartesian<2>> + BoundingSphereRadius,
90    B: SupportMapping<Cartesian<2>> + BoundingSphereRadius,
91    R: Rotate<Cartesian<2>> + Rotation,
92    RotationMatrix<2>: From<R>,
93{
94    #[inline]
95    fn intersects_at(&self, other: &Convex<A>, v_ij: &Cartesian<2>, o_ij: &R) -> bool {
96        let self_circle = Circle::with_radius(self.0.bounding_sphere_radius());
97        let other_circle = Circle::with_radius(other.0.bounding_sphere_radius());
98
99        self_circle.intersects_at(&other_circle, v_ij, o_ij) && collide2d(self, other, v_ij, o_ij)
100    }
101}
102
103impl<A, B, R> IntersectsAtGlobal<Convex<A>, Cartesian<2>, R> for Convex<B>
104where
105    A: SupportMapping<Cartesian<2>> + BoundingSphereRadius,
106    B: SupportMapping<Cartesian<2>> + BoundingSphereRadius,
107    R: Rotate<Cartesian<2>> + Rotation,
108    RotationMatrix<2>: From<R>,
109{
110    #[inline]
111    fn intersects_at_global(
112        &self,
113        other: &Convex<A>,
114        r_self: &Cartesian<2>,
115        o_self: &R,
116        r_other: &Cartesian<2>,
117        o_other: &R,
118    ) -> bool {
119        let max_separation =
120            self.0.bounding_sphere_radius().get() + other.0.bounding_sphere_radius().get();
121        if r_self.distance_squared(r_other) >= max_separation.powi(2) {
122            return false;
123        }
124
125        let (v_ij, o_ij) = hoomd_vector::pair_system_to_local(r_self, o_self, r_other, o_other);
126
127        collide2d(self, other, &v_ij, &o_ij)
128    }
129}
130
131impl<A, B, R> IntersectsAt<Convex<A>, Cartesian<3>, R> for Convex<B>
132where
133    A: SupportMapping<Cartesian<3>> + BoundingSphereRadius,
134    B: SupportMapping<Cartesian<3>> + BoundingSphereRadius,
135    R: Rotate<Cartesian<3>> + Rotation,
136    RotationMatrix<3>: From<R>,
137{
138    #[inline]
139    fn intersects_at(&self, other: &Convex<A>, v_ij: &Cartesian<3>, o_ij: &R) -> bool {
140        let self_sphere = Sphere::with_radius(self.0.bounding_sphere_radius());
141        let other_sphere = Sphere::with_radius(other.0.bounding_sphere_radius());
142
143        self_sphere.intersects_at(&other_sphere, v_ij, o_ij) && collide3d(self, other, v_ij, o_ij)
144    }
145}
146
147impl<A, B, R> IntersectsAtGlobal<Convex<A>, Cartesian<3>, R> for Convex<B>
148where
149    A: SupportMapping<Cartesian<3>> + BoundingSphereRadius,
150    B: SupportMapping<Cartesian<3>> + BoundingSphereRadius,
151    R: Rotate<Cartesian<3>> + Rotation,
152    RotationMatrix<3>: From<R>,
153{
154    #[inline]
155    fn intersects_at_global(
156        &self,
157        other: &Convex<A>,
158        r_self: &Cartesian<3>,
159        o_self: &R,
160        r_other: &Cartesian<3>,
161        o_other: &R,
162    ) -> bool {
163        let r_cut = self.0.bounding_sphere_radius().get() + other.0.bounding_sphere_radius().get();
164        if r_self.distance_squared(r_other) >= r_cut.powi(2) {
165            return false;
166        }
167
168        let (v_ij, o_ij) = hoomd_vector::pair_system_to_local(r_self, o_self, r_other, o_other);
169
170        collide3d(self, other, &v_ij, &o_ij)
171    }
172}