parry3d/shape/
voxels.rs

1use crate::bounding_volume::Aabb;
2use crate::math::{Point, Real, Vector, DIM};
3use alloc::{vec, vec::Vec};
4#[cfg(not(feature = "std"))]
5use na::ComplexField;
6
7/// Categorization of a voxel based on its neighbors.
8#[derive(Copy, Clone, Debug, PartialEq, Eq)]
9pub enum VoxelType {
10    /// The voxel is empty.
11    Empty,
12    /// The voxel is a vertex if all three coordinate axis directions have at
13    /// least one empty neighbor.
14    Vertex,
15    /// The voxel is on an edge if it has non-empty neighbors in both directions of
16    /// a single coordinate axis.
17    #[cfg(feature = "dim3")]
18    Edge,
19    /// The voxel is on an edge if it has non-empty neighbors in both directions of
20    /// two coordinate axes.
21    Face,
22    /// The voxel is on an edge if it has non-empty neighbors in both directions of
23    /// all coordinate axes.
24    Interior,
25}
26
27#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
28/// The status of the cell of an heightfield.
29pub struct AxisMask(u8);
30
31bitflags::bitflags! {
32    /// Flags for identifying signed directions along coordinate axes, or faces of a voxel.
33    impl AxisMask: u8 {
34        /// The direction or face along the `+x` coordinate axis.
35        const X_POS = 1 << 0;
36        /// The direction or face along the `-x` coordinate axis.
37        const X_NEG = 1 << 1;
38        /// The direction or face along the `+y` coordinate axis.
39        const Y_POS = 1 << 2;
40        /// The direction or face along the `-y` coordinate axis.
41        const Y_NEG = 1 << 3;
42        /// The direction or face along the `+z` coordinate axis.
43        #[cfg(feature= "dim3")]
44        const Z_POS = 1 << 4;
45        /// The direction or face along the `-z` coordinate axis.
46        #[cfg(feature= "dim3")]
47        const Z_NEG = 1 << 5;
48    }
49}
50
51/// Indicates the local shape of a voxel on each octant.
52///
53/// This provides geometric information of the shape’s exposed features on each octant.
54// This is an alternative to `FACES_TO_FEATURE_MASKS` that can be more convenient for some
55// collision-detection algorithms.
56#[derive(Copy, Clone, Debug, PartialEq, Eq)]
57pub struct OctantPattern;
58
59// NOTE: it is important that the max value of any OctantPattern variant
60//       is 7 because we don’t allocate more than 3 bits to store it in
61//      `FACES_TO_OCTANT_MASKS`.
62/// Indicates the local shape of a voxel on each octant.
63///
64/// This provides geometric information of the shape’s exposed features on each octant.
65// This is an alternative to `FACES_TO_FEATURE_MASKS` that can be more convenient for some
66// collision-detection algorithms.
67#[cfg(feature = "dim3")]
68impl OctantPattern {
69    /// The voxel doesn't have any exposed feature on the octant with this mask.
70    pub const INTERIOR: u32 = 0;
71    /// The voxel has an exposed vertex on the octant with this mask.
72    pub const VERTEX: u32 = 1;
73    /// The voxel has an exposed edges with direction X on the octant with this mask.
74    pub const EDGE_X: u32 = 2;
75    /// The voxel has an exposed edges with direction Y on the octant with this mask.
76    pub const EDGE_Y: u32 = 3;
77    /// The voxel has an exposed edges with direction Z on the octant with this mask.
78    pub const EDGE_Z: u32 = 4;
79    /// The voxel has an exposed face with normal X on the octant with this mask.
80    pub const FACE_X: u32 = 5;
81    /// The voxel has an exposed face with normal Y on the octant with this mask.
82    pub const FACE_Y: u32 = 6;
83    /// The voxel has an exposed face with normal Z on the octant with this mask.
84    pub const FACE_Z: u32 = 7;
85}
86
87// NOTE: it is important that the max value of any OctantPattern variant
88//       is 7 because we don’t allocate more than 3 bits to store it in
89//      `FACES_TO_OCTANT_MASKS`.
90/// Indicates the local shape of a voxel on each octant.
91///
92/// This provides geometric information of the shape’s exposed features on each octant.
93/// This is an alternative to `FACES_TO_FEATURE_MASKS` that can be more convenient for some
94/// collision-detection algorithms.
95#[cfg(feature = "dim2")]
96impl OctantPattern {
97    /// The voxel doesn't have any exposed feature on the octant with this mask.
98    pub const INTERIOR: u32 = 0;
99    /// The voxel has an exposed vertex on the octant with this mask.
100    pub const VERTEX: u32 = 1;
101    /// The voxel has an exposed face with normal X on the octant with this mask.
102    pub const FACE_X: u32 = 2;
103    /// The voxel has an exposed face with normal Y on the octant with this mask.
104    pub const FACE_Y: u32 = 3;
105}
106
107// The local neighborhood information is encoded in a 8-bits number in groups of two bits
108// per coordinate axis: `0bwwzzyyxx`. In each group of two bits, e.g. `xx`, the rightmost (resp.
109// leftmost) bit set to 1 means that the neighbor voxel with coordinate `+1` (resp `-1`) relative
110// to the current voxel along the `x` axis is filled. If the bit is 0, then the corresponding
111// neighbor is empty. See the `AxisMask` bitflags.
112// For example, in 2D, the mask `0b00_00_10_01` matches the following configuration (assuming +y goes
113// up, and +x goes right):
114//
115// ```txt
116//  0 0 0
117//  0 x 1
118//  0 1 0
119// ```
120//
121// The special value `0b01000000` indicates that the voxel is empty.
122// And the value `0b00111111` (`0b00001111` in 2D) indicates that the voxel is an interior voxel (its whole neighborhood
123// is filled).
124/// A description of the local neighborhood of a voxel.
125#[derive(Copy, Clone, Debug, PartialEq, Eq)]
126#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
127pub struct VoxelState(u8);
128
129impl VoxelState {
130    /// The value of empty voxels.
131    pub const EMPTY: VoxelState = VoxelState(EMPTY_FACE_MASK);
132    /// The value of a voxel with non-empty neighbors in all directions.
133    pub const INTERIOR: VoxelState = VoxelState(INTERIOR_FACE_MASK);
134
135    /// Is this voxel empty?
136    pub const fn is_empty(self) -> bool {
137        self.0 == EMPTY_FACE_MASK
138    }
139
140    /// A bit mask indicating which faces of the voxel don’t have any
141    /// adjacent non-empty voxel.
142    pub const fn free_faces(self) -> AxisMask {
143        if self.0 == INTERIOR_FACE_MASK || self.0 == EMPTY_FACE_MASK {
144            AxisMask::empty()
145        } else {
146            AxisMask::from_bits_truncate((!self.0) & INTERIOR_FACE_MASK)
147        }
148    }
149
150    /// The [`VoxelType`] of this voxel.
151    pub const fn voxel_type(self) -> VoxelType {
152        FACES_TO_VOXEL_TYPES[self.0 as usize]
153    }
154
155    // Bitmask indicating what vertices, edges, or faces of the voxel are "free".
156    pub(crate) const fn feature_mask(self) -> u16 {
157        FACES_TO_FEATURE_MASKS[self.0 as usize]
158    }
159
160    pub(crate) const fn octant_mask(self) -> u32 {
161        FACES_TO_OCTANT_MASKS[self.0 as usize]
162    }
163}
164
165/// Information associated to a voxel.
166#[derive(Copy, Clone, Debug, PartialEq)]
167pub struct VoxelData {
168    /// The temporary index in the internal voxels’ storage.
169    ///
170    /// This index can be invalidated after a call to [`Voxels::set_voxel`], or
171    /// [`Voxels::resize_domain`].
172    pub linear_id: u32,
173    /// The voxel’s integer grid coordinates.
174    pub grid_coords: Point<i32>,
175    /// The voxel’s center position in the local-space of the [`Voxels`] shape it is part of.
176    pub center: Point<Real>,
177    /// The voxel’s state, indicating if it’s empty or full.
178    pub state: VoxelState,
179}
180
181/// A shape made of axis-aligned, uniformly sized, cubes (aka. voxels).
182///
183/// This shape is specialized to handle voxel worlds and voxelized obojects efficiently why ensuring
184/// that collision-detection isn’t affected by the so-called "internal edges problem" that can create
185/// artifacts when another objects rolls or slides against a flat voxelized surface.
186///
187/// The internal storage is compact (but not sparse at the moment), storing only one byte per voxel
188/// in the allowed domain. This has a generally smaller memory footprint than a mesh representation
189/// of the voxels.
190#[derive(Clone, Debug)]
191#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
192pub struct Voxels {
193    domain_mins: Point<i32>,
194    domain_maxs: Point<i32>,
195    states: Vec<VoxelState>, // Somehow switch to a sparse representation?
196    voxel_size: Vector<Real>,
197}
198
199impl Voxels {
200    /// Initializes a voxel shapes from the voxels grid coordinates.
201    ///
202    /// Each voxel will have its bottom-left-back corner located at
203    /// `grid_coordinates * voxel_size`; and its center at `(grid_coordinates + 0.5) * voxel_size`.
204    pub fn new(voxel_size: Vector<Real>, grid_coordinates: &[Point<i32>]) -> Self {
205        let mut domain_mins = grid_coordinates[0];
206        let mut domain_maxs = grid_coordinates[0];
207
208        for vox in grid_coordinates {
209            domain_mins = domain_mins.inf(vox);
210            domain_maxs = domain_maxs.sup(vox);
211        }
212
213        domain_maxs += Vector::repeat(1);
214        let dimensions = domain_maxs - domain_mins;
215        let voxels_count = dimensions.product();
216        let mut result = Self {
217            domain_mins,
218            domain_maxs,
219            states: vec![VoxelState::EMPTY; voxels_count as usize],
220            voxel_size,
221        };
222
223        for vox in grid_coordinates {
224            let index = result.linear_index(*vox);
225            result.states[index as usize] = VoxelState::INTERIOR;
226        }
227
228        result.recompute_voxels_data();
229        result
230    }
231
232    /// Computes a voxels shape from the set of `points`.
233    ///
234    /// The points are mapped to a regular grid centered at the provided point with smallest
235    /// coordinates, and with grid cell size equal to `scale`. It is OK if multiple points
236    /// fall into the same grid cell.
237    pub fn from_points(voxel_size: Vector<Real>, points: &[Point<Real>]) -> Self {
238        let voxels: Vec<_> = points
239            .iter()
240            .map(|pt| {
241                Point::from(
242                    pt.coords
243                        .component_div(&voxel_size)
244                        .map(|x| x.floor() as i32),
245                )
246            })
247            .collect();
248        Self::new(voxel_size, &voxels)
249    }
250
251    // TODO: support a crate like get_size2 (will require support on nalgebra too)?
252    /// An approximation of the memory usage (in bytes) for this struct plus
253    /// the memory it allocates dynamically.
254    pub fn total_memory_size(&self) -> usize {
255        size_of::<Self>() + self.heap_memory_size()
256    }
257
258    /// An approximation of the memory dynamically-allocated by this struct.
259    pub fn heap_memory_size(&self) -> usize {
260        // NOTE: if a new field is added to `Self`, adjust this function result.
261        let Self {
262            domain_mins: _,
263            domain_maxs: _,
264            states: data,
265            voxel_size: _,
266        } = self;
267        data.capacity() * size_of::<VoxelState>()
268    }
269
270    /// The extents of the total axis-aligned volume covered by this [`Voxels`] shape.
271    ///
272    /// This accounts for all the voxels reserved in the internal buffer of `self`, including empty
273    /// ones.
274    pub fn extents(&self) -> Vector<Real> {
275        self.dimensions()
276            .cast::<Real>()
277            .component_mul(&self.voxel_size)
278    }
279
280    /// The center of this shape’s domain (accounting for both empty and filled voxels).
281    pub fn domain_center(&self) -> Point<Real> {
282        (self
283            .domain_mins
284            .coords
285            .cast::<Real>()
286            .component_mul(&self.voxel_size)
287            + self.extents() / 2.0)
288            .into()
289    }
290
291    /// Sets the size of each voxel along each local coordinate axis.
292    pub fn set_voxel_size(&mut self, size: Vector<Real>) {
293        self.voxel_size = size;
294    }
295
296    /// The valid semi-open range of voxel grid indices.
297    ///
298    /// With `let [mins, maxs] = voxels.domain();` the valid indices along the dimension `i` are
299    /// all the indices in the range `mins[i]..maxs[i]` (i.e. `maxs[i]` is excluded).
300    pub fn domain(&self) -> [&Point<i32>; 2] {
301        [&self.domain_mins, &self.domain_maxs]
302    }
303
304    /// The number of voxels along each coordinate axis.
305    pub fn dimensions(&self) -> Vector<u32> {
306        (self.domain_maxs - self.domain_mins).map(|e| e as u32)
307    }
308
309    /// The size of each voxel part this [`Voxels`] shape.
310    pub fn voxel_size(&self) -> Vector<Real> {
311        self.voxel_size
312    }
313
314    /// Merges voxel state (neighborhood) information of a given voxel (and all its neighbors)
315    /// from `self` and `other`, to account for a recent change to the given `voxel` in `self`.
316    ///
317    /// This is designed to be called after `self` was modified with [`Voxels::set_voxel`] or
318    /// [`Voxels::try_set_voxel`].
319    ///
320    /// This is the same as [`Voxels::combine_voxel_states`] but localized to a single voxel and its
321    /// neighbors.
322    pub fn propagate_voxel_change(
323        &mut self,
324        other: &mut Self,
325        voxel: Point<i32>,
326        origin_shift: Vector<i32>,
327    ) {
328        let center_is_empty = self
329            .get_voxel_state(voxel)
330            .map(|vox| vox.is_empty())
331            .unwrap_or(true);
332        let center_state_delta =
333            other.update_neighbors_state(voxel - origin_shift, center_is_empty);
334
335        if let Some(state_id) = self.get_linear_index(voxel) {
336            self.states[state_id as usize].0 |= center_state_delta.0;
337        }
338    }
339
340    /// Merges voxel state (neighborhood) information of each voxel from `self` and `other`.
341    ///
342    /// This allows each voxel from one shape to be aware of the presence of neighbors belonging to
343    /// the other so that collision detection is capable of transitioning between the boundaries of
344    /// one shape to the other without hitting an internal edge.
345    ///
346    /// Both voxels shapes are assumed to have the same [`Self::voxel_size`].
347    /// If `other` lives in a coordinate space with a different origin than `self`, then
348    /// `origin_shift` represents the distance (as a multiple of the `voxel_size`) from the origin
349    /// of `self` to the origin of `other`. Therefore, a voxel with coordinates `key` on `other`
350    /// will have coordinates `key + origin_shift` on `self`.
351    pub fn combine_voxel_states(&mut self, other: &mut Self, origin_shift: Vector<i32>) {
352        let one = Vector::repeat(1);
353
354        // Intersect the domains + 1 cell.
355        let d0 = [self.domain_mins - one, self.domain_maxs + one * 2];
356        let d1 = [
357            other.domain_mins - one + origin_shift,
358            other.domain_maxs + one * 2 + origin_shift,
359        ];
360
361        let d01 = [d0[0].sup(&d1[0]), d0[1].inf(&d1[1])];
362        // Iterate on the domain intersection. If the voxel exists (and is non-empty) on both shapes, we
363        // simply need to combine their bitmasks. If it doesn’t exist on both shapes, we need to
364        // actually check the neighbors.
365        //
366        // The `domain` is expressed in the grid coordinate space of `self`.
367        for i in d01[0].x..d01[1].x {
368            for j in d01[0].y..d01[1].y {
369                #[cfg(feature = "dim2")]
370                let k_range = 0..1;
371                #[cfg(feature = "dim3")]
372                let k_range = d01[0].z..d01[1].z;
373                for _k in k_range {
374                    #[cfg(feature = "dim2")]
375                    let key0 = Point::new(i, j);
376                    #[cfg(feature = "dim3")]
377                    let key0 = Point::new(i, j, _k);
378                    let key1 = key0 - origin_shift;
379                    let id0 = self
380                        .get_linear_index(key0)
381                        .filter(|id| !self.states[*id as usize].is_empty());
382                    let id1 = other
383                        .get_linear_index(key1)
384                        .filter(|id| !other.states[*id as usize].is_empty());
385
386                    match (id0, id1) {
387                        (Some(id0), Some(id1)) => {
388                            self.states[id0 as usize].0 |= other.states[id1 as usize].0;
389                            other.states[id1 as usize].0 |= self.states[id0 as usize].0;
390                        }
391                        (Some(id0), None) => {
392                            self.states[id0 as usize].0 |=
393                                other.compute_voxel_neighborhood_bits(key1).0;
394                        }
395                        (None, Some(id1)) => {
396                            other.states[id1 as usize].0 |=
397                                self.compute_voxel_neighborhood_bits(key0).0;
398                        }
399                        (None, None) => { /* Nothing to adjust. */ }
400                    }
401                }
402            }
403        }
404    }
405
406    fn recompute_voxels_data(&mut self) {
407        for i in 0..self.states.len() {
408            let key = self.voxel_at_id(i as u32);
409            self.states[i] = self.compute_voxel_state(key);
410        }
411    }
412
413    /// Scale this shape.
414    pub fn scaled(mut self, scale: &Vector<Real>) -> Self {
415        self.voxel_size.component_mul_assign(scale);
416        self
417    }
418
419    /// Sets the voxel at the given grid coordinates, returning `None` if it lies outside [`Self::domain`].
420    ///
421    /// See [`Self::set_voxel`] for a method that automatically resizes the internal
422    /// storage of `self` if the key is out of the valid bounds.
423    pub fn try_set_voxel(&mut self, key: Point<i32>, is_filled: bool) -> Option<VoxelState> {
424        let id = self.get_linear_index(key)?;
425        let prev = self.states[id as usize];
426        let new_is_empty = !is_filled;
427
428        if prev.is_empty() ^ new_is_empty {
429            self.states[id as usize] = self.update_neighbors_state(key, new_is_empty);
430        }
431
432        Some(prev)
433    }
434
435    /// Inserts a voxel at the given key, even if it is out of the bounds of this shape.
436    ///
437    /// If `is_filed` is `true` and the key lies out of the bounds on this shape, the internal
438    /// voxels storage will be resized automatically. If a resize happens, the cost of the insertion
439    /// is `O(n)` where `n` is the capacity of `self`. If no resize happens, then the cost of
440    /// insertion is `O(1)`.
441    ///
442    /// Use [`Self::try_set_voxel`] instead for a version that will be a no-op if the provided
443    /// coordinates are outside the [`Self::domain`], avoiding potential internal reallocations.
444    pub fn set_voxel(&mut self, key: Point<i32>, is_filled: bool) -> Option<VoxelState> {
445        if !self.is_voxel_in_bounds(key) && is_filled {
446            let dims = self.dimensions();
447
448            // Add 10% extra padding.
449            let extra = dims.map(|k| k * 10 / 100);
450            let mut new_domain_mins = self.domain_mins;
451            let mut new_domain_maxs = self.domain_maxs;
452
453            for k in 0..DIM {
454                if key[k] < self.domain_mins[k] {
455                    new_domain_mins[k] = key[k] - extra[k] as i32;
456                }
457
458                if key[k] >= self.domain_maxs[k] {
459                    new_domain_maxs[k] = key[k] + extra[k] as i32 + 1;
460                }
461            }
462
463            self.resize_domain(new_domain_mins, new_domain_maxs);
464
465            self.try_set_voxel(key, is_filled)
466        } else {
467            self.try_set_voxel(key, is_filled)
468        }
469    }
470
471    /// Set the model domain.
472    ///
473    /// The domain can be smaller or larger than the current one. Voxels in any overlap between
474    /// the current and new domain will be preserved.
475    ///
476    /// If for any index `i`, `domain_maxs[i] <= domain_mins[i]`, then the new domain is invalid
477    /// and this operation will result in a no-op.
478    pub fn resize_domain(&mut self, domain_mins: Point<i32>, domain_maxs: Point<i32>) {
479        if self.domain_mins == domain_mins && self.domain_maxs == domain_maxs {
480            // Nothing to change.
481            return;
482        }
483
484        if let Some(new_shape) = self.with_resized_domain(domain_mins, domain_maxs) {
485            *self = new_shape;
486        }
487    }
488
489    /// Clone this voxels shape with a new size.
490    ///
491    /// The domain can be smaller or larger than the current one. Voxels in any overlap between
492    /// the current and new domain will be preserved.
493    ///
494    /// If for any index `i`, `domain_maxs[i] <= domain_mins[i]`, then the new domain is invalid
495    /// and this operation returns `None`.
496    #[must_use]
497    pub fn with_resized_domain(
498        &self,
499        domain_mins: Point<i32>,
500        domain_maxs: Point<i32>,
501    ) -> Option<Self> {
502        if self.domain_mins == domain_mins && self.domain_maxs == domain_maxs {
503            // Nothing to change, just clone as-is.
504            return Some(self.clone());
505        }
506
507        let new_dim = domain_maxs - domain_mins;
508        if new_dim.iter().any(|d| *d <= 0) {
509            log::error!("Invalid domain provided for resizing a voxels shape. New domain: {domain_mins:?} - {domain_maxs:?}; new domain size: {new_dim:?}");
510            return None;
511        }
512
513        let new_len = new_dim.iter().map(|x| *x as usize).product();
514
515        let mut new_shape = Self {
516            domain_mins,
517            domain_maxs,
518            states: vec![VoxelState::EMPTY; new_len],
519            voxel_size: self.voxel_size,
520        };
521
522        for i in 0..self.states.len() {
523            let key = self.voxel_at_id(i as u32);
524            let new_i = new_shape.linear_index(key);
525            new_shape.states[new_i as usize] = self.states[i];
526        }
527
528        Some(new_shape)
529    }
530
531    /// Checks if the given key is within [`Self::domain`].
532    #[cfg(feature = "dim2")]
533    pub fn is_voxel_in_bounds(&self, key: Point<i32>) -> bool {
534        key[0] >= self.domain_mins[1]
535            && key[0] < self.domain_maxs[0]
536            && key[1] >= self.domain_mins[1]
537            && key[1] < self.domain_maxs[1]
538    }
539
540    /// Checks if the given key is within [`Self::domain`].
541    #[cfg(feature = "dim3")]
542    pub fn is_voxel_in_bounds(&self, key: Point<i32>) -> bool {
543        key[0] >= self.domain_mins[0]
544            && key[0] < self.domain_maxs[0]
545            && key[1] >= self.domain_mins[1]
546            && key[1] < self.domain_maxs[1]
547            && key[2] >= self.domain_mins[2]
548            && key[2] < self.domain_maxs[2]
549    }
550
551    /// Updates the state of the neighbors of the voxel `key`.
552    ///
553    /// Modifies the state of the neighbors of `key` to account for it being empty or full.
554    /// Returns (but doesn’t modify) the new state of the voxel specified by `key`.
555    #[must_use]
556    fn update_neighbors_state(&mut self, key: Point<i32>, center_is_empty: bool) -> VoxelState {
557        let mut key_data = 0;
558
559        for k in 0..DIM {
560            let mut left = key;
561            let mut right = key;
562            left[k] -= 1;
563            right[k] += 1;
564
565            if let Some(left_id) = self.get_linear_index(left) {
566                if !self.states[left_id as usize].is_empty() {
567                    if center_is_empty {
568                        self.states[left_id as usize].0 &= !(1 << (k * 2));
569                    } else {
570                        self.states[left_id as usize].0 |= 1 << (k * 2);
571                        key_data |= 1 << (k * 2 + 1);
572                    }
573                }
574            }
575
576            if let Some(right_id) = self.get_linear_index(right) {
577                if !self.states[right_id as usize].is_empty() {
578                    if center_is_empty {
579                        self.states[right_id as usize].0 &= !(1 << (k * 2 + 1));
580                    } else {
581                        self.states[right_id as usize].0 |= 1 << (k * 2 + 1);
582                        key_data |= 1 << (k * 2);
583                    }
584                }
585            }
586        }
587
588        if center_is_empty {
589            VoxelState::EMPTY
590        } else {
591            VoxelState(key_data)
592        }
593    }
594
595    /// The AABB of the voxel with the given quantized `key`.
596    pub fn voxel_aabb(&self, key: Point<i32>) -> Aabb {
597        let center = self.voxel_center(key);
598        let hext = self.voxel_size / 2.0;
599        Aabb::from_half_extents(center, hext)
600    }
601
602    /// Returns the state of a given voxel.
603    ///
604    /// Panics if the key is out of the bounds defined by [`Self::domain`].
605    pub fn voxel_state(&self, key: Point<i32>) -> VoxelState {
606        self.states[self.linear_index(key) as usize]
607    }
608
609    /// Returns the state of a given voxel, if it is within the bounds of [`Self::domain`].
610    pub fn get_voxel_state(&self, key: Point<i32>) -> Option<VoxelState> {
611        Some(self.states[self.get_linear_index(key)? as usize])
612    }
613
614    /// Calculates the grid coordinates of the voxel containing the given `point`, regardless
615    /// of [`Self::domain`].
616    pub fn voxel_at_point_unchecked(&self, point: Point<Real>) -> Point<i32> {
617        point
618            .coords
619            .component_div(&self.voxel_size)
620            .map(|x| x.floor() as i32)
621            .into()
622    }
623
624    /// Gets the key of the voxel containing the given `pt`.
625    ///
626    /// Note that the returned key might address a voxel that is empty.
627    /// `None` is returned if the point is out of the domain of `self`.
628    pub fn voxel_at_point(&self, pt: Point<Real>) -> Option<Point<i32>> {
629        let quant = self.voxel_at_point_unchecked(pt);
630        if quant[0] < self.domain_mins[0]
631            || quant[1] < self.domain_mins[1]
632            || quant[0] >= self.domain_maxs[0]
633            || quant[1] >= self.domain_maxs[1]
634        {
635            return None;
636        }
637
638        #[cfg(feature = "dim3")]
639        if quant[2] < self.domain_mins[2] || quant[2] >= self.domain_maxs[2] {
640            return None;
641        }
642
643        Some(quant)
644    }
645
646    /// Clamps an arbitrary voxel into the valid domain of `self`.
647    pub fn clamp_voxel(&self, key: Point<i32>) -> Point<i32> {
648        key.coords
649            .zip_zip_map(
650                &self.domain_mins.coords,
651                &self.domain_maxs.coords,
652                |k, min, max| k.clamp(min, max - 1),
653            )
654            .into()
655    }
656
657    /// The range of grid coordinates of voxels intersecting the given AABB.
658    ///
659    /// The returned range covers both empty and non-empty voxels, and is not limited to the
660    /// bounds defined by [`Self::domain`].
661    /// The range is semi, open, i.e., the range along each dimension `i` is understood as
662    /// the semi-open interval: `range[0][i]..range[1][i]`.
663    pub fn voxel_range_intersecting_local_aabb(&self, aabb: &Aabb) -> [Point<i32>; 2] {
664        let mins = aabb
665            .mins
666            .coords
667            .component_div(&self.voxel_size)
668            .map(|x| x.floor() as i32);
669        let maxs = aabb
670            .maxs
671            .coords
672            .component_div(&self.voxel_size)
673            .map(|x| x.ceil() as i32);
674        [mins.into(), maxs.into()]
675    }
676
677    /// The AABB of a given range of voxels.
678    ///
679    /// The AABB is computed independently of [`Self::domain`] and independently of whether
680    /// the voxels contained within are empty or not.
681    pub fn voxel_range_aabb(&self, mins: Point<i32>, maxs: Point<i32>) -> Aabb {
682        Aabb {
683            mins: mins
684                .cast::<Real>()
685                .coords
686                .component_mul(&self.voxel_size)
687                .into(),
688            maxs: maxs
689                .cast::<Real>()
690                .coords
691                .component_mul(&self.voxel_size)
692                .into(),
693        }
694    }
695
696    /// Aligns the given AABB with the voxelized grid.
697    ///
698    /// The aligned is calculated such that the returned AABB has corners lying at the grid
699    /// intersections (i.e. matches voxel corners) and fully contains the input `aabb`.
700    pub fn align_aabb_to_grid(&self, aabb: &Aabb) -> Aabb {
701        let mins = aabb
702            .mins
703            .coords
704            .zip_map(&self.voxel_size, |m, sz| (m / sz).floor() * m)
705            .into();
706        let maxs = aabb
707            .maxs
708            .coords
709            .zip_map(&self.voxel_size, |m, sz| (m / sz).ceil() * m)
710            .into();
711        Aabb { mins, maxs }
712    }
713
714    /// Iterates through every voxel intersecting the given aabb.
715    ///
716    /// Returns the voxel’s linearized id, center, and state.
717    pub fn voxels_intersecting_local_aabb(
718        &self,
719        aabb: &Aabb,
720    ) -> impl Iterator<Item = VoxelData> + '_ {
721        let [mins, maxs] = self.voxel_range_intersecting_local_aabb(aabb);
722        self.voxels_in_range(mins, maxs)
723    }
724
725    /// The center point of all the voxels in this shape (including empty ones).
726    ///
727    /// The voxel data associated to each center is provided to determine what kind of voxel
728    /// it is (and, in particular, if it is empty or full).
729    pub fn voxels(&self) -> impl Iterator<Item = VoxelData> + '_ {
730        self.voxels_in_range(self.domain_mins, self.domain_maxs)
731    }
732
733    /// Splits this voxels shape into two subshapes.
734    ///
735    /// The first subshape contains all the voxels which centers are inside the `aabb`.
736    /// The second subshape contains all the remaining voxels.
737    pub fn split_with_box(&self, aabb: &Aabb) -> (Option<Self>, Option<Self>) {
738        // TODO: optimize this?
739        let mut in_box = vec![];
740        let mut rest = vec![];
741        for vox in self.voxels() {
742            if !vox.state.is_empty() {
743                if aabb.contains_local_point(&vox.center) {
744                    in_box.push(vox.center);
745                } else {
746                    rest.push(vox.center);
747                }
748            }
749        }
750
751        let in_box = if !in_box.is_empty() {
752            Some(Voxels::from_points(self.voxel_size, &in_box))
753        } else {
754            None
755        };
756
757        let rest = if !rest.is_empty() {
758            Some(Voxels::from_points(self.voxel_size, &rest))
759        } else {
760            None
761        };
762
763        (in_box, rest)
764    }
765
766    /// Iterate through the data of all the voxels within the given (semi-open) voxel grid indices.
767    ///
768    /// Note that this yields both empty and non-empty voxels within the range. This does not
769    /// include any voxel that falls outside [`Self::domain`].
770    #[cfg(feature = "dim2")]
771    pub fn voxels_in_range(
772        &self,
773        mins: Point<i32>,
774        maxs: Point<i32>,
775    ) -> impl Iterator<Item = VoxelData> + '_ {
776        let mins = mins.coords.sup(&self.domain_mins.coords);
777        let maxs = maxs.coords.inf(&self.domain_maxs.coords);
778
779        (mins[0]..maxs[0]).flat_map(move |ix| {
780            (mins[1]..maxs[1]).map(move |iy| {
781                let grid_coords = Point::new(ix, iy);
782                let vid = self.linear_index(grid_coords);
783                let center =
784                    Vector::new(ix as Real + 0.5, iy as Real + 0.5).component_mul(&self.voxel_size);
785                VoxelData {
786                    linear_id: vid,
787                    grid_coords,
788                    center: center.into(),
789                    state: self.states[vid as usize],
790                }
791            })
792        })
793    }
794
795    /// Iterate through the data of all the voxels within the given (semi-open) voxel grid indices.
796    ///
797    /// Note that this yields both empty and non-empty voxels within the range. This does not
798    /// include any voxel that falls outside [`Self::domain`].
799    #[cfg(feature = "dim3")]
800    pub fn voxels_in_range(
801        &self,
802        mins: Point<i32>,
803        maxs: Point<i32>,
804    ) -> impl Iterator<Item = VoxelData> + '_ {
805        let mins = mins.coords.sup(&self.domain_mins.coords);
806        let maxs = maxs.coords.inf(&self.domain_maxs.coords);
807
808        (mins[0]..maxs[0]).flat_map(move |ix| {
809            (mins[1]..maxs[1]).flat_map(move |iy| {
810                (mins[2]..maxs[2]).map(move |iz| {
811                    let grid_coords = Point::new(ix, iy, iz);
812                    let vid = self.linear_index(grid_coords);
813                    let center = Vector::new(ix as Real + 0.5, iy as Real + 0.5, iz as Real + 0.5)
814                        .component_mul(&self.voxel_size)
815                        .into();
816                    VoxelData {
817                        linear_id: vid,
818                        grid_coords,
819                        center,
820                        state: self.states[vid as usize],
821                    }
822                })
823            })
824        })
825    }
826
827    /// Gets linearized index associated to the given voxel key if it lies within [`Self::domain`].
828    pub fn get_linear_index(&self, key: Point<i32>) -> Option<u32> {
829        if key[0] < self.domain_mins[0]
830            || key[0] >= self.domain_maxs[0]
831            || key[1] < self.domain_mins[1]
832            || key[1] >= self.domain_maxs[1]
833        {
834            return None;
835        }
836
837        #[cfg(feature = "dim3")]
838        if key[2] < self.domain_mins[2] || key[2] >= self.domain_maxs[2] {
839            return None;
840        }
841
842        Some(self.linear_index(key))
843    }
844
845    /// The linearized index associated to the given voxel key.
846    #[cfg(feature = "dim2")]
847    pub fn linear_index(&self, voxel_key: Point<i32>) -> u32 {
848        let dims = self.dimensions();
849        let rel_key = voxel_key - self.domain_mins;
850        (rel_key.x + rel_key.y * dims[0] as i32) as u32
851    }
852
853    /// The linearized index associated to the given voxel key.
854    #[cfg(feature = "dim3")]
855    pub fn linear_index(&self, voxel_key: Point<i32>) -> u32 {
856        let dims = self.dimensions();
857        let rel_key = voxel_key - self.domain_mins;
858        rel_key.x as u32 + rel_key.y as u32 * dims[0] + rel_key.z as u32 * dims[0] * dims[1]
859    }
860
861    /// The key of the voxel at the given linearized index.
862    #[cfg(feature = "dim2")]
863    pub fn voxel_at_id(&self, linear_index: u32) -> Point<i32> {
864        let dim0 = self.domain_maxs[0] - self.domain_mins[0];
865        let y = linear_index as i32 / dim0;
866        let x = linear_index as i32 % dim0;
867        self.domain_mins + Vector::new(x, y)
868    }
869
870    /// The key of the voxel at the given linearized index.
871    #[cfg(feature = "dim3")]
872    pub fn voxel_at_id(&self, linear_index: u32) -> Point<i32> {
873        let dims = self.dimensions();
874
875        let d0d1 = dims[0] * dims[1];
876        let z = linear_index / d0d1;
877        let y = (linear_index - z * d0d1) / dims[0];
878        let x = linear_index % dims[0];
879        self.domain_mins + Vector::new(x as i32, y as i32, z as i32)
880    }
881
882    /// The center of the voxel with the given key.
883    pub fn voxel_center(&self, key: Point<i32>) -> Point<Real> {
884        (key.cast::<Real>() + Vector::repeat(0.5))
885            .coords
886            .component_mul(&self.voxel_size)
887            .into()
888    }
889
890    fn compute_voxel_state(&self, key: Point<i32>) -> VoxelState {
891        if self.states[self.linear_index(key) as usize].is_empty() {
892            return VoxelState::EMPTY;
893        }
894
895        self.compute_voxel_neighborhood_bits(key)
896    }
897
898    fn compute_voxel_neighborhood_bits(&self, key: Point<i32>) -> VoxelState {
899        let mut occupied_faces = 0;
900
901        for k in 0..DIM {
902            let (mut prev, mut next) = (key, key);
903            prev[k] -= 1;
904            next[k] += 1;
905
906            if let Some(next_id) = self.get_linear_index(next) {
907                if !self.states[next_id as usize].is_empty() {
908                    occupied_faces |= 1 << (k * 2);
909                }
910            }
911            if let Some(prev_id) = self.get_linear_index(prev) {
912                if !self.states[prev_id as usize].is_empty() {
913                    occupied_faces |= 1 << (k * 2 + 1);
914                }
915            }
916        }
917
918        VoxelState(occupied_faces)
919    }
920}
921
922// NOTE: this code is used to generate the constant tables
923// FACES_TO_VOXEL_TYPES, FACES_TO_FEATURE_MASKS, FACES_TO_OCTANT_MASKS.
924#[allow(dead_code)]
925#[cfg(feature = "dim2")]
926#[cfg(test)]
927fn gen_const_tables() {
928    // The `j-th` bit of `faces_adj_to_vtx[i]` is set to 1, if the j-th face of the AABB (based on
929    // the face order depicted in `AABB::FACES_VERTEX_IDS`) is adjacent to the `i` vertex of the AABB
930    // (vertices are indexed as per the diagram depicted in the `FACES_VERTEX_IDS` doc.
931    // Each entry of this will always have exactly 3 bits set.
932    let mut faces_adj_to_vtx = [0usize; 4];
933
934    for fid in 0..4 {
935        let vids = Aabb::FACES_VERTEX_IDS[fid];
936        let key = 1 << fid;
937        faces_adj_to_vtx[vids.0] |= key;
938        faces_adj_to_vtx[vids.1] |= key;
939    }
940
941    /*
942     * FACES_TO_VOXEL_TYPES
943     */
944    std::println!("const FACES_TO_VOXEL_TYPES: [VoxelType; 17] = [");
945    'outer: for i in 0usize..16 {
946        // If any vertex of the voxel has three faces with no adjacent voxels,
947        // then the voxel type is Vertex.
948        for adjs in faces_adj_to_vtx.iter() {
949            if (*adjs & i) == 0 {
950                std::println!("VoxelType::Vertex,");
951                continue 'outer;
952            }
953        }
954
955        // If one face doesn’t have any adjacent voxel,
956        // then the voxel type is Face.
957        for fid in 0..4 {
958            if ((1 << fid) & i) == 0 {
959                std::println!("VoxelType::Face,");
960                continue 'outer;
961            }
962        }
963    }
964
965    // Add final entries for special values.
966    std::println!("VoxelType::Interior,");
967    std::println!("VoxelType::Empty,");
968    std::println!("];");
969
970    /*
971     * FACES_TO_FEATURE_MASKS
972     */
973    std::println!("const FACES_TO_FEATURE_MASKS: [u16; 17] = [");
974    for i in 0usize..16 {
975        // Each bit set indicates a convex vertex that can lead to collisions.
976        // The result will be nonzero only for `VoxelType::Vertex` voxels.
977        let mut vtx_key = 0;
978        for (vid, adjs) in faces_adj_to_vtx.iter().enumerate() {
979            if (*adjs & i) == 0 {
980                vtx_key |= 1 << vid;
981            }
982        }
983
984        if vtx_key != 0 {
985            std::println!("0b{:b},", vtx_key as u16);
986            continue;
987        }
988
989        // Each bit set indicates an exposed face that can lead to collisions.
990        // The result will be nonzero only for `VoxelType::Face` voxels.
991        let mut face_key = 0;
992        for fid in 0..4 {
993            if ((1 << fid) & i) == 0 {
994                face_key |= 1 << fid;
995            }
996        }
997
998        if face_key != 0 {
999            std::println!("0b{:b},", face_key as u16);
1000            continue;
1001        }
1002    }
1003
1004    std::println!("0b{:b},", u16::MAX);
1005    std::println!("0,");
1006    std::println!("];");
1007
1008    /*
1009     * Faces to octant masks.
1010     */
1011    std::println!("const FACES_TO_OCTANT_MASKS: [u32; 17] = [");
1012    for i in 0usize..16 {
1013        // First test if we have vertices.
1014        let mut octant_mask = 0;
1015        let mut set_mask = |mask, octant| {
1016            // NOTE: we don’t overwrite any mask already set for the octant.
1017            if (octant_mask >> (octant * 3)) & 0b0111 == 0 {
1018                octant_mask |= mask << (octant * 3);
1019            }
1020        };
1021
1022        for (vid, adjs) in faces_adj_to_vtx.iter().enumerate() {
1023            if (*adjs & i) == 0 {
1024                set_mask(1, vid);
1025            }
1026        }
1027
1028        // This is the index of the normal of the faces given by
1029        // Aabb::FACES_VERTEX_IDS.
1030        const FX: u32 = OctantPattern::FACE_X;
1031        const FY: u32 = OctantPattern::FACE_Y;
1032        const FACE_NORMALS: [u32; 4] = [FX, FX, FY, FY];
1033
1034        #[allow(clippy::needless_range_loop)]
1035        for fid in 0..4 {
1036            if ((1 << fid) & i) == 0 {
1037                let vid = Aabb::FACES_VERTEX_IDS[fid];
1038                let mask = FACE_NORMALS[fid];
1039
1040                set_mask(mask, vid.0);
1041                set_mask(mask, vid.1);
1042            }
1043        }
1044        std::println!("0b{:b},", octant_mask);
1045    }
1046    std::println!("0,");
1047    std::println!("];");
1048}
1049
1050// NOTE: this code is used to generate the constant tables
1051// FACES_TO_VOXEL_TYPES, FACES_TO_FEATURE_MASKS, FACES_TO_OCTANT_MASKS.
1052#[allow(dead_code)]
1053#[cfg(feature = "dim3")]
1054#[cfg(test)]
1055fn gen_const_tables() {
1056    // The `j-th` bit of `faces_adj_to_vtx[i]` is set to 1, if the j-th face of the AABB (based on
1057    // the face order depicted in `AABB::FACES_VERTEX_IDS`) is adjacent to the `i` vertex of the AABB
1058    // (vertices are indexed as per the diagram depicted in the `FACES_VERTEX_IDS` doc.
1059    // Each entry of this will always have exactly 3 bits set.
1060    let mut faces_adj_to_vtx = [0usize; 8];
1061
1062    // The `j-th` bit of `faces_adj_to_vtx[i]` is set to 1, if the j-th edge of the AABB (based on
1063    // the edge order depicted in `AABB::EDGES_VERTEX_IDS`) is adjacent to the `i` vertex of the AABB
1064    // (vertices are indexed as per the diagram depicted in the `FACES_VERTEX_IDS` doc.
1065    // Each entry of this will always have exactly 2 bits set.
1066    let mut faces_adj_to_edge = [0usize; 12];
1067
1068    for fid in 0..6 {
1069        let vids = Aabb::FACES_VERTEX_IDS[fid];
1070        let key = 1 << fid;
1071        faces_adj_to_vtx[vids.0] |= key;
1072        faces_adj_to_vtx[vids.1] |= key;
1073        faces_adj_to_vtx[vids.2] |= key;
1074        faces_adj_to_vtx[vids.3] |= key;
1075    }
1076
1077    #[allow(clippy::needless_range_loop)]
1078    for eid in 0..12 {
1079        let evids = Aabb::EDGES_VERTEX_IDS[eid];
1080        for fid in 0..6 {
1081            let fvids = Aabb::FACES_VERTEX_IDS[fid];
1082            if (fvids.0 == evids.0
1083                || fvids.1 == evids.0
1084                || fvids.2 == evids.0
1085                || fvids.3 == evids.0)
1086                && (fvids.0 == evids.1
1087                    || fvids.1 == evids.1
1088                    || fvids.2 == evids.1
1089                    || fvids.3 == evids.1)
1090            {
1091                let key = 1 << fid;
1092                faces_adj_to_edge[eid] |= key;
1093            }
1094        }
1095    }
1096
1097    /*
1098     * FACES_TO_VOXEL_TYPES
1099     */
1100    std::println!("const FACES_TO_VOXEL_TYPES: [VoxelType; 65] = [");
1101    'outer: for i in 0usize..64 {
1102        // If any vertex of the voxel has three faces with no adjacent voxels,
1103        // then the voxel type is Vertex.
1104        for adjs in faces_adj_to_vtx.iter() {
1105            if (*adjs & i) == 0 {
1106                std::println!("VoxelType::Vertex,");
1107                continue 'outer;
1108            }
1109        }
1110
1111        // If any vertex of the voxel has three faces with no adjacent voxels,
1112        // then the voxel type is Edge.
1113        for adjs in faces_adj_to_edge.iter() {
1114            if (*adjs & i) == 0 {
1115                std::println!("VoxelType::Edge,");
1116                continue 'outer;
1117            }
1118        }
1119
1120        // If one face doesn’t have any adjacent voxel,
1121        // then the voxel type is Face.
1122        for fid in 0..6 {
1123            if ((1 << fid) & i) == 0 {
1124                std::println!("VoxelType::Face,");
1125                continue 'outer;
1126            }
1127        }
1128    }
1129
1130    // Add final entries for special values.
1131    std::println!("VoxelType::Interior,");
1132    std::println!("VoxelType::Empty,");
1133    std::println!("];");
1134
1135    /*
1136     * FACES_TO_FEATURE_MASKS
1137     */
1138    std::println!("const FACES_TO_FEATURE_MASKS: [u16; 65] = [");
1139    for i in 0usize..64 {
1140        // Each bit set indicates a convex vertex that can lead to collisions.
1141        // The result will be nonzero only for `VoxelType::Vertex` voxels.
1142        let mut vtx_key = 0;
1143        for (vid, adjs) in faces_adj_to_vtx.iter().enumerate() {
1144            if (*adjs & i) == 0 {
1145                vtx_key |= 1 << vid;
1146            }
1147        }
1148
1149        if vtx_key != 0 {
1150            std::println!("0b{:b},", vtx_key as u16);
1151            continue;
1152        }
1153
1154        // Each bit set indicates a convex edge that can lead to collisions.
1155        // The result will be nonzero only for `VoxelType::Edge` voxels.
1156        let mut edge_key = 0;
1157        for (eid, adjs) in faces_adj_to_edge.iter().enumerate() {
1158            if (*adjs & i) == 0 {
1159                edge_key |= 1 << eid;
1160            }
1161        }
1162
1163        if edge_key != 0 {
1164            std::println!("0b{:b},", edge_key as u16);
1165            continue;
1166        }
1167
1168        // Each bit set indicates an exposed face that can lead to collisions.
1169        // The result will be nonzero only for `VoxelType::Face` voxels.
1170        let mut face_key = 0;
1171        for fid in 0..6 {
1172            if ((1 << fid) & i) == 0 {
1173                face_key |= 1 << fid;
1174            }
1175        }
1176
1177        if face_key != 0 {
1178            std::println!("0b{:b},", face_key as u16);
1179            continue;
1180        }
1181    }
1182
1183    std::println!("0b{:b},", u16::MAX);
1184    std::println!("0,");
1185    std::println!("];");
1186
1187    /*
1188     * Faces to octant masks.
1189     */
1190    std::println!("const FACES_TO_OCTANT_MASKS: [u32; 65] = [");
1191    for i in 0usize..64 {
1192        // First test if we have vertices.
1193        let mut octant_mask = 0;
1194        let mut set_mask = |mask, octant| {
1195            // NOTE: we don’t overwrite any mask already set for the octant.
1196            if (octant_mask >> (octant * 3)) & 0b0111 == 0 {
1197                octant_mask |= mask << (octant * 3);
1198            }
1199        };
1200
1201        for (vid, adjs) in faces_adj_to_vtx.iter().enumerate() {
1202            if (*adjs & i) == 0 {
1203                set_mask(1, vid);
1204            }
1205        }
1206
1207        // This is the index of the axis porting the edges given by
1208        // Aabb::EDGES_VERTEX_IDS.
1209        const EX: u32 = OctantPattern::EDGE_X;
1210        const EY: u32 = OctantPattern::EDGE_Y;
1211        const EZ: u32 = OctantPattern::EDGE_Z;
1212        const EDGE_AXIS: [u32; 12] = [EX, EY, EX, EY, EX, EY, EX, EY, EZ, EZ, EZ, EZ];
1213        for (eid, adjs) in faces_adj_to_edge.iter().enumerate() {
1214            if (*adjs & i) == 0 {
1215                let vid = Aabb::EDGES_VERTEX_IDS[eid];
1216                let mask = EDGE_AXIS[eid];
1217
1218                set_mask(mask, vid.0);
1219                set_mask(mask, vid.1);
1220            }
1221        }
1222
1223        // This is the index of the normal of the faces given by
1224        // Aabb::FACES_VERTEX_IDS.
1225        const FX: u32 = OctantPattern::FACE_X;
1226        const FY: u32 = OctantPattern::FACE_Y;
1227        const FZ: u32 = OctantPattern::FACE_Z;
1228        const FACE_NORMALS: [u32; 6] = [FX, FX, FY, FY, FZ, FZ];
1229
1230        #[allow(clippy::needless_range_loop)]
1231        for fid in 0..6 {
1232            if ((1 << fid) & i) == 0 {
1233                let vid = Aabb::FACES_VERTEX_IDS[fid];
1234                let mask = FACE_NORMALS[fid];
1235
1236                set_mask(mask, vid.0);
1237                set_mask(mask, vid.1);
1238                set_mask(mask, vid.2);
1239                set_mask(mask, vid.3);
1240            }
1241        }
1242        std::println!("0b{:b},", octant_mask);
1243    }
1244    std::println!("0,");
1245    std::println!("];");
1246}
1247
1248// Index to the item of FACES_TO_VOXEL_TYPES which identifies interior voxels.
1249#[cfg(feature = "dim2")]
1250const INTERIOR_FACE_MASK: u8 = 0b0000_1111;
1251#[cfg(feature = "dim3")]
1252const INTERIOR_FACE_MASK: u8 = 0b0011_1111;
1253// Index to the item of FACES_TO_VOXEL_TYPES which identifies empty voxels.
1254
1255#[cfg(feature = "dim2")]
1256const EMPTY_FACE_MASK: u8 = 0b0001_0000;
1257#[cfg(feature = "dim3")]
1258const EMPTY_FACE_MASK: u8 = 0b0100_0000;
1259
1260/// The voxel type deduced from adjacency information.
1261///
1262/// See the documentation of [`VoxelType`] for additional information on what each enum variant
1263/// means.
1264///
1265/// In 3D there are 6 neighbor faces => 64 cases + 1 empty case.
1266#[cfg(feature = "dim3")]
1267const FACES_TO_VOXEL_TYPES: [VoxelType; 65] = [
1268    VoxelType::Vertex,
1269    VoxelType::Vertex,
1270    VoxelType::Vertex,
1271    VoxelType::Edge,
1272    VoxelType::Vertex,
1273    VoxelType::Vertex,
1274    VoxelType::Vertex,
1275    VoxelType::Edge,
1276    VoxelType::Vertex,
1277    VoxelType::Vertex,
1278    VoxelType::Vertex,
1279    VoxelType::Edge,
1280    VoxelType::Edge,
1281    VoxelType::Edge,
1282    VoxelType::Edge,
1283    VoxelType::Face,
1284    VoxelType::Vertex,
1285    VoxelType::Vertex,
1286    VoxelType::Vertex,
1287    VoxelType::Edge,
1288    VoxelType::Vertex,
1289    VoxelType::Vertex,
1290    VoxelType::Vertex,
1291    VoxelType::Edge,
1292    VoxelType::Vertex,
1293    VoxelType::Vertex,
1294    VoxelType::Vertex,
1295    VoxelType::Edge,
1296    VoxelType::Edge,
1297    VoxelType::Edge,
1298    VoxelType::Edge,
1299    VoxelType::Face,
1300    VoxelType::Vertex,
1301    VoxelType::Vertex,
1302    VoxelType::Vertex,
1303    VoxelType::Edge,
1304    VoxelType::Vertex,
1305    VoxelType::Vertex,
1306    VoxelType::Vertex,
1307    VoxelType::Edge,
1308    VoxelType::Vertex,
1309    VoxelType::Vertex,
1310    VoxelType::Vertex,
1311    VoxelType::Edge,
1312    VoxelType::Edge,
1313    VoxelType::Edge,
1314    VoxelType::Edge,
1315    VoxelType::Face,
1316    VoxelType::Edge,
1317    VoxelType::Edge,
1318    VoxelType::Edge,
1319    VoxelType::Face,
1320    VoxelType::Edge,
1321    VoxelType::Edge,
1322    VoxelType::Edge,
1323    VoxelType::Face,
1324    VoxelType::Edge,
1325    VoxelType::Edge,
1326    VoxelType::Edge,
1327    VoxelType::Face,
1328    VoxelType::Face,
1329    VoxelType::Face,
1330    VoxelType::Face,
1331    VoxelType::Interior,
1332    VoxelType::Empty,
1333];
1334
1335/// Indicates the convex features of each voxel that can lead to collisions.
1336///
1337/// The interpretation of each bit differs depending on the corresponding voxel type in
1338/// `FACES_TO_VOXEL_TYPES`:
1339/// - For `VoxelType::Vertex`: the i-th bit set to `1` indicates that the i-th AABB vertex is convex
1340///   and might lead to collisions.
1341/// - For `VoxelType::Edge`: the i-th bit set to `1` indicates that the i-th edge from `Aabb::EDGES_VERTEX_IDS`
1342///   is convex and might lead to collisions.
1343/// - For `VoxelType::Face`: the i-th bit set to `1` indicates that the i-th face from `Aabb::FACES_VERTEX_IDS`
1344///   is exposed and might lead to collisions.
1345#[cfg(feature = "dim3")]
1346const FACES_TO_FEATURE_MASKS: [u16; 65] = [
1347    0b11111111,
1348    0b10011001,
1349    0b1100110,
1350    0b1010101,
1351    0b110011,
1352    0b10001,
1353    0b100010,
1354    0b10001,
1355    0b11001100,
1356    0b10001000,
1357    0b1000100,
1358    0b1000100,
1359    0b10101010,
1360    0b10001000,
1361    0b100010,
1362    0b110000,
1363    0b1111,
1364    0b1001,
1365    0b110,
1366    0b101,
1367    0b11,
1368    0b1,
1369    0b10,
1370    0b1,
1371    0b1100,
1372    0b1000,
1373    0b100,
1374    0b100,
1375    0b1010,
1376    0b1000,
1377    0b10,
1378    0b100000,
1379    0b11110000,
1380    0b10010000,
1381    0b1100000,
1382    0b1010000,
1383    0b110000,
1384    0b10000,
1385    0b100000,
1386    0b10000,
1387    0b11000000,
1388    0b10000000,
1389    0b1000000,
1390    0b1000000,
1391    0b10100000,
1392    0b10000000,
1393    0b100000,
1394    0b10000,
1395    0b111100000000,
1396    0b100100000000,
1397    0b11000000000,
1398    0b1100,
1399    0b1100000000,
1400    0b100000000,
1401    0b1000000000,
1402    0b1000,
1403    0b110000000000,
1404    0b100000000000,
1405    0b10000000000,
1406    0b100,
1407    0b11,
1408    0b10,
1409    0b1,
1410    0b1111111111111111,
1411    0,
1412];
1413
1414/// Each octant is assigned three contiguous bits.
1415#[cfg(feature = "dim3")]
1416const FACES_TO_OCTANT_MASKS: [u32; 65] = [
1417    0b1001001001001001001001,
1418    0b1010010001001010010001,
1419    0b10001001010010001001010,
1420    0b10010010010010010010010,
1421    0b11011001001011011001001,
1422    0b11111010001011111010001,
1423    0b111011001010111011001010,
1424    0b111111010010111111010010,
1425    0b1001011011001001011011,
1426    0b1010111011001010111011,
1427    0b10001011111010001011111,
1428    0b10010111111010010111111,
1429    0b11011011011011011011011,
1430    0b11111111011011111111011,
1431    0b111011011111111011011111,
1432    0b111111111111111111111111,
1433    0b100100100100001001001001,
1434    0b100110110100001010010001,
1435    0b110100100110010001001010,
1436    0b110110110110010010010010,
1437    0b101101100100011011001001,
1438    0b101000110100011111010001,
1439    0b101100110111011001010,
1440    0b110110111111010010,
1441    0b100100101101001001011011,
1442    0b100110000101001010111011,
1443    0b110100101000010001011111,
1444    0b110110000000010010111111,
1445    0b101101101101011011011011,
1446    0b101000000101011111111011,
1447    0b101101000111011011111,
1448    0b111111111111,
1449    0b1001001001100100100100,
1450    0b1010010001100110110100,
1451    0b10001001010110100100110,
1452    0b10010010010110110110110,
1453    0b11011001001101101100100,
1454    0b11111010001101000110100,
1455    0b111011001010000101100110,
1456    0b111111010010000000110110,
1457    0b1001011011100100101101,
1458    0b1010111011100110000101,
1459    0b10001011111110100101000,
1460    0b10010111111110110000000,
1461    0b11011011011101101101101,
1462    0b11111111011101000000101,
1463    0b111011011111000101101000,
1464    0b111111111111000000000000,
1465    0b100100100100100100100100,
1466    0b100110110100100110110100,
1467    0b110100100110110100100110,
1468    0b110110110110110110110110,
1469    0b101101100100101101100100,
1470    0b101000110100101000110100,
1471    0b101100110000101100110,
1472    0b110110000000110110,
1473    0b100100101101100100101101,
1474    0b100110000101100110000101,
1475    0b110100101000110100101000,
1476    0b110110000000110110000000,
1477    0b101101101101101101101101,
1478    0b101000000101101000000101,
1479    0b101101000000101101000,
1480    0b0,
1481    0,
1482];
1483
1484#[cfg(feature = "dim2")]
1485const FACES_TO_VOXEL_TYPES: [VoxelType; 17] = [
1486    VoxelType::Vertex,
1487    VoxelType::Vertex,
1488    VoxelType::Vertex,
1489    VoxelType::Face,
1490    VoxelType::Vertex,
1491    VoxelType::Vertex,
1492    VoxelType::Vertex,
1493    VoxelType::Face,
1494    VoxelType::Vertex,
1495    VoxelType::Vertex,
1496    VoxelType::Vertex,
1497    VoxelType::Face,
1498    VoxelType::Face,
1499    VoxelType::Face,
1500    VoxelType::Face,
1501    VoxelType::Interior,
1502    VoxelType::Empty,
1503];
1504
1505#[cfg(feature = "dim2")]
1506const FACES_TO_FEATURE_MASKS: [u16; 17] = [
1507    0b1111,
1508    0b1001,
1509    0b110,
1510    0b1100,
1511    0b11,
1512    0b1,
1513    0b10,
1514    0b1000,
1515    0b1100,
1516    0b1000,
1517    0b100,
1518    0b100,
1519    0b11,
1520    0b10,
1521    0b1,
1522    0b1111111111111111,
1523    0,
1524];
1525
1526// NOTE: in 2D we are also using 3 bits per octant even though we technically only need two.
1527//       This keeps some collision-detection easier by avoiding some special-casing.
1528#[cfg(feature = "dim2")]
1529const FACES_TO_OCTANT_MASKS: [u32; 17] = [
1530    0b1001001001,
1531    0b1011011001,
1532    0b11001001011,
1533    0b11011011011,
1534    0b10010001001,
1535    0b10000011001,
1536    0b10001011,
1537    0b11011,
1538    0b1001010010,
1539    0b1011000010,
1540    0b11001010000,
1541    0b11011000000,
1542    0b10010010010,
1543    0b10000000010,
1544    0b10010000,
1545    0b0,
1546    0,
1547];
1548
1549#[cfg(test)]
1550mod test {
1551    #[test]
1552    fn gen_const_tables() {
1553        super::gen_const_tables();
1554    }
1555}