Skip to main content

parry3d/shape/
heightfield3.rs

1use core::ops::Range;
2// TODO: Replace nalgebra Array2 with a custom Vec-based storage for heightfield data
3#[cfg(feature = "alloc")]
4use crate::utils::Array2;
5
6use crate::bounding_volume::Aabb;
7use crate::math::{Real, Vector};
8use crate::shape::{FeatureId, SubShapeId, Triangle, TrianglePseudoNormals};
9
10#[cfg(not(feature = "std"))]
11use crate::math::ComplexField;
12
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[cfg_attr(
15    feature = "rkyv",
16    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
17)]
18#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
19/// The status of the cell of a heightfield.
20pub struct HeightFieldCellStatus(u8);
21
22bitflags::bitflags! {
23    impl HeightFieldCellStatus: u8 {
24        /// If this bit is set, the concerned heightfield cell is subdivided using a Z pattern.
25        const ZIGZAG_SUBDIVISION = 0b00000001;
26        /// If this bit is set, the leftmost triangle of the concerned heightfield cell is removed.
27        const LEFT_TRIANGLE_REMOVED = 0b00000010;
28        /// If this bit is set, the rightmost triangle of the concerned heightfield cell is removed.
29        const RIGHT_TRIANGLE_REMOVED = 0b00000100;
30        /// If this bit is set, both triangles of the concerned heightfield cell are removed.
31        const CELL_REMOVED = Self::LEFT_TRIANGLE_REMOVED.bits() | Self::RIGHT_TRIANGLE_REMOVED.bits();
32    }
33}
34
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36#[cfg_attr(
37    feature = "rkyv",
38    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
39)]
40#[repr(C)]
41#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
42/// Flags controlling the behavior of some operations involving heightfields.
43pub struct HeightFieldFlags(u8);
44
45bitflags::bitflags! {
46    impl HeightFieldFlags: u8 {
47        /// If set, a special treatment will be applied to contact manifold calculation to eliminate
48        /// or fix contacts normals that could lead to incorrect bumps in physics simulation (especially
49        /// on flat surfaces).
50        ///
51        /// This is achieved by taking into account adjacent triangle normals when computing contact
52        /// points for a given triangle.
53        const FIX_INTERNAL_EDGES = 1 << 0;
54    }
55}
56
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58// TODO: Archive isn’t implemented for VecStorage yet.
59// #[cfg_attr(
60//     feature = "rkyv",
61//     derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
62//     archive(check_bytes)
63// )]
64#[derive(Debug, Clone)]
65#[repr(C)]
66/// A 3D heightfield.
67pub struct HeightField {
68    heights: Array2<Real>,
69    status: Array2<HeightFieldCellStatus>,
70
71    scale: Vector,
72    aabb: Aabb,
73    num_triangles: usize,
74    flags: HeightFieldFlags,
75}
76
77#[cfg(feature = "alloc")]
78impl HeightField {
79    /// Initializes a new heightfield with the given heights, scaling factor, and flags.
80    ///
81    /// The heights are specified in such a way that the row index `i` of the array advances
82    /// grid coordinates along the `z` axis, and the column index `j` of the array advances
83    /// grid coordinates along the `x` axis.
84    pub fn new(heights_zx: Array2<Real>, scale: Vector) -> Self {
85        Self::with_flags(heights_zx, scale, HeightFieldFlags::empty())
86    }
87
88    /// Initializes a new heightfield with the given heights and a scaling factor.
89    pub fn with_flags(heights_zx: Array2<Real>, scale: Vector, flags: HeightFieldFlags) -> Self {
90        assert!(
91            heights_zx.nrows() > 1 && heights_zx.ncols() > 1,
92            "A heightfield heights must have at least 2 rows and columns."
93        );
94        let max = heights_zx.max();
95        let min = heights_zx.min();
96        let hscale = scale * 0.5;
97        let aabb = Aabb::new(
98            Vector::new(-hscale.x, min * scale.y, -hscale.z),
99            Vector::new(hscale.x, max * scale.y, hscale.z),
100        );
101        let num_triangles = (heights_zx.nrows() - 1) * (heights_zx.ncols() - 1) * 2;
102        let status = Array2::repeat(
103            heights_zx.nrows() - 1,
104            heights_zx.ncols() - 1,
105            HeightFieldCellStatus::default(),
106        );
107
108        HeightField {
109            heights: heights_zx,
110            scale,
111            aabb,
112            num_triangles,
113            status,
114            flags,
115        }
116    }
117}
118
119impl HeightField {
120    /// The number of rows of this heightfield.
121    pub fn nrows(&self) -> usize {
122        self.heights.nrows() - 1
123    }
124
125    /// The number of columns of this heightfield.
126    pub fn ncols(&self) -> usize {
127        self.heights.ncols() - 1
128    }
129
130    pub(crate) fn triangle_id(&self, i: usize, j: usize, left: bool) -> u32 {
131        let tid = j * (self.heights.nrows() - 1) + i;
132        if left {
133            tid as u32
134        } else {
135            (tid + self.num_triangles / 2) as u32
136        }
137    }
138
139    fn split_triangle_id(&self, id: u32) -> (usize, usize, bool) {
140        let left = id < self.num_triangles as u32 / 2;
141        let tri_id = if left {
142            id as usize
143        } else {
144            id as usize - self.num_triangles / 2
145        };
146        let j = tri_id / (self.heights.nrows() - 1);
147        let i = tri_id - j * (self.heights.nrows() - 1);
148        (i, j, left)
149    }
150
151    fn face_id(&self, i: usize, j: usize, left: bool, front: bool) -> u32 {
152        let tid = self.triangle_id(i, j, left);
153        if front {
154            tid
155        } else {
156            tid + self.num_triangles as u32
157        }
158    }
159
160    fn quantize_floor_unclamped(&self, val: Real, cell_size: Real) -> isize {
161        ((val + 0.5) / cell_size).floor() as isize
162    }
163
164    fn quantize_ceil_unclamped(&self, val: Real, cell_size: Real) -> isize {
165        ((val + 0.5) / cell_size).ceil() as isize
166    }
167
168    fn quantize_floor(&self, val: Real, cell_size: Real, num_cells: usize) -> usize {
169        ((val + 0.5) / cell_size)
170            .floor()
171            .clamp(0.0, (num_cells - 1) as Real) as usize
172    }
173
174    fn quantize_ceil(&self, val: Real, cell_size: Real, num_cells: usize) -> usize {
175        ((val + 0.5) / cell_size)
176            .ceil()
177            .clamp(0.0, num_cells as Real) as usize
178    }
179
180    /// The pair of index of the cell containing the vertical projection of the given point.
181    pub fn closest_cell_at_point(&self, pt: Vector) -> (usize, usize) {
182        let scaled_pt = pt / self.scale;
183        let cell_width = self.unit_cell_width();
184        let cell_height = self.unit_cell_height();
185        let ncells_x = self.ncols();
186        let ncells_z = self.nrows();
187
188        let j = self.quantize_floor(scaled_pt.x, cell_width, ncells_x);
189        let i = self.quantize_floor(scaled_pt.z, cell_height, ncells_z);
190        (i, j)
191    }
192
193    /// The pair of index of the cell containing the vertical projection of the given point.
194    pub fn cell_at_point(&self, pt: Vector) -> Option<(usize, usize)> {
195        let scaled_pt = pt / self.scale;
196        let cell_width = self.unit_cell_width();
197        let cell_height = self.unit_cell_height();
198        let ncells_x = self.ncols();
199        let ncells_z = self.nrows();
200
201        if scaled_pt.x < -0.5 || scaled_pt.x > 0.5 || scaled_pt.z < -0.5 || scaled_pt.z > 0.5 {
202            // Outside of the heightfield bounds.
203            None
204        } else {
205            let j = self.quantize_floor(scaled_pt.x, cell_width, ncells_x);
206            let i = self.quantize_floor(scaled_pt.z, cell_height, ncells_z);
207            Some((i, j))
208        }
209    }
210
211    /// The pair of index of the cell containing the vertical projection of the given point.
212    pub fn unclamped_cell_at_point(&self, pt: Vector) -> (isize, isize) {
213        let scaled_pt = pt / self.scale;
214        let cell_width = self.unit_cell_width();
215        let cell_height = self.unit_cell_height();
216
217        let j = self.quantize_floor_unclamped(scaled_pt.x, cell_width);
218        let i = self.quantize_floor_unclamped(scaled_pt.z, cell_height);
219        (i, j)
220    }
221
222    /// The smallest x coordinate of the `j`-th column of this heightfield.
223    pub fn x_at(&self, j: usize) -> Real {
224        (-0.5 + self.unit_cell_width() * (j as Real)) * self.scale.x
225    }
226
227    /// The smallest z coordinate of the start of the `i`-th row of this heightfield.
228    pub fn z_at(&self, i: usize) -> Real {
229        (-0.5 + self.unit_cell_height() * (i as Real)) * self.scale.z
230    }
231
232    /// The smallest x coordinate of the `j`-th column of this heightfield.
233    pub fn signed_x_at(&self, j: isize) -> Real {
234        (-0.5 + self.unit_cell_width() * (j as Real)) * self.scale.x
235    }
236
237    /// The smallest z coordinate of the start of the `i`-th row of this heightfield.
238    pub fn signed_z_at(&self, i: isize) -> Real {
239        (-0.5 + self.unit_cell_height() * (i as Real)) * self.scale.z
240    }
241
242    /// An iterator through all the triangles of this heightfield.
243    pub fn triangles(&self) -> impl Iterator<Item = Triangle> + '_ {
244        HeightFieldTriangles {
245            heightfield: self,
246            curr: (0, 0),
247            tris: self.triangles_at(0, 0),
248        }
249    }
250
251    /// An iterator through all the triangles around the given point, after vertical projection on the heightfield.
252    pub fn triangles_around_point(&self, point: Vector) -> HeightFieldRadialTriangles<'_> {
253        let center = self.closest_cell_at_point(point);
254        HeightFieldRadialTriangles {
255            heightfield: self,
256            center,
257            curr_radius: 0,
258            curr_element: 0,
259            tris: self.triangles_at(center.0, center.1),
260        }
261    }
262
263    /// Gets the vertices of the triangle identified by `id`.
264    pub fn triangle_at_id(&self, id: u32) -> Option<Triangle> {
265        let (i, j, left) = self.split_triangle_id(id);
266        if left {
267            self.triangles_at(i, j).0
268        } else {
269            self.triangles_at(i, j).1
270        }
271    }
272
273    /// Gets the vertex indices of the triangle identified by `id`.
274    pub fn triangle_vids_at_id(&self, id: u32) -> Option<[u32; 3]> {
275        let (i, j, left) = self.split_triangle_id(id);
276        if left {
277            self.triangles_vids_at(i, j).0
278        } else {
279            self.triangles_vids_at(i, j).1
280        }
281    }
282
283    /// Gets the indices of the vertices of the (up to) two triangles for the cell (i, j).
284    pub fn triangles_vids_at(&self, i: usize, j: usize) -> (Option<[u32; 3]>, Option<[u32; 3]>) {
285        if i >= self.heights.nrows() - 1 || j >= self.heights.ncols() - 1 {
286            return (None, None);
287        }
288
289        let status = self.status[(i, j)];
290
291        if status.contains(
292            HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED
293                | HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED,
294        ) {
295            return (None, None);
296        }
297
298        let p00 = (i + j * self.heights.nrows()) as u32;
299        let p10 = ((i + 1) + j * self.heights.nrows()) as u32;
300        let p01 = (i + (j + 1) * self.heights.nrows()) as u32;
301        let p11 = ((i + 1) + (j + 1) * self.heights.nrows()) as u32;
302
303        if status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
304            let tri1 = if status.contains(HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED) {
305                None
306            } else {
307                Some([p00, p10, p11])
308            };
309
310            let tri2 = if status.contains(HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED) {
311                None
312            } else {
313                Some([p00, p11, p01])
314            };
315
316            (tri1, tri2)
317        } else {
318            let tri1 = if status.contains(HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED) {
319                None
320            } else {
321                Some([p00, p10, p01])
322            };
323
324            let tri2 = if status.contains(HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED) {
325                None
326            } else {
327                Some([p10, p11, p01])
328            };
329
330            (tri1, tri2)
331        }
332    }
333
334    /// The two triangles at the cell (i, j) of this heightfield.
335    ///
336    /// Returns `None` fore triangles that have been removed because of their user-defined status
337    /// flags (described by the `HeightFieldCellStatus` bitfield).
338    pub fn triangles_at(&self, i: usize, j: usize) -> (Option<Triangle>, Option<Triangle>) {
339        if i >= self.heights.nrows() - 1 || j >= self.heights.ncols() - 1 {
340            return (None, None);
341        }
342
343        let status = self.status[(i, j)];
344
345        if status.contains(
346            HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED
347                | HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED,
348        ) {
349            return (None, None);
350        }
351
352        let cell_width = self.unit_cell_width();
353        let cell_height = self.unit_cell_height();
354
355        let z0 = -0.5 + cell_height * (i as Real);
356        let z1 = -0.5 + cell_height * ((i + 1) as Real);
357
358        let x0 = -0.5 + cell_width * (j as Real);
359        let x1 = -0.5 + cell_width * ((j + 1) as Real);
360
361        let y00 = self.heights[(i, j)];
362        let y10 = self.heights[(i + 1, j)];
363        let y01 = self.heights[(i, j + 1)];
364        let y11 = self.heights[(i + 1, j + 1)];
365
366        let mut p00 = Vector::new(x0, y00, z0);
367        let mut p10 = Vector::new(x0, y10, z1);
368        let mut p01 = Vector::new(x1, y01, z0);
369        let mut p11 = Vector::new(x1, y11, z1);
370
371        // Apply scales:
372        p00 *= self.scale;
373        p10 *= self.scale;
374        p01 *= self.scale;
375        p11 *= self.scale;
376
377        if status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
378            let tri1 = if status.contains(HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED) {
379                None
380            } else {
381                Some(Triangle::new(p00, p10, p11))
382            };
383
384            let tri2 = if status.contains(HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED) {
385                None
386            } else {
387                Some(Triangle::new(p00, p11, p01))
388            };
389
390            (tri1, tri2)
391        } else {
392            let tri1 = if status.contains(HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED) {
393                None
394            } else {
395                Some(Triangle::new(p00, p10, p01))
396            };
397
398            let tri2 = if status.contains(HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED) {
399                None
400            } else {
401                Some(Triangle::new(p10, p11, p01))
402            };
403
404            (tri1, tri2)
405        }
406    }
407
408    /// Computes the pseudo-normals of the triangle identified by the given id.
409    ///
410    /// Returns `None` if the heightfield’s [`HeightFieldFlags::FIX_INTERNAL_EDGES`] isn’t set, or
411    /// if the triangle doesn’t exist due to it being removed by its status flag
412    /// (`HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED` or
413    /// `HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED`).
414    pub fn triangle_normal_constraints(&self, id: u32) -> Option<TrianglePseudoNormals> {
415        if self.flags.contains(HeightFieldFlags::FIX_INTERNAL_EDGES) {
416            let (i, j, left) = self.split_triangle_id(id);
417            let status = self.status[(i, j)];
418
419            let (tri_left, tri_right) = self.triangles_at(i, j);
420            let tri_normal = if left {
421                tri_left?.normal()?
422            } else {
423                tri_right?.normal()?
424            };
425
426            // TODO: we only compute bivectors where v is a specific direction
427            //       (+/-X, +/-Z, or a combination of both). So this bivector
428            //       calculation could be simplified/optimized quite a bit.
429            // Computes the pseudo-normal of an edge where the adjacent triangle is missing.
430            let bivector = |v: Vector| tri_normal.cross(v).cross(tri_normal).normalize();
431            // Pseudo-normal computed from an adjacent triangle’s normal and the current triangle’s normal.
432            let adj_pseudo_normal =
433                |adj: Option<Triangle>| adj.map(|adj| adj.normal().unwrap_or(tri_normal));
434
435            let diag_dir = if status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
436                Vector::new(-1.0, 0.0, 1.0)
437            } else {
438                Vector::new(1.0, 0.0, 1.0)
439            };
440
441            let (left_pseudo_normal, right_pseudo_normal) = if left {
442                let adj_left = adj_pseudo_normal(self.triangles_at(i.overflowing_sub(1).0, j).1)
443                    .unwrap_or_else(|| bivector(-Vector::Z));
444                let adj_right = adj_pseudo_normal(tri_right).unwrap_or_else(|| bivector(diag_dir));
445                (adj_left, adj_right)
446            } else {
447                let adj_left = adj_pseudo_normal(tri_left).unwrap_or_else(|| bivector(-diag_dir));
448                let adj_right = adj_pseudo_normal(self.triangles_at(i + 1, j).0)
449                    .unwrap_or_else(|| bivector(Vector::Z));
450                (adj_left, adj_right)
451            };
452
453            // The third neighbor depends on the combination of zigzag scheme
454            // and right/left position.
455            let top_or_bottom_pseudo_normal = if left
456                != status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION)
457            {
458                // The neighbor is below.
459                let ((bot_left, bot_right), bot_status) = if j > 0 {
460                    (self.triangles_at(i, j - 1), self.status[(i, j - 1)])
461                } else {
462                    ((None, None), HeightFieldCellStatus::empty())
463                };
464
465                let bot_tri = if bot_status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
466                    bot_left
467                } else {
468                    bot_right
469                };
470
471                adj_pseudo_normal(bot_tri).unwrap_or_else(|| bivector(-Vector::X))
472            } else {
473                // The neighbor is above.
474                let ((top_left, top_right), top_status) = if j < self.heights.ncols() - 2 {
475                    (self.triangles_at(i, j + 1), self.status[(i, j + 1)])
476                } else {
477                    ((None, None), HeightFieldCellStatus::empty())
478                };
479
480                let top_tri = if top_status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
481                    top_right
482                } else {
483                    top_left
484                };
485
486                adj_pseudo_normal(top_tri).unwrap_or_else(|| bivector(Vector::X))
487            };
488
489            // NOTE: the normalization can only succeed due to the heightfield's definition.
490            let pseudo_normal1 = (tri_normal + left_pseudo_normal).normalize();
491            let pseudo_normal2 = (tri_normal + right_pseudo_normal).normalize();
492            let pseudo_normal3 = (tri_normal + top_or_bottom_pseudo_normal).normalize();
493
494            Some(TrianglePseudoNormals {
495                face: tri_normal, // No need to re-normalize.
496                // TODO: the normals are given in no particular order. So they are **not**
497                //       guaranteed to be provided in the same order as the triangle’s edge.
498                edges: [pseudo_normal1, pseudo_normal2, pseudo_normal3],
499                two_sided: false,
500            })
501        } else {
502            None
503        }
504    }
505
506    /// The number of cells of this heightfield along each dimension.
507    pub fn num_cells_ij(&self) -> (usize, usize) {
508        (self.nrows(), self.ncols())
509    }
510
511    /// The status of the `(i, j)`-th cell.
512    pub fn cell_status(&self, i: usize, j: usize) -> HeightFieldCellStatus {
513        self.status[(i, j)]
514    }
515
516    /// Set the status of the `(i, j)`-th cell.
517    pub fn set_cell_status(&mut self, i: usize, j: usize, status: HeightFieldCellStatus) {
518        self.status[(i, j)] = status;
519    }
520
521    /// The statuses of all the cells of this heightfield.
522    pub fn cells_statuses(&self) -> &Array2<HeightFieldCellStatus> {
523        &self.status
524    }
525
526    /// The mutable statuses of all the cells of this heightfield.
527    pub fn cells_statuses_mut(&mut self) -> &mut Array2<HeightFieldCellStatus> {
528        &mut self.status
529    }
530
531    /// The heightfield’s flags controlling internal-edges handling.
532    pub fn flags(&self) -> HeightFieldFlags {
533        self.flags
534    }
535
536    /// Sets the heightfield’s flags controlling internal-edges handling.
537    pub fn set_flags(&mut self, flags: HeightFieldFlags) {
538        self.flags = flags;
539    }
540
541    /// The heights of this heightfield.
542    pub fn heights(&self) -> &Array2<Real> {
543        &self.heights
544    }
545
546    /// The scale factor applied to this heightfield.
547    pub fn scale(&self) -> Vector {
548        self.scale
549    }
550
551    /// Sets the scale factor applied to this heightfield.
552    pub fn set_scale(&mut self, new_scale: Vector) {
553        let ratio = new_scale / self.scale;
554        self.aabb.mins *= ratio;
555        self.aabb.maxs *= ratio;
556        self.scale = new_scale;
557    }
558
559    /// Returns a scaled version of this heightfield.
560    pub fn scaled(mut self, scale: Vector) -> Self {
561        self.set_scale(self.scale * scale);
562        self
563    }
564
565    /// The width (extent along its local `x` axis) of each cell of this heightmap, including the scale factor.
566    pub fn cell_width(&self) -> Real {
567        self.unit_cell_width() * self.scale.x
568    }
569
570    /// The height (extent along its local `z` axis) of each cell of this heightmap, including the scale factor.
571    pub fn cell_height(&self) -> Real {
572        self.unit_cell_height() * self.scale.z
573    }
574
575    /// The width (extent along its local `x` axis) of each cell of this heightmap, excluding the scale factor.
576    pub fn unit_cell_width(&self) -> Real {
577        1.0 / (self.heights.ncols() as Real - 1.0)
578    }
579
580    /// The height (extent along its local `z` axis) of each cell of this heightmap, excluding the scale factor.
581    pub fn unit_cell_height(&self) -> Real {
582        1.0 / (self.heights.nrows() as Real - 1.0)
583    }
584
585    /// The [`Aabb`] of this heightmap.
586    pub fn root_aabb(&self) -> &Aabb {
587        &self.aabb
588    }
589
590    /// Converts a FeatureId of the triangle `triangle_id` into a FeatureId of the whole
591    /// heightfield.
592    pub fn convert_triangle_feature_id(
593        &self,
594        triangle_id: SubShapeId,
595        fid: FeatureId,
596    ) -> FeatureId {
597        let (i, j, left) = self.split_triangle_id(triangle_id);
598
599        match fid {
600            FeatureId::Vertex(ivertex) => {
601                let nrows = self.heights.nrows();
602                let ij = i + j * nrows;
603
604                if self.status[(i, j)].contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
605                    if left {
606                        FeatureId::Vertex([ij, ij + 1, ij + 1 + nrows][ivertex as usize] as u32)
607                    } else {
608                        FeatureId::Vertex([ij, ij + 1 + nrows, ij + nrows][ivertex as usize] as u32)
609                    }
610                } else if left {
611                    FeatureId::Vertex([ij, ij + 1, ij + nrows][ivertex as usize] as u32)
612                } else {
613                    FeatureId::Vertex([ij + 1, ij + 1 + nrows, ij + nrows][ivertex as usize] as u32)
614                }
615            }
616            FeatureId::Edge(iedge) => {
617                let (nrows, ncols) = (self.heights.nrows(), self.heights.ncols());
618                let vshift = 0; // First vertical line index.
619                let hshift = (nrows - 1) * ncols; // First horizontal line index.
620                let dshift = hshift + nrows * (ncols - 1); // First diagonal line index.
621                let idiag = dshift + i + j * (nrows - 1);
622                let itop = hshift + i + j * nrows;
623                let ibottom = itop + 1;
624                let ileft = vshift + i + j * (nrows - 1);
625                let iright = ileft + nrows - 1;
626
627                if self.status[(i, j)].contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
628                    if left {
629                        // Triangle:
630                        //
631                        // |\
632                        // |_\
633                        //
634                        FeatureId::Edge([ileft, ibottom, idiag][iedge as usize] as u32)
635                    } else {
636                        // Triangle:
637                        // ___
638                        // \ |
639                        //  \|
640                        //
641                        FeatureId::Edge([idiag, iright, itop][iedge as usize] as u32)
642                    }
643                } else if left {
644                    // Triangle:
645                    // ___
646                    // | /
647                    // |/
648                    //
649                    FeatureId::Edge([ileft, idiag, itop][iedge as usize] as u32)
650                } else {
651                    // Triangle:
652                    //
653                    //  /|
654                    // /_|
655                    //
656                    FeatureId::Edge([ibottom, iright, idiag][iedge as usize] as u32)
657                }
658            }
659            FeatureId::Face(iface) => {
660                if iface == 0 {
661                    FeatureId::Face(self.face_id(i, j, left, true))
662                } else {
663                    FeatureId::Face(self.face_id(i, j, left, false))
664                }
665            }
666            FeatureId::Unknown => FeatureId::Unknown,
667        }
668    }
669
670    /// The range of segment ids that may intersect the given local Aabb.
671    pub fn unclamped_elements_range_in_local_aabb(
672        &self,
673        aabb: &Aabb,
674    ) -> (Range<isize>, Range<isize>) {
675        let ref_mins = aabb.mins / self.scale;
676        let ref_maxs = aabb.maxs / self.scale;
677        let cell_width = self.unit_cell_width();
678        let cell_height = self.unit_cell_height();
679
680        let min_x = self.quantize_floor_unclamped(ref_mins.x, cell_width);
681        let min_z = self.quantize_floor_unclamped(ref_mins.z, cell_height);
682
683        let max_x = self.quantize_ceil_unclamped(ref_maxs.x, cell_width);
684        let max_z = self.quantize_ceil_unclamped(ref_maxs.z, cell_height);
685        (min_z..max_z, min_x..max_x)
686    }
687
688    /// Applies the function `f` to all the triangles of this heightfield intersecting the given Aabb.
689    pub fn map_elements_in_local_aabb(&self, aabb: &Aabb, f: &mut impl FnMut(u32, &Triangle)) {
690        let ncells_x = self.ncols();
691        let ncells_z = self.nrows();
692
693        let ref_mins = aabb.mins / self.scale;
694        let ref_maxs = aabb.maxs / self.scale;
695        let cell_width = self.unit_cell_width();
696        let cell_height = self.unit_cell_height();
697
698        if ref_maxs.x <= -0.5 || ref_maxs.z <= -0.5 || ref_mins.x >= 0.5 || ref_mins.z >= 0.5 {
699            // Outside of the heightfield bounds.
700            return;
701        }
702
703        let min_x = self.quantize_floor(ref_mins.x, cell_width, ncells_x);
704        let min_z = self.quantize_floor(ref_mins.z, cell_height, ncells_z);
705
706        let max_x = self.quantize_ceil(ref_maxs.x, cell_width, ncells_x);
707        let max_z = self.quantize_ceil(ref_maxs.z, cell_height, ncells_z);
708
709        // TODO: find a way to avoid recomputing the same vertices
710        // multiple times.
711        for j in min_x..max_x {
712            for i in min_z..max_z {
713                let status = self.status[(i, j)];
714
715                if status.contains(HeightFieldCellStatus::CELL_REMOVED) {
716                    continue;
717                }
718
719                let z0 = -0.5 + cell_height * (i as Real);
720                let z1 = z0 + cell_height;
721
722                let x0 = -0.5 + cell_width * (j as Real);
723                let x1 = x0 + cell_width;
724
725                let y00 = self.heights[(i, j)];
726                let y10 = self.heights[(i + 1, j)];
727                let y01 = self.heights[(i, j + 1)];
728                let y11 = self.heights[(i + 1, j + 1)];
729
730                if (y00 > ref_maxs.y && y10 > ref_maxs.y && y01 > ref_maxs.y && y11 > ref_maxs.y)
731                    || (y00 < ref_mins.y
732                        && y10 < ref_mins.y
733                        && y01 < ref_mins.y
734                        && y11 < ref_mins.y)
735                {
736                    continue;
737                }
738
739                let mut p00 = Vector::new(x0, y00, z0);
740                let mut p10 = Vector::new(x0, y10, z1);
741                let mut p01 = Vector::new(x1, y01, z0);
742                let mut p11 = Vector::new(x1, y11, z1);
743
744                // Apply scales:
745                p00 *= self.scale;
746                p10 *= self.scale;
747                p01 *= self.scale;
748                p11 *= self.scale;
749
750                // Build the two triangles, contact processors and call f.
751                if !status.contains(HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED) {
752                    let tri1 = if status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
753                        Triangle::new(p00, p10, p11)
754                    } else {
755                        Triangle::new(p00, p10, p01)
756                    };
757
758                    let tid = self.triangle_id(i, j, true);
759                    f(tid, &tri1);
760                }
761
762                if !status.contains(HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED) {
763                    let tri2 = if status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
764                        Triangle::new(p00, p11, p01)
765                    } else {
766                        Triangle::new(p10, p11, p01)
767                    };
768                    let tid = self.triangle_id(i, j, false);
769                    f(tid, &tri2);
770                }
771            }
772        }
773    }
774}
775
776struct HeightFieldTriangles<'a> {
777    heightfield: &'a HeightField,
778    curr: (usize, usize),
779    tris: (Option<Triangle>, Option<Triangle>),
780}
781
782impl Iterator for HeightFieldTriangles<'_> {
783    type Item = Triangle;
784
785    fn next(&mut self) -> Option<Triangle> {
786        loop {
787            if let Some(tri1) = self.tris.0.take() {
788                return Some(tri1);
789            } else if let Some(tri2) = self.tris.1.take() {
790                return Some(tri2);
791            } else {
792                self.curr.0 += 1;
793
794                if self.curr.0 >= self.heightfield.nrows() {
795                    if self.curr.1 >= self.heightfield.ncols() - 1 {
796                        return None;
797                    }
798
799                    self.curr.0 = 0;
800                    self.curr.1 += 1;
801                }
802
803                // tri1 and tri2 are None
804                self.tris = self.heightfield.triangles_at(self.curr.0, self.curr.1);
805            }
806        }
807    }
808}
809
810/// An iterator through all the triangles around the given point, after vertical projection on the heightfield.
811pub struct HeightFieldRadialTriangles<'a> {
812    heightfield: &'a HeightField,
813    center: (usize, usize),
814    curr_radius: usize,
815    curr_element: usize,
816    tris: (Option<Triangle>, Option<Triangle>),
817}
818
819impl HeightFieldRadialTriangles<'_> {
820    /// Returns the next triangle in this iterator.
821    ///
822    /// Returns `None` no triangle closest than `max_dist` remain
823    /// to be yielded. The `max_dist` can be modified at each iteration
824    /// as long as the new value is smaller or equal to the previous value.
825    pub fn next(&mut self, max_dist: Real) -> Option<Triangle> {
826        let max_rad = if max_dist == Real::MAX {
827            usize::MAX
828        } else {
829            (max_dist / self.heightfield.cell_width())
830                .ceil()
831                .max((max_dist / self.heightfield.cell_height()).ceil()) as usize
832        };
833
834        loop {
835            if let Some(tri1) = self.tris.0.take() {
836                return Some(tri1);
837            } else if let Some(tri2) = self.tris.1.take() {
838                return Some(tri2);
839            } else {
840                let mut curr_cell = (0, 0);
841
842                loop {
843                    self.curr_element += 1;
844
845                    if self.curr_element >= 8 * self.curr_radius {
846                        if self.curr_radius >= max_rad {
847                            return None;
848                        }
849
850                        self.curr_element = 0;
851                        self.curr_radius += 1;
852                    }
853
854                    let side_max_index = self.curr_radius + self.curr_radius;
855                    let curr_index_in_side = self.curr_element % side_max_index;
856                    let curr_side = self.curr_element / side_max_index;
857
858                    let mins = (
859                        self.center.0 as isize - self.curr_radius as isize,
860                        self.center.1 as isize - self.curr_radius as isize,
861                    );
862                    let maxs = (
863                        self.center.0 as isize + self.curr_radius as isize,
864                        self.center.1 as isize + self.curr_radius as isize,
865                    );
866
867                    if curr_cell.0 < 0
868                        && curr_cell.1 < 0
869                        && (curr_cell.0 as usize) >= self.heightfield.nrows()
870                        && (curr_cell.1 as usize) >= self.heightfield.ncols()
871                    {
872                        // We already visited all the triangles of the heightfield.
873                        return None;
874                    }
875
876                    let side_origins = [
877                        (mins.0, mins.1),
878                        (maxs.0, mins.1),
879                        (maxs.0, maxs.1),
880                        (mins.0, maxs.1),
881                    ];
882                    let side_directions = [(1, 0), (0, 1), (-1, 0), (0, -1)];
883
884                    curr_cell = (
885                        side_origins[curr_side].0
886                            + side_directions[curr_side].0 * (curr_index_in_side as isize),
887                        side_origins[curr_side].1
888                            + side_directions[curr_side].1 * (curr_index_in_side as isize),
889                    );
890
891                    if curr_cell.0 >= 0
892                        && curr_cell.1 >= 0
893                        && (curr_cell.0 as usize) < self.heightfield.nrows()
894                        && (curr_cell.1 as usize) < self.heightfield.ncols()
895                    {
896                        break;
897                    }
898                }
899
900                self.tris = self
901                    .heightfield
902                    .triangles_at(curr_cell.0 as usize, curr_cell.1 as usize);
903            }
904        }
905    }
906}
907
908#[cfg(test)]
909#[cfg(all(feature = "dim3", feature = "alloc", feature = "std"))]
910mod test {
911    use super::HeightField;
912    use crate::math::{Real, Vector};
913    use crate::shape::FeatureId;
914    use crate::utils::Array2;
915    use alloc::vec::Vec;
916
917    /// The conversion recovers the cell and side from the triangle id, so every triangle has to
918    /// come out with a face of its own.
919    #[test]
920    fn every_triangle_converts_to_its_own_feature() {
921        let (nrows, ncols) = (4, 5);
922        let heights: Vec<Real> = (0..nrows * ncols).map(|k| k as Real * 0.13).collect();
923        let heightfield = HeightField::new(
924            Array2::new(nrows, ncols, heights),
925            Vector::new(2.0, 1.0, 3.0),
926        );
927
928        let num_triangles = (nrows - 1) * (ncols - 1) * 2;
929        let faces: Vec<_> = (0..num_triangles as u32)
930            .map(|triangle| heightfield.convert_triangle_feature_id(triangle, FeatureId::Face(0)))
931            .collect();
932
933        for (triangle, face) in faces.iter().enumerate() {
934            assert!(
935                !faces[..triangle].contains(face),
936                "triangle {triangle} reuses {face:?}"
937            );
938        }
939    }
940}