meshless_voronoi/voronoi/
voronoi_cell.rs

1use glam::DVec3;
2
3use crate::voronoi::{half_space::HalfSpace, voronoi_face::VoronoiFace, Voronoi};
4
5use super::{
6    convex_cell::{ConvexCell, ConvexCellMarker},
7    integrals::{CellIntegral, VolumeCentroidIntegral},
8};
9
10/// A Voronoi cell.
11#[derive(Default, Debug, Clone)]
12pub struct VoronoiCell {
13    loc: DVec3,
14    centroid: DVec3,
15    volume: f64,
16    safety_radius: f64,
17    face_connections_offset: usize,
18    face_count: usize,
19    idx: usize,
20}
21
22impl VoronoiCell {
23    fn init(loc: DVec3, centroid: DVec3, volume: f64, safety_radius: f64, idx: usize) -> Self {
24        Self {
25            loc,
26            centroid,
27            volume,
28            safety_radius,
29            face_connections_offset: 0,
30            face_count: 0,
31            idx,
32        }
33    }
34
35    /// Build a [`VoronoiCell`] from a [`ConvexCell`] by computing the relevant
36    /// integrals.
37    ///
38    /// Any Voronoi faces that are created by the construction of this cell are
39    /// stored in the `faces` vector.
40    pub(super) fn from_convex_cell<'a, M: ConvexCellMarker + 'static>(
41        convex_cell: &'a ConvexCell<M>,
42        faces: &mut Vec<VoronoiFace>,
43        mask: Option<&[bool]>,
44    ) -> Self {
45        let idx = convex_cell.idx;
46        let loc = convex_cell.loc;
47        let mut volume_centroid_integral = VolumeCentroidIntegral::init();
48
49        let mut maybe_faces: Vec<Option<VoronoiFace>> =
50            (0..convex_cell.clipping_planes.len()).map(|_| None).collect();
51
52        // Helper function to decide which faces should be constucted.
53        let maybe_init_face = |maybe_face: &mut Option<VoronoiFace>, clipping_plane_idx: usize| {
54            // Only construct faces for clipping planes of valid dimensionality.
55            let half_space = &convex_cell.clipping_planes[clipping_plane_idx];
56            let should_construct_face =
57                convex_cell.dimensionality.vector_is_valid(half_space.normal())
58                    && match half_space {
59                        // Don't construct internal (non-boundary) faces twice.
60                        HalfSpace {
61                            right_idx: Some(right_idx),
62                            shift: None,
63                            ..
64                        } => {
65                            // Only construct face if: neighbour has not been treated yet or is inactive
66                            *right_idx > idx || mask.map_or(false, |mask| !mask[*right_idx])
67                        }
68                        _ => true,
69                    };
70            if should_construct_face {
71                maybe_face.get_or_insert(VoronoiFace::init(convex_cell, clipping_plane_idx));
72            }
73        };
74
75        // Loop over the decomposition of this convex cell into tetrahedra to compute
76        // the necessary integrals/barycenter calculations
77        for tet in convex_cell.decompose() {
78            // Update the volume and centroid of the cell
79            volume_centroid_integral.collect(
80                tet.vertices[0],
81                tet.vertices[1],
82                tet.vertices[2],
83                loc,
84            );
85
86            // Initialize a new face if necessary
87            let maybe_face = &mut maybe_faces[tet.plane_idx];
88            maybe_init_face(maybe_face, tet.plane_idx);
89            // Update this face's area and centroid if necessary
90            if let Some(face) = maybe_face {
91                face.collect(tet.vertices[0], tet.vertices[1], tet.vertices[2], loc)
92            }
93        }
94        // Filter out uninitialized faces and finalize the rest
95        faces.extend(maybe_faces.into_iter().flatten().map(|face| face.finalize()));
96
97        let VolumeCentroidIntegral { volume, centroid } = volume_centroid_integral.finalize();
98
99        VoronoiCell::init(loc, centroid, volume, convex_cell.safety_radius, convex_cell.idx)
100    }
101
102    pub(super) fn finalize(&mut self, face_connections_offset: usize, face_count: usize) {
103        self.face_connections_offset = face_connections_offset;
104        self.face_count = face_count;
105    }
106
107    /// Get the position of the generator of this Voronoi cell.
108    pub fn loc(&self) -> DVec3 {
109        self.loc
110    }
111
112    /// Get the position of the centroid of this cell
113    pub fn centroid(&self) -> DVec3 {
114        self.centroid
115    }
116
117    /// Get the volume of this cell
118    pub fn volume(&self) -> f64 {
119        self.volume
120    }
121
122    /// Get the safety radius of this cell
123    pub fn safety_radius(&self) -> f64 {
124        self.safety_radius
125    }
126
127    /// Get the indices of the faces that have this cell as its left or right
128    /// neighbour.
129    pub fn face_indices<'a>(&'a self, voronoi: &'a Voronoi) -> &[usize] {
130        &voronoi.cell_face_connections
131            [self.face_connections_offset..(self.face_connections_offset + self.face_count)]
132    }
133
134    /// Get an `Iterator` over the Voronoi faces that have this cell as their
135    /// left _or_ right generator.
136    pub fn faces<'a>(&'a self, voronoi: &'a Voronoi) -> impl Iterator<Item = &VoronoiFace> + 'a {
137        self.face_indices(voronoi).iter().map(|&i| &voronoi.faces[i])
138    }
139
140    /// Get an `Iterator` over the indices of the neighbouring generators of this Voronoi cell.
141    pub fn neighbour_ids<'a>(&'a self, voronoi: &'a Voronoi) -> impl Iterator<Item = usize> + 'a {
142        self.face_indices(voronoi).iter().filter_map(|&i| {
143            let face = &voronoi.faces[i];
144            if face.is_periodic() || face.is_boundary() {
145                return None;
146            }
147            Some(if face.left() == self.idx {
148                face.right().expect("Face is guaranteed to not be a boundary face by now")
149            } else {
150                face.left()
151            })
152        })
153    }
154
155    /// Get the offset of the slice of the indices of this cell's faces in the
156    /// `Voronoi::cell_face_connections` array.
157    pub fn face_connections_offset(&self) -> usize {
158        self.face_connections_offset
159    }
160
161    /// Get the length of the slice of the indices of this cell's faces in the
162    /// `Voronoi::cell_face_connections` array.
163    pub fn face_count(&self) -> usize {
164        self.face_count
165    }
166}
167
168#[cfg(test)]
169mod tests {}