Skip to main content

vtk_pure_rs/data/
poly_data.rs

1use crate::types::BoundingBox;
2
3use crate::data::{CellArray, DataSetAttributes, FieldData, Points};
4use crate::data::traits::{DataObject, DataSet};
5
6/// Polygonal mesh consisting of vertices, lines, polygons, and triangle strips.
7///
8/// Analogous to VTK's `vtkPolyData`. Points are stored explicitly, with four
9/// separate `CellArray`s for the four cell categories.
10///
11/// # Examples
12///
13/// ```
14/// use crate::data::PolyData;
15///
16/// // Create a triangle mesh
17/// let pd = PolyData::from_triangles(
18///     vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
19///     vec![[0, 1, 2]],
20/// );
21/// assert_eq!(pd.points.len(), 3);
22/// assert_eq!(pd.polys.num_cells(), 1);
23/// ```
24#[derive(Debug, Clone, Default, PartialEq)]
25pub struct PolyData {
26    pub points: Points<f64>,
27    pub verts: CellArray,
28    pub lines: CellArray,
29    pub polys: CellArray,
30    pub strips: CellArray,
31    point_data: DataSetAttributes,
32    cell_data: DataSetAttributes,
33    field_data: FieldData,
34}
35
36impl PolyData {
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    /// Create a PolyData from points and triangle connectivity.
42    ///
43    /// Each element of `triangles` is `[i0, i1, i2]` indexing into `points`.
44    pub fn from_triangles(points: Vec<[f64; 3]>, triangles: Vec<[i64; 3]>) -> Self {
45        let pts = Points::from_vec(points);
46        let mut polys = CellArray::new();
47        for tri in &triangles {
48            polys.push_cell(&[tri[0], tri[1], tri[2]]);
49        }
50        Self {
51            points: pts,
52            polys,
53            ..Default::default()
54        }
55    }
56
57    /// Create a PolyData from points and generic polygon cells.
58    ///
59    /// Each element of `cells` is a Vec of point indices for one polygon.
60    /// Supports mixed triangle/quad/polygon meshes.
61    pub fn from_polygons(points: Vec<[f64; 3]>, cells: Vec<Vec<i64>>) -> Self {
62        let pts = Points::from_vec(points);
63        let mut polys = CellArray::new();
64        for cell in &cells {
65            polys.push_cell(cell);
66        }
67        Self { points: pts, polys, ..Default::default() }
68    }
69
70    /// Create a PolyData from points and quad connectivity.
71    ///
72    /// Each element of `quads` is `[i0, i1, i2, i3]` indexing into `points`.
73    pub fn from_quads(points: Vec<[f64; 3]>, quads: Vec<[i64; 4]>) -> Self {
74        let pts = Points::from_vec(points);
75        let mut polys = CellArray::new();
76        for q in &quads {
77            polys.push_cell(&[q[0], q[1], q[2], q[3]]);
78        }
79        Self { points: pts, polys, ..Default::default() }
80    }
81
82    /// Create a PolyData with line cells.
83    ///
84    /// Each element of `segments` is `[i0, i1]` indexing into `points`.
85    pub fn from_lines(points: Vec<[f64; 3]>, segments: Vec<[i64; 2]>) -> Self {
86        let pts = Points::from_vec(points);
87        let mut lines = CellArray::new();
88        for seg in &segments {
89            lines.push_cell(&[seg[0], seg[1]]);
90        }
91        Self { points: pts, lines, ..Default::default() }
92    }
93
94    /// Create a PolyData from flat coordinate and index arrays.
95    ///
96    /// `coords` is `[x0,y0,z0, x1,y1,z1, ...]` (length must be divisible by 3).
97    /// `indices` is `[i0,i1,i2, i3,i4,i5, ...]` (length must be divisible by 3, triangles).
98    ///
99    /// This is the most common format when receiving data from other libraries,
100    /// GPU buffers, or numpy arrays.
101    ///
102    /// ```
103    /// use crate::data::PolyData;
104    ///
105    /// let pd = PolyData::from_flat_arrays(
106    ///     &[0.0,0.0,0.0, 1.0,0.0,0.0, 0.0,1.0,0.0],
107    ///     &[0, 1, 2],
108    /// );
109    /// assert_eq!(pd.points.len(), 3);
110    /// assert_eq!(pd.polys.num_cells(), 1);
111    /// ```
112    pub fn from_flat_arrays(coords: &[f64], indices: &[i64]) -> Self {
113        assert!(coords.len() % 3 == 0, "coords length must be divisible by 3");
114        assert!(indices.len() % 3 == 0, "indices length must be divisible by 3");
115
116        let pts: Vec<[f64; 3]> = coords.chunks_exact(3)
117            .map(|c| [c[0], c[1], c[2]])
118            .collect();
119        let tris: Vec<[i64; 3]> = indices.chunks_exact(3)
120            .map(|c| [c[0], c[1], c[2]])
121            .collect();
122        Self::from_triangles(pts, tris)
123    }
124
125    /// Create a PolyData with points only (no cells).
126    ///
127    /// Useful for point clouds that will later have cells added.
128    pub fn from_points(points: Vec<[f64; 3]>) -> Self {
129        Self { points: Points::from_vec(points), ..Default::default() }
130    }
131
132    /// Create a PolyData with vertex cells (one vertex per point).
133    pub fn from_vertices(points: Vec<[f64; 3]>) -> Self {
134        let n = points.len();
135        let pts = Points::from_vec(points);
136        let mut verts = CellArray::new();
137        for i in 0..n {
138            verts.push_cell(&[i as i64]);
139        }
140        Self { points: pts, verts, ..Default::default() }
141    }
142
143    /// Create a PolyData with a single polyline through all points.
144    pub fn from_polyline(points: Vec<[f64; 3]>) -> Self {
145        let n = points.len();
146        let pts = Points::from_vec(points);
147        let mut lines = CellArray::new();
148        let ids: Vec<i64> = (0..n as i64).collect();
149        lines.push_cell(&ids);
150        Self { points: pts, lines, ..Default::default() }
151    }
152
153    /// Push a single point and return its index.
154    pub fn push_point(&mut self, point: [f64; 3]) -> i64 {
155        let idx = self.points.len() as i64;
156        self.points.push(point);
157        idx
158    }
159
160    /// Push a triangle cell from 3 point indices.
161    pub fn push_triangle(&mut self, i0: i64, i1: i64, i2: i64) {
162        self.polys.push_cell(&[i0, i1, i2]);
163    }
164
165    /// Push a quad cell from 4 point indices.
166    pub fn push_quad(&mut self, i0: i64, i1: i64, i2: i64, i3: i64) {
167        self.polys.push_cell(&[i0, i1, i2, i3]);
168    }
169
170    /// Push a line cell from 2 point indices.
171    pub fn push_line(&mut self, i0: i64, i1: i64) {
172        self.lines.push_cell(&[i0, i1]);
173    }
174
175    /// Total number of cells across all four categories.
176    pub fn total_cells(&self) -> usize {
177        self.verts.num_cells()
178            + self.lines.num_cells()
179            + self.polys.num_cells()
180            + self.strips.num_cells()
181    }
182
183    pub fn point_data(&self) -> &DataSetAttributes {
184        &self.point_data
185    }
186
187    pub fn point_data_mut(&mut self) -> &mut DataSetAttributes {
188        &mut self.point_data
189    }
190
191    pub fn cell_data(&self) -> &DataSetAttributes {
192        &self.cell_data
193    }
194
195    pub fn cell_data_mut(&mut self) -> &mut DataSetAttributes {
196        &mut self.cell_data
197    }
198
199    /// Access field data directly (convenience, same as DataObject::field_data).
200    pub fn field_data(&self) -> &FieldData {
201        &self.field_data
202    }
203
204    /// Mutable access to field data.
205    pub fn field_data_mut(&mut self) -> &mut FieldData {
206        &mut self.field_data
207    }
208
209    /// Reverse the winding order of all polygon cells.
210    ///
211    /// Useful for flipping normals direction.
212    pub fn reverse_cells(&mut self) {
213        let mut new_polys = CellArray::new();
214        for cell in self.polys.iter() {
215            let reversed: Vec<i64> = cell.iter().copied().rev().collect();
216            new_polys.push_cell(&reversed);
217        }
218        self.polys = new_polys;
219    }
220
221    /// Builder: add a point data array.
222    pub fn with_point_array(mut self, array: crate::data::AnyDataArray) -> Self {
223        let name = array.name().to_string();
224        self.point_data.add_array(array);
225        if self.point_data.scalars().is_none() {
226            self.point_data.set_active_scalars(&name);
227        }
228        self
229    }
230
231    /// Builder: add a cell data array.
232    pub fn with_cell_array(mut self, array: crate::data::AnyDataArray) -> Self {
233        self.cell_data.add_array(array);
234        self
235    }
236
237    /// Builder: set active scalars by name.
238    pub fn with_active_scalars(mut self, name: &str) -> Self {
239        self.point_data.set_active_scalars(name);
240        self
241    }
242
243    /// Create from separate X, Y, Z coordinate arrays and triangle indices.
244    pub fn from_xyz_arrays(
245        x: &[f64], y: &[f64], z: &[f64],
246        triangles: &[[i64; 3]],
247    ) -> Self {
248        assert_eq!(x.len(), y.len());
249        assert_eq!(x.len(), z.len());
250        let pts: Vec<[f64; 3]> = x.iter().zip(y.iter()).zip(z.iter())
251            .map(|((&xi, &yi), &zi)| [xi, yi, zi])
252            .collect();
253        Self::from_triangles(pts, triangles.to_vec())
254    }
255
256    /// Append another PolyData in-place (mutates self).
257    pub fn append(&mut self, other: &PolyData) {
258        let base = self.points.len() as i64;
259        for p in &other.points {
260            self.points.push(p);
261        }
262        for cell in other.polys.iter() {
263            let offset: Vec<i64> = cell.iter().map(|&id| id + base).collect();
264            self.polys.push_cell(&offset);
265        }
266        for cell in other.lines.iter() {
267            let offset: Vec<i64> = cell.iter().map(|&id| id + base).collect();
268            self.lines.push_cell(&offset);
269        }
270        for cell in other.verts.iter() {
271            let offset: Vec<i64> = cell.iter().map(|&id| id + base).collect();
272            self.verts.push_cell(&offset);
273        }
274        for cell in other.strips.iter() {
275            let offset: Vec<i64> = cell.iter().map(|&id| id + base).collect();
276            self.strips.push_cell(&offset);
277        }
278    }
279
280    /// Number of polygon faces.
281    pub fn num_polys(&self) -> usize {
282        self.polys.num_cells()
283    }
284
285    /// Count actual triangles (cells with exactly 3 points).
286    pub fn num_triangles(&self) -> usize {
287        self.polys.iter().filter(|c| c.len() == 3).count()
288    }
289
290    /// Compute the centroid (average position of all points).
291    pub fn centroid(&self) -> [f64; 3] {
292        self.points.centroid()
293    }
294
295    /// Compute a bounding sphere: (center, radius).
296    pub fn bounding_sphere(&self) -> ([f64; 3], f64) {
297        let center = self.centroid();
298        let mut max_r2 = 0.0f64;
299        for p in &self.points {
300            let dx = p[0] - center[0];
301            let dy = p[1] - center[1];
302            let dz = p[2] - center[2];
303            max_r2 = max_r2.max(dx*dx + dy*dy + dz*dz);
304        }
305        (center, max_r2.sqrt())
306    }
307
308    /// Whether all polygon cells are triangles.
309    pub fn is_all_triangles(&self) -> bool {
310        self.polys.num_cells() > 0 && self.polys.iter().all(|c| c.len() == 3)
311    }
312
313    /// Number of line cells.
314    pub fn num_lines(&self) -> usize {
315        self.lines.num_cells()
316    }
317
318    /// Number of vertex cells.
319    pub fn num_verts(&self) -> usize {
320        self.verts.num_cells()
321    }
322
323    /// Count unique edges in the polygon mesh.
324    pub fn num_edges(&self) -> usize {
325        let mut edges = std::collections::HashSet::new();
326        for cell in self.polys.iter() {
327            let n = cell.len();
328            for i in 0..n {
329                let a = cell[i] as usize;
330                let b = cell[(i + 1) % n] as usize;
331                edges.insert(if a < b { (a, b) } else { (b, a) });
332            }
333        }
334        edges.len()
335    }
336
337    /// Add a scalar (1-component f64) point data array and set it as active scalars.
338    ///
339    /// ```
340    /// use crate::data::PolyData;
341    ///
342    /// let mut pd = PolyData::from_triangles(
343    ///     vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
344    ///     vec![[0, 1, 2]],
345    /// );
346    /// pd.add_scalars("temperature", vec![10.0, 20.0, 30.0]);
347    /// assert_eq!(pd.get_scalars("temperature"), Some(vec![10.0, 20.0, 30.0]));
348    /// ```
349    pub fn add_scalars(&mut self, name: &str, values: Vec<f64>) {
350        let arr = crate::data::DataArray::from_vec(name, values, 1);
351        self.point_data.add_array(crate::data::AnyDataArray::F64(arr));
352        self.point_data.set_active_scalars(name);
353    }
354
355    /// Add a vector (3-component f64) point data array.
356    pub fn add_vectors(&mut self, name: &str, values: Vec<[f64; 3]>) {
357        let flat: Vec<f64> = values.into_iter().flat_map(|v| v).collect();
358        let arr = crate::data::DataArray::from_vec(name, flat, 3);
359        self.point_data.add_array(crate::data::AnyDataArray::F64(arr));
360        self.point_data.set_active_vectors(name);
361    }
362
363    /// Get scalar values by name as `Vec<f64>`. Returns None if array not found.
364    pub fn get_scalars(&self, name: &str) -> Option<Vec<f64>> {
365        self.point_data.get_array(name).map(|a| a.to_f64_vec())
366    }
367
368    /// Get vector values by name. Returns None if array not found.
369    pub fn get_vectors(&self, name: &str) -> Option<Vec<[f64; 3]>> {
370        let arr = self.point_data.get_array(name)?;
371        if arr.num_components() != 3 { return None; }
372        let mut result = Vec::with_capacity(arr.num_tuples());
373        let mut buf = [0.0f64; 3];
374        for i in 0..arr.num_tuples() {
375            arr.tuple_as_f64(i, &mut buf);
376            result.push(buf);
377        }
378        Some(result)
379    }
380
381    /// Human-readable summary of the PolyData.
382    pub fn summary(&self) -> String {
383        format!(
384            "PolyData: {} points, {} polys, {} lines, {} verts, {} point arrays, {} cell arrays",
385            self.points.len(),
386            self.polys.num_cells(),
387            self.lines.num_cells(),
388            self.verts.num_cells(),
389            self.point_data.num_arrays(),
390            self.cell_data.num_arrays(),
391        )
392    }
393
394    /// Convert point coordinates and point data to a Table.
395    ///
396    /// Creates columns "x", "y", "z" from point coordinates,
397    /// plus one column per point data array.
398    pub fn to_table(&self) -> crate::data::Table {
399        let n = self.points.len();
400        let mut x = Vec::with_capacity(n);
401        let mut y = Vec::with_capacity(n);
402        let mut z = Vec::with_capacity(n);
403        for p in &self.points {
404            x.push(p[0]);
405            y.push(p[1]);
406            z.push(p[2]);
407        }
408        let mut table = crate::data::Table::new();
409        table.add_column(crate::data::AnyDataArray::F64(crate::data::DataArray::from_vec("x", x, 1)));
410        table.add_column(crate::data::AnyDataArray::F64(crate::data::DataArray::from_vec("y", y, 1)));
411        table.add_column(crate::data::AnyDataArray::F64(crate::data::DataArray::from_vec("z", z, 1)));
412
413        for i in 0..self.point_data.num_arrays() {
414            if let Some(arr) = self.point_data.get_array_by_index(i) {
415                table.add_column(arr.clone());
416            }
417        }
418        table
419    }
420
421    /// Check approximate equality with another PolyData (for testing).
422    pub fn approx_eq(&self, other: &PolyData, tolerance: f64) -> bool {
423        if self.points.len() != other.points.len() { return false; }
424        if self.polys.num_cells() != other.polys.num_cells() { return false; }
425        for i in 0..self.points.len() {
426            let a = self.points.get(i);
427            let b = other.points.get(i);
428            if (a[0]-b[0]).abs() > tolerance || (a[1]-b[1]).abs() > tolerance || (a[2]-b[2]).abs() > tolerance {
429                return false;
430            }
431        }
432        for (ca, cb) in self.polys.iter().zip(other.polys.iter()) {
433            if ca != cb { return false; }
434        }
435        true
436    }
437}
438
439impl DataObject for PolyData {
440    fn field_data(&self) -> &FieldData {
441        &self.field_data
442    }
443
444    fn field_data_mut(&mut self) -> &mut FieldData {
445        &mut self.field_data
446    }
447}
448
449impl DataSet for PolyData {
450    fn num_points(&self) -> usize {
451        self.points.len()
452    }
453
454    fn num_cells(&self) -> usize {
455        self.total_cells()
456    }
457
458    fn point(&self, idx: usize) -> [f64; 3] {
459        self.points.get(idx)
460    }
461
462    fn bounds(&self) -> BoundingBox {
463        self.points.bounds()
464    }
465
466    fn point_data(&self) -> &DataSetAttributes {
467        &self.point_data
468    }
469
470    fn point_data_mut(&mut self) -> &mut DataSetAttributes {
471        &mut self.point_data
472    }
473
474    fn cell_data(&self) -> &DataSetAttributes {
475        &self.cell_data
476    }
477
478    fn cell_data_mut(&mut self) -> &mut DataSetAttributes {
479        &mut self.cell_data
480    }
481}
482
483impl std::fmt::Display for PolyData {
484    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485        write!(f, "{}", self.summary())
486    }
487}
488
489#[cfg(test)]
490mod tests {
491    use super::*;
492
493    #[test]
494    fn from_triangles() {
495        let pd = PolyData::from_triangles(
496            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.5, 1.0, 0.0]],
497            vec![[0, 1, 2]],
498        );
499        assert_eq!(pd.num_points(), 3);
500        assert_eq!(pd.num_cells(), 1);
501        assert_eq!(pd.polys.cell(0), &[0, 1, 2]);
502    }
503
504    #[test]
505    fn bounds() {
506        let pd = PolyData::from_triangles(
507            vec![[0.0, 0.0, 0.0], [1.0, 2.0, 3.0], [0.5, 1.0, 1.5]],
508            vec![[0, 1, 2]],
509        );
510        let bb = pd.bounds();
511        assert_eq!(bb.x_min, 0.0);
512        assert_eq!(bb.x_max, 1.0);
513        assert_eq!(bb.y_max, 2.0);
514        assert_eq!(bb.z_max, 3.0);
515    }
516
517    #[test]
518    fn empty_poly_data() {
519        let pd = PolyData::new();
520        assert_eq!(pd.num_points(), 0);
521        assert_eq!(pd.num_cells(), 0);
522    }
523
524    #[test]
525    fn from_quads() {
526        let pd = PolyData::from_quads(
527            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0]],
528            vec![[0, 1, 2, 3]],
529        );
530        assert_eq!(pd.num_points(), 4);
531        assert_eq!(pd.polys.num_cells(), 1);
532        assert_eq!(pd.polys.cell(0).len(), 4);
533    }
534
535    #[test]
536    fn from_lines() {
537        let pd = PolyData::from_lines(
538            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0]],
539            vec![[0, 1], [1, 2]],
540        );
541        assert_eq!(pd.num_points(), 3);
542        assert_eq!(pd.lines.num_cells(), 2);
543    }
544
545    #[test]
546    fn from_vertices() {
547        let pd = PolyData::from_vertices(vec![[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]]);
548        assert_eq!(pd.num_points(), 2);
549        assert_eq!(pd.verts.num_cells(), 2);
550    }
551
552    #[test]
553    fn from_polyline() {
554        let pd = PolyData::from_polyline(vec![
555            [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [2.0, 1.0, 0.0],
556        ]);
557        assert_eq!(pd.num_points(), 3);
558        assert_eq!(pd.lines.num_cells(), 1);
559        assert_eq!(pd.lines.cell(0).len(), 3);
560    }
561
562    #[test]
563    fn from_xyz_arrays() {
564        let x = vec![0.0, 1.0, 0.0];
565        let y = vec![0.0, 0.0, 1.0];
566        let z = vec![0.0, 0.0, 0.0];
567        let pd = PolyData::from_xyz_arrays(&x, &y, &z, &[[0, 1, 2]]);
568        assert_eq!(pd.num_points(), 3);
569        assert_eq!(pd.num_polys(), 1);
570    }
571
572    #[test]
573    fn append() {
574        let mut a = PolyData::from_triangles(
575            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
576            vec![[0, 1, 2]],
577        );
578        let b = PolyData::from_triangles(
579            vec![[5.0, 0.0, 0.0], [6.0, 0.0, 0.0], [5.0, 1.0, 0.0]],
580            vec![[0, 1, 2]],
581        );
582        a.append(&b);
583        assert_eq!(a.points.len(), 6);
584        assert_eq!(a.polys.num_cells(), 2);
585        assert_eq!(a.polys.cell(1), &[3, 4, 5]);
586    }
587
588    #[test]
589    fn edge_count() {
590        let pd = PolyData::from_triangles(
591            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
592            vec![[0, 1, 2]],
593        );
594        assert_eq!(pd.num_edges(), 3);
595    }
596
597    #[test]
598    fn summary() {
599        let pd = PolyData::from_triangles(
600            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
601            vec![[0, 1, 2]],
602        );
603        let s = pd.summary();
604        assert!(s.contains("3 points"));
605        assert!(s.contains("1 polys"));
606    }
607
608    #[test]
609    fn approx_eq() {
610        let a = PolyData::from_triangles(
611            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
612            vec![[0, 1, 2]],
613        );
614        let b = a.clone();
615        assert!(a.approx_eq(&b, 1e-10));
616    }
617
618    #[test]
619    fn num_convenience() {
620        let pd = PolyData::from_triangles(
621            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
622            vec![[0, 1, 2]],
623        );
624        assert_eq!(pd.num_polys(), 1);
625        assert_eq!(pd.num_lines(), 0);
626        assert_eq!(pd.num_verts(), 0);
627    }
628
629    #[test]
630    fn display() {
631        let pd = PolyData::from_triangles(
632            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
633            vec![[0, 1, 2]],
634        );
635        let s = format!("{pd}");
636        assert!(s.contains("3 points"));
637    }
638
639    #[test]
640    fn to_table() {
641        let mut pd = PolyData::from_triangles(
642            vec![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]],
643            vec![[0, 1, 2]],
644        );
645        let s = crate::data::DataArray::from_vec("temp", vec![10.0f64, 20.0, 30.0], 1);
646        pd.point_data_mut().add_array(crate::data::AnyDataArray::F64(s));
647
648        let table = pd.to_table();
649        assert_eq!(table.num_rows(), 3);
650        assert_eq!(table.num_columns(), 4); // x, y, z, temp
651        assert_eq!(table.value_f64(0, "x"), Some(1.0));
652        assert_eq!(table.value_f64(1, "y"), Some(5.0));
653        assert_eq!(table.value_f64(2, "temp"), Some(30.0));
654    }
655
656    #[test]
657    fn from_points_no_cells() {
658        let pd = PolyData::from_points(vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]]);
659        assert_eq!(pd.points.len(), 2);
660        assert_eq!(pd.polys.num_cells(), 0);
661        assert_eq!(pd.verts.num_cells(), 0);
662    }
663
664    #[test]
665    fn from_polygons_mixed() {
666        let pd = PolyData::from_polygons(
667            vec![[0.0,0.0,0.0],[1.0,0.0,0.0],[1.0,1.0,0.0],[0.0,1.0,0.0],[2.0,0.0,0.0]],
668            vec![vec![0, 1, 2], vec![0, 2, 3], vec![1, 4, 2]],
669        );
670        assert_eq!(pd.points.len(), 5);
671        assert_eq!(pd.polys.num_cells(), 3);
672    }
673
674    #[test]
675    fn triangle_counting() {
676        let pd = PolyData::from_polygons(
677            vec![[0.0,0.0,0.0],[1.0,0.0,0.0],[1.0,1.0,0.0],[0.0,1.0,0.0]],
678            vec![vec![0,1,2], vec![0,1,2,3]],
679        );
680        assert_eq!(pd.num_triangles(), 1);
681        assert!(!pd.is_all_triangles());
682
683        let tri = PolyData::from_triangles(
684            vec![[0.0,0.0,0.0],[1.0,0.0,0.0],[0.0,1.0,0.0]],
685            vec![[0,1,2]],
686        );
687        assert!(tri.is_all_triangles());
688    }
689
690    #[test]
691    fn add_get_scalars() {
692        let mut pd = PolyData::from_triangles(
693            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
694            vec![[0, 1, 2]],
695        );
696        pd.add_scalars("temp", vec![10.0, 20.0, 30.0]);
697        let vals = pd.get_scalars("temp").unwrap();
698        assert_eq!(vals, vec![10.0, 20.0, 30.0]);
699        assert!(pd.get_scalars("nonexistent").is_none());
700    }
701
702    #[test]
703    fn add_get_vectors() {
704        let mut pd = PolyData::from_triangles(
705            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
706            vec![[0, 1, 2]],
707        );
708        pd.add_vectors("velocity", vec![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]);
709        let vecs = pd.get_vectors("velocity").unwrap();
710        assert_eq!(vecs.len(), 3);
711        assert_eq!(vecs[0], [1.0, 0.0, 0.0]);
712    }
713
714    #[test]
715    fn incremental_building() {
716        let mut pd = PolyData::new();
717        let i0 = pd.push_point([0.0, 0.0, 0.0]);
718        let i1 = pd.push_point([1.0, 0.0, 0.0]);
719        let i2 = pd.push_point([0.0, 1.0, 0.0]);
720        let i3 = pd.push_point([1.0, 1.0, 0.0]);
721        pd.push_triangle(i0, i1, i2);
722        pd.push_triangle(i1, i3, i2);
723        pd.push_line(i0, i1);
724
725        assert_eq!(pd.points.len(), 4);
726        assert_eq!(pd.num_polys(), 2);
727        assert_eq!(pd.num_lines(), 1);
728        assert_eq!(pd.polys.cell(0), &[0, 1, 2]);
729    }
730
731    #[test]
732    fn reverse_cells() {
733        let mut pd = PolyData::from_triangles(
734            vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
735            vec![[0, 1, 2]],
736        );
737        pd.reverse_cells();
738        assert_eq!(pd.polys.cell(0), &[2, 1, 0]);
739    }
740
741    #[test]
742    fn field_data_access() {
743        let mut pd = PolyData::new();
744        assert!(pd.field_data().is_empty());
745        pd.field_data_mut().add_array(crate::data::AnyDataArray::F64(
746            crate::data::DataArray::from_vec("meta", vec![42.0], 1),
747        ));
748        assert!(pd.field_data().has_array("meta"));
749    }
750}