Skip to main content

modelio/
voxel_array.rs

1use std::ptr;
2
3use crate::asset::Asset;
4use crate::error::Result;
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::mesh::Mesh;
8use crate::mesh_buffer::MeshBufferAllocator;
9use crate::object::Object;
10use crate::types::{BoundingBox, VoxelArrayInfo, VoxelIndexExtent};
11use crate::util::{parse_json, required_handle};
12
13#[derive(Debug, Clone)]
14/// Wraps the corresponding Model I/O voxel array counterpart.
15pub struct VoxelArray {
16    handle: ObjectHandle,
17}
18
19impl VoxelArray {
20    /// Builds this wrapper from the retained handle of the wrapped Model I/O voxel array counterpart.
21    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
22        Self { handle }
23    }
24
25    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O voxel array counterpart.
26    pub fn new(
27        voxel_indices: &[[i32; 4]],
28        bounding_box: BoundingBox,
29        voxel_extent: f32,
30    ) -> Result<Self> {
31        let flattened = voxel_indices
32            .iter()
33            .flat_map(|index| index.iter().copied())
34            .collect::<Vec<_>>();
35        let mut out_voxel_array = ptr::null_mut();
36        let mut out_error = ptr::null_mut();
37        // SAFETY: The unsafe operation is valid in this context.
38        let status = unsafe {
39            ffi::mdl_voxel_array_new_with_indices(
40                flattened.as_ptr(),
41                voxel_indices.len() as u64,
42                bounding_box.min[0],
43                bounding_box.min[1],
44                bounding_box.min[2],
45                bounding_box.max[0],
46                bounding_box.max[1],
47                bounding_box.max[2],
48                voxel_extent,
49                &mut out_voxel_array,
50                &mut out_error,
51            )
52        };
53        crate::util::status_result(status, out_error)?;
54        Ok(Self::from_handle(required_handle(
55            out_voxel_array,
56            "MDLVoxelArray",
57        )?))
58    }
59
60    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
61    pub fn from_asset(asset: &Asset, divisions: i32, patch_radius: f32) -> Result<Self> {
62        let mut out_voxel_array = ptr::null_mut();
63        let mut out_error = ptr::null_mut();
64        // SAFETY: The unsafe operation is valid in this context.
65        let status = unsafe {
66            ffi::mdl_voxel_array_new_with_asset(
67                asset.as_ptr(),
68                divisions,
69                patch_radius,
70                &mut out_voxel_array,
71                &mut out_error,
72            )
73        };
74        crate::util::status_result(status, out_error)?;
75        Ok(Self::from_handle(required_handle(
76            out_voxel_array,
77            "MDLVoxelArray",
78        )?))
79    }
80
81    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
82    pub fn info(&self) -> Result<VoxelArrayInfo> {
83        parse_json(
84            // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
85            unsafe { ffi::mdl_voxel_array_info_json(self.handle.as_ptr()) },
86            "MDLVoxelArray",
87        )
88    }
89
90    #[must_use]
91    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
92    pub fn count(&self) -> usize {
93        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
94        unsafe { ffi::mdl_voxel_array_count(self.handle.as_ptr()) as usize }
95    }
96
97    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
98    pub fn set_voxel(&self, index: [i32; 4]) {
99        // SAFETY: The unsafe operation is valid in this context.
100        unsafe {
101            ffi::mdl_voxel_array_set_voxel(
102                self.handle.as_ptr(),
103                index[0],
104                index[1],
105                index[2],
106                index[3],
107            );
108        };
109    }
110
111    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
112    pub fn set_voxels_for_mesh(&self, mesh: &Mesh, divisions: i32, patch_radius: f32) {
113        // SAFETY: The unsafe operation is valid in this context.
114        unsafe {
115            ffi::mdl_voxel_array_set_voxels_for_mesh(
116                self.handle.as_ptr(),
117                mesh.as_ptr(),
118                divisions,
119                patch_radius,
120            );
121        };
122    }
123
124    #[must_use]
125    #[allow(clippy::fn_params_excessive_bools)]
126    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
127    pub fn voxel_exists(
128        &self,
129        index: [i32; 4],
130        allow_any_x: bool,
131        allow_any_y: bool,
132        allow_any_z: bool,
133        allow_any_shell: bool,
134    ) -> bool {
135        // SAFETY: The unsafe operation is valid in this context.
136        unsafe {
137            ffi::mdl_voxel_array_voxel_exists(
138                self.handle.as_ptr(),
139                index[0],
140                index[1],
141                index[2],
142                index[3],
143                i32::from(allow_any_x),
144                i32::from(allow_any_y),
145                i32::from(allow_any_z),
146                i32::from(allow_any_shell),
147            ) != 0
148        }
149    }
150
151    #[must_use]
152    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
153    pub fn voxel_indices(&self) -> Vec<[i32; 4]> {
154        let count = self.count();
155        if count == 0 {
156            return Vec::new();
157        }
158        let mut flattened = vec![0_i32; count * 4];
159        // SAFETY: The unsafe operation is valid in this context.
160        let written = unsafe {
161            ffi::mdl_voxel_array_copy_indices(
162                self.handle.as_ptr(),
163                flattened.as_mut_ptr(),
164                count as u64,
165            )
166        } as usize;
167        flattened.truncate(written * 4);
168        flattened
169            .chunks_exact(4)
170            .map(|chunk| [chunk[0], chunk[1], chunk[2], chunk[3]])
171            .collect()
172    }
173
174    #[must_use]
175    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
176    pub fn voxels_within_extent(&self, extent: &VoxelIndexExtent) -> Vec<[i32; 4]> {
177        let capacity = self.count();
178        if capacity == 0 {
179            return Vec::new();
180        }
181        let mut flattened = vec![0_i32; capacity * 4];
182        // SAFETY: The unsafe operation is valid in this context.
183        let written = unsafe {
184            ffi::mdl_voxel_array_copy_voxels_within_extent(
185                self.handle.as_ptr(),
186                extent.minimum_extent[0],
187                extent.minimum_extent[1],
188                extent.minimum_extent[2],
189                extent.minimum_extent[3],
190                extent.maximum_extent[0],
191                extent.maximum_extent[1],
192                extent.maximum_extent[2],
193                extent.maximum_extent[3],
194                flattened.as_mut_ptr(),
195                capacity as u64,
196            )
197        } as usize;
198        flattened.truncate(written * 4);
199        flattened
200            .chunks_exact(4)
201            .map(|chunk| [chunk[0], chunk[1], chunk[2], chunk[3]])
202            .collect()
203    }
204
205    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
206    pub fn union_with(&self, other: &Self) {
207        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
208        unsafe { ffi::mdl_voxel_array_union(self.handle.as_ptr(), other.handle.as_ptr()) };
209    }
210
211    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
212    pub fn intersect_with(&self, other: &Self) {
213        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
214        unsafe { ffi::mdl_voxel_array_intersect(self.handle.as_ptr(), other.handle.as_ptr()) };
215    }
216
217    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
218    pub fn difference_with(&self, other: &Self) {
219        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
220        unsafe { ffi::mdl_voxel_array_difference(self.handle.as_ptr(), other.handle.as_ptr()) };
221    }
222
223    #[must_use]
224    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
225    pub fn index_of_spatial_location(&self, location: [f32; 3]) -> [i32; 4] {
226        let mut values = [0_i32; 4];
227        // SAFETY: The unsafe operation is valid in this context.
228        unsafe {
229            ffi::mdl_voxel_array_index_of_spatial_location(
230                self.handle.as_ptr(),
231                location[0],
232                location[1],
233                location[2],
234                values.as_mut_ptr(),
235            );
236        };
237        values
238    }
239
240    #[must_use]
241    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
242    pub fn spatial_location_of_index(&self, index: [i32; 4]) -> [f32; 3] {
243        let mut values = [0.0_f32; 3];
244        // SAFETY: The unsafe operation is valid in this context.
245        unsafe {
246            ffi::mdl_voxel_array_spatial_location_of_index(
247                self.handle.as_ptr(),
248                index[0],
249                index[1],
250                index[2],
251                index[3],
252                &mut values[0],
253                &mut values[1],
254                &mut values[2],
255            );
256        };
257        values
258    }
259
260    #[must_use]
261    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
262    pub fn voxel_bounding_box_at_index(&self, index: [i32; 4]) -> BoundingBox {
263        let mut min = [0.0_f32; 3];
264        let mut max = [0.0_f32; 3];
265        // SAFETY: The unsafe operation is valid in this context.
266        unsafe {
267            ffi::mdl_voxel_array_voxel_bounding_box_at_index(
268                self.handle.as_ptr(),
269                index[0],
270                index[1],
271                index[2],
272                index[3],
273                &mut min[0],
274                &mut min[1],
275                &mut min[2],
276                &mut max[0],
277                &mut max[1],
278                &mut max[2],
279            );
280        };
281        BoundingBox { min, max }
282    }
283
284    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
285    pub fn convert_to_signed_shell_field(&self) {
286        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
287        unsafe { ffi::mdl_voxel_array_convert_to_signed_shell_field(self.handle.as_ptr()) };
288    }
289
290    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
291    pub fn set_shell_field_interior_thickness(&self, value: f32) {
292        // SAFETY: The unsafe operation is valid in this context.
293        unsafe {
294            ffi::mdl_voxel_array_set_shell_field_interior_thickness(self.handle.as_ptr(), value);
295        };
296    }
297
298    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
299    pub fn set_shell_field_exterior_thickness(&self, value: f32) {
300        // SAFETY: The unsafe operation is valid in this context.
301        unsafe {
302            ffi::mdl_voxel_array_set_shell_field_exterior_thickness(self.handle.as_ptr(), value);
303        };
304    }
305
306    #[must_use]
307    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
308    pub fn coarse_mesh(&self) -> Option<Mesh> {
309        self.coarse_mesh_with_allocator(None)
310    }
311
312    #[must_use]
313    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
314    pub fn coarse_mesh_with_allocator(
315        &self,
316        allocator: Option<&MeshBufferAllocator>,
317    ) -> Option<Mesh> {
318        // SAFETY: The unsafe operation is valid in this context.
319        let ptr = unsafe {
320            ffi::mdl_voxel_array_coarse_mesh_with_allocator(
321                self.handle.as_ptr(),
322                allocator.map_or(ptr::null_mut(), MeshBufferAllocator::as_ptr),
323            )
324        };
325        // SAFETY: The unsafe operation is valid in this context.
326        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Mesh::from_handle)
327    }
328
329    #[must_use]
330    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
331    pub fn mesh(&self) -> Option<Mesh> {
332        self.mesh_with_allocator(None)
333    }
334
335    #[must_use]
336    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
337    pub fn mesh_with_allocator(&self, allocator: Option<&MeshBufferAllocator>) -> Option<Mesh> {
338        // SAFETY: The unsafe operation is valid in this context.
339        let ptr = unsafe {
340            ffi::mdl_voxel_array_mesh_with_allocator(
341                self.handle.as_ptr(),
342                allocator.map_or(ptr::null_mut(), MeshBufferAllocator::as_ptr),
343            )
344        };
345        // SAFETY: The unsafe operation is valid in this context.
346        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Mesh::from_handle)
347    }
348
349    #[must_use]
350    /// Calls the corresponding Model I/O method on the wrapped Model I/O voxel array counterpart.
351    pub fn as_object(&self) -> Object {
352        Object::from_handle(self.handle.clone())
353    }
354}