meshless_voronoi/voronoi/
voronoi_cell.rs1use 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#[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 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 let maybe_init_face = |maybe_face: &mut Option<VoronoiFace>, clipping_plane_idx: usize| {
54 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 HalfSpace {
61 right_idx: Some(right_idx),
62 shift: None,
63 ..
64 } => {
65 *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 for tet in convex_cell.decompose() {
78 volume_centroid_integral.collect(
80 tet.vertices[0],
81 tet.vertices[1],
82 tet.vertices[2],
83 loc,
84 );
85
86 let maybe_face = &mut maybe_faces[tet.plane_idx];
88 maybe_init_face(maybe_face, tet.plane_idx);
89 if let Some(face) = maybe_face {
91 face.collect(tet.vertices[0], tet.vertices[1], tet.vertices[2], loc)
92 }
93 }
94 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 pub fn loc(&self) -> DVec3 {
109 self.loc
110 }
111
112 pub fn centroid(&self) -> DVec3 {
114 self.centroid
115 }
116
117 pub fn volume(&self) -> f64 {
119 self.volume
120 }
121
122 pub fn safety_radius(&self) -> f64 {
124 self.safety_radius
125 }
126
127 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 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 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 pub fn face_connections_offset(&self) -> usize {
158 self.face_connections_offset
159 }
160
161 pub fn face_count(&self) -> usize {
164 self.face_count
165 }
166}
167
168#[cfg(test)]
169mod tests {}