1use core::ops::Range;
2#[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)]
19pub struct HeightFieldCellStatus(u8);
21
22bitflags::bitflags! {
23 impl HeightFieldCellStatus: u8 {
24 const ZIGZAG_SUBDIVISION = 0b00000001;
26 const LEFT_TRIANGLE_REMOVED = 0b00000010;
28 const RIGHT_TRIANGLE_REMOVED = 0b00000100;
30 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)]
42pub struct HeightFieldFlags(u8);
44
45bitflags::bitflags! {
46 impl HeightFieldFlags: u8 {
47 const FIX_INTERNAL_EDGES = 1 << 0;
54 }
55}
56
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58#[derive(Debug, Clone)]
65#[repr(C)]
66pub 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 pub fn new(heights_zx: Array2<Real>, scale: Vector) -> Self {
85 Self::with_flags(heights_zx, scale, HeightFieldFlags::empty())
86 }
87
88 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 pub fn nrows(&self) -> usize {
122 self.heights.nrows() - 1
123 }
124
125 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 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 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 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 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 pub fn x_at(&self, j: usize) -> Real {
224 (-0.5 + self.unit_cell_width() * (j as Real)) * self.scale.x
225 }
226
227 pub fn z_at(&self, i: usize) -> Real {
229 (-0.5 + self.unit_cell_height() * (i as Real)) * self.scale.z
230 }
231
232 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 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 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 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 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 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 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 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 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 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 let bivector = |v: Vector| tri_normal.cross(v).cross(tri_normal).normalize();
431 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 let top_or_bottom_pseudo_normal = if left
456 != status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION)
457 {
458 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 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 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, edges: [pseudo_normal1, pseudo_normal2, pseudo_normal3],
499 two_sided: false,
500 })
501 } else {
502 None
503 }
504 }
505
506 pub fn num_cells_ij(&self) -> (usize, usize) {
508 (self.nrows(), self.ncols())
509 }
510
511 pub fn cell_status(&self, i: usize, j: usize) -> HeightFieldCellStatus {
513 self.status[(i, j)]
514 }
515
516 pub fn set_cell_status(&mut self, i: usize, j: usize, status: HeightFieldCellStatus) {
518 self.status[(i, j)] = status;
519 }
520
521 pub fn cells_statuses(&self) -> &Array2<HeightFieldCellStatus> {
523 &self.status
524 }
525
526 pub fn cells_statuses_mut(&mut self) -> &mut Array2<HeightFieldCellStatus> {
528 &mut self.status
529 }
530
531 pub fn flags(&self) -> HeightFieldFlags {
533 self.flags
534 }
535
536 pub fn set_flags(&mut self, flags: HeightFieldFlags) {
538 self.flags = flags;
539 }
540
541 pub fn heights(&self) -> &Array2<Real> {
543 &self.heights
544 }
545
546 pub fn scale(&self) -> Vector {
548 self.scale
549 }
550
551 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 pub fn scaled(mut self, scale: Vector) -> Self {
561 self.set_scale(self.scale * scale);
562 self
563 }
564
565 pub fn cell_width(&self) -> Real {
567 self.unit_cell_width() * self.scale.x
568 }
569
570 pub fn cell_height(&self) -> Real {
572 self.unit_cell_height() * self.scale.z
573 }
574
575 pub fn unit_cell_width(&self) -> Real {
577 1.0 / (self.heights.ncols() as Real - 1.0)
578 }
579
580 pub fn unit_cell_height(&self) -> Real {
582 1.0 / (self.heights.nrows() as Real - 1.0)
583 }
584
585 pub fn root_aabb(&self) -> &Aabb {
587 &self.aabb
588 }
589
590 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; let hshift = (nrows - 1) * ncols; let dshift = hshift + nrows * (ncols - 1); 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 FeatureId::Edge([ileft, ibottom, idiag][iedge as usize] as u32)
635 } else {
636 FeatureId::Edge([idiag, iright, itop][iedge as usize] as u32)
642 }
643 } else if left {
644 FeatureId::Edge([ileft, idiag, itop][iedge as usize] as u32)
650 } else {
651 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 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 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 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 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 p00 *= self.scale;
746 p10 *= self.scale;
747 p01 *= self.scale;
748 p11 *= self.scale;
749
750 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 self.tris = self.heightfield.triangles_at(self.curr.0, self.curr.1);
805 }
806 }
807 }
808}
809
810pub 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 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 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 #[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}