parry2d/shape/
heightfield2.rs

1#[cfg(not(feature = "std"))]
2use crate::math::ComplexField;
3#[cfg(feature = "alloc")]
4use alloc::{vec, vec::Vec};
5use core::ops::Range;
6
7use crate::math::Vector2;
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: Vec<Real>,
30    status: Vec<HeightFieldCellStatus>,
31
32    scale: Vector,
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: Vec<Real>, scale: Vector) -> Self {
40        assert!(
41            heights.len() > 1,
42            "A heightfield heights must have at least 2 elements."
43        );
44
45        let max = heights.iter().fold(Real::MIN, |a, b| a.max(*b));
46        let min = heights.iter().fold(Real::MAX, |a, b| a.min(*b));
47        let hscale = scale * 0.5;
48        let aabb = Aabb::new(
49            Vector2::new(-hscale.x, min * scale.y),
50            Vector2::new(hscale.x, max * scale.y),
51        );
52        let num_segments = heights.len() - 1;
53
54        HeightField {
55            heights,
56            status: vec![true; num_segments],
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) -> &Vec<Real> {
71        &self.heights
72    }
73
74    /// The scale factor applied to this heightfield.
75    pub fn scale(&self) -> Vector {
76        self.scale
77    }
78
79    /// Sets the scale factor applied to this heightfield.
80    pub fn set_scale(&mut self, new_scale: Vector) {
81        let ratio = new_scale / self.scale;
82        self.aabb.mins *= ratio;
83        self.aabb.maxs *= ratio;
84        self.scale = new_scale;
85    }
86
87    /// Returns a scaled version of this heightfield.
88    pub fn scaled(mut self, scale: Vector) -> Self {
89        self.set_scale(self.scale * 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        ((val + 0.5) / seg_length)
123            .floor()
124            .clamp(0.0, (self.num_cells() - 1) as Real) as usize
125    }
126
127    fn quantize_ceil(&self, val: Real, seg_length: Real) -> usize {
128        ((val + 0.5) / seg_length)
129            .ceil()
130            .clamp(0.0, self.num_cells() as Real) as usize
131    }
132
133    /// Index of the cell a point is on after vertical projection.
134    pub fn cell_at_point(&self, pt: Vector2) -> Option<usize> {
135        let scaled_pt = pt / self.scale;
136        let seg_length = self.unit_cell_width();
137
138        if scaled_pt.x < -0.5 || scaled_pt.x > 0.5 {
139            // Outside of the heightfield bounds.
140            None
141        } else {
142            Some(self.quantize_floor(scaled_pt.x, seg_length))
143        }
144    }
145
146    /// Height of the heightfield at the given point after vertical projection on the heightfield surface.
147    pub fn height_at_point(&self, pt: Vector2) -> Option<Real> {
148        let cell = self.cell_at_point(pt)?;
149        let seg = self.segment_at(cell)?;
150        let inter = crate::query::details::closest_points_line_line_parameters(
151            seg.a,
152            seg.scaled_direction(),
153            pt,
154            Vector::Y,
155        );
156        Some(seg.a.y + inter.1)
157    }
158
159    /// Iterator through all the segments of this heightfield.
160    pub fn segments(&self) -> impl Iterator<Item = Segment> + '_ {
161        // TODO: this is not very efficient since this will
162        // recompute shared points twice.
163        (0..self.num_cells()).filter_map(move |i| self.segment_at(i))
164    }
165
166    /// The i-th segment of the heightfield if it has not been removed.
167    pub fn segment_at(&self, i: usize) -> Option<Segment> {
168        if i >= self.num_cells() || self.is_segment_removed(i) {
169            return None;
170        }
171
172        let seg_length = 1.0 / (self.heights.len() as Real - 1.0);
173
174        let x0 = -0.5 + seg_length * (i as Real);
175        let x1 = x0 + seg_length;
176
177        let y0 = self.heights[i];
178        let y1 = self.heights[i + 1];
179
180        let mut p0 = Vector2::new(x0, y0);
181        let mut p1 = Vector2::new(x1, y1);
182
183        // Apply scales:
184        p0 *= self.scale;
185        p1 *= self.scale;
186
187        Some(Segment::new(p0, p1))
188    }
189
190    /// Mark the i-th segment of this heightfield as removed or not.
191    pub fn set_segment_removed(&mut self, i: usize, removed: bool) {
192        self.status[i] = !removed
193    }
194
195    /// Checks if the i-th segment has been removed.
196    pub fn is_segment_removed(&self, i: usize) -> bool {
197        !self.status[i]
198    }
199
200    /// The range of segment ids that may intersect the given local Aabb.
201    pub fn unclamped_elements_range_in_local_aabb(&self, aabb: &Aabb) -> Range<isize> {
202        let ref_mins = aabb.mins / self.scale;
203        let ref_maxs = aabb.maxs / self.scale;
204        let seg_length = 1.0 / (self.heights.len() as Real - 1.0);
205
206        let min_x = self.quantize_floor_unclamped(ref_mins.x, seg_length);
207        let max_x = self.quantize_ceil_unclamped(ref_maxs.x, seg_length);
208        min_x..max_x
209    }
210
211    /// Applies `f` to each segment of this heightfield that intersects the given `aabb`.
212    pub fn map_elements_in_local_aabb(&self, aabb: &Aabb, f: &mut impl FnMut(u32, &Segment)) {
213        let ref_mins = aabb.mins / self.scale;
214        let ref_maxs = aabb.maxs / self.scale;
215        let seg_length = 1.0 / (self.heights.len() as Real - 1.0);
216
217        if ref_maxs.x < -0.5 || ref_mins.x > 0.5 {
218            // Outside of the heightfield bounds.
219            return;
220        }
221
222        let min_x = self.quantize_floor(ref_mins.x, seg_length);
223        let max_x = self.quantize_ceil(ref_maxs.x, seg_length);
224
225        // TODO: find a way to avoid recomputing the same vertices
226        // multiple times.
227        for i in min_x..max_x {
228            if self.is_segment_removed(i) {
229                continue;
230            }
231
232            let x0 = -0.5 + seg_length * (i as Real);
233            let x1 = x0 + seg_length;
234
235            let y0 = self.heights[i];
236            let y1 = self.heights[i + 1];
237
238            if (y0 > ref_maxs.y && y1 > ref_maxs.y) || (y0 < ref_mins.y && y1 < ref_mins.y) {
239                continue;
240            }
241
242            let mut p0 = Vector2::new(x0, y0);
243            let mut p1 = Vector2::new(x1, y1);
244
245            // Apply scales:
246            p0 *= self.scale;
247            p1 *= self.scale;
248
249            // Build the segment.
250            let seg = Segment::new(p0, p1);
251
252            // Call the callback.
253            f(i as u32, &seg);
254        }
255    }
256}