1use crate::types::BoundingBox;
2
3use crate::data::{DataSetAttributes, FieldData, Points};
4use crate::data::traits::{DataObject, DataSet};
5
6#[derive(Debug, Clone)]
12pub struct StructuredGrid {
13 dimensions: [usize; 3],
14 pub points: Points<f64>,
15 point_data: DataSetAttributes,
16 cell_data: DataSetAttributes,
17 field_data: FieldData,
18}
19
20impl Default for StructuredGrid {
21 fn default() -> Self {
22 Self {
23 dimensions: [1, 1, 1],
24 points: Points::new(),
25 point_data: DataSetAttributes::new(),
26 cell_data: DataSetAttributes::new(),
27 field_data: FieldData::new(),
28 }
29 }
30}
31
32impl StructuredGrid {
33 pub fn new() -> Self {
34 Self::default()
35 }
36
37 pub fn from_dimensions_and_points(
40 dimensions: [usize; 3],
41 points: Points<f64>,
42 ) -> Self {
43 assert_eq!(
44 points.len(),
45 dimensions[0] * dimensions[1] * dimensions[2],
46 "points count must match dimensions product"
47 );
48 Self {
49 dimensions,
50 points,
51 ..Default::default()
52 }
53 }
54
55 pub fn dimensions(&self) -> [usize; 3] {
56 self.dimensions
57 }
58
59 pub fn set_dimensions(&mut self, dims: [usize; 3]) {
60 self.dimensions = dims;
61 }
62
63 pub fn ijk_from_index(&self, idx: usize) -> (usize, usize, usize) {
65 let k = idx / (self.dimensions[0] * self.dimensions[1]);
66 let remainder = idx % (self.dimensions[0] * self.dimensions[1]);
67 let j = remainder / self.dimensions[0];
68 let i = remainder % self.dimensions[0];
69 (i, j, k)
70 }
71
72 pub fn index_from_ijk(&self, i: usize, j: usize, k: usize) -> usize {
74 k * self.dimensions[0] * self.dimensions[1] + j * self.dimensions[0] + i
75 }
76
77 pub fn point_data(&self) -> &DataSetAttributes {
78 &self.point_data
79 }
80
81 pub fn point_data_mut(&mut self) -> &mut DataSetAttributes {
82 &mut self.point_data
83 }
84
85 pub fn cell_data(&self) -> &DataSetAttributes {
86 &self.cell_data
87 }
88
89 pub fn cell_data_mut(&mut self) -> &mut DataSetAttributes {
90 &mut self.cell_data
91 }
92
93 pub fn from_function(
95 dims: [usize; 3],
96 f: impl Fn(usize, usize, usize) -> [f64; 3],
97 ) -> Self {
98 let mut pts = Points::new();
99 for k in 0..dims[2] {
100 for j in 0..dims[1] {
101 for i in 0..dims[0] {
102 pts.push(f(i, j, k));
103 }
104 }
105 }
106 Self::from_dimensions_and_points(dims, pts)
107 }
108
109 pub fn uniform(dims: [usize; 3], spacing: [f64; 3], origin: [f64; 3]) -> Self {
111 Self::from_function(dims, |i, j, k| [
112 origin[0] + i as f64 * spacing[0],
113 origin[1] + j as f64 * spacing[1],
114 origin[2] + k as f64 * spacing[2],
115 ])
116 }
117
118 pub fn with_point_array(mut self, array: crate::data::AnyDataArray) -> Self {
120 let name = array.name().to_string();
121 self.point_data.add_array(array);
122 if self.point_data.scalars().is_none() {
123 self.point_data.set_active_scalars(&name);
124 }
125 self
126 }
127}
128
129impl std::fmt::Display for StructuredGrid {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 let d = self.dimensions;
132 write!(f, "StructuredGrid: {}x{}x{}, {} points, {} point arrays",
133 d[0], d[1], d[2], self.points.len(), self.point_data.num_arrays())
134 }
135}
136
137impl DataObject for StructuredGrid {
138 fn field_data(&self) -> &FieldData {
139 &self.field_data
140 }
141
142 fn field_data_mut(&mut self) -> &mut FieldData {
143 &mut self.field_data
144 }
145}
146
147impl DataSet for StructuredGrid {
148 fn num_points(&self) -> usize {
149 self.dimensions[0] * self.dimensions[1] * self.dimensions[2]
150 }
151
152 fn num_cells(&self) -> usize {
153 let d = self.dimensions;
154 let cx = d[0].saturating_sub(1);
155 let cy = d[1].saturating_sub(1);
156 let cz = d[2].saturating_sub(1);
157 cx.max(1) * cy.max(1) * cz.max(1)
158 }
159
160 fn point(&self, idx: usize) -> [f64; 3] {
161 self.points.get(idx)
162 }
163
164 fn bounds(&self) -> BoundingBox {
165 self.points.bounds()
166 }
167
168 fn point_data(&self) -> &DataSetAttributes {
169 &self.point_data
170 }
171
172 fn point_data_mut(&mut self) -> &mut DataSetAttributes {
173 &mut self.point_data
174 }
175
176 fn cell_data(&self) -> &DataSetAttributes {
177 &self.cell_data
178 }
179
180 fn cell_data_mut(&mut self) -> &mut DataSetAttributes {
181 &mut self.cell_data
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn basic_structured_grid() {
191 let mut pts = Points::new();
192 for j in 0..2 {
194 for i in 0..3 {
195 let x = i as f64;
196 let y = j as f64 + 0.1 * (i as f64).sin();
197 pts.push([x, y, 0.0]);
198 }
199 }
200 let grid = StructuredGrid::from_dimensions_and_points([3, 2, 1], pts);
201 assert_eq!(grid.num_points(), 6);
202 assert_eq!(grid.num_cells(), 2);
203 assert_eq!(grid.dimensions(), [3, 2, 1]);
204 }
205
206 #[test]
207 fn index_roundtrip() {
208 let mut pts = Points::new();
209 for _ in 0..24 { pts.push([0.0, 0.0, 0.0]); }
210 let grid = StructuredGrid::from_dimensions_and_points([4, 3, 2], pts);
211 for idx in 0..24 {
212 let (i, j, k) = grid.ijk_from_index(idx);
213 assert_eq!(grid.index_from_ijk(i, j, k), idx);
214 }
215 }
216
217 #[test]
218 fn bounds_from_points() {
219 let mut pts = Points::new();
220 pts.push([0.0, 0.0, 0.0]);
221 pts.push([10.0, 5.0, 3.0]);
222 let grid = StructuredGrid::from_dimensions_and_points([2, 1, 1], pts);
223 let bb = grid.bounds();
224 assert_eq!(bb.x_max, 10.0);
225 assert_eq!(bb.y_max, 5.0);
226 }
227}