Skip to main content

vtk_pure_rs/data/
cell_array.rs

1/// Storage for cell topology using offsets + connectivity arrays.
2///
3/// Mirrors VTK's `vtkCellArray` design: an offsets array of length `num_cells + 1`
4/// and a connectivity array containing the point indices for all cells concatenated.
5///
6/// For cell `i`, the point indices are `connectivity[offsets[i]..offsets[i+1]]`.
7///
8/// # Examples
9///
10/// ```
11/// use crate::data::CellArray;
12///
13/// let mut cells = CellArray::new();
14/// cells.push_cell(&[0, 1, 2]);    // triangle
15/// cells.push_cell(&[3, 4, 5, 6]); // quad
16/// assert_eq!(cells.num_cells(), 2);
17/// assert_eq!(cells.cell(0), &[0, 1, 2]);
18/// ```
19use std::sync::Arc;
20
21/// Storage for cell topology using offsets + connectivity arrays.
22/// Uses Arc<Vec<i64>> for zero-copy clone with copy-on-write semantics.
23#[derive(Debug)]
24pub struct CellArray {
25    offsets: Arc<Vec<i64>>,
26    connectivity: Arc<Vec<i64>>,
27}
28
29impl Clone for CellArray {
30    fn clone(&self) -> Self {
31        Self {
32            offsets: Arc::clone(&self.offsets),
33            connectivity: Arc::clone(&self.connectivity),
34        }
35    }
36}
37
38impl PartialEq for CellArray {
39    fn eq(&self, other: &Self) -> bool {
40        (Arc::ptr_eq(&self.offsets, &other.offsets) || *self.offsets == *other.offsets)
41            && (Arc::ptr_eq(&self.connectivity, &other.connectivity) || *self.connectivity == *other.connectivity)
42    }
43}
44
45impl Default for CellArray {
46    fn default() -> Self {
47        Self::new()
48    }
49}
50
51impl CellArray {
52    pub fn new() -> Self {
53        Self {
54            offsets: Arc::new(vec![0]),
55            connectivity: Arc::new(Vec::new()),
56        }
57    }
58
59    /// Create a CellArray from raw offsets and connectivity.
60    pub fn from_raw(offsets: Vec<i64>, connectivity: Vec<i64>) -> Self {
61        assert!(!offsets.is_empty(), "offsets must have at least one element");
62        assert_eq!(offsets[0], 0, "first offset must be 0");
63        Self {
64            offsets: Arc::new(offsets),
65            connectivity: Arc::new(connectivity),
66        }
67    }
68
69    /// Create a CellArray from triangle index arrays.
70    pub fn from_triangles(tris: &[[i64; 3]]) -> Self {
71        let mut ca = Self::new();
72        for tri in tris {
73            ca.push_cell(tri);
74        }
75        ca
76    }
77
78    /// Create a CellArray from quad index arrays.
79    pub fn from_quads(quads: &[[i64; 4]]) -> Self {
80        let mut ca = Self::new();
81        for quad in quads {
82            ca.push_cell(quad);
83        }
84        ca
85    }
86
87    /// Append a cell with the given point indices.
88    pub fn push_cell(&mut self, point_ids: &[i64]) {
89        // Fast path: if sole owner, avoid Arc::make_mut's atomic CAS
90        if let Some(c) = Arc::get_mut(&mut self.connectivity) {
91            c.extend_from_slice(point_ids);
92        } else {
93            Arc::make_mut(&mut self.connectivity).extend_from_slice(point_ids);
94        }
95        let conn_len = self.connectivity.len() as i64;
96        if let Some(o) = Arc::get_mut(&mut self.offsets) {
97            o.push(conn_len);
98        } else {
99            Arc::make_mut(&mut self.offsets).push(conn_len);
100        }
101    }
102
103    pub fn num_cells(&self) -> usize {
104        self.offsets.len().saturating_sub(1)
105    }
106
107    /// Returns the point indices for cell `idx`.
108    pub fn cell(&self, idx: usize) -> &[i64] {
109        let start = self.offsets[idx] as usize;
110        let end = self.offsets[idx + 1] as usize;
111        &self.connectivity[start..end]
112    }
113
114    pub fn is_empty(&self) -> bool {
115        self.num_cells() == 0
116    }
117
118    pub fn offsets(&self) -> &[i64] {
119        &self.offsets
120    }
121
122    pub fn connectivity(&self) -> &[i64] {
123        &self.connectivity
124    }
125
126    /// Total number of point index entries across all cells.
127    pub fn connectivity_len(&self) -> usize {
128        self.connectivity.len()
129    }
130
131    pub fn clear(&mut self) {
132        let off = Arc::make_mut(&mut self.offsets);
133        off.clear();
134        off.push(0);
135        Arc::make_mut(&mut self.connectivity).clear();
136    }
137
138    /// Get the number of points in cell at `idx`.
139    pub fn cell_size(&self, idx: usize) -> usize {
140        let start = self.offsets[idx] as usize;
141        let end = self.offsets[idx + 1] as usize;
142        end - start
143    }
144
145    /// Iterator over cell sizes (number of points per cell).
146    pub fn cell_sizes(&self) -> impl Iterator<Item = usize> + '_ {
147        (0..self.num_cells()).map(move |i| self.cell_size(i))
148    }
149
150    /// Maximum cell size (max number of points in any cell).
151    pub fn max_cell_size(&self) -> usize {
152        self.cell_sizes().max().unwrap_or(0)
153    }
154
155    /// Check if all cells have the same size.
156    pub fn is_homogeneous(&self) -> Option<usize> {
157        let mut sizes = self.cell_sizes();
158        let first = sizes.next()?;
159        if sizes.all(|s| s == first) { Some(first) } else { None }
160    }
161
162    /// Iterate over cells, yielding a slice of point indices for each.
163    pub fn iter(&self) -> CellIter<'_> {
164        CellIter {
165            cells: self,
166            idx: 0,
167        }
168    }
169}
170
171pub struct CellIter<'a> {
172    cells: &'a CellArray,
173    idx: usize,
174}
175
176impl<'a> Iterator for CellIter<'a> {
177    type Item = &'a [i64];
178
179    fn next(&mut self) -> Option<Self::Item> {
180        if self.idx >= self.cells.num_cells() {
181            return None;
182        }
183        let cell = self.cells.cell(self.idx);
184        self.idx += 1;
185        Some(cell)
186    }
187
188    fn size_hint(&self) -> (usize, Option<usize>) {
189        let remaining = self.cells.num_cells() - self.idx;
190        (remaining, Some(remaining))
191    }
192}
193
194impl ExactSizeIterator for CellIter<'_> {}
195
196impl<'a> IntoIterator for &'a CellArray {
197    type Item = &'a [i64];
198    type IntoIter = CellIter<'a>;
199
200    fn into_iter(self) -> Self::IntoIter {
201        self.iter()
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208
209    #[test]
210    fn push_and_iterate() {
211        let mut cells = CellArray::new();
212        cells.push_cell(&[0, 1, 2]);
213        cells.push_cell(&[2, 3, 4, 5]);
214        assert_eq!(cells.num_cells(), 2);
215        assert_eq!(cells.cell(0), &[0, 1, 2]);
216        assert_eq!(cells.cell(1), &[2, 3, 4, 5]);
217
218        let collected: Vec<&[i64]> = cells.iter().collect();
219        assert_eq!(collected.len(), 2);
220    }
221
222    #[test]
223    fn empty_cell_array() {
224        let cells = CellArray::new();
225        assert!(cells.is_empty());
226        assert_eq!(cells.num_cells(), 0);
227        assert_eq!(cells.iter().count(), 0);
228    }
229
230    #[test]
231    fn from_raw() {
232        let cells = CellArray::from_raw(vec![0, 3, 7], vec![0, 1, 2, 3, 4, 5, 6]);
233        assert_eq!(cells.num_cells(), 2);
234        assert_eq!(cells.cell(0), &[0, 1, 2]);
235        assert_eq!(cells.cell(1), &[3, 4, 5, 6]);
236    }
237
238    #[test]
239    fn cell_sizes() {
240        let mut cells = CellArray::new();
241        cells.push_cell(&[0, 1, 2]);
242        cells.push_cell(&[2, 3, 4, 5]);
243        assert_eq!(cells.cell_size(0), 3);
244        assert_eq!(cells.cell_size(1), 4);
245        assert_eq!(cells.max_cell_size(), 4);
246        let sizes: Vec<usize> = cells.cell_sizes().collect();
247        assert_eq!(sizes, vec![3, 4]);
248    }
249
250    #[test]
251    fn homogeneous() {
252        let mut tris = CellArray::new();
253        tris.push_cell(&[0, 1, 2]);
254        tris.push_cell(&[3, 4, 5]);
255        assert_eq!(tris.is_homogeneous(), Some(3));
256
257        let mut mixed = CellArray::new();
258        mixed.push_cell(&[0, 1, 2]);
259        mixed.push_cell(&[0, 1, 2, 3]);
260        assert_eq!(mixed.is_homogeneous(), None);
261    }
262
263    #[test]
264    fn into_iterator() {
265        let mut cells = CellArray::new();
266        cells.push_cell(&[0, 1]);
267        cells.push_cell(&[2, 3]);
268        let mut count = 0;
269        for _cell in &cells {
270            count += 1;
271        }
272        assert_eq!(count, 2);
273    }
274
275    #[test]
276    fn from_triangles() {
277        let cells = CellArray::from_triangles(&[[0, 1, 2], [3, 4, 5]]);
278        assert_eq!(cells.num_cells(), 2);
279        assert_eq!(cells.cell(0), &[0, 1, 2]);
280        assert_eq!(cells.is_homogeneous(), Some(3));
281    }
282
283    #[test]
284    fn from_quads() {
285        let cells = CellArray::from_quads(&[[0, 1, 2, 3]]);
286        assert_eq!(cells.num_cells(), 1);
287        assert_eq!(cells.cell(0).len(), 4);
288    }
289}