Skip to main content

modelio/
voxel_array.rs

1use std::ptr;
2
3use crate::error::Result;
4use crate::ffi;
5use crate::handle::ObjectHandle;
6use crate::mesh::Mesh;
7use crate::object::Object;
8use crate::types::{BoundingBox, VoxelArrayInfo, VoxelIndexExtent};
9use crate::util::{parse_json, required_handle};
10
11#[derive(Debug, Clone)]
12pub struct VoxelArray {
13    handle: ObjectHandle,
14}
15
16impl VoxelArray {
17    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
18        Self { handle }
19    }
20
21    pub fn new(
22        voxel_indices: &[[i32; 4]],
23        bounding_box: BoundingBox,
24        voxel_extent: f32,
25    ) -> Result<Self> {
26        let flattened = voxel_indices
27            .iter()
28            .flat_map(|index| index.iter().copied())
29            .collect::<Vec<_>>();
30        let mut out_voxel_array = ptr::null_mut();
31        let mut out_error = ptr::null_mut();
32        let status = unsafe {
33            ffi::mdl_voxel_array_new_with_indices(
34                flattened.as_ptr(),
35                voxel_indices.len() as u64,
36                bounding_box.min[0],
37                bounding_box.min[1],
38                bounding_box.min[2],
39                bounding_box.max[0],
40                bounding_box.max[1],
41                bounding_box.max[2],
42                voxel_extent,
43                &mut out_voxel_array,
44                &mut out_error,
45            )
46        };
47        crate::util::status_result(status, out_error)?;
48        Ok(Self::from_handle(required_handle(
49            out_voxel_array,
50            "MDLVoxelArray",
51        )?))
52    }
53
54    pub fn info(&self) -> Result<VoxelArrayInfo> {
55        parse_json(
56            unsafe { ffi::mdl_voxel_array_info_json(self.handle.as_ptr()) },
57            "MDLVoxelArray",
58        )
59    }
60
61    #[must_use]
62    pub fn count(&self) -> usize {
63        unsafe { ffi::mdl_voxel_array_count(self.handle.as_ptr()) as usize }
64    }
65
66    pub fn set_voxel(&self, index: [i32; 4]) {
67        unsafe {
68            ffi::mdl_voxel_array_set_voxel(
69                self.handle.as_ptr(),
70                index[0],
71                index[1],
72                index[2],
73                index[3],
74            );
75        };
76    }
77
78    #[must_use]
79    #[allow(clippy::fn_params_excessive_bools)]
80    pub fn voxel_exists(
81        &self,
82        index: [i32; 4],
83        allow_any_x: bool,
84        allow_any_y: bool,
85        allow_any_z: bool,
86        allow_any_shell: bool,
87    ) -> bool {
88        unsafe {
89            ffi::mdl_voxel_array_voxel_exists(
90                self.handle.as_ptr(),
91                index[0],
92                index[1],
93                index[2],
94                index[3],
95                i32::from(allow_any_x),
96                i32::from(allow_any_y),
97                i32::from(allow_any_z),
98                i32::from(allow_any_shell),
99            ) != 0
100        }
101    }
102
103    #[must_use]
104    pub fn voxel_indices(&self) -> Vec<[i32; 4]> {
105        let count = self.count();
106        if count == 0 {
107            return Vec::new();
108        }
109        let mut flattened = vec![0_i32; count * 4];
110        let written = unsafe {
111            ffi::mdl_voxel_array_copy_indices(
112                self.handle.as_ptr(),
113                flattened.as_mut_ptr(),
114                count as u64,
115            )
116        } as usize;
117        flattened.truncate(written * 4);
118        flattened
119            .chunks_exact(4)
120            .map(|chunk| [chunk[0], chunk[1], chunk[2], chunk[3]])
121            .collect()
122    }
123
124    #[must_use]
125    pub fn voxels_within_extent(&self, extent: &VoxelIndexExtent) -> Vec<[i32; 4]> {
126        let capacity = self.count();
127        if capacity == 0 {
128            return Vec::new();
129        }
130        let mut flattened = vec![0_i32; capacity * 4];
131        let written = unsafe {
132            ffi::mdl_voxel_array_copy_voxels_within_extent(
133                self.handle.as_ptr(),
134                extent.minimum_extent[0],
135                extent.minimum_extent[1],
136                extent.minimum_extent[2],
137                extent.minimum_extent[3],
138                extent.maximum_extent[0],
139                extent.maximum_extent[1],
140                extent.maximum_extent[2],
141                extent.maximum_extent[3],
142                flattened.as_mut_ptr(),
143                capacity as u64,
144            )
145        } as usize;
146        flattened.truncate(written * 4);
147        flattened
148            .chunks_exact(4)
149            .map(|chunk| [chunk[0], chunk[1], chunk[2], chunk[3]])
150            .collect()
151    }
152
153    pub fn union_with(&self, other: &Self) {
154        unsafe { ffi::mdl_voxel_array_union(self.handle.as_ptr(), other.handle.as_ptr()) };
155    }
156
157    pub fn intersect_with(&self, other: &Self) {
158        unsafe { ffi::mdl_voxel_array_intersect(self.handle.as_ptr(), other.handle.as_ptr()) };
159    }
160
161    pub fn difference_with(&self, other: &Self) {
162        unsafe { ffi::mdl_voxel_array_difference(self.handle.as_ptr(), other.handle.as_ptr()) };
163    }
164
165    #[must_use]
166    pub fn index_of_spatial_location(&self, location: [f32; 3]) -> [i32; 4] {
167        let mut values = [0_i32; 4];
168        unsafe {
169            ffi::mdl_voxel_array_index_of_spatial_location(
170                self.handle.as_ptr(),
171                location[0],
172                location[1],
173                location[2],
174                values.as_mut_ptr(),
175            );
176        };
177        values
178    }
179
180    #[must_use]
181    pub fn spatial_location_of_index(&self, index: [i32; 4]) -> [f32; 3] {
182        let mut values = [0.0_f32; 3];
183        unsafe {
184            ffi::mdl_voxel_array_spatial_location_of_index(
185                self.handle.as_ptr(),
186                index[0],
187                index[1],
188                index[2],
189                index[3],
190                &mut values[0],
191                &mut values[1],
192                &mut values[2],
193            );
194        };
195        values
196    }
197
198    #[must_use]
199    pub fn voxel_bounding_box_at_index(&self, index: [i32; 4]) -> BoundingBox {
200        let mut min = [0.0_f32; 3];
201        let mut max = [0.0_f32; 3];
202        unsafe {
203            ffi::mdl_voxel_array_voxel_bounding_box_at_index(
204                self.handle.as_ptr(),
205                index[0],
206                index[1],
207                index[2],
208                index[3],
209                &mut min[0],
210                &mut min[1],
211                &mut min[2],
212                &mut max[0],
213                &mut max[1],
214                &mut max[2],
215            );
216        };
217        BoundingBox { min, max }
218    }
219
220    pub fn convert_to_signed_shell_field(&self) {
221        unsafe { ffi::mdl_voxel_array_convert_to_signed_shell_field(self.handle.as_ptr()) };
222    }
223
224    pub fn set_shell_field_interior_thickness(&self, value: f32) {
225        unsafe {
226            ffi::mdl_voxel_array_set_shell_field_interior_thickness(self.handle.as_ptr(), value);
227        };
228    }
229
230    pub fn set_shell_field_exterior_thickness(&self, value: f32) {
231        unsafe {
232            ffi::mdl_voxel_array_set_shell_field_exterior_thickness(self.handle.as_ptr(), value);
233        };
234    }
235
236    #[must_use]
237    pub fn coarse_mesh(&self) -> Option<Mesh> {
238        let ptr = unsafe { ffi::mdl_voxel_array_coarse_mesh(self.handle.as_ptr()) };
239        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Mesh::from_handle)
240    }
241
242    #[must_use]
243    pub fn mesh(&self) -> Option<Mesh> {
244        let ptr = unsafe { ffi::mdl_voxel_array_mesh(self.handle.as_ptr()) };
245        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Mesh::from_handle)
246    }
247
248    #[must_use]
249    pub fn as_object(&self) -> Object {
250        Object::from_handle(self.handle.clone())
251    }
252}