parry3d_f64/transformation/
convex_hull_utils.rs

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