parry3d/shape/
heightfield3.rs

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