modelio-rs 0.2.1

Safe Rust bindings for Apple's ModelIO framework — assets, meshes, materials, lights, cameras, voxels, textures, and animation on macOS
Documentation
use std::ptr;

use crate::error::Result;
use crate::ffi;
use crate::handle::ObjectHandle;
use crate::mesh::Mesh;
use crate::object::Object;
use crate::types::{BoundingBox, VoxelArrayInfo, VoxelIndexExtent};
use crate::util::{parse_json, required_handle};

#[derive(Debug, Clone)]
pub struct VoxelArray {
    handle: ObjectHandle,
}

impl VoxelArray {
    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
        Self { handle }
    }

    pub fn new(
        voxel_indices: &[[i32; 4]],
        bounding_box: BoundingBox,
        voxel_extent: f32,
    ) -> Result<Self> {
        let flattened = voxel_indices
            .iter()
            .flat_map(|index| index.iter().copied())
            .collect::<Vec<_>>();
        let mut out_voxel_array = ptr::null_mut();
        let mut out_error = ptr::null_mut();
        let status = unsafe {
            ffi::mdl_voxel_array_new_with_indices(
                flattened.as_ptr(),
                voxel_indices.len() as u64,
                bounding_box.min[0],
                bounding_box.min[1],
                bounding_box.min[2],
                bounding_box.max[0],
                bounding_box.max[1],
                bounding_box.max[2],
                voxel_extent,
                &mut out_voxel_array,
                &mut out_error,
            )
        };
        crate::util::status_result(status, out_error)?;
        Ok(Self::from_handle(required_handle(
            out_voxel_array,
            "MDLVoxelArray",
        )?))
    }

    pub fn info(&self) -> Result<VoxelArrayInfo> {
        parse_json(
            unsafe { ffi::mdl_voxel_array_info_json(self.handle.as_ptr()) },
            "MDLVoxelArray",
        )
    }

    #[must_use]
    pub fn count(&self) -> usize {
        unsafe { ffi::mdl_voxel_array_count(self.handle.as_ptr()) as usize }
    }

    pub fn set_voxel(&self, index: [i32; 4]) {
        unsafe {
            ffi::mdl_voxel_array_set_voxel(
                self.handle.as_ptr(),
                index[0],
                index[1],
                index[2],
                index[3],
            );
        };
    }

    #[must_use]
    #[allow(clippy::fn_params_excessive_bools)]
    pub fn voxel_exists(
        &self,
        index: [i32; 4],
        allow_any_x: bool,
        allow_any_y: bool,
        allow_any_z: bool,
        allow_any_shell: bool,
    ) -> bool {
        unsafe {
            ffi::mdl_voxel_array_voxel_exists(
                self.handle.as_ptr(),
                index[0],
                index[1],
                index[2],
                index[3],
                i32::from(allow_any_x),
                i32::from(allow_any_y),
                i32::from(allow_any_z),
                i32::from(allow_any_shell),
            ) != 0
        }
    }

    #[must_use]
    pub fn voxel_indices(&self) -> Vec<[i32; 4]> {
        let count = self.count();
        if count == 0 {
            return Vec::new();
        }
        let mut flattened = vec![0_i32; count * 4];
        let written = unsafe {
            ffi::mdl_voxel_array_copy_indices(
                self.handle.as_ptr(),
                flattened.as_mut_ptr(),
                count as u64,
            )
        } as usize;
        flattened.truncate(written * 4);
        flattened
            .chunks_exact(4)
            .map(|chunk| [chunk[0], chunk[1], chunk[2], chunk[3]])
            .collect()
    }

    #[must_use]
    pub fn voxels_within_extent(&self, extent: &VoxelIndexExtent) -> Vec<[i32; 4]> {
        let capacity = self.count();
        if capacity == 0 {
            return Vec::new();
        }
        let mut flattened = vec![0_i32; capacity * 4];
        let written = unsafe {
            ffi::mdl_voxel_array_copy_voxels_within_extent(
                self.handle.as_ptr(),
                extent.minimum_extent[0],
                extent.minimum_extent[1],
                extent.minimum_extent[2],
                extent.minimum_extent[3],
                extent.maximum_extent[0],
                extent.maximum_extent[1],
                extent.maximum_extent[2],
                extent.maximum_extent[3],
                flattened.as_mut_ptr(),
                capacity as u64,
            )
        } as usize;
        flattened.truncate(written * 4);
        flattened
            .chunks_exact(4)
            .map(|chunk| [chunk[0], chunk[1], chunk[2], chunk[3]])
            .collect()
    }

    pub fn union_with(&self, other: &Self) {
        unsafe { ffi::mdl_voxel_array_union(self.handle.as_ptr(), other.handle.as_ptr()) };
    }

    pub fn intersect_with(&self, other: &Self) {
        unsafe { ffi::mdl_voxel_array_intersect(self.handle.as_ptr(), other.handle.as_ptr()) };
    }

    pub fn difference_with(&self, other: &Self) {
        unsafe { ffi::mdl_voxel_array_difference(self.handle.as_ptr(), other.handle.as_ptr()) };
    }

    #[must_use]
    pub fn index_of_spatial_location(&self, location: [f32; 3]) -> [i32; 4] {
        let mut values = [0_i32; 4];
        unsafe {
            ffi::mdl_voxel_array_index_of_spatial_location(
                self.handle.as_ptr(),
                location[0],
                location[1],
                location[2],
                values.as_mut_ptr(),
            );
        };
        values
    }

    #[must_use]
    pub fn spatial_location_of_index(&self, index: [i32; 4]) -> [f32; 3] {
        let mut values = [0.0_f32; 3];
        unsafe {
            ffi::mdl_voxel_array_spatial_location_of_index(
                self.handle.as_ptr(),
                index[0],
                index[1],
                index[2],
                index[3],
                &mut values[0],
                &mut values[1],
                &mut values[2],
            );
        };
        values
    }

    #[must_use]
    pub fn voxel_bounding_box_at_index(&self, index: [i32; 4]) -> BoundingBox {
        let mut min = [0.0_f32; 3];
        let mut max = [0.0_f32; 3];
        unsafe {
            ffi::mdl_voxel_array_voxel_bounding_box_at_index(
                self.handle.as_ptr(),
                index[0],
                index[1],
                index[2],
                index[3],
                &mut min[0],
                &mut min[1],
                &mut min[2],
                &mut max[0],
                &mut max[1],
                &mut max[2],
            );
        };
        BoundingBox { min, max }
    }

    pub fn convert_to_signed_shell_field(&self) {
        unsafe { ffi::mdl_voxel_array_convert_to_signed_shell_field(self.handle.as_ptr()) };
    }

    pub fn set_shell_field_interior_thickness(&self, value: f32) {
        unsafe {
            ffi::mdl_voxel_array_set_shell_field_interior_thickness(self.handle.as_ptr(), value);
        };
    }

    pub fn set_shell_field_exterior_thickness(&self, value: f32) {
        unsafe {
            ffi::mdl_voxel_array_set_shell_field_exterior_thickness(self.handle.as_ptr(), value);
        };
    }

    #[must_use]
    pub fn coarse_mesh(&self) -> Option<Mesh> {
        let ptr = unsafe { ffi::mdl_voxel_array_coarse_mesh(self.handle.as_ptr()) };
        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Mesh::from_handle)
    }

    #[must_use]
    pub fn mesh(&self) -> Option<Mesh> {
        let ptr = unsafe { ffi::mdl_voxel_array_mesh(self.handle.as_ptr()) };
        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(Mesh::from_handle)
    }

    #[must_use]
    pub fn as_object(&self) -> Object {
        Object::from_handle(self.handle.clone())
    }
}