1use crate::types::BoundingBox;
2
3use crate::data::{DataSetAttributes, FieldData};
4use crate::data::traits::{DataObject, DataSet};
5
6#[derive(Debug, Clone)]
23pub struct ImageData {
24 extent: [i64; 6],
25 spacing: [f64; 3],
26 origin: [f64; 3],
27 point_data: DataSetAttributes,
28 cell_data: DataSetAttributes,
29 field_data: FieldData,
30}
31
32impl Default for ImageData {
33 fn default() -> Self {
34 Self {
35 extent: [0, 0, 0, 0, 0, 0],
36 spacing: [1.0, 1.0, 1.0],
37 origin: [0.0, 0.0, 0.0],
38 point_data: DataSetAttributes::new(),
39 cell_data: DataSetAttributes::new(),
40 field_data: FieldData::new(),
41 }
42 }
43}
44
45impl ImageData {
46 pub fn new() -> Self {
47 Self::default()
48 }
49
50 pub fn with_dimensions(nx: usize, ny: usize, nz: usize) -> Self {
52 Self {
53 extent: [0, (nx as i64) - 1, 0, (ny as i64) - 1, 0, (nz as i64) - 1],
54 ..Default::default()
55 }
56 }
57
58 pub fn extent(&self) -> [i64; 6] {
59 self.extent
60 }
61
62 pub fn set_extent(&mut self, extent: [i64; 6]) {
63 self.extent = extent;
64 }
65
66 pub fn spacing(&self) -> [f64; 3] {
67 self.spacing
68 }
69
70 pub fn set_spacing(&mut self, spacing: [f64; 3]) {
71 self.spacing = spacing;
72 }
73
74 pub fn origin(&self) -> [f64; 3] {
75 self.origin
76 }
77
78 pub fn set_origin(&mut self, origin: [f64; 3]) {
79 self.origin = origin;
80 }
81
82 pub fn dimensions(&self) -> [usize; 3] {
84 [
85 (self.extent[1] - self.extent[0] + 1).max(0) as usize,
86 (self.extent[3] - self.extent[2] + 1).max(0) as usize,
87 (self.extent[5] - self.extent[4] + 1).max(0) as usize,
88 ]
89 }
90
91 pub fn point_from_ijk(&self, i: usize, j: usize, k: usize) -> [f64; 3] {
93 [
94 self.origin[0] + (self.extent[0] as f64 + i as f64) * self.spacing[0],
95 self.origin[1] + (self.extent[2] as f64 + j as f64) * self.spacing[1],
96 self.origin[2] + (self.extent[4] as f64 + k as f64) * self.spacing[2],
97 ]
98 }
99
100 pub fn ijk_from_index(&self, idx: usize) -> (usize, usize, usize) {
102 let dims = self.dimensions();
103 let k = idx / (dims[0] * dims[1]);
104 let remainder = idx % (dims[0] * dims[1]);
105 let j = remainder / dims[0];
106 let i = remainder % dims[0];
107 (i, j, k)
108 }
109
110 pub fn point_at(&self, idx: usize) -> [f64; 3] {
112 let (i, j, k) = self.ijk_from_index(idx);
113 self.point_from_ijk(i, j, k)
114 }
115
116 pub fn index_from_ijk(&self, i: usize, j: usize, k: usize) -> usize {
118 let dims = self.dimensions();
119 k * dims[0] * dims[1] + j * dims[0] + i
120 }
121
122 pub fn scalar_at(&self, i: usize, j: usize, k: usize) -> Option<f64> {
126 let scalars = self.point_data.scalars()?;
127 let idx = self.index_from_ijk(i, j, k);
128 let mut buf = [0.0f64];
129 scalars.tuple_as_f64(idx, &mut buf);
130 Some(buf[0])
131 }
132
133 pub fn point_positions(&self) -> Vec<[f64; 3]> {
135 let n = self.num_points();
136 (0..n).map(|idx| self.point_at(idx)).collect()
137 }
138
139 pub fn num_points(&self) -> usize {
141 let d = self.dimensions();
142 d[0] * d[1] * d[2]
143 }
144
145 pub fn point_data(&self) -> &DataSetAttributes {
146 &self.point_data
147 }
148
149 pub fn point_data_mut(&mut self) -> &mut DataSetAttributes {
150 &mut self.point_data
151 }
152
153 pub fn cell_data(&self) -> &DataSetAttributes {
154 &self.cell_data
155 }
156
157 pub fn cell_data_mut(&mut self) -> &mut DataSetAttributes {
158 &mut self.cell_data
159 }
160
161 pub fn with_spacing(mut self, spacing: [f64; 3]) -> Self {
163 self.spacing = spacing;
164 self
165 }
166
167 pub fn with_origin(mut self, origin: [f64; 3]) -> Self {
169 self.origin = origin;
170 self
171 }
172
173 pub fn from_function(
192 dims: [usize; 3],
193 spacing: [f64; 3],
194 origin: [f64; 3],
195 name: &str,
196 f: impl Fn(f64, f64, f64) -> f64,
197 ) -> Self {
198 let mut img = Self::with_dimensions(dims[0], dims[1], dims[2]);
199 img.set_spacing(spacing);
200 img.set_origin(origin);
201
202 let mut values = Vec::with_capacity(dims[0] * dims[1] * dims[2]);
203 for k in 0..dims[2] {
204 for j in 0..dims[1] {
205 for i in 0..dims[0] {
206 let x = origin[0] + i as f64 * spacing[0];
207 let y = origin[1] + j as f64 * spacing[1];
208 let z = origin[2] + k as f64 * spacing[2];
209 values.push(f(x, y, z));
210 }
211 }
212 }
213
214 let arr = crate::data::DataArray::from_vec(name, values, 1);
215 img.point_data.add_array(crate::data::AnyDataArray::F64(arr));
216 img.point_data.set_active_scalars(name);
217 img
218 }
219
220 pub fn with_point_array(mut self, array: crate::data::AnyDataArray) -> Self {
222 let name = array.name().to_string();
223 self.point_data.add_array(array);
224 if self.point_data.scalars().is_none() {
225 self.point_data.set_active_scalars(&name);
226 }
227 self
228 }
229}
230
231impl DataObject for ImageData {
232 fn field_data(&self) -> &FieldData {
233 &self.field_data
234 }
235
236 fn field_data_mut(&mut self) -> &mut FieldData {
237 &mut self.field_data
238 }
239}
240
241impl DataSet for ImageData {
242 fn num_points(&self) -> usize {
243 let d = self.dimensions();
244 d[0] * d[1] * d[2]
245 }
246
247 fn num_cells(&self) -> usize {
248 let d = self.dimensions();
249 let cx = d[0].saturating_sub(1);
250 let cy = d[1].saturating_sub(1);
251 let cz = d[2].saturating_sub(1);
252 match (cx, cy, cz) {
254 (0, _, _) | (_, 0, _) | (_, _, 0) => cx.max(1) * cy.max(1) * cz.max(1),
255 _ => cx * cy * cz,
256 }
257 }
258
259 fn point(&self, idx: usize) -> [f64; 3] {
260 let (i, j, k) = self.ijk_from_index(idx);
261 self.point_from_ijk(i, j, k)
262 }
263
264 fn bounds(&self) -> BoundingBox {
265 let d = self.dimensions();
266 if d[0] == 0 || d[1] == 0 || d[2] == 0 {
267 return BoundingBox::empty();
268 }
269 let p0 = self.point_from_ijk(0, 0, 0);
270 let p1 = self.point_from_ijk(d[0] - 1, d[1] - 1, d[2] - 1);
271 let mut bb = BoundingBox::empty();
272 bb.expand(p0);
273 bb.expand(p1);
274 bb
275 }
276
277 fn point_data(&self) -> &DataSetAttributes {
278 &self.point_data
279 }
280
281 fn point_data_mut(&mut self) -> &mut DataSetAttributes {
282 &mut self.point_data
283 }
284
285 fn cell_data(&self) -> &DataSetAttributes {
286 &self.cell_data
287 }
288
289 fn cell_data_mut(&mut self) -> &mut DataSetAttributes {
290 &mut self.cell_data
291 }
292}
293
294impl std::fmt::Display for ImageData {
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 let d = self.dimensions();
297 write!(f, "ImageData: {}x{}x{}, spacing {:?}, origin {:?}, {} point arrays",
298 d[0], d[1], d[2], self.spacing(), self.origin(), self.point_data.num_arrays())
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn dimensions_and_points() {
308 let img = ImageData::with_dimensions(3, 4, 5);
309 assert_eq!(img.dimensions(), [3, 4, 5]);
310 assert_eq!(img.num_points(), 60);
311 assert_eq!(img.point_from_ijk(0, 0, 0), [0.0, 0.0, 0.0]);
312 assert_eq!(img.point_from_ijk(2, 3, 4), [2.0, 3.0, 4.0]);
313 }
314
315 #[test]
316 fn custom_spacing_and_origin() {
317 let mut img = ImageData::with_dimensions(2, 2, 2);
318 img.set_spacing([0.5, 0.5, 0.5]);
319 img.set_origin([1.0, 2.0, 3.0]);
320 assert_eq!(img.point_from_ijk(1, 1, 1), [1.5, 2.5, 3.5]);
321 }
322
323 #[test]
324 fn index_roundtrip() {
325 let img = ImageData::with_dimensions(3, 4, 5);
326 for idx in 0..60 {
327 let (i, j, k) = img.ijk_from_index(idx);
328 assert_eq!(img.index_from_ijk(i, j, k), idx);
329 }
330 }
331
332 #[test]
333 fn bounds_computation() {
334 let mut img = ImageData::with_dimensions(10, 10, 10);
335 img.set_spacing([0.1, 0.1, 0.1]);
336 let bb = img.bounds();
337 assert!((bb.x_max - 0.9).abs() < 1e-10);
338 assert!((bb.y_max - 0.9).abs() < 1e-10);
339 }
340
341 #[test]
342 fn num_cells() {
343 let img = ImageData::with_dimensions(3, 4, 5);
344 assert_eq!(img.num_cells(), 2 * 3 * 4); }
346
347 #[test]
348 fn from_function() {
349 let img = ImageData::from_function(
350 [5, 5, 5], [0.2, 0.2, 0.2], [0.0, 0.0, 0.0],
351 "dist",
352 |x, y, z| (x*x + y*y + z*z).sqrt(),
353 );
354 assert_eq!(img.dimensions(), [5, 5, 5]);
355 assert!(img.point_data().scalars().is_some());
356 assert_eq!(img.point_data().scalars().unwrap().name(), "dist");
357 assert_eq!(img.point_data().scalars().unwrap().num_tuples(), 125);
358 }
359
360 #[test]
361 fn builder_chain() {
362 let img = ImageData::with_dimensions(3, 3, 3)
363 .with_spacing([0.5, 0.5, 0.5])
364 .with_origin([1.0, 2.0, 3.0]);
365 assert_eq!(img.spacing(), [0.5, 0.5, 0.5]);
366 assert_eq!(img.origin(), [1.0, 2.0, 3.0]);
367 }
368
369 #[test]
370 fn scalar_at() {
371 let img = ImageData::from_function(
372 [3, 3, 3], [1.0, 1.0, 1.0], [0.0, 0.0, 0.0],
373 "val", |x, _y, _z| x,
374 );
375 assert!((img.scalar_at(0, 0, 0).unwrap()).abs() < 1e-10);
377 assert!((img.scalar_at(2, 0, 0).unwrap() - 2.0).abs() < 1e-10);
378 }
379
380 #[test]
381 fn point_positions() {
382 let img = ImageData::with_dimensions(2, 2, 1);
383 let positions = img.point_positions();
384 assert_eq!(positions.len(), 4);
385 assert_eq!(positions[0], [0.0, 0.0, 0.0]);
386 assert_eq!(positions[1], [1.0, 0.0, 0.0]);
387 }
388}