parry2d/transformation/voxelization/
voxelized_volume.rs

1// Rust port, with modifications, of https://github.com/kmammou/v-hacd/blob/master/src/VHACD_Lib/src/vhacdVolume.cpp
2// By Khaled Mamou
3//
4// # License of the original C++ code:
5// > Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
6// > All rights reserved.
7// >
8// >
9// > Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10// >
11// > 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12// >
13// > 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
14// >
15// > 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
16// >
17// > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18
19use crate::bounding_volume::Aabb;
20#[cfg(not(feature = "std"))]
21use crate::math::ComplexField;
22use crate::math::{IVector, Int, Real, Vector, DIM};
23use crate::query;
24use crate::transformation::voxelization::{Voxel, VoxelSet};
25use alloc::sync::Arc;
26use alloc::vec::Vec;
27
28/// Controls how voxelization determines which voxels are filled vs empty.
29///
30/// The fill mode is a critical parameter that determines the structure of the resulting voxel set.
31/// Choose the appropriate mode based on whether you need just the surface boundary or a solid volume.
32///
33/// # Variants
34///
35/// ## `SurfaceOnly`
36///
37/// Only voxels that intersect the input surface boundary (triangle mesh in 3D, polyline in 2D)
38/// are marked as filled. This creates a hollow shell representation.
39///
40/// **Use this when:**
41/// - You only need the surface boundary
42/// - Memory is limited (fewer voxels to store)
43/// - You're approximating a thin shell or surface
44///
45/// **Example:**
46/// ```
47/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
48/// use parry3d::transformation::voxelization::{FillMode, VoxelSet};
49/// use parry3d::shape::Ball;
50/// ///
51/// let ball = Ball::new(1.0);
52/// let (vertices, indices) = ball.to_trimesh(20, 20);
53///
54/// let surface_voxels = VoxelSet::voxelize(
55///     &vertices,
56///     &indices,
57///     10,
58///     FillMode::SurfaceOnly,  // Only the shell
59///     false,
60/// );
61///
62/// // All voxels are on the surface
63/// assert!(surface_voxels.voxels().iter().all(|v| v.is_on_surface));
64/// # }
65/// ```
66///
67/// ## `FloodFill`
68///
69/// Marks surface voxels AND all interior voxels as filled, creating a solid volume. Uses a
70/// flood-fill algorithm starting from outside the shape to determine inside vs outside.
71///
72/// **Fields:**
73/// - `detect_cavities`: If `true`, properly detects and preserves internal cavities/holes.
74///   If `false`, all enclosed spaces are filled (faster but may fill unintended regions).
75///
76/// - `detect_self_intersections` (2D only): If `true`, attempts to handle self-intersecting
77///   polylines correctly. More expensive but more robust.
78///
79/// **Use this when:**
80/// - You need volume information (mass properties, volume computation)
81/// - You want a solid representation for collision detection
82/// - You need to distinguish between interior and surface voxels
83///
84/// **Example:**
85/// ```
86/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
87/// use parry3d::transformation::voxelization::{FillMode, VoxelSet};
88/// use parry3d::shape::Ball;
89/// ///
90/// let ball = Ball::new(1.0);
91/// let (vertices, indices) = ball.to_trimesh(20, 20);
92///
93/// let solid_voxels = VoxelSet::voxelize(
94///     &vertices,
95///     &indices,
96///     15,
97///     FillMode::FloodFill {
98///         detect_cavities: false,  // No cavities in a sphere
99///     },
100///     false,
101/// );
102///
103/// // Mix of surface and interior voxels
104/// let surface_count = solid_voxels.voxels().iter().filter(|v| v.is_on_surface).count();
105/// let interior_count = solid_voxels.voxels().iter().filter(|v| !v.is_on_surface).count();
106/// assert!(surface_count > 0 && interior_count > 0);
107/// # }
108/// ```
109///
110/// # Performance Notes
111///
112/// - `SurfaceOnly` is faster and uses less memory than `FloodFill`
113/// - `detect_cavities = true` adds computational overhead but gives more accurate results
114/// - For simple convex shapes, `detect_cavities = false` is usually sufficient
115#[derive(Copy, Clone, Debug, PartialEq, Eq)]
116pub enum FillMode {
117    /// Only consider full the voxels intersecting the surface of the
118    /// shape being voxelized.
119    SurfaceOnly,
120    /// Use a flood-fill technique to consider fill the voxels intersecting
121    /// the surface of the shape being voxelized, as well as all the voxels
122    /// bounded of them.
123    FloodFill {
124        /// Detects holes inside of a solid contour.
125        detect_cavities: bool,
126        /// Attempts to properly handle self-intersections.
127        #[cfg(feature = "dim2")]
128        detect_self_intersections: bool,
129    },
130    // RaycastFill
131}
132
133impl Default for FillMode {
134    fn default() -> Self {
135        Self::FloodFill {
136            detect_cavities: false,
137            #[cfg(feature = "dim2")]
138            detect_self_intersections: false,
139        }
140    }
141}
142
143impl FillMode {
144    #[cfg(feature = "dim2")]
145    pub(crate) fn detect_cavities(self) -> bool {
146        match self {
147            FillMode::FloodFill {
148                detect_cavities, ..
149            } => detect_cavities,
150            _ => false,
151        }
152    }
153
154    #[cfg(feature = "dim2")]
155    pub(crate) fn detect_self_intersections(self) -> bool {
156        match self {
157            FillMode::FloodFill {
158                detect_self_intersections,
159                ..
160            } => detect_self_intersections,
161            _ => false,
162        }
163    }
164
165    #[cfg(feature = "dim3")]
166    pub(crate) fn detect_self_intersections(self) -> bool {
167        false
168    }
169}
170
171/// The state of a voxel during and after voxelization.
172///
173/// This enum represents the classification of each voxel in the grid. After voxelization completes,
174/// only three values are meaningful for end-user code: `PrimitiveOutsideSurface`,
175/// `PrimitiveInsideSurface`, and `PrimitiveOnSurface`. The other variants are intermediate
176/// states used during the flood-fill algorithm.
177///
178/// # Usage
179///
180/// Most users will work with [`VoxelSet`] which filters out outside voxels and only stores
181/// filled ones. However, if you're working directly with [`VoxelizedVolume`], you'll encounter
182/// all these values.
183///
184/// # Final States (After Voxelization)
185///
186/// - **`PrimitiveOutsideSurface`**: Voxel is completely outside the shape
187/// - **`PrimitiveInsideSurface`**: Voxel is fully inside the shape (only with [`FillMode::FloodFill`])
188/// - **`PrimitiveOnSurface`**: Voxel intersects the boundary surface
189///
190/// # Intermediate States (During Voxelization)
191///
192/// The following are temporary states used during the flood-fill algorithm and should be
193/// ignored by user code:
194/// - `PrimitiveUndefined`
195/// - `PrimitiveOutsideSurfaceToWalk`
196/// - `PrimitiveInsideSurfaceToWalk`
197/// - `PrimitiveOnSurfaceNoWalk`
198/// - `PrimitiveOnSurfaceToWalk1`
199/// - `PrimitiveOnSurfaceToWalk2`
200///
201/// # Example
202///
203/// ```
204/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
205/// use parry3d::transformation::voxelization::{FillMode, VoxelValue, VoxelizedVolume};
206/// use parry3d::shape::Cuboid;
207/// use parry3d::math::Vector;
208///
209/// let cuboid = Cuboid::new(Vector::new(1.0, 1.0, 1.0));
210/// let (vertices, indices) = cuboid.to_trimesh();
211///
212/// // Create a dense voxelized volume
213/// let volume = VoxelizedVolume::voxelize(
214///     &vertices,
215///     &indices,
216///     8,
217///     FillMode::FloodFill { detect_cavities: false },
218///     false,
219/// );
220///
221/// // Query individual voxel values
222/// let resolution = volume.resolution();
223/// for i in 0..resolution[0] {
224///     for j in 0..resolution[1] {
225///         for k in 0..resolution[2] {
226///             match volume.voxel(i, j, k) {
227///                 VoxelValue::PrimitiveOnSurface => {
228///                     // This voxel is on the boundary
229///                 }
230///                 VoxelValue::PrimitiveInsideSurface => {
231///                     // This voxel is inside the shape
232///                 }
233///                 VoxelValue::PrimitiveOutsideSurface => {
234///                     // This voxel is outside the shape
235///                 }
236///                 _ => {
237///                     // Should not happen after voxelization completes
238///                 }
239///             }
240///         }
241///     }
242/// }
243/// # }
244/// ```
245#[derive(Copy, Clone, Debug, PartialEq, Eq)]
246pub enum VoxelValue {
247    /// Intermediate value, should be ignored by end-user code.
248    PrimitiveUndefined,
249    /// Intermediate value, should be ignored by end-user code.
250    PrimitiveOutsideSurfaceToWalk,
251    /// Intermediate value, should be ignored by end-user code.
252    PrimitiveInsideSurfaceToWalk,
253    /// Intermediate value, should be ignored by end-user code.
254    PrimitiveOnSurfaceNoWalk,
255    /// Intermediate value, should be ignored by end-user code.
256    PrimitiveOnSurfaceToWalk1,
257    /// Intermediate value, should be ignored by end-user code.
258    PrimitiveOnSurfaceToWalk2,
259    /// A voxel that is outside of the voxelized shape.
260    PrimitiveOutsideSurface,
261    /// A voxel that is on the interior of the voxelized shape.
262    PrimitiveInsideSurface,
263    /// Voxel that intersects the surface of the voxelized shape.
264    PrimitiveOnSurface,
265}
266
267#[derive(Copy, Clone, Debug, PartialEq, Eq)]
268struct VoxelData {
269    #[cfg(feature = "dim2")]
270    multiplicity: u32,
271    num_primitive_intersections: u32,
272}
273
274/// A dense voxel grid storing the state of every voxel in a cubic volume.
275///
276/// `VoxelizedVolume` is the intermediate representation used during the voxelization process.
277/// Unlike [`VoxelSet`] which only stores filled voxels, `VoxelizedVolume` stores a complete
278/// dense 3D array where every grid cell has an associated [`VoxelValue`].
279///
280/// # When to Use
281///
282/// Most users should use [`VoxelSet`] instead, which is converted from `VoxelizedVolume`
283/// automatically and is much more memory-efficient. You might want to use `VoxelizedVolume`
284/// directly if you need to:
285///
286/// - Query the state of ALL voxels, including empty ones
287/// - Implement custom voxel processing algorithms
288/// - Generate visualizations showing both filled and empty voxels
289///
290/// # Memory Usage
291///
292/// `VoxelizedVolume` stores **all** voxels in a dense 3D array:
293/// - Memory usage: `O(resolution^3)` in 3D or `O(resolution^2)` in 2D
294/// - A 100×100×100 grid requires 1 million voxel entries
295///
296/// For this reason, [`VoxelSet`] is usually preferred for storage.
297///
298/// # Conversion
299///
300/// `VoxelizedVolume` automatically converts to `VoxelSet`:
301///
302/// ```
303/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
304/// use parry3d::transformation::voxelization::{FillMode, VoxelSet, VoxelizedVolume};
305/// use parry3d::shape::Ball;
306/// ///
307/// let ball = Ball::new(1.0);
308/// let (vertices, indices) = ball.to_trimesh(10, 10);
309///
310/// // Create dense volume
311/// let volume = VoxelizedVolume::voxelize(
312///     &vertices,
313///     &indices,
314///     10,
315///     FillMode::SurfaceOnly,
316///     false,
317/// );
318///
319/// // Convert to sparse set (automatic via Into trait)
320/// let voxel_set: VoxelSet = volume.into();
321/// # }
322/// ```
323///
324/// # Example: Querying All Voxels
325///
326/// ```
327/// # #[cfg(all(feature = "dim3", feature = "f32"))] {
328/// use parry3d::transformation::voxelization::{FillMode, VoxelValue, VoxelizedVolume};
329/// use parry3d::shape::Cuboid;
330/// use parry3d::math::Vector;
331///
332/// let cuboid = Cuboid::new(Vector::new(1.0, 1.0, 1.0));
333/// let (vertices, indices) = cuboid.to_trimesh();
334///
335/// let volume = VoxelizedVolume::voxelize(
336///     &vertices,
337///     &indices,
338///     8,
339///     FillMode::FloodFill { detect_cavities: false },
340///     false,
341/// );
342///
343/// // Count voxels by state
344/// let resolution = volume.resolution();
345/// let mut inside = 0;
346/// let mut surface = 0;
347/// let mut outside = 0;
348///
349/// for i in 0..resolution[0] {
350///     for j in 0..resolution[1] {
351///         for k in 0..resolution[2] {
352///             match volume.voxel(i, j, k) {
353///                 VoxelValue::PrimitiveInsideSurface => inside += 1,
354///                 VoxelValue::PrimitiveOnSurface => surface += 1,
355///                 VoxelValue::PrimitiveOutsideSurface => outside += 1,
356///                 _ => {}
357///             }
358///         }
359///     }
360/// }
361///
362/// println!("Inside: {}, Surface: {}, Outside: {}", inside, surface, outside);
363/// # }
364/// ```
365pub struct VoxelizedVolume {
366    origin: Vector,
367    scale: Real,
368    resolution: [u32; DIM],
369    values: Vec<VoxelValue>,
370    data: Vec<VoxelData>,
371    primitive_intersections: Vec<(u32, u32)>,
372}
373
374impl VoxelizedVolume {
375    /// Voxelizes the given shape described by its boundary:
376    /// a triangle mesh (in 3D) or polyline (in 2D).
377    ///
378    /// # Parameters
379    /// * `points` - The vertex buffer of the boundary of the shape to voxelize.
380    /// * `indices` - The index buffer of the boundary of the shape to voxelize.
381    /// * `resolution` - Controls the number of subdivision done along each axis. This number
382    ///   is the number of subdivisions along the axis where the input shape has the largest extent.
383    ///   The other dimensions will have a different automatically-determined resolution (in order to
384    ///   keep the voxels cubic).
385    /// * `fill_mode` - Controls what is being voxelized.
386    /// * `keep_voxel_to_primitives_map` - If set to `true` a map between the voxels
387    ///   and the primitives (3D triangles or 2D segments) it intersects will be computed.
388    pub fn with_voxel_size(
389        points: &[Vector],
390        indices: &[[u32; DIM]],
391        voxel_size: Real,
392        fill_mode: FillMode,
393        keep_voxel_to_primitives_map: bool,
394    ) -> Self {
395        let mut result = VoxelizedVolume {
396            resolution: [0; DIM],
397            origin: Vector::ZERO,
398            scale: voxel_size,
399            values: Vec::new(),
400            data: Vec::new(),
401            primitive_intersections: Vec::new(),
402        };
403
404        if points.is_empty() {
405            return result;
406        }
407
408        let aabb = crate::bounding_volume::details::local_point_cloud_aabb_ref(points);
409        result.origin = aabb.mins;
410        let extents = aabb.extents() / voxel_size;
411        #[cfg(feature = "dim2")]
412        {
413            result.resolution = [
414                (extents.x.ceil() as u32).max(2) + 1,
415                (extents.y.ceil() as u32).max(2) + 1,
416            ];
417        }
418        #[cfg(feature = "dim3")]
419        {
420            result.resolution = [
421                (extents.x.ceil() as u32).max(2) + 1,
422                (extents.y.ceil() as u32).max(2) + 1,
423                (extents.z.ceil() as u32).max(2) + 1,
424            ];
425        }
426
427        result.do_voxelize(points, indices, fill_mode, keep_voxel_to_primitives_map);
428        result
429    }
430
431    /// Voxelizes the given shape described by its boundary:
432    /// a triangle mesh (in 3D) or polyline (in 2D).
433    ///
434    /// # Parameters
435    /// * `points` - The vertex buffer of the boundary of the shape to voxelize.
436    /// * `indices` - The index buffer of the boundary of the shape to voxelize.
437    /// * `resolution` - Controls the number of subdivision done along each axis. This number
438    ///   is the number of subdivisions along the axis where the input shape has the largest extent.
439    ///   The other dimensions will have a different automatically-determined resolution (in order to
440    ///   keep the voxels cubic).
441    /// * `fill_mode` - Controls what is being voxelized.
442    /// * `keep_voxel_to_primitives_map` - If set to `true` a map between the voxels
443    ///   and the primitives (3D triangles or 2D segments) it intersects will be computed.
444    pub fn voxelize(
445        points: &[Vector],
446        indices: &[[u32; DIM]],
447        resolution: u32,
448        fill_mode: FillMode,
449        keep_voxel_to_primitives_map: bool,
450    ) -> Self {
451        let mut result = VoxelizedVolume {
452            resolution: [0; DIM],
453            origin: Vector::ZERO,
454            scale: 1.0,
455            values: Vec::new(),
456            data: Vec::new(),
457            primitive_intersections: Vec::new(),
458        };
459
460        if points.is_empty() {
461            return result;
462        }
463
464        let aabb = crate::bounding_volume::details::local_point_cloud_aabb_ref(points);
465        result.origin = aabb.mins;
466
467        let d = aabb.maxs - aabb.mins;
468        let r;
469
470        #[cfg(feature = "dim2")]
471        if d[0] > d[1] {
472            r = d[0];
473            result.resolution[0] = resolution;
474            result.resolution[1] = 2 + (resolution as Real * d[1] / d[0]) as u32;
475        } else {
476            r = d[1];
477            result.resolution[1] = resolution;
478            result.resolution[0] = 2 + (resolution as Real * d[0] / d[1]) as u32;
479        }
480
481        #[cfg(feature = "dim3")]
482        if d[0] >= d[1] && d[0] >= d[2] {
483            r = d[0];
484            result.resolution[0] = resolution;
485            result.resolution[1] = 2 + (resolution as Real * d[1] / d[0]) as u32;
486            result.resolution[2] = 2 + (resolution as Real * d[2] / d[0]) as u32;
487        } else if d[1] >= d[0] && d[1] >= d[2] {
488            r = d[1];
489            result.resolution[1] = resolution;
490            result.resolution[0] = 2 + (resolution as Real * d[0] / d[1]) as u32;
491            result.resolution[2] = 2 + (resolution as Real * d[2] / d[1]) as u32;
492        } else {
493            r = d[2];
494            result.resolution[2] = resolution;
495            result.resolution[0] = 2 + (resolution as Real * d[0] / d[2]) as u32;
496            result.resolution[1] = 2 + (resolution as Real * d[1] / d[2]) as u32;
497        }
498
499        result.scale = r / (resolution as Real - 1.0);
500        result.do_voxelize(points, indices, fill_mode, keep_voxel_to_primitives_map);
501        result
502    }
503
504    fn do_voxelize(
505        &mut self,
506        points: &[Vector],
507        indices: &[[u32; DIM]],
508        fill_mode: FillMode,
509        keep_voxel_to_primitives_map: bool,
510    ) {
511        let inv_scale = 1.0 / self.scale;
512        self.allocate();
513
514        let mut tri_pts = [Vector::ZERO; DIM];
515        let box_half_size = Vector::splat(0.5);
516        let mut ijk0 = IVector::ZERO;
517        let mut ijk1 = IVector::ZERO;
518
519        let detect_self_intersections = fill_mode.detect_self_intersections();
520        #[cfg(feature = "dim2")]
521        let lock_high_multiplicities =
522            fill_mode.detect_cavities() && fill_mode.detect_self_intersections();
523
524        for (tri_id, tri) in indices.iter().enumerate() {
525            // Find the range of voxels potentially intersecting the triangle.
526            for c in 0..DIM {
527                let pt = points[tri[c] as usize];
528                tri_pts[c] = (pt - self.origin) * inv_scale;
529
530                let i = (tri_pts[c].x + 0.5) as Int;
531                let j = (tri_pts[c].y + 0.5) as Int;
532                #[cfg(feature = "dim3")]
533                let k = (tri_pts[c].z + 0.5) as Int;
534
535                assert!((i as u32) < self.resolution[0] && (j as u32) < self.resolution[1]);
536                #[cfg(feature = "dim3")]
537                assert!((k as u32) < self.resolution[2]);
538
539                #[cfg(feature = "dim2")]
540                let ijk = IVector::new(i, j);
541                #[cfg(feature = "dim3")]
542                let ijk = IVector::new(i, j, k);
543
544                if c == 0 {
545                    ijk0 = ijk;
546                    ijk1 = ijk;
547                } else {
548                    ijk0 = ijk0.min(ijk);
549                    ijk1 = ijk1.max(ijk);
550                }
551            }
552
553            ijk0 = (ijk0 - IVector::ONE).max(IVector::ZERO);
554            #[cfg(feature = "dim2")]
555            let resolution_vec = IVector::new(self.resolution[0] as Int, self.resolution[1] as Int);
556            #[cfg(feature = "dim3")]
557            let resolution_vec = IVector::new(
558                self.resolution[0] as Int,
559                self.resolution[1] as Int,
560                self.resolution[2] as Int,
561            );
562            ijk1 = (ijk1 + IVector::ONE).min(resolution_vec);
563
564            #[cfg(feature = "dim2")]
565            let range_k = 0..1;
566            #[cfg(feature = "dim3")]
567            let range_k = ijk0.z..ijk1.z;
568
569            // Determine exactly what voxel intersect the triangle.
570            for i in ijk0.x..ijk1.x {
571                for j in ijk0.y..ijk1.y {
572                    for k in range_k.clone() {
573                        #[cfg(feature = "dim2")]
574                        let pt = Vector::new(i as Real, j as Real);
575                        #[cfg(feature = "dim3")]
576                        let pt = Vector::new(i as Real, j as Real, k as Real);
577
578                        let id = self.voxel_index(i as u32, j as u32, k as u32);
579                        let value = &mut self.values[id as usize];
580                        let data = &mut self.data[id as usize];
581
582                        if detect_self_intersections
583                            || keep_voxel_to_primitives_map
584                            || *value == VoxelValue::PrimitiveUndefined
585                        {
586                            let aabb = Aabb::from_half_extents(pt, box_half_size);
587
588                            #[cfg(feature = "dim2")]
589                            if !detect_self_intersections {
590                                let segment = crate::shape::Segment::from(tri_pts);
591                                let intersect =
592                                    query::details::intersection_test_aabb_segment(&aabb, &segment);
593
594                                if intersect {
595                                    if keep_voxel_to_primitives_map {
596                                        data.num_primitive_intersections += 1;
597                                        self.primitive_intersections.push((id, tri_id as u32));
598                                    }
599
600                                    *value = VoxelValue::PrimitiveOnSurface;
601                                }
602                            } else if let Some(params) =
603                                aabb.clip_line_parameters(tri_pts[0], tri_pts[1] - tri_pts[0])
604                            {
605                                let eps = 0.0; // -1.0e-6;
606
607                                assert!(params.0 <= params.1);
608                                if params.0 > 1.0 + eps || params.1 < 0.0 - eps {
609                                    continue;
610                                }
611
612                                if keep_voxel_to_primitives_map {
613                                    data.num_primitive_intersections += 1;
614                                    self.primitive_intersections.push((id, tri_id as u32));
615                                }
616
617                                if data.multiplicity > 4 && lock_high_multiplicities {
618                                    *value = VoxelValue::PrimitiveOnSurfaceNoWalk;
619                                } else {
620                                    *value = VoxelValue::PrimitiveOnSurface;
621                                }
622                            };
623
624                            #[cfg(feature = "dim3")]
625                            {
626                                let triangle = crate::shape::Triangle::from(tri_pts);
627                                let intersect = query::details::intersection_test_aabb_triangle(
628                                    &aabb, &triangle,
629                                );
630
631                                if intersect {
632                                    *value = VoxelValue::PrimitiveOnSurface;
633
634                                    if keep_voxel_to_primitives_map {
635                                        data.num_primitive_intersections += 1;
636                                        self.primitive_intersections.push((id, tri_id as u32));
637                                    }
638                                }
639                            };
640                        }
641                    }
642                }
643            }
644        }
645
646        match fill_mode {
647            FillMode::SurfaceOnly => {
648                for value in &mut self.values {
649                    if *value != VoxelValue::PrimitiveOnSurface {
650                        *value = VoxelValue::PrimitiveOutsideSurface
651                    }
652                }
653            }
654            FillMode::FloodFill {
655                detect_cavities, ..
656            } => {
657                #[cfg(feature = "dim2")]
658                {
659                    self.mark_outside_surface(0, 0, self.resolution[0], 1);
660                    self.mark_outside_surface(
661                        0,
662                        self.resolution[1] - 1,
663                        self.resolution[0],
664                        self.resolution[1],
665                    );
666                    self.mark_outside_surface(0, 0, 1, self.resolution[1]);
667                    self.mark_outside_surface(
668                        self.resolution[0] - 1,
669                        0,
670                        self.resolution[0],
671                        self.resolution[1],
672                    );
673                }
674
675                #[cfg(feature = "dim3")]
676                {
677                    self.mark_outside_surface(0, 0, 0, self.resolution[0], self.resolution[1], 1);
678                    self.mark_outside_surface(
679                        0,
680                        0,
681                        self.resolution[2] - 1,
682                        self.resolution[0],
683                        self.resolution[1],
684                        self.resolution[2],
685                    );
686                    self.mark_outside_surface(0, 0, 0, self.resolution[0], 1, self.resolution[2]);
687                    self.mark_outside_surface(
688                        0,
689                        self.resolution[1] - 1,
690                        0,
691                        self.resolution[0],
692                        self.resolution[1],
693                        self.resolution[2],
694                    );
695                    self.mark_outside_surface(0, 0, 0, 1, self.resolution[1], self.resolution[2]);
696                    self.mark_outside_surface(
697                        self.resolution[0] - 1,
698                        0,
699                        0,
700                        self.resolution[0],
701                        self.resolution[1],
702                        self.resolution[2],
703                    );
704                }
705
706                if detect_cavities {
707                    let _ = self.propagate_values(
708                        VoxelValue::PrimitiveOutsideSurfaceToWalk,
709                        VoxelValue::PrimitiveOutsideSurface,
710                        None,
711                        VoxelValue::PrimitiveOnSurfaceToWalk1,
712                    );
713
714                    loop {
715                        if !self.propagate_values(
716                            VoxelValue::PrimitiveInsideSurfaceToWalk,
717                            VoxelValue::PrimitiveInsideSurface,
718                            Some(VoxelValue::PrimitiveOnSurfaceToWalk1),
719                            VoxelValue::PrimitiveOnSurfaceToWalk2,
720                        ) {
721                            break;
722                        }
723
724                        if !self.propagate_values(
725                            VoxelValue::PrimitiveOutsideSurfaceToWalk,
726                            VoxelValue::PrimitiveOutsideSurface,
727                            Some(VoxelValue::PrimitiveOnSurfaceToWalk2),
728                            VoxelValue::PrimitiveOnSurfaceToWalk1,
729                        ) {
730                            break;
731                        }
732                    }
733
734                    for voxel in &mut self.values {
735                        if *voxel == VoxelValue::PrimitiveOnSurfaceToWalk1
736                            || *voxel == VoxelValue::PrimitiveOnSurfaceToWalk2
737                            || *voxel == VoxelValue::PrimitiveOnSurfaceNoWalk
738                        {
739                            *voxel = VoxelValue::PrimitiveOnSurface;
740                        }
741                    }
742                } else {
743                    let _ = self.propagate_values(
744                        VoxelValue::PrimitiveOutsideSurfaceToWalk,
745                        VoxelValue::PrimitiveOutsideSurface,
746                        None,
747                        VoxelValue::PrimitiveOnSurface,
748                    );
749
750                    self.replace_value(
751                        VoxelValue::PrimitiveUndefined,
752                        VoxelValue::PrimitiveInsideSurface,
753                    );
754                }
755            }
756        }
757    }
758
759    /// The number of voxel subdivisions along each coordinate axis.
760    pub fn resolution(&self) -> [u32; DIM] {
761        self.resolution
762    }
763
764    /// The scale factor that needs to be applied to the voxels of `self`
765    /// in order to give them the size matching the original model's size.
766    pub fn scale(&self) -> Real {
767        self.scale
768    }
769
770    fn allocate(&mut self) {
771        #[cfg(feature = "dim2")]
772        let len = self.resolution[0] * self.resolution[1];
773        #[cfg(feature = "dim3")]
774        let len = self.resolution[0] * self.resolution[1] * self.resolution[2];
775        self.values
776            .resize(len as usize, VoxelValue::PrimitiveUndefined);
777        self.data.resize(
778            len as usize,
779            VoxelData {
780                #[cfg(feature = "dim2")]
781                multiplicity: 0,
782                num_primitive_intersections: 0,
783            },
784        );
785    }
786
787    fn voxel_index(&self, i: u32, j: u32, _k: u32) -> u32 {
788        #[cfg(feature = "dim2")]
789        return i + j * self.resolution[0];
790        #[cfg(feature = "dim3")]
791        return i + j * self.resolution[0] + _k * self.resolution[0] * self.resolution[1];
792    }
793
794    fn voxel_mut(&mut self, i: u32, j: u32, k: u32) -> &mut VoxelValue {
795        let idx = self.voxel_index(i, j, k);
796        &mut self.values[idx as usize]
797    }
798
799    /// The value of the given voxel.
800    ///
801    /// In 2D, the `k` argument is ignored.
802    pub fn voxel(&self, i: u32, j: u32, k: u32) -> VoxelValue {
803        let idx = self.voxel_index(i, j, k);
804        self.values[idx as usize]
805    }
806
807    /// Mark all the PrimitiveUndefined voxels within the given bounds as PrimitiveOutsideSurfaceToWalk.
808    #[cfg(feature = "dim2")]
809    fn mark_outside_surface(&mut self, i0: u32, j0: u32, i1: u32, j1: u32) {
810        for i in i0..i1 {
811            for j in j0..j1 {
812                let v = self.voxel_mut(i, j, 0);
813
814                if *v == VoxelValue::PrimitiveUndefined {
815                    *v = VoxelValue::PrimitiveOutsideSurfaceToWalk;
816                }
817            }
818        }
819    }
820
821    /// Mark all the PrimitiveUndefined voxels within the given bounds as PrimitiveOutsideSurfaceToWalk.
822    #[cfg(feature = "dim3")]
823    fn mark_outside_surface(&mut self, i0: u32, j0: u32, k0: u32, i1: u32, j1: u32, k1: u32) {
824        for i in i0..i1 {
825            for j in j0..j1 {
826                for k in k0..k1 {
827                    let v = self.voxel_mut(i, j, k);
828
829                    if *v == VoxelValue::PrimitiveUndefined {
830                        *v = VoxelValue::PrimitiveOutsideSurfaceToWalk;
831                    }
832                }
833            }
834        }
835    }
836
837    fn walk_forward(
838        primitive_undefined_value_to_set: VoxelValue,
839        on_surface_value_to_set: VoxelValue,
840        start: isize,
841        end: isize,
842        mut ptr: isize,
843        out: &mut [VoxelValue],
844        stride: isize,
845        max_distance: isize,
846    ) {
847        let mut i = start;
848        let mut count = 0;
849
850        while count < max_distance && i < end {
851            if out[ptr as usize] == VoxelValue::PrimitiveUndefined {
852                out[ptr as usize] = primitive_undefined_value_to_set;
853            } else if out[ptr as usize] == VoxelValue::PrimitiveOnSurface {
854                out[ptr as usize] = on_surface_value_to_set;
855                break;
856            } else {
857                break;
858            }
859
860            i += 1;
861            ptr += stride;
862            count += 1;
863        }
864    }
865
866    fn walk_backward(
867        primitive_undefined_value_to_set: VoxelValue,
868        on_surface_value_to_set: VoxelValue,
869        start: isize,
870        end: isize,
871        mut ptr: isize,
872        out: &mut [VoxelValue],
873        stride: isize,
874        max_distance: isize,
875    ) {
876        let mut i = start;
877        let mut count = 0;
878
879        while count < max_distance && i >= end {
880            if out[ptr as usize] == VoxelValue::PrimitiveUndefined {
881                out[ptr as usize] = primitive_undefined_value_to_set;
882            } else if out[ptr as usize] == VoxelValue::PrimitiveOnSurface {
883                out[ptr as usize] = on_surface_value_to_set;
884                break;
885            } else {
886                break;
887            }
888
889            i -= 1;
890            ptr -= stride;
891            count += 1;
892        }
893    }
894
895    fn propagate_values(
896        &mut self,
897        inside_surface_value_to_walk: VoxelValue,
898        inside_surface_value_to_set: VoxelValue,
899        on_surface_value_to_walk: Option<VoxelValue>,
900        on_surface_value_to_set: VoxelValue,
901    ) -> bool {
902        let mut voxels_walked;
903        let mut walked_at_least_once = false;
904        let i0 = self.resolution[0];
905        let j0 = self.resolution[1];
906        #[cfg(feature = "dim2")]
907        let k0 = 1;
908        #[cfg(feature = "dim3")]
909        let k0 = self.resolution[2];
910
911        // Avoid striding too far in each direction to stay in L1 cache as much as possible.
912        // The cache size required for the walk is roughly (4 * walk_distance * 64) since
913        // the k direction doesn't count as it's walking byte per byte directly in a cache lines.
914        // ~16k is required for a walk distance of 64 in each directions.
915        let walk_distance = 64;
916
917        // using the stride directly instead of calling get_voxel for each iterations saves
918        // a lot of multiplications and pipeline stalls due to values dependencies on imul.
919        let istride = self.voxel_index(1, 0, 0) as isize - self.voxel_index(0, 0, 0) as isize;
920        let jstride = self.voxel_index(0, 1, 0) as isize - self.voxel_index(0, 0, 0) as isize;
921        #[cfg(feature = "dim3")]
922        let kstride = self.voxel_index(0, 0, 1) as isize - self.voxel_index(0, 0, 0) as isize;
923
924        // It might seem counter intuitive to go over the whole voxel range multiple times
925        // but since we do the run in memory order, it leaves us with far fewer cache misses
926        // than a BFS algorithm and it has the additional benefit of not requiring us to
927        // store and manipulate a fifo for recursion that might become huge when the number
928        // of voxels is large.
929        // This will outperform the BFS algorithm by several orders of magnitude in practice.
930        loop {
931            voxels_walked = 0;
932
933            for i in 0..i0 {
934                for j in 0..j0 {
935                    for k in 0..k0 {
936                        let idx = self.voxel_index(i, j, k) as isize;
937                        let voxel = self.voxel_mut(i, j, k);
938
939                        if *voxel == inside_surface_value_to_walk {
940                            voxels_walked += 1;
941                            walked_at_least_once = true;
942                            *voxel = inside_surface_value_to_set;
943                        } else if Some(*voxel) != on_surface_value_to_walk {
944                            continue;
945                        }
946
947                        // walk in each direction to mark other voxel that should be walked.
948                        // this will generate a 3d pattern that will help the overall
949                        // algorithm converge faster while remaining cache friendly.
950                        #[cfg(feature = "dim3")]
951                        Self::walk_forward(
952                            inside_surface_value_to_walk,
953                            on_surface_value_to_set,
954                            k as isize + 1,
955                            k0 as isize,
956                            idx + kstride,
957                            &mut self.values,
958                            kstride,
959                            walk_distance,
960                        );
961                        #[cfg(feature = "dim3")]
962                        Self::walk_backward(
963                            inside_surface_value_to_walk,
964                            on_surface_value_to_set,
965                            k as isize - 1,
966                            0,
967                            idx - kstride,
968                            &mut self.values,
969                            kstride,
970                            walk_distance,
971                        );
972
973                        Self::walk_forward(
974                            inside_surface_value_to_walk,
975                            on_surface_value_to_set,
976                            j as isize + 1,
977                            j0 as isize,
978                            idx + jstride,
979                            &mut self.values,
980                            jstride,
981                            walk_distance,
982                        );
983                        Self::walk_backward(
984                            inside_surface_value_to_walk,
985                            on_surface_value_to_set,
986                            j as isize - 1,
987                            0,
988                            idx - jstride,
989                            &mut self.values,
990                            jstride,
991                            walk_distance,
992                        );
993
994                        Self::walk_forward(
995                            inside_surface_value_to_walk,
996                            on_surface_value_to_set,
997                            (i + 1) as isize,
998                            i0 as isize,
999                            idx + istride,
1000                            &mut self.values,
1001                            istride,
1002                            walk_distance,
1003                        );
1004                        Self::walk_backward(
1005                            inside_surface_value_to_walk,
1006                            on_surface_value_to_set,
1007                            i as isize - 1,
1008                            0,
1009                            idx - istride,
1010                            &mut self.values,
1011                            istride,
1012                            walk_distance,
1013                        );
1014                    }
1015                }
1016            }
1017
1018            if voxels_walked == 0 {
1019                break;
1020            }
1021        }
1022
1023        walked_at_least_once
1024    }
1025
1026    fn replace_value(&mut self, current_value: VoxelValue, new_value: VoxelValue) {
1027        for voxel in &mut self.values {
1028            if *voxel == current_value {
1029                *voxel = new_value;
1030            }
1031        }
1032    }
1033
1034    /// Naive conversion of all the voxels with the given `value` to a triangle-mesh.
1035    ///
1036    /// This conversion is extremely naive: it will simply collect all the 12 triangles forming
1037    /// the faces of each voxel. No actual boundary extraction is done.
1038    #[cfg(feature = "dim3")]
1039    pub fn to_trimesh(&self, value: VoxelValue) -> (Vec<Vector>, Vec<[u32; DIM]>) {
1040        let mut vertices = Vec::new();
1041        let mut indices = Vec::new();
1042
1043        for i in 0..self.resolution[0] {
1044            for j in 0..self.resolution[1] {
1045                for k in 0..self.resolution[2] {
1046                    let voxel = self.voxel(i, j, k);
1047
1048                    if voxel == value {
1049                        let ijk = Vector::new(i as Real, j as Real, k as Real);
1050
1051                        let shifts = [
1052                            Vector::new(-0.5, -0.5, -0.5),
1053                            Vector::new(0.5, -0.5, -0.5),
1054                            Vector::new(0.5, 0.5, -0.5),
1055                            Vector::new(-0.5, 0.5, -0.5),
1056                            Vector::new(-0.5, -0.5, 0.5),
1057                            Vector::new(0.5, -0.5, 0.5),
1058                            Vector::new(0.5, 0.5, 0.5),
1059                            Vector::new(-0.5, 0.5, 0.5),
1060                        ];
1061
1062                        for shift in &shifts {
1063                            vertices.push(self.origin + (ijk + shift) * self.scale);
1064                        }
1065
1066                        let s = vertices.len() as u32;
1067                        indices.push([s, s + 2, s + 1]);
1068                        indices.push([s, s + 3, s + 2]);
1069                        indices.push([s + 4, s + 5, s + 6]);
1070                        indices.push([s + 4, s + 6, s + 7]);
1071                        indices.push([s + 7, s + 6, s + 2]);
1072                        indices.push([s + 7, s + 2, s + 3]);
1073                        indices.push([s + 4, s + 1, s + 5]);
1074                        indices.push([s + 4, s, s + 1]);
1075                        indices.push([s + 6, s + 5, s + 1]);
1076                        indices.push([s + 6, s + 1, s + 2]);
1077                        indices.push([s + 7, s, s + 4]);
1078                        indices.push([s + 7, s + 3, s]);
1079                    }
1080                }
1081            }
1082        }
1083
1084        (vertices, indices)
1085    }
1086}
1087
1088impl From<VoxelizedVolume> for VoxelSet {
1089    fn from(mut shape: VoxelizedVolume) -> Self {
1090        let mut curr_intersection_index = 0;
1091        let mut vset = VoxelSet::new();
1092        let mut vset_intersections = Vec::new();
1093        vset.origin = shape.origin;
1094        vset.scale = shape.scale;
1095
1096        #[cfg(feature = "dim2")]
1097        let k1 = 1;
1098        #[cfg(feature = "dim3")]
1099        let k1 = shape.resolution[2];
1100
1101        for i in 0..shape.resolution[0] {
1102            for j in 0..shape.resolution[1] {
1103                for k in 0..k1 {
1104                    let id = shape.voxel_index(i, j, k) as usize;
1105                    let value = shape.values[id];
1106                    #[cfg(feature = "dim2")]
1107                    let coords = IVector::new(i as Int, j as Int);
1108                    #[cfg(feature = "dim3")]
1109                    let coords = IVector::new(i as Int, j as Int, k as Int);
1110
1111                    if value == VoxelValue::PrimitiveInsideSurface {
1112                        let voxel = Voxel {
1113                            coords,
1114                            is_on_surface: false,
1115                            intersections_range: (curr_intersection_index, curr_intersection_index),
1116                        };
1117                        vset.voxels.push(voxel);
1118                    } else if value == VoxelValue::PrimitiveOnSurface {
1119                        let mut voxel = Voxel {
1120                            coords,
1121                            is_on_surface: true,
1122                            intersections_range: (curr_intersection_index, curr_intersection_index),
1123                        };
1124
1125                        if !shape.primitive_intersections.is_empty() {
1126                            let num_intersections =
1127                                shape.data[id].num_primitive_intersections as usize;
1128                            // We store the index where we should write the intersection on the
1129                            // vset into num_primitive_intersections. That way we can reuse it
1130                            // afterwards when copying the set of intersection into a single
1131                            // flat Vec.
1132                            shape.data[id].num_primitive_intersections =
1133                                curr_intersection_index as u32;
1134                            curr_intersection_index += num_intersections;
1135                            voxel.intersections_range.1 = curr_intersection_index;
1136                        }
1137
1138                        vset.voxels.push(voxel);
1139                    }
1140                }
1141            }
1142        }
1143
1144        if !shape.primitive_intersections.is_empty() {
1145            vset_intersections.resize(shape.primitive_intersections.len(), 0);
1146            for (voxel_id, prim_id) in shape.primitive_intersections {
1147                let num_inter = &mut shape.data[voxel_id as usize].num_primitive_intersections;
1148                vset_intersections[*num_inter as usize] = prim_id;
1149                *num_inter += 1;
1150            }
1151        }
1152
1153        vset.intersections = Arc::new(vset_intersections);
1154
1155        vset
1156    }
1157}
1158
1159/*
1160fn traceRay(
1161    mesh: &RaycastMesh,
1162    start: Real,
1163    dir: Vector,
1164    inside_count: &mut u32,
1165    outside_count: &mut u32,
1166) {
1167    let out_t;
1168    let u;
1169    let v;
1170    let w;
1171    let face_sign;
1172    let face_index;
1173    let hit = raycast_mesh.raycast(start, dir, out_t, u, v, w, face_sign, face_index);
1174
1175    if hit {
1176        if face_sign >= 0 {
1177            *inside_count += 1;
1178        } else {
1179            *outside_count += 1;
1180        }
1181    }
1182}
1183
1184
1185fn raycast_fill(volume: &Volume, raycast_mesh: &RaycastMesh) {
1186if !raycast_mesh {
1187    return;
1188}
1189
1190let scale = volume.scale;
1191let bmin = volume.min_bb;
1192
1193let i0 = volume.resolution[0];
1194let j0 = volume.resolution[1];
1195let k0 = volume.resolution[2];
1196
1197for i in 0..i0 {
1198    for j in 0..j0 {
1199        for k in 0..k0 {
1200            let voxel = volume.get_voxel(i, j, k);
1201
1202            if voxel != VoxelValue::PrimitiveOnSurface {
1203                let start = Vector::new(
1204                    i as Real * scale + bmin[0],
1205                    j as Real * scale + bmin[1],
1206                    k as Real * scale + bmin[2],
1207                );
1208
1209                let mut inside_count = 0;
1210                let mut outside_count = 0;
1211
1212                let directions = [
1213                    Vector::X,
1214                    -Vector::X,
1215                    Vector::Y,
1216                    -Vector::Y,
1217                    Vector::Z,
1218                    -Vector::Z,
1219                ];
1220
1221                for r in 0..6 {
1222                    traceRay(
1223                        raycast_mesh,
1224                        start,
1225                        &directions[r * 3],
1226                        &mut inside_count,
1227                        &mut outside_count,
1228                    );
1229
1230                    // Early out if we hit the outside of the mesh
1231                    if outside_count != 0 {
1232                        break;
1233                    }
1234
1235                    // Early out if we accumulated 3 inside hits
1236                    if inside_count >= 3 {
1237                        break;
1238                    }
1239                }
1240
1241                if outside_count == 0 && inside_count >= 3 {
1242                    volume.set_voxel(i, j, k, VoxelValue::PrimitiveInsideSurface);
1243                } else {
1244                    volume.set_voxel(i, j, k, VoxelValue::PrimitiveOutsideSurface);
1245                }
1246            }
1247        }
1248    }
1249}
1250}
1251 */