use manifold_csg_sys::*;
use crate::types::CsgError;
#[derive(Debug, Clone, Copy, Default)]
pub struct MeshGLOptions<'a> {
runs: Option<(&'a [u32], &'a [u32])>,
merge_vertices: Option<(&'a [u32], &'a [u32])>,
halfedge_tangents: Option<&'a [f32]>,
}
impl<'a> MeshGLOptions<'a> {
#[must_use]
pub const fn new() -> Self {
Self {
runs: None,
merge_vertices: None,
halfedge_tangents: None,
}
}
#[must_use]
pub const fn runs(mut self, run_indices: &'a [u32], run_original_ids: &'a [u32]) -> Self {
self.runs = Some((run_indices, run_original_ids));
self
}
#[must_use]
pub const fn merge_vertices(mut self, merge_from: &'a [u32], merge_to: &'a [u32]) -> Self {
self.merge_vertices = Some((merge_from, merge_to));
self
}
#[must_use]
pub const fn halfedge_tangents(mut self, halfedge_tangents: &'a [f32]) -> Self {
self.halfedge_tangents = Some(halfedge_tangents);
self
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct MeshGL64Options<'a> {
runs: Option<(&'a [u64], &'a [u32])>,
merge_vertices: Option<(&'a [u64], &'a [u64])>,
halfedge_tangents: Option<&'a [f64]>,
}
impl<'a> MeshGL64Options<'a> {
#[must_use]
pub const fn new() -> Self {
Self {
runs: None,
merge_vertices: None,
halfedge_tangents: None,
}
}
#[must_use]
pub const fn runs(mut self, run_indices: &'a [u64], run_original_ids: &'a [u32]) -> Self {
self.runs = Some((run_indices, run_original_ids));
self
}
#[must_use]
pub const fn merge_vertices(mut self, merge_from: &'a [u64], merge_to: &'a [u64]) -> Self {
self.merge_vertices = Some((merge_from, merge_to));
self
}
#[must_use]
pub const fn halfedge_tangents(mut self, halfedge_tangents: &'a [f64]) -> Self {
self.halfedge_tangents = Some(halfedge_tangents);
self
}
}
pub struct MeshGL {
pub(crate) ptr: *mut ManifoldMeshGL,
}
unsafe impl Send for MeshGL {}
unsafe impl Sync for MeshGL {}
impl Drop for MeshGL {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { manifold_delete_meshgl(self.ptr) };
}
}
}
impl MeshGL {
pub fn new(vert_props: &[f32], n_props: usize, tri_indices: &[u32]) -> Result<Self, CsgError> {
validate_mesh_shape(vert_props.len(), n_props, tri_indices.len())?;
let n_verts = vert_props.len() / n_props;
let n_tris = tri_indices.len() / 3;
let ptr = unsafe { manifold_alloc_meshgl() };
unsafe {
manifold_meshgl(
ptr,
vert_props.as_ptr(),
n_verts,
n_props,
tri_indices.as_ptr(),
n_tris,
);
}
Ok(Self { ptr })
}
pub fn new_with_tangents(
vert_props: &[f32],
n_props: usize,
tri_indices: &[u32],
halfedge_tangent: &[f32],
) -> Result<Self, CsgError> {
validate_mesh_shape(vert_props.len(), n_props, tri_indices.len())?;
let n_verts = vert_props.len() / n_props;
let n_tris = tri_indices.len() / 3;
validate_tangent_len(halfedge_tangent.len(), n_tris)?;
let ptr = unsafe { manifold_alloc_meshgl() };
unsafe {
manifold_meshgl_w_tangents(
ptr,
vert_props.as_ptr(),
n_verts,
n_props,
tri_indices.as_ptr(),
n_tris,
halfedge_tangent.as_ptr(),
);
}
Ok(Self { ptr })
}
pub fn new_with_options(
vert_props: &[f32],
n_props: usize,
tri_indices: &[u32],
options: MeshGLOptions<'_>,
) -> Result<Self, CsgError> {
validate_mesh_shape(vert_props.len(), n_props, tri_indices.len())?;
let n_verts = vert_props.len() / n_props;
let n_tris = tri_indices.len() / 3;
validate_meshgl_options(n_tris, tri_indices.len(), options)?;
let (run_indices, run_original_ids) = options.runs.unwrap_or((&[], &[]));
let (merge_from, merge_to) = options.merge_vertices.unwrap_or((&[], &[]));
let halfedge_tangents = options.halfedge_tangents.unwrap_or(&[]);
let sys_options = ManifoldMeshGLOptions {
run_indices: slice_ptr(run_indices),
run_indices_length: run_indices.len(),
run_original_ids: slice_ptr(run_original_ids),
run_original_ids_length: run_original_ids.len(),
merge_from_vert: slice_ptr(merge_from),
merge_to_vert: slice_ptr(merge_to),
merge_verts_length: merge_from.len(),
halfedge_tangents: slice_ptr(halfedge_tangents),
};
let ptr = unsafe { manifold_alloc_meshgl() };
unsafe {
manifold_meshgl_w_options(
ptr,
vert_props.as_ptr(),
n_verts,
n_props,
tri_indices.as_ptr(),
n_tris,
&sys_options,
);
}
Ok(Self { ptr })
}
#[must_use]
pub fn num_vert(&self) -> usize {
unsafe { manifold_meshgl_num_vert(self.ptr) }
}
#[must_use]
pub fn num_tri(&self) -> usize {
unsafe { manifold_meshgl_num_tri(self.ptr) }
}
#[must_use]
pub fn num_prop(&self) -> usize {
unsafe { manifold_meshgl_num_prop(self.ptr) }
}
#[must_use]
pub fn vert_properties(&self) -> Vec<f32> {
let len = unsafe { manifold_meshgl_vert_properties_length(self.ptr) };
let mut buf = vec![0.0f32; len];
unsafe { manifold_meshgl_vert_properties(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn tri_verts(&self) -> Vec<u32> {
let len = unsafe { manifold_meshgl_tri_length(self.ptr) };
let mut buf = vec![0u32; len];
unsafe { manifold_meshgl_tri_verts(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn merge(&self) -> Self {
let ptr = unsafe { manifold_alloc_meshgl() };
unsafe { manifold_meshgl_merge(ptr, self.ptr) };
Self { ptr }
}
#[must_use]
pub fn merge_from_vert(&self) -> Vec<u32> {
let len = unsafe { manifold_meshgl_merge_length(self.ptr) };
let mut buf = vec![0u32; len];
unsafe { manifold_meshgl_merge_from_vert(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn merge_to_vert(&self) -> Vec<u32> {
let len = unsafe { manifold_meshgl_merge_length(self.ptr) };
let mut buf = vec![0u32; len];
unsafe { manifold_meshgl_merge_to_vert(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn run_index(&self) -> Vec<u32> {
let len = unsafe { manifold_meshgl_run_index_length(self.ptr) };
let mut buf = vec![0u32; len];
unsafe { manifold_meshgl_run_index(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn run_original_id(&self) -> Vec<u32> {
let len = unsafe { manifold_meshgl_run_original_id_length(self.ptr) };
let mut buf = vec![0u32; len];
unsafe { manifold_meshgl_run_original_id(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn run_transform(&self) -> Vec<f32> {
let len = unsafe { manifold_meshgl_run_transform_length(self.ptr) };
let mut buf = vec![0.0f32; len];
unsafe { manifold_meshgl_run_transform(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn face_id(&self) -> Vec<u32> {
let len = unsafe { manifold_meshgl_face_id_length(self.ptr) };
let mut buf = vec![0u32; len];
unsafe { manifold_meshgl_face_id(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn halfedge_tangent(&self) -> Vec<f32> {
let len = unsafe { manifold_meshgl_tangent_length(self.ptr) };
let mut buf = vec![0.0f32; len];
unsafe { manifold_meshgl_halfedge_tangent(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn tolerance(&self) -> f32 {
unsafe { manifold_meshgl_tolerance(self.ptr) }
}
#[must_use]
pub fn num_run(&self) -> usize {
unsafe { manifold_meshgl_num_run(self.ptr) }
}
#[must_use]
pub fn run_flags(&self) -> Vec<u8> {
let len = unsafe { manifold_meshgl_run_flags_length(self.ptr) };
let mut buf = vec![0u8; len];
unsafe { manifold_meshgl_run_flags(buf.as_mut_ptr(), self.ptr) };
buf
}
}
impl Clone for MeshGL {
fn clone(&self) -> Self {
let ptr = unsafe { manifold_alloc_meshgl() };
unsafe { manifold_meshgl_copy(ptr, self.ptr) };
Self { ptr }
}
}
pub struct MeshGL64 {
pub(crate) ptr: *mut ManifoldMeshGL64,
}
unsafe impl Send for MeshGL64 {}
unsafe impl Sync for MeshGL64 {}
impl Drop for MeshGL64 {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { manifold_delete_meshgl64(self.ptr) };
}
}
}
impl MeshGL64 {
pub fn new(vert_props: &[f64], n_props: usize, tri_indices: &[u64]) -> Result<Self, CsgError> {
validate_mesh_shape(vert_props.len(), n_props, tri_indices.len())?;
let n_verts = vert_props.len() / n_props;
let n_tris = tri_indices.len() / 3;
let ptr = unsafe { manifold_alloc_meshgl64() };
unsafe {
manifold_meshgl64(
ptr,
vert_props.as_ptr(),
n_verts,
n_props,
tri_indices.as_ptr(),
n_tris,
);
}
Ok(Self { ptr })
}
pub fn new_with_tangents(
vert_props: &[f64],
n_props: usize,
tri_indices: &[u64],
halfedge_tangent: &[f64],
) -> Result<Self, CsgError> {
validate_mesh_shape(vert_props.len(), n_props, tri_indices.len())?;
let n_verts = vert_props.len() / n_props;
let n_tris = tri_indices.len() / 3;
validate_tangent_len(halfedge_tangent.len(), n_tris)?;
let ptr = unsafe { manifold_alloc_meshgl64() };
unsafe {
manifold_meshgl64_w_tangents(
ptr,
vert_props.as_ptr(),
n_verts,
n_props,
tri_indices.as_ptr(),
n_tris,
halfedge_tangent.as_ptr(),
);
}
Ok(Self { ptr })
}
pub fn new_with_options(
vert_props: &[f64],
n_props: usize,
tri_indices: &[u64],
options: MeshGL64Options<'_>,
) -> Result<Self, CsgError> {
validate_mesh_shape(vert_props.len(), n_props, tri_indices.len())?;
let n_verts = vert_props.len() / n_props;
let n_tris = tri_indices.len() / 3;
validate_meshgl64_options(n_tris, tri_indices.len(), options)?;
let (run_indices, run_original_ids) = options.runs.unwrap_or((&[], &[]));
let (merge_from, merge_to) = options.merge_vertices.unwrap_or((&[], &[]));
let halfedge_tangents = options.halfedge_tangents.unwrap_or(&[]);
let sys_options = ManifoldMeshGL64Options {
run_indices: slice_ptr(run_indices),
run_indices_length: run_indices.len(),
run_original_ids: slice_ptr(run_original_ids),
run_original_ids_length: run_original_ids.len(),
merge_from_vert: slice_ptr(merge_from),
merge_to_vert: slice_ptr(merge_to),
merge_verts_length: merge_from.len(),
halfedge_tangents: slice_ptr(halfedge_tangents),
};
let ptr = unsafe { manifold_alloc_meshgl64() };
unsafe {
manifold_meshgl64_w_options(
ptr,
vert_props.as_ptr(),
n_verts,
n_props,
tri_indices.as_ptr(),
n_tris,
&sys_options,
);
}
Ok(Self { ptr })
}
#[must_use]
pub fn num_vert(&self) -> usize {
unsafe { manifold_meshgl64_num_vert(self.ptr) }
}
#[must_use]
pub fn num_tri(&self) -> usize {
unsafe { manifold_meshgl64_num_tri(self.ptr) }
}
#[must_use]
pub fn num_prop(&self) -> usize {
unsafe { manifold_meshgl64_num_prop(self.ptr) }
}
#[must_use]
pub fn vert_properties(&self) -> Vec<f64> {
let len = unsafe { manifold_meshgl64_vert_properties_length(self.ptr) };
let mut buf = vec![0.0f64; len];
unsafe { manifold_meshgl64_vert_properties(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn tri_verts(&self) -> Vec<u64> {
let len = unsafe { manifold_meshgl64_tri_length(self.ptr) };
let mut buf = vec![0u64; len];
unsafe { manifold_meshgl64_tri_verts(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn merge(&self) -> Self {
let ptr = unsafe { manifold_alloc_meshgl64() };
unsafe { manifold_meshgl64_merge(ptr, self.ptr) };
Self { ptr }
}
#[must_use]
pub fn merge_from_vert(&self) -> Vec<u64> {
let len = unsafe { manifold_meshgl64_merge_length(self.ptr) };
let mut buf = vec![0u64; len];
unsafe { manifold_meshgl64_merge_from_vert(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn merge_to_vert(&self) -> Vec<u64> {
let len = unsafe { manifold_meshgl64_merge_length(self.ptr) };
let mut buf = vec![0u64; len];
unsafe { manifold_meshgl64_merge_to_vert(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn run_index(&self) -> Vec<u64> {
let len = unsafe { manifold_meshgl64_run_index_length(self.ptr) };
let mut buf = vec![0u64; len];
unsafe { manifold_meshgl64_run_index(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn run_original_id(&self) -> Vec<u32> {
let len = unsafe { manifold_meshgl64_run_original_id_length(self.ptr) };
let mut buf = vec![0u32; len];
unsafe { manifold_meshgl64_run_original_id(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn run_transform(&self) -> Vec<f64> {
let len = unsafe { manifold_meshgl64_run_transform_length(self.ptr) };
let mut buf = vec![0.0f64; len];
unsafe { manifold_meshgl64_run_transform(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn face_id(&self) -> Vec<u64> {
let len = unsafe { manifold_meshgl64_face_id_length(self.ptr) };
let mut buf = vec![0u64; len];
unsafe { manifold_meshgl64_face_id(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn halfedge_tangent(&self) -> Vec<f64> {
let len = unsafe { manifold_meshgl64_tangent_length(self.ptr) };
let mut buf = vec![0.0f64; len];
unsafe { manifold_meshgl64_halfedge_tangent(buf.as_mut_ptr(), self.ptr) };
buf
}
#[must_use]
pub fn tolerance(&self) -> f64 {
unsafe { manifold_meshgl64_tolerance(self.ptr) }
}
#[must_use]
pub fn num_run(&self) -> usize {
unsafe { manifold_meshgl64_num_run(self.ptr) }
}
#[must_use]
pub fn run_flags(&self) -> Vec<u8> {
let len = unsafe { manifold_meshgl64_run_flags_length(self.ptr) };
let mut buf = vec![0u8; len];
unsafe { manifold_meshgl64_run_flags(buf.as_mut_ptr(), self.ptr) };
buf
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub fn from_obj(obj_content: &str) -> Result<Self, crate::types::CsgError> {
let c_str = std::ffi::CString::new(obj_content).map_err(|_| {
crate::types::CsgError::InvalidInput("OBJ content contains null byte".into())
})?;
let ptr = unsafe { manifold_alloc_meshgl64() };
unsafe { manifold_meshgl64_read_obj(ptr, c_str.as_ptr()) };
Ok(Self { ptr })
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
#[must_use]
pub fn to_obj(&self) -> String {
let mut result = String::new();
unsafe extern "C" fn callback(data: *mut std::ffi::c_char, ctx: *mut std::ffi::c_void) {
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let result = unsafe { &mut *(ctx as *mut String) };
let c_str = unsafe { std::ffi::CStr::from_ptr(data) };
*result = c_str.to_string_lossy().into_owned();
}));
}
let ctx = &mut result as *mut String as *mut std::ffi::c_void;
unsafe { manifold_meshgl64_write_obj(self.ptr, Some(callback), ctx) };
result
}
}
impl Clone for MeshGL64 {
fn clone(&self) -> Self {
let ptr = unsafe { manifold_alloc_meshgl64() };
unsafe { manifold_meshgl64_copy(ptr, self.ptr) };
Self { ptr }
}
}
fn validate_mesh_shape(
vert_props_len: usize,
n_props: usize,
tri_indices_len: usize,
) -> Result<(), CsgError> {
if n_props < 3 {
return Err(CsgError::InvalidInput(
"n_props must be >= 3 (x, y, z)".into(),
));
}
if vert_props_len % n_props != 0 {
return Err(CsgError::InvalidInput(
"vert_props length must be divisible by n_props".into(),
));
}
if tri_indices_len % 3 != 0 {
return Err(CsgError::InvalidInput(
"tri_indices length must be divisible by 3".into(),
));
}
Ok(())
}
fn validate_tangent_len(tangent_len: usize, n_tris: usize) -> Result<(), CsgError> {
let expected = n_tris
.checked_mul(3)
.and_then(|n| n.checked_mul(4))
.ok_or_else(|| CsgError::InvalidInput("tangent length overflow".into()))?;
if tangent_len != expected {
return Err(CsgError::InvalidInput(format!(
"halfedge_tangent length must be num_tri * 3 * 4 ({expected}), got {tangent_len}"
)));
}
Ok(())
}
fn validate_meshgl_options(
n_tris: usize,
tri_indices_len: usize,
options: MeshGLOptions<'_>,
) -> Result<(), CsgError> {
if let Some(tangents) = options.halfedge_tangents {
validate_tangent_len(tangents.len(), n_tris)?;
}
if let Some((run_indices, run_original_ids)) = options.runs {
validate_run_metadata(run_indices, run_original_ids.len(), tri_indices_len)?;
}
if let Some((merge_from, merge_to)) = options.merge_vertices {
validate_merge_metadata(merge_from.len(), merge_to.len())?;
}
Ok(())
}
fn validate_meshgl64_options(
n_tris: usize,
tri_indices_len: usize,
options: MeshGL64Options<'_>,
) -> Result<(), CsgError> {
if let Some(tangents) = options.halfedge_tangents {
validate_tangent_len(tangents.len(), n_tris)?;
}
if let Some((run_indices, run_original_ids)) = options.runs {
validate_run_metadata(run_indices, run_original_ids.len(), tri_indices_len)?;
}
if let Some((merge_from, merge_to)) = options.merge_vertices {
validate_merge_metadata(merge_from.len(), merge_to.len())?;
}
Ok(())
}
fn validate_run_metadata<I>(
run_indices: &[I],
run_original_ids_len: usize,
tri_indices_len: usize,
) -> Result<(), CsgError>
where
I: Copy + Into<u64>,
{
if run_original_ids_len == 0 {
return Err(CsgError::InvalidInput(
"run metadata requires at least one run_original_id".into(),
));
}
if run_indices.len() != run_original_ids_len && run_indices.len() != run_original_ids_len + 1 {
return Err(CsgError::InvalidInput(
"run_indices length must equal run_original_ids length, or be one longer".into(),
));
}
let Some(first) = run_indices.first() else {
return Err(CsgError::InvalidInput(
"run metadata requires at least one run_index".into(),
));
};
if (*first).into() != 0 {
return Err(CsgError::InvalidInput("run_indices must start at 0".into()));
}
let mut previous = (*first).into();
for &index in run_indices {
let index = index.into();
if index % 3 != 0 {
return Err(CsgError::InvalidInput(
"run_indices values must be divisible by 3".into(),
));
}
if index > tri_indices_len as u64 {
return Err(CsgError::InvalidInput(
"run_indices values must not exceed tri_indices length".into(),
));
}
if index < previous {
return Err(CsgError::InvalidInput("run_indices must be sorted".into()));
}
previous = index;
}
if run_indices.len() == run_original_ids_len + 1
&& run_indices.last().copied().map(Into::into) != Some(tri_indices_len as u64)
{
return Err(CsgError::InvalidInput(
"run_indices sentinel must equal tri_indices length".into(),
));
}
Ok(())
}
fn validate_merge_metadata(merge_from_len: usize, merge_to_len: usize) -> Result<(), CsgError> {
if merge_from_len != merge_to_len {
return Err(CsgError::InvalidInput(
"merge_from and merge_to must have the same length".into(),
));
}
Ok(())
}
fn slice_ptr<T>(slice: &[T]) -> *const T {
if slice.is_empty() {
std::ptr::null()
} else {
slice.as_ptr()
}
}