parry3d_f64/transformation/convex_hull_utils.rs
1use crate::math::Real;
2use crate::num::Bounded;
3use na;
4#[cfg(feature = "dim3")]
5use {crate::bounding_volume, crate::math::Point};
6
7/// Returns the index of the support point of a list of points.
8///
9/// The support point is the point that extends furthest in the given direction.
10/// This is a fundamental operation used in convex hull algorithms and collision
11/// detection (especially GJK/EPA algorithms).
12///
13/// # Arguments
14/// * `direction` - The direction vector to test against
15/// * `points` - A slice of points to search
16///
17/// # Returns
18/// * `Some(index)` - Index of the support point (furthest in the given direction)
19/// * `None` - If the points slice is empty
20///
21/// # Example
22///
23/// ```ignore
24/// # // This is a pub(crate) function. Can’t really run it.
25/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
26/// use parry3d::transformation::convex_hull_utils::support_point_id;
27/// use parry3d::na::{Point3, Vector3};
28///
29/// let points = vec![
30/// Point3::origin(),
31/// Point3::new(1.0, 0.0, 0.0),
32/// Point3::new(0.0, 1.0, 0.0),
33/// Point3::new(0.0, 0.0, 1.0),
34/// ];
35///
36/// // Find point furthest in the positive X direction
37/// let dir = Vector3::new(1.0, 0.0, 0.0);
38/// let support_id = support_point_id(&dir, &points);
39/// assert_eq!(support_id, Some(1)); // Point at (1, 0, 0)
40///
41/// // Find point furthest in the positive Y direction
42/// let dir = Vector3::new(0.0, 1.0, 0.0);
43/// let support_id = support_point_id(&dir, &points);
44/// assert_eq!(support_id, Some(2)); // Point at (0, 1, 0)
45/// # }
46/// ```
47pub fn support_point_id<const D: usize>(
48 direction: &na::SVector<Real, D>,
49 points: &[na::Point<Real, D>],
50) -> Option<usize> {
51 let mut argmax = None;
52 let _max: Real = Bounded::max_value();
53 let mut max = -_max;
54
55 for (id, pt) in points.iter().enumerate() {
56 let dot = direction.dot(&pt.coords);
57
58 if dot > max {
59 argmax = Some(id);
60 max = dot;
61 }
62 }
63
64 argmax
65}
66
67/// Returns the index of the support point of an indexed list of points.
68///
69/// This is similar to [`support_point_id`], but only considers points at the indices
70/// provided by the iterator. This is useful when you want to find the support point
71/// within a subset of points without creating a new array.
72///
73/// # Arguments
74/// * `direction` - The direction vector to test against
75/// * `points` - The full array of points
76/// * `idx` - Iterator yielding indices of points to consider
77///
78/// # Returns
79/// * `Some(index)` - Index into the original `points` array of the support point
80/// * `None` - If the iterator is empty
81///
82/// # Example
83///
84/// ```ignore
85/// # // This is a pub(crate) function. Can’t really run it.
86/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
87/// use parry3d::transformation::convex_hull_utils::indexed_support_point_id;
88/// use parry3d::na::{Point3, Vector3};
89///
90/// let points = vec![
91/// Point3::origin(),
92/// Point3::new(1.0, 0.0, 0.0),
93/// Point3::new(2.0, 0.0, 0.0),
94/// Point3::new(0.0, 1.0, 0.0),
95/// ];
96///
97/// // Only consider points at indices 0, 1, and 3 (skip index 2)
98/// let subset = vec![0, 1, 3];
99/// let dir = Vector3::new(1.0, 0.0, 0.0);
100///
101/// let support_id = indexed_support_point_id(&dir, &points, subset.into_iter());
102/// // Returns 1 (not 2, since we skipped that index)
103/// assert_eq!(support_id, Some(1));
104/// # }
105/// ```
106pub fn indexed_support_point_id<I, const D: usize>(
107 direction: &na::SVector<Real, D>,
108 points: &[na::Point<Real, D>],
109 idx: I,
110) -> Option<usize>
111where
112 I: Iterator<Item = usize>,
113{
114 let mut argmax = None;
115 let mut max = -Real::MAX;
116
117 for i in idx.into_iter() {
118 let dot = direction.dot(&points[i].coords);
119
120 if dot > max {
121 argmax = Some(i);
122 max = dot;
123 }
124 }
125
126 argmax
127}
128
129/// Returns the position in the iterator where the support point is found.
130///
131/// This is similar to [`indexed_support_point_id`], but returns the position within
132/// the iterator rather than the index in the original points array. In other words,
133/// if the iterator yields indices `[5, 7, 2, 9]` and the support point is at index 2,
134/// this function returns `Some(2)` (the 3rd position in the iterator), not `Some(2)`.
135///
136/// # Arguments
137/// * `direction` - The direction vector to test against
138/// * `points` - The full array of points
139/// * `idx` - Iterator yielding indices of points to consider
140///
141/// # Returns
142/// * `Some(n)` - The `n`th element of the iterator corresponds to the support point
143/// * `None` - If the iterator is empty
144///
145/// # Example
146///
147/// ```ignore
148/// # // This is a pub(crate) function. Can’t really run it.
149/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
150/// use parry3d::transformation::convex_hull_utils::indexed_support_point_nth;
151/// use parry3d::na::{Point3, Vector3};
152///
153/// let points = vec![
154/// Point3::origin(), // index 0
155/// Point3::new(1.0, 0.0, 0.0), // index 1
156/// Point3::new(5.0, 0.0, 0.0), // index 2
157/// Point3::new(0.0, 1.0, 0.0), // index 3
158/// ];
159///
160/// // Consider points at indices [3, 0, 2, 1]
161/// let indices = vec![3, 0, 2, 1];
162/// let dir = Vector3::new(1.0, 0.0, 0.0);
163///
164/// let nth = indexed_support_point_nth(&dir, &points, indices.into_iter());
165/// // The support point is at original index 2, which is position 2 in our iterator
166/// assert_eq!(nth, Some(2));
167/// # }
168/// ```
169#[cfg(feature = "dim3")] // We only use this in 3D right now.
170pub fn indexed_support_point_nth<I, const D: usize>(
171 direction: &na::SVector<Real, D>,
172 points: &[na::Point<Real, D>],
173 idx: I,
174) -> Option<usize>
175where
176 I: Iterator<Item = usize>,
177{
178 let mut argmax = None;
179 let mut max = -Real::MAX;
180
181 for (k, i) in idx.into_iter().enumerate() {
182 let dot = direction.dot(&points[i].coords);
183
184 if dot > max {
185 argmax = Some(k);
186 max = dot;
187 }
188 }
189
190 argmax
191}
192
193/// Normalizes a point cloud by centering and scaling it to fit within a unit cube.
194///
195/// This function computes the axis-aligned bounding box (AABB) of the input points,
196/// then translates all points so they're centered at the origin, and scales them
197/// so the bounding box diagonal has length 1. This normalization is useful for
198/// improving numerical stability in geometric algorithms like convex hull computation.
199///
200/// The transformation is reversible using the returned center and scale values.
201///
202/// # Arguments
203/// * `coords` - Mutable slice of points to normalize in-place
204///
205/// # Returns
206/// A tuple `(center, scale)` where:
207/// * `center` - The original center point of the AABB (before normalization)
208/// * `scale` - The original AABB diagonal length (before normalization)
209///
210/// To reverse the transformation: `original_point = normalized_point * scale + center`
211///
212/// # Example
213///
214/// ```ignore
215/// # // This is a pub(crate) function. Can’t really run it.
216/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
217/// use parry3d::transformation::convex_hull_utils::normalize;
218/// use parry3d::math::Point;
219///
220/// let mut points = vec![
221/// Point::new(10.0, 10.0, 10.0),
222/// Point::new(20.0, 20.0, 20.0),
223/// Point::new(15.0, 15.0, 15.0),
224/// ];
225///
226/// let (center, scale) = normalize(&mut points);
227///
228/// // Points are now centered around origin and scaled
229/// // The AABB diagonal is now approximately 1.0
230/// println!("Original center: {:?}", center);
231/// println!("Original scale: {}", scale);
232///
233/// // To recover original points:
234/// for p in &mut points {
235/// *p = *p * scale + center.coords;
236/// }
237/// # }
238/// ```
239#[cfg(feature = "dim3")]
240pub fn normalize(coords: &mut [Point<Real>]) -> (Point<Real>, Real) {
241 let aabb = bounding_volume::details::local_point_cloud_aabb_ref(&*coords);
242 let diag = na::distance(&aabb.mins, &aabb.maxs);
243 let center = aabb.center();
244
245 for c in coords.iter_mut() {
246 *c = (*c + (-center.coords)) / diag;
247 }
248
249 (center, diag)
250}