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, 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    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            })
500        } else {
501            None
502        }
503    }
504
505    /// The number of cells of this heightfield along each dimension.
506    pub fn num_cells_ij(&self) -> (usize, usize) {
507        (self.nrows(), self.ncols())
508    }
509
510    /// The status of the `(i, j)`-th cell.
511    pub fn cell_status(&self, i: usize, j: usize) -> HeightFieldCellStatus {
512        self.status[(i, j)]
513    }
514
515    /// Set the status of the `(i, j)`-th cell.
516    pub fn set_cell_status(&mut self, i: usize, j: usize, status: HeightFieldCellStatus) {
517        self.status[(i, j)] = status;
518    }
519
520    /// The statuses of all the cells of this heightfield.
521    pub fn cells_statuses(&self) -> &Array2<HeightFieldCellStatus> {
522        &self.status
523    }
524
525    /// The mutable statuses of all the cells of this heightfield.
526    pub fn cells_statuses_mut(&mut self) -> &mut Array2<HeightFieldCellStatus> {
527        &mut self.status
528    }
529
530    /// The heightfield’s flags controlling internal-edges handling.
531    pub fn flags(&self) -> HeightFieldFlags {
532        self.flags
533    }
534
535    /// Sets the heightfield’s flags controlling internal-edges handling.
536    pub fn set_flags(&mut self, flags: HeightFieldFlags) {
537        self.flags = flags;
538    }
539
540    /// The heights of this heightfield.
541    pub fn heights(&self) -> &Array2<Real> {
542        &self.heights
543    }
544
545    /// The scale factor applied to this heightfield.
546    pub fn scale(&self) -> Vector {
547        self.scale
548    }
549
550    /// Sets the scale factor applied to this heightfield.
551    pub fn set_scale(&mut self, new_scale: Vector) {
552        let ratio = new_scale / self.scale;
553        self.aabb.mins *= ratio;
554        self.aabb.maxs *= ratio;
555        self.scale = new_scale;
556    }
557
558    /// Returns a scaled version of this heightfield.
559    pub fn scaled(mut self, scale: Vector) -> Self {
560        self.set_scale(self.scale * scale);
561        self
562    }
563
564    /// The width (extent along its local `x` axis) of each cell of this heightmap, including the scale factor.
565    pub fn cell_width(&self) -> Real {
566        self.unit_cell_width() * self.scale.x
567    }
568
569    /// The height (extent along its local `z` axis) of each cell of this heightmap, including the scale factor.
570    pub fn cell_height(&self) -> Real {
571        self.unit_cell_height() * self.scale.z
572    }
573
574    /// The width (extent along its local `x` axis) of each cell of this heightmap, excluding the scale factor.
575    pub fn unit_cell_width(&self) -> Real {
576        1.0 / (self.heights.ncols() as Real - 1.0)
577    }
578
579    /// The height (extent along its local `z` axis) of each cell of this heightmap, excluding the scale factor.
580    pub fn unit_cell_height(&self) -> Real {
581        1.0 / (self.heights.nrows() as Real - 1.0)
582    }
583
584    /// The [`Aabb`] of this heightmap.
585    pub fn root_aabb(&self) -> &Aabb {
586        &self.aabb
587    }
588
589    /// Converts the FeatureID of the left or right triangle at the cell `(i, j)` into a FeatureId
590    /// of the whole heightfield.
591    pub fn convert_triangle_feature_id(
592        &self,
593        i: usize,
594        j: usize,
595        left: bool,
596        fid: FeatureId,
597    ) -> FeatureId {
598        match fid {
599            FeatureId::Vertex(ivertex) => {
600                let nrows = self.heights.nrows();
601                let ij = i + j * nrows;
602
603                if self.status[(i, j)].contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
604                    if left {
605                        FeatureId::Vertex([ij, ij + 1, ij + 1 + nrows][ivertex as usize] as u32)
606                    } else {
607                        FeatureId::Vertex([ij, ij + 1 + nrows, ij + nrows][ivertex as usize] as u32)
608                    }
609                } else if left {
610                    FeatureId::Vertex([ij, ij + 1, ij + nrows][ivertex as usize] as u32)
611                } else {
612                    FeatureId::Vertex([ij + 1, ij + 1 + nrows, ij + nrows][ivertex as usize] as u32)
613                }
614            }
615            FeatureId::Edge(iedge) => {
616                let (nrows, ncols) = (self.heights.nrows(), self.heights.ncols());
617                let vshift = 0; // First vertical line index.
618                let hshift = (nrows - 1) * ncols; // First horizontal line index.
619                let dshift = hshift + nrows * (ncols - 1); // First diagonal line index.
620                let idiag = dshift + i + j * (nrows - 1);
621                let itop = hshift + i + j * nrows;
622                let ibottom = itop + 1;
623                let ileft = vshift + i + j * (nrows - 1);
624                let iright = ileft + nrows - 1;
625
626                if self.status[(i, j)].contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
627                    if left {
628                        // Triangle:
629                        //
630                        // |\
631                        // |_\
632                        //
633                        FeatureId::Edge([ileft, ibottom, idiag][iedge as usize] as u32)
634                    } else {
635                        // Triangle:
636                        // ___
637                        // \ |
638                        //  \|
639                        //
640                        FeatureId::Edge([idiag, iright, itop][iedge as usize] as u32)
641                    }
642                } else if left {
643                    // Triangle:
644                    // ___
645                    // | /
646                    // |/
647                    //
648                    FeatureId::Edge([ileft, idiag, itop][iedge as usize] as u32)
649                } else {
650                    // Triangle:
651                    //
652                    //  /|
653                    // /_|
654                    //
655                    FeatureId::Edge([ibottom, iright, idiag][iedge as usize] as u32)
656                }
657            }
658            FeatureId::Face(iface) => {
659                if iface == 0 {
660                    FeatureId::Face(self.face_id(i, j, left, true))
661                } else {
662                    FeatureId::Face(self.face_id(i, j, left, false))
663                }
664            }
665            FeatureId::Unknown => FeatureId::Unknown,
666        }
667    }
668
669    /// The range of segment ids that may intersect the given local Aabb.
670    pub fn unclamped_elements_range_in_local_aabb(
671        &self,
672        aabb: &Aabb,
673    ) -> (Range<isize>, Range<isize>) {
674        let ref_mins = aabb.mins / self.scale;
675        let ref_maxs = aabb.maxs / self.scale;
676        let cell_width = self.unit_cell_width();
677        let cell_height = self.unit_cell_height();
678
679        let min_x = self.quantize_floor_unclamped(ref_mins.x, cell_width);
680        let min_z = self.quantize_floor_unclamped(ref_mins.z, cell_height);
681
682        let max_x = self.quantize_ceil_unclamped(ref_maxs.x, cell_width);
683        let max_z = self.quantize_ceil_unclamped(ref_maxs.z, cell_height);
684        (min_z..max_z, min_x..max_x)
685    }
686
687    /// Applies the function `f` to all the triangles of this heightfield intersecting the given Aabb.
688    pub fn map_elements_in_local_aabb(&self, aabb: &Aabb, f: &mut impl FnMut(u32, &Triangle)) {
689        let ncells_x = self.ncols();
690        let ncells_z = self.nrows();
691
692        let ref_mins = aabb.mins / self.scale;
693        let ref_maxs = aabb.maxs / self.scale;
694        let cell_width = self.unit_cell_width();
695        let cell_height = self.unit_cell_height();
696
697        if ref_maxs.x <= -0.5 || ref_maxs.z <= -0.5 || ref_mins.x >= 0.5 || ref_mins.z >= 0.5 {
698            // Outside of the heightfield bounds.
699            return;
700        }
701
702        let min_x = self.quantize_floor(ref_mins.x, cell_width, ncells_x);
703        let min_z = self.quantize_floor(ref_mins.z, cell_height, ncells_z);
704
705        let max_x = self.quantize_ceil(ref_maxs.x, cell_width, ncells_x);
706        let max_z = self.quantize_ceil(ref_maxs.z, cell_height, ncells_z);
707
708        // TODO: find a way to avoid recomputing the same vertices
709        // multiple times.
710        for j in min_x..max_x {
711            for i in min_z..max_z {
712                let status = self.status[(i, j)];
713
714                if status.contains(HeightFieldCellStatus::CELL_REMOVED) {
715                    continue;
716                }
717
718                let z0 = -0.5 + cell_height * (i as Real);
719                let z1 = z0 + cell_height;
720
721                let x0 = -0.5 + cell_width * (j as Real);
722                let x1 = x0 + cell_width;
723
724                let y00 = self.heights[(i, j)];
725                let y10 = self.heights[(i + 1, j)];
726                let y01 = self.heights[(i, j + 1)];
727                let y11 = self.heights[(i + 1, j + 1)];
728
729                if (y00 > ref_maxs.y && y10 > ref_maxs.y && y01 > ref_maxs.y && y11 > ref_maxs.y)
730                    || (y00 < ref_mins.y
731                        && y10 < ref_mins.y
732                        && y01 < ref_mins.y
733                        && y11 < ref_mins.y)
734                {
735                    continue;
736                }
737
738                let mut p00 = Vector::new(x0, y00, z0);
739                let mut p10 = Vector::new(x0, y10, z1);
740                let mut p01 = Vector::new(x1, y01, z0);
741                let mut p11 = Vector::new(x1, y11, z1);
742
743                // Apply scales:
744                p00 *= self.scale;
745                p10 *= self.scale;
746                p01 *= self.scale;
747                p11 *= self.scale;
748
749                // Build the two triangles, contact processors and call f.
750                if !status.contains(HeightFieldCellStatus::LEFT_TRIANGLE_REMOVED) {
751                    let tri1 = if status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
752                        Triangle::new(p00, p10, p11)
753                    } else {
754                        Triangle::new(p00, p10, p01)
755                    };
756
757                    let tid = self.triangle_id(i, j, true);
758                    f(tid, &tri1);
759                }
760
761                if !status.contains(HeightFieldCellStatus::RIGHT_TRIANGLE_REMOVED) {
762                    let tri2 = if status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION) {
763                        Triangle::new(p00, p11, p01)
764                    } else {
765                        Triangle::new(p10, p11, p01)
766                    };
767                    let tid = self.triangle_id(i, j, false);
768                    f(tid, &tri2);
769                }
770            }
771        }
772    }
773}
774
775struct HeightFieldTriangles<'a> {
776    heightfield: &'a HeightField,
777    curr: (usize, usize),
778    tris: (Option<Triangle>, Option<Triangle>),
779}
780
781impl Iterator for HeightFieldTriangles<'_> {
782    type Item = Triangle;
783
784    fn next(&mut self) -> Option<Triangle> {
785        loop {
786            if let Some(tri1) = self.tris.0.take() {
787                return Some(tri1);
788            } else if let Some(tri2) = self.tris.1.take() {
789                return Some(tri2);
790            } else {
791                self.curr.0 += 1;
792
793                if self.curr.0 >= self.heightfield.nrows() {
794                    if self.curr.1 >= self.heightfield.ncols() - 1 {
795                        return None;
796                    }
797
798                    self.curr.0 = 0;
799                    self.curr.1 += 1;
800                }
801
802                // tri1 and tri2 are None
803                self.tris = self.heightfield.triangles_at(self.curr.0, self.curr.1);
804            }
805        }
806    }
807}
808
809/// An iterator through all the triangles around the given point, after vertical projection on the heightfield.
810pub struct HeightFieldRadialTriangles<'a> {
811    heightfield: &'a HeightField,
812    center: (usize, usize),
813    curr_radius: usize,
814    curr_element: usize,
815    tris: (Option<Triangle>, Option<Triangle>),
816}
817
818impl HeightFieldRadialTriangles<'_> {
819    /// Returns the next triangle in this iterator.
820    ///
821    /// Returns `None` no triangle closest than `max_dist` remain
822    /// to be yielded. The `max_dist` can be modified at each iteration
823    /// as long as the new value is smaller or equal to the previous value.
824    pub fn next(&mut self, max_dist: Real) -> Option<Triangle> {
825        let max_rad = if max_dist == Real::MAX {
826            usize::MAX
827        } else {
828            (max_dist / self.heightfield.cell_width())
829                .ceil()
830                .max((max_dist / self.heightfield.cell_height()).ceil()) as usize
831        };
832
833        loop {
834            if let Some(tri1) = self.tris.0.take() {
835                return Some(tri1);
836            } else if let Some(tri2) = self.tris.1.take() {
837                return Some(tri2);
838            } else {
839                let mut curr_cell = (0, 0);
840
841                loop {
842                    self.curr_element += 1;
843
844                    if self.curr_element >= 8 * self.curr_radius {
845                        if self.curr_radius >= max_rad {
846                            return None;
847                        }
848
849                        self.curr_element = 0;
850                        self.curr_radius += 1;
851                    }
852
853                    let side_max_index = self.curr_radius + self.curr_radius;
854                    let curr_index_in_side = self.curr_element % side_max_index;
855                    let curr_side = self.curr_element / side_max_index;
856
857                    let mins = (
858                        self.center.0 as isize - self.curr_radius as isize,
859                        self.center.1 as isize - self.curr_radius as isize,
860                    );
861                    let maxs = (
862                        self.center.0 as isize + self.curr_radius as isize,
863                        self.center.1 as isize + self.curr_radius as isize,
864                    );
865
866                    if curr_cell.0 < 0
867                        && curr_cell.1 < 0
868                        && (curr_cell.0 as usize) >= self.heightfield.nrows()
869                        && (curr_cell.1 as usize) >= self.heightfield.ncols()
870                    {
871                        // We already visited all the triangles of the heightfield.
872                        return None;
873                    }
874
875                    let side_origins = [
876                        (mins.0, mins.1),
877                        (maxs.0, mins.1),
878                        (maxs.0, maxs.1),
879                        (mins.0, maxs.1),
880                    ];
881                    let side_directions = [(1, 0), (0, 1), (-1, 0), (0, -1)];
882
883                    curr_cell = (
884                        side_origins[curr_side].0
885                            + side_directions[curr_side].0 * (curr_index_in_side as isize),
886                        side_origins[curr_side].1
887                            + side_directions[curr_side].1 * (curr_index_in_side as isize),
888                    );
889
890                    if curr_cell.0 >= 0
891                        && curr_cell.1 >= 0
892                        && (curr_cell.0 as usize) < self.heightfield.nrows()
893                        && (curr_cell.1 as usize) < self.heightfield.ncols()
894                    {
895                        break;
896                    }
897                }
898
899                self.tris = self
900                    .heightfield
901                    .triangles_at(curr_cell.0 as usize, curr_cell.1 as usize);
902            }
903        }
904    }
905}