1use super::{FillMode, VoxelizedVolume};
20use crate::bounding_volume::Aabb;
21use crate::math::{Matrix, Point, Real, Vector, DIM};
22use crate::transformation::vhacd::CutPlane;
23use alloc::sync::Arc;
24use alloc::{vec, vec::Vec};
25
26#[cfg(feature = "dim2")]
27type ConvexHull = Vec<Point<Real>>;
28#[cfg(feature = "dim3")]
29type ConvexHull = (Vec<Point<Real>>, Vec<[u32; DIM]>);
30
31#[derive(Copy, Clone, Debug)]
33pub struct Voxel {
34 pub coords: Point<u32>,
36 pub is_on_surface: bool,
38 pub(crate) intersections_range: (usize, usize),
41}
42
43impl Default for Voxel {
44 fn default() -> Self {
45 Self {
46 coords: Point::origin(),
47 is_on_surface: false,
48 intersections_range: (0, 0),
49 }
50 }
51}
52
53pub struct VoxelSet {
57 pub origin: Point<Real>,
59 pub scale: Real,
62 pub(crate) min_bb_voxels: Point<u32>,
63 pub(crate) max_bb_voxels: Point<u32>,
64 pub(crate) voxels: Vec<Voxel>,
65 pub(crate) intersections: Arc<Vec<u32>>,
66 pub(crate) primitive_classes: Arc<Vec<u32>>,
67}
68
69impl Default for VoxelSet {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl VoxelSet {
76 pub fn new() -> Self {
78 Self {
79 origin: Point::origin(),
80 min_bb_voxels: Point::origin(),
81 max_bb_voxels: Vector::repeat(1).into(),
82 scale: 1.0,
83 voxels: Vec::new(),
84 intersections: Arc::new(Vec::new()),
85 primitive_classes: Arc::new(Vec::new()),
86 }
87 }
88
89 #[cfg(feature = "dim2")]
91 pub fn voxel_volume(&self) -> Real {
92 self.scale * self.scale
93 }
94
95 #[cfg(feature = "dim3")]
97 pub fn voxel_volume(&self) -> Real {
98 self.scale * self.scale * self.scale
99 }
100
101 pub fn with_voxel_size(
112 points: &[Point<Real>],
113 indices: &[[u32; DIM]],
114 voxel_size: Real,
115 fill_mode: FillMode,
116 keep_voxel_to_primitives_map: bool,
117 ) -> Self {
118 VoxelizedVolume::with_voxel_size(
119 points,
120 indices,
121 voxel_size,
122 fill_mode,
123 keep_voxel_to_primitives_map,
124 )
125 .into()
126 }
127
128 pub fn voxelize(
142 points: &[Point<Real>],
143 indices: &[[u32; DIM]],
144 resolution: u32,
145 fill_mode: FillMode,
146 keep_voxel_to_primitives_map: bool,
147 ) -> Self {
148 VoxelizedVolume::voxelize(
149 points,
150 indices,
151 resolution,
152 fill_mode,
153 keep_voxel_to_primitives_map,
154 )
155 .into()
156 }
157
158 pub fn min_bb_voxels(&self) -> Point<u32> {
160 self.min_bb_voxels
161 }
162
163 pub fn max_bb_voxels(&self) -> Point<u32> {
165 self.max_bb_voxels
166 }
167
168 pub fn compute_volume(&self) -> Real {
170 self.voxel_volume() * self.voxels.len() as Real
171 }
172
173 pub fn get_voxel_point(&self, voxel: &Voxel) -> Point<Real> {
175 self.get_point(na::convert(voxel.coords))
176 }
177
178 pub(crate) fn get_point(&self, voxel: Point<Real>) -> Point<Real> {
179 self.origin + voxel.coords * self.scale
180 }
181
182 pub fn is_empty(&self) -> bool {
184 self.len() == 0
185 }
186
187 pub fn len(&self) -> usize {
189 self.voxels.len()
190 }
191
192 pub fn voxels(&self) -> &[Voxel] {
194 &self.voxels
195 }
196
197 pub fn compute_bb(&mut self) {
199 let num_voxels = self.voxels.len();
200
201 if num_voxels == 0 {
202 return;
203 }
204
205 self.min_bb_voxels = self.voxels[0].coords;
206 self.max_bb_voxels = self.voxels[0].coords;
207
208 for p in 0..num_voxels {
209 self.min_bb_voxels = self.min_bb_voxels.inf(&self.voxels[p].coords);
210 self.max_bb_voxels = self.max_bb_voxels.sup(&self.voxels[p].coords);
211 }
212 }
213
214 #[cfg(feature = "dim2")]
224 pub fn compute_exact_convex_hull(
225 &self,
226 points: &[Point<Real>],
227 indices: &[[u32; DIM]],
228 ) -> Vec<Point<Real>> {
229 self.do_compute_exact_convex_hull(points, indices)
230 }
231
232 #[cfg(feature = "dim3")]
237 pub fn compute_exact_convex_hull(
238 &self,
239 points: &[Point<Real>],
240 indices: &[[u32; DIM]],
241 ) -> (Vec<Point<Real>>, Vec<[u32; DIM]>) {
242 self.do_compute_exact_convex_hull(points, indices)
243 }
244
245 fn do_compute_exact_convex_hull(
246 &self,
247 points: &[Point<Real>],
248 indices: &[[u32; DIM]],
249 ) -> ConvexHull {
250 assert!(!self.intersections.is_empty(),
251 "Cannot compute exact convex hull without voxel-to-primitives-map. Consider passing voxel_to_primitives_map = true to the voxelizer.");
252 let mut surface_points = Vec::new();
253 #[cfg(feature = "dim3")]
254 let (mut polygon, mut workspace) = (Vec::new(), Vec::new());
255 let mut pushed_points = vec![false; points.len()];
256
257 for voxel in self.voxels.iter().filter(|v| v.is_on_surface) {
259 let intersections =
260 &self.intersections[voxel.intersections_range.0..voxel.intersections_range.1];
261 for prim_id in intersections {
262 let ia = indices[*prim_id as usize][0] as usize;
263 let ib = indices[*prim_id as usize][1] as usize;
264 #[cfg(feature = "dim3")]
265 let ic = indices[*prim_id as usize][2] as usize;
266
267 let prim_class = self.primitive_classes.get(*prim_id as usize).copied();
277 if prim_class == Some(u32::MAX) || prim_class.is_none() {
278 let aabb_center =
279 self.origin + voxel.coords.coords.map(|k| k as Real) * self.scale;
280 let aabb =
281 Aabb::from_half_extents(aabb_center, Vector::repeat(self.scale / 2.0));
282
283 #[cfg(feature = "dim2")]
284 if let Some(seg) = aabb.clip_segment(&points[ia], &points[ib]) {
285 surface_points.push(seg.a);
286 surface_points.push(seg.b);
287 }
288
289 #[cfg(feature = "dim3")]
290 {
291 polygon.clear();
292 polygon.extend_from_slice(&[points[ia], points[ib], points[ic]]);
293 aabb.clip_polygon_with_workspace(&mut polygon, &mut workspace);
294 surface_points.append(&mut polygon);
295 }
296 } else {
297 let mut push_pt = |i: usize| {
307 if !pushed_points[i] {
308 surface_points.push(points[i]);
309 pushed_points[i] = true;
310 }
311 };
312
313 push_pt(ia);
314 push_pt(ib);
315 #[cfg(feature = "dim3")]
316 push_pt(ic);
317 }
318 }
319
320 if intersections.is_empty() {
321 self.map_voxel_points(voxel, |p| surface_points.push(p));
322 }
323 }
324
325 convex_hull(&surface_points)
327 }
328
329 pub fn compute_primitive_intersections(
336 &self,
337 points: &[Point<Real>],
338 indices: &[[u32; DIM]],
339 ) -> Vec<Point<Real>> {
340 assert!(!self.intersections.is_empty(),
341 "Cannot compute primitive intersections voxel-to-primitives-map. Consider passing voxel_to_primitives_map = true to the voxelizer.");
342 let mut surface_points = Vec::new();
343 #[cfg(feature = "dim3")]
344 let (mut polygon, mut workspace) = (Vec::new(), Vec::new());
345
346 for voxel in self.voxels.iter().filter(|v| v.is_on_surface) {
348 let intersections =
349 &self.intersections[voxel.intersections_range.0..voxel.intersections_range.1];
350 for prim_id in intersections {
351 let aabb_center = self.origin + voxel.coords.coords.map(|k| k as Real) * self.scale;
352 let aabb = Aabb::from_half_extents(aabb_center, Vector::repeat(self.scale / 2.0));
353
354 let pa = points[indices[*prim_id as usize][0] as usize];
355 let pb = points[indices[*prim_id as usize][1] as usize];
356 #[cfg(feature = "dim3")]
357 let pc = points[indices[*prim_id as usize][2] as usize];
358
359 #[cfg(feature = "dim2")]
360 if let Some(seg) = aabb.clip_segment(&pa, &pb) {
361 surface_points.push(seg.a);
362 surface_points.push(seg.b);
363 }
364
365 #[cfg(feature = "dim3")]
366 {
367 workspace.clear();
368 polygon.clear();
369 polygon.extend_from_slice(&[pa, pb, pc]);
370 aabb.clip_polygon_with_workspace(&mut polygon, &mut workspace);
371
372 if polygon.len() > 2 {
373 for i in 1..polygon.len() - 1 {
374 surface_points.push(polygon[0]);
375 surface_points.push(polygon[i]);
376 surface_points.push(polygon[i + 1]);
377 }
378 }
379 }
380 }
381 }
382
383 surface_points
384 }
385
386 #[cfg(feature = "dim2")]
393 pub fn compute_convex_hull(&self, sampling: u32) -> Vec<Point<Real>> {
394 let mut points = Vec::new();
395
396 for voxel in self
398 .voxels
399 .iter()
400 .filter(|v| v.is_on_surface)
401 .step_by(sampling as usize)
402 {
403 self.map_voxel_points(voxel, |p| points.push(p));
404 }
405
406 convex_hull(&points)
408 }
409
410 #[cfg(feature = "dim3")]
417 pub fn compute_convex_hull(&self, sampling: u32) -> (Vec<Point<Real>>, Vec<[u32; DIM]>) {
418 let mut points = Vec::new();
419
420 for voxel in self
422 .voxels
423 .iter()
424 .filter(|v| v.is_on_surface)
425 .step_by(sampling as usize)
426 {
427 self.map_voxel_points(voxel, |p| points.push(p));
428 }
429
430 convex_hull(&points)
432 }
433
434 fn map_voxel_points(&self, voxel: &Voxel, mut f: impl FnMut(Point<Real>)) {
436 let ijk = voxel.coords.coords.map(|e| e as Real);
437
438 #[cfg(feature = "dim2")]
439 let shifts = [
440 Vector::new(-0.5, -0.5),
441 Vector::new(0.5, -0.5),
442 Vector::new(0.5, 0.5),
443 Vector::new(-0.5, 0.5),
444 ];
445
446 #[cfg(feature = "dim3")]
447 let shifts = [
448 Vector::new(-0.5, -0.5, -0.5),
449 Vector::new(0.5, -0.5, -0.5),
450 Vector::new(0.5, 0.5, -0.5),
451 Vector::new(-0.5, 0.5, -0.5),
452 Vector::new(-0.5, -0.5, 0.5),
453 Vector::new(0.5, -0.5, 0.5),
454 Vector::new(0.5, 0.5, 0.5),
455 Vector::new(-0.5, 0.5, 0.5),
456 ];
457
458 for shift in &shifts {
459 f(self.origin + (ijk + *shift) * self.scale)
460 }
461 }
462
463 pub(crate) fn intersect(
464 &self,
465 plane: &CutPlane,
466 positive_pts: &mut Vec<Point<Real>>,
467 negative_pts: &mut Vec<Point<Real>>,
468 sampling: u32,
469 ) {
470 let num_voxels = self.voxels.len();
471
472 if num_voxels == 0 {
473 return;
474 }
475
476 let d0 = self.scale;
477 let mut sp = 0;
478 let mut sn = 0;
479
480 for v in 0..num_voxels {
481 let voxel = self.voxels[v];
482 let pt = self.get_voxel_point(&voxel);
483 let d = plane.abc.dot(&pt.coords) + plane.d;
484
485 if d >= 0.0 {
489 if d <= d0 {
490 self.map_voxel_points(&voxel, |p| positive_pts.push(p));
491 } else {
492 sp += 1;
493
494 if sp == sampling {
495 self.map_voxel_points(&voxel, |p| positive_pts.push(p));
496 sp = 0;
497 }
498 }
499 } else if -d <= d0 {
500 self.map_voxel_points(&voxel, |p| negative_pts.push(p));
501 } else {
502 sn += 1;
503 if sn == sampling {
504 self.map_voxel_points(&voxel, |p| negative_pts.push(p));
505 sn = 0;
506 }
507 }
508 }
509 }
510
511 pub(crate) fn compute_clipped_volumes(&self, plane: &CutPlane) -> (Real, Real) {
513 if self.voxels.is_empty() {
514 return (0.0, 0.0);
515 }
516
517 let mut num_positive_voxels = 0;
518
519 for voxel in &self.voxels {
520 let pt = self.get_voxel_point(voxel);
521 let d = plane.abc.dot(&pt.coords) + plane.d;
522 num_positive_voxels += (d >= 0.0) as usize;
523 }
524
525 let num_negative_voxels = self.voxels.len() - num_positive_voxels;
526 let positive_volume = self.voxel_volume() * (num_positive_voxels as Real);
527 let negative_volume = self.voxel_volume() * (num_negative_voxels as Real);
528
529 (negative_volume, positive_volume)
530 }
531
532 pub(crate) fn select_on_surface(&self, on_surf: &mut VoxelSet) {
534 on_surf.origin = self.origin;
535 on_surf.voxels.clear();
536 on_surf.scale = self.scale;
537
538 for voxel in &self.voxels {
539 if voxel.is_on_surface {
540 on_surf.voxels.push(*voxel);
541 }
542 }
543 }
544
545 pub(crate) fn clip(
547 &self,
548 plane: &CutPlane,
549 positive_part: &mut VoxelSet,
550 negative_part: &mut VoxelSet,
551 ) {
552 let num_voxels = self.voxels.len();
553
554 if num_voxels == 0 {
555 return;
556 }
557
558 negative_part.origin = self.origin;
559 negative_part.voxels.clear();
560 negative_part.voxels.reserve(num_voxels);
561 negative_part.scale = self.scale;
562
563 positive_part.origin = self.origin;
564 positive_part.voxels.clear();
565 positive_part.voxels.reserve(num_voxels);
566 positive_part.scale = self.scale;
567
568 let d0 = self.scale;
569
570 for v in 0..num_voxels {
571 let mut voxel = self.voxels[v];
572 let pt = self.get_voxel_point(&voxel);
573 let d = plane.abc.dot(&pt.coords) + plane.d;
574
575 if d >= 0.0 {
576 if voxel.is_on_surface || d <= d0 {
577 voxel.is_on_surface = true;
578 positive_part.voxels.push(voxel);
579 } else {
580 positive_part.voxels.push(voxel);
581 }
582 } else if voxel.is_on_surface || -d <= d0 {
583 voxel.is_on_surface = true;
584 negative_part.voxels.push(voxel);
585 } else {
586 negative_part.voxels.push(voxel);
587 }
588 }
589 }
590
591 #[cfg(feature = "dim3")]
594 pub fn to_trimesh(
595 &self,
596 base_index: u32,
597 is_on_surface: bool,
598 ) -> (Vec<Point<Real>>, Vec<[u32; DIM]>) {
599 let mut vertices = Vec::new();
600 let mut indices = Vec::new();
601
602 for voxel in &self.voxels {
603 if voxel.is_on_surface == is_on_surface {
604 self.map_voxel_points(voxel, |p| vertices.push(p));
605
606 indices.push([base_index, base_index + 2, base_index + 1]);
607 indices.push([base_index, base_index + 3, base_index + 2]);
608 indices.push([base_index + 4, base_index + 5, base_index + 6]);
609 indices.push([base_index + 4, base_index + 6, base_index + 7]);
610 indices.push([base_index + 7, base_index + 6, base_index + 2]);
611 indices.push([base_index + 7, base_index + 2, base_index + 3]);
612 indices.push([base_index + 4, base_index + 1, base_index + 5]);
613 indices.push([base_index + 4, base_index, base_index + 1]);
614 indices.push([base_index + 6, base_index + 5, base_index + 1]);
615 indices.push([base_index + 6, base_index + 1, base_index + 2]);
616 indices.push([base_index + 7, base_index, base_index + 4]);
617 indices.push([base_index + 7, base_index + 3, base_index]);
618 }
619 }
620
621 (vertices, indices)
622 }
623
624 pub(crate) fn compute_principal_axes(&self) -> Vector<Real> {
625 let num_voxels = self.voxels.len();
626 if num_voxels == 0 {
627 return Vector::zeros();
628 }
629
630 let mut center = Point::origin();
635 let denom = 1.0 / (num_voxels as Real);
636
637 for voxel in &self.voxels {
638 center += voxel.coords.map(|e| e as Real).coords * denom;
639 }
640
641 let mut cov_mat = Matrix::zeros();
642 for voxel in &self.voxels {
643 let xyz = voxel.coords.map(|e| e as Real) - center;
644 cov_mat.syger(denom, &xyz, &xyz, 1.0);
645 }
646
647 cov_mat.symmetric_eigenvalues()
648 }
649
650 pub(crate) fn compute_axes_aligned_clipping_planes(
651 &self,
652 downsampling: u32,
653 planes: &mut Vec<CutPlane>,
654 ) {
655 let min_v = self.min_bb_voxels();
656 let max_v = self.max_bb_voxels();
657
658 for dim in 0..DIM {
659 let i0 = min_v[dim];
660 let i1 = max_v[dim];
661
662 for i in (i0..=i1).step_by(downsampling as usize) {
663 let plane = CutPlane {
664 abc: Vector::ith(dim, 1.0),
665 axis: dim as u8,
666 d: -(self.origin[dim] + (i as Real + 0.5) * self.scale),
667 index: i,
668 };
669
670 planes.push(plane);
671 }
672 }
673 }
674}
675
676#[cfg(feature = "dim2")]
677fn convex_hull(vertices: &[Point<Real>]) -> Vec<Point<Real>> {
678 if vertices.len() > 1 {
679 crate::transformation::convex_hull(vertices)
680 } else {
681 Vec::new()
682 }
683}
684
685#[cfg(feature = "dim3")]
686fn convex_hull(vertices: &[Point<Real>]) -> (Vec<Point<Real>>, Vec<[u32; DIM]>) {
687 if vertices.len() > 2 {
688 crate::transformation::convex_hull(vertices)
689 } else {
690 (Vec::new(), Vec::new())
691 }
692}