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)]
20pub struct HeightFieldCellStatus(u8);
22
23bitflags::bitflags! {
24 impl HeightFieldCellStatus: u8 {
25 const ZIGZAG_SUBDIVISION = 0b00000001;
27 const LEFT_TRIANGLE_REMOVED = 0b00000010;
29 const RIGHT_TRIANGLE_REMOVED = 0b00000100;
31 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)]
44pub struct HeightFieldFlags(u8);
46
47bitflags::bitflags! {
48 impl HeightFieldFlags: u8 {
49 const FIX_INTERNAL_EDGES = 1 << 0;
56 }
57}
58
59#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
60#[derive(Debug, Clone)]
67#[repr(C)]
68pub 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 pub fn new(heights: DMatrix<Real>, scale: Vector<Real>) -> Self {
83 Self::with_flags(heights, scale, HeightFieldFlags::empty())
84 }
85
86 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 pub fn nrows(&self) -> usize {
124 self.heights.nrows() - 1
125 }
126
127 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 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 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 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 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 pub fn x_at(&self, j: usize) -> Real {
226 (-0.5 + self.unit_cell_width() * (j as Real)) * self.scale.x
227 }
228
229 pub fn z_at(&self, i: usize) -> Real {
231 (-0.5 + self.unit_cell_height() * (i as Real)) * self.scale.z
232 }
233
234 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 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 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 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 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 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 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 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 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 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 let bivector = |v: Vector<Real>| tri_normal.cross(&v).cross(&tri_normal).normalize();
433 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 let top_or_bottom_pseudo_normal = if left
459 != status.contains(HeightFieldCellStatus::ZIGZAG_SUBDIVISION)
460 {
461 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 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 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), edges: [pseudo_normal1, pseudo_normal2, pseudo_normal3],
503 })
504 } else {
505 None
506 }
507 }
508
509 pub fn num_cells_ij(&self) -> (usize, usize) {
511 (self.nrows(), self.ncols())
512 }
513
514 pub fn cell_status(&self, i: usize, j: usize) -> HeightFieldCellStatus {
516 self.status[(i, j)]
517 }
518
519 pub fn set_cell_status(&mut self, i: usize, j: usize, status: HeightFieldCellStatus) {
521 self.status[(i, j)] = status;
522 }
523
524 pub fn cells_statuses(&self) -> &DMatrix<HeightFieldCellStatus> {
526 &self.status
527 }
528
529 pub fn cells_statuses_mut(&mut self) -> &mut DMatrix<HeightFieldCellStatus> {
531 &mut self.status
532 }
533
534 pub fn flags(&self) -> HeightFieldFlags {
536 self.flags
537 }
538
539 pub fn set_flags(&mut self, flags: HeightFieldFlags) {
541 self.flags = flags;
542 }
543
544 pub fn heights(&self) -> &DMatrix<Real> {
546 &self.heights
547 }
548
549 pub fn scale(&self) -> &Vector<Real> {
551 &self.scale
552 }
553
554 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 pub fn scaled(mut self, scale: &Vector<Real>) -> Self {
564 self.set_scale(self.scale.component_mul(scale));
565 self
566 }
567
568 pub fn cell_width(&self) -> Real {
570 self.unit_cell_width() * self.scale.x
571 }
572
573 pub fn cell_height(&self) -> Real {
575 self.unit_cell_height() * self.scale.z
576 }
577
578 pub fn unit_cell_width(&self) -> Real {
580 1.0 / (self.heights.ncols() as Real - 1.0)
581 }
582
583 pub fn unit_cell_height(&self) -> Real {
585 1.0 / (self.heights.nrows() as Real - 1.0)
586 }
587
588 pub fn root_aabb(&self) -> &Aabb {
590 &self.aabb
591 }
592
593 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; let hshift = (nrows - 1) * ncols; let dshift = hshift + nrows * (ncols - 1); 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 FeatureId::Edge([ileft, ibottom, idiag][iedge as usize] as u32)
638 } else {
639 FeatureId::Edge([idiag, iright, itop][iedge as usize] as u32)
645 }
646 } else if left {
647 FeatureId::Edge([ileft, idiag, itop][iedge as usize] as u32)
653 } else {
654 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 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 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 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 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 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 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 self.tris = self.heightfield.triangles_at(self.curr.0, self.curr.1);
808 }
809 }
810 }
811}
812
813pub 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 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 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}