parry2d/shape/
heightfield2.rs

1use core::ops::Range;
2#[cfg(not(feature = "std"))]
3use na::ComplexField;
4#[cfg(feature = "alloc")]
5use na::DVector;
6
7use na::Point2;
8
9use crate::bounding_volume::Aabb;
10use crate::math::{Real, Vector};
11
12use crate::shape::Segment;
13
14/// Indicates if a cell of a heightfield is removed or not. Set this to `false` for
15/// a removed cell.
16pub type HeightFieldCellStatus = bool;
17
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19// TODO: Archive isn’t implemented for VecStorage yet.
20// #[cfg_attr(
21//     feature = "rkyv",
22//     derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
23//     archive(check_bytes)
24// )]
25#[derive(Debug, Clone)]
26#[repr(C)]
27/// A 2D heightfield with a generic storage buffer for its heights.
28pub struct HeightField {
29    heights: DVector<Real>,
30    status: DVector<HeightFieldCellStatus>,
31
32    scale: Vector<Real>,
33    aabb: Aabb,
34}
35
36#[cfg(feature = "alloc")]
37impl HeightField {
38    /// Creates a new 2D heightfield with the given heights and scale factor.
39    pub fn new(heights: DVector<Real>, scale: Vector<Real>) -> Self {
40        assert!(
41            heights.len() > 1,
42            "A heightfield heights must have at least 2 elements."
43        );
44
45        let max = heights.max();
46        let min = heights.min();
47        let hscale = scale * 0.5;
48        let aabb = Aabb::new(
49            Point2::new(-hscale.x, min * scale.y),
50            Point2::new(hscale.x, max * scale.y),
51        );
52        let num_segments = heights.len() - 1;
53
54        HeightField {
55            heights,
56            status: DVector::repeat(num_segments, true),
57            scale,
58            aabb,
59        }
60    }
61}
62
63impl HeightField {
64    /// The number of cells of this heightfield.
65    pub fn num_cells(&self) -> usize {
66        self.heights.len() - 1
67    }
68
69    /// The height at each cell endpoint.
70    pub fn heights(&self) -> &DVector<Real> {
71        &self.heights
72    }
73
74    /// The scale factor applied to this heightfield.
75    pub fn scale(&self) -> &Vector<Real> {
76        &self.scale
77    }
78
79    /// Sets the scale factor applied to this heightfield.
80    pub fn set_scale(&mut self, new_scale: Vector<Real>) {
81        let ratio = new_scale.component_div(&self.scale);
82        self.aabb.mins.coords.component_mul_assign(&ratio);
83        self.aabb.maxs.coords.component_mul_assign(&ratio);
84        self.scale = new_scale;
85    }
86
87    /// Returns a scaled version of this heightfield.
88    pub fn scaled(mut self, scale: &Vector<Real>) -> Self {
89        self.set_scale(self.scale.component_mul(scale));
90        self
91    }
92
93    /// The [`Aabb`] of this heightfield.
94    pub fn root_aabb(&self) -> &Aabb {
95        &self.aabb
96    }
97
98    /// The width of a single cell of this heightfield.
99    pub fn cell_width(&self) -> Real {
100        self.unit_cell_width() * self.scale.x
101    }
102
103    /// The width of a single cell of this heightfield, without taking the scale factor into account.
104    pub fn unit_cell_width(&self) -> Real {
105        1.0 / (self.heights.len() as Real - 1.0)
106    }
107
108    /// The left-most x-coordinate of this heightfield.
109    pub fn start_x(&self) -> Real {
110        self.scale.x * -0.5
111    }
112
113    fn quantize_floor_unclamped(&self, val: Real, seg_length: Real) -> isize {
114        ((val + 0.5) / seg_length).floor() as isize
115    }
116
117    fn quantize_ceil_unclamped(&self, val: Real, seg_length: Real) -> isize {
118        ((val + 0.5) / seg_length).ceil() as isize
119    }
120
121    fn quantize_floor(&self, val: Real, seg_length: Real) -> usize {
122        na::clamp(
123            ((val + 0.5) / seg_length).floor(),
124            0.0,
125            (self.num_cells() - 1) as Real,
126        ) as usize
127    }
128
129    fn quantize_ceil(&self, val: Real, seg_length: Real) -> usize {
130        na::clamp(
131            ((val + 0.5) / seg_length).ceil(),
132            0.0,
133            self.num_cells() as Real,
134        ) as usize
135    }
136
137    /// Index of the cell a point is on after vertical projection.
138    pub fn cell_at_point(&self, pt: &Point2<Real>) -> Option<usize> {
139        let scaled_pt = pt.coords.component_div(&self.scale);
140        let seg_length = self.unit_cell_width();
141
142        if scaled_pt.x < -0.5 || scaled_pt.x > 0.5 {
143            // Outside of the heightfield bounds.
144            None
145        } else {
146            Some(self.quantize_floor(scaled_pt.x, seg_length))
147        }
148    }
149
150    /// Height of the heightfield at the given point after vertical projection on the heightfield surface.
151    pub fn height_at_point(&self, pt: &Point2<Real>) -> Option<Real> {
152        let cell = self.cell_at_point(pt)?;
153        let seg = self.segment_at(cell)?;
154        let inter = crate::query::details::closest_points_line_line_parameters(
155            &seg.a,
156            &seg.scaled_direction(),
157            pt,
158            &Vector::y(),
159        );
160        Some(seg.a.y + inter.1)
161    }
162
163    /// Iterator through all the segments of this heightfield.
164    pub fn segments(&self) -> impl Iterator<Item = Segment> + '_ {
165        // TODO: this is not very efficient since this will
166        // recompute shared points twice.
167        (0..self.num_cells()).filter_map(move |i| self.segment_at(i))
168    }
169
170    /// The i-th segment of the heightfield if it has not been removed.
171    pub fn segment_at(&self, i: usize) -> Option<Segment> {
172        if i >= self.num_cells() || self.is_segment_removed(i) {
173            return None;
174        }
175
176        let seg_length = 1.0 / (self.heights.len() as Real - 1.0);
177
178        let x0 = -0.5 + seg_length * (i as Real);
179        let x1 = x0 + seg_length;
180
181        let y0 = self.heights[i];
182        let y1 = self.heights[i + 1];
183
184        let mut p0 = Point2::new(x0, y0);
185        let mut p1 = Point2::new(x1, y1);
186
187        // Apply scales:
188        p0.coords.component_mul_assign(&self.scale);
189        p1.coords.component_mul_assign(&self.scale);
190
191        Some(Segment::new(p0, p1))
192    }
193
194    /// Mark the i-th segment of this heightfield as removed or not.
195    pub fn set_segment_removed(&mut self, i: usize, removed: bool) {
196        self.status[i] = !removed
197    }
198
199    /// Checks if the i-th segment has been removed.
200    pub fn is_segment_removed(&self, i: usize) -> bool {
201        !self.status[i]
202    }
203
204    /// The range of segment ids that may intersect the given local Aabb.
205    pub fn unclamped_elements_range_in_local_aabb(&self, aabb: &Aabb) -> Range<isize> {
206        let ref_mins = aabb.mins.coords.component_div(&self.scale);
207        let ref_maxs = aabb.maxs.coords.component_div(&self.scale);
208        let seg_length = 1.0 / (self.heights.len() as Real - 1.0);
209
210        let min_x = self.quantize_floor_unclamped(ref_mins.x, seg_length);
211        let max_x = self.quantize_ceil_unclamped(ref_maxs.x, seg_length);
212        min_x..max_x
213    }
214
215    /// Applies `f` to each segment of this heightfield that intersects the given `aabb`.
216    pub fn map_elements_in_local_aabb(&self, aabb: &Aabb, f: &mut impl FnMut(u32, &Segment)) {
217        let ref_mins = aabb.mins.coords.component_div(&self.scale);
218        let ref_maxs = aabb.maxs.coords.component_div(&self.scale);
219        let seg_length = 1.0 / (self.heights.len() as Real - 1.0);
220
221        if ref_maxs.x < -0.5 || ref_mins.x > 0.5 {
222            // Outside of the heightfield bounds.
223            return;
224        }
225
226        let min_x = self.quantize_floor(ref_mins.x, seg_length);
227        let max_x = self.quantize_ceil(ref_maxs.x, seg_length);
228
229        // TODO: find a way to avoid recomputing the same vertices
230        // multiple times.
231        for i in min_x..max_x {
232            if self.is_segment_removed(i) {
233                continue;
234            }
235
236            let x0 = -0.5 + seg_length * (i as Real);
237            let x1 = x0 + seg_length;
238
239            let y0 = self.heights[i];
240            let y1 = self.heights[i + 1];
241
242            if (y0 > ref_maxs.y && y1 > ref_maxs.y) || (y0 < ref_mins.y && y1 < ref_mins.y) {
243                continue;
244            }
245
246            let mut p0 = Point2::new(x0, y0);
247            let mut p1 = Point2::new(x1, y1);
248
249            // Apply scales:
250            p0.coords.component_mul_assign(&self.scale);
251            p1.coords.component_mul_assign(&self.scale);
252
253            // Build the segment.
254            let seg = Segment::new(p0, p1);
255
256            // Call the callback.
257            f(i as u32, &seg);
258        }
259    }
260}