use std::error::Error;
use std::ffi::{c_char, c_double, c_uint};
use std::fmt::{Display, Formatter};
use std::marker::PhantomData;
use std::ptr::NonNull;
use std::rc::Rc;
const STATUS_OK: u32 = 0;
const ABI_VERSION: u32 = 1;
#[repr(C)]
struct NativeMesh {
_private: [u8; 0],
}
#[repr(C)]
struct NativeObstruction {
code: u32,
coordinate_error: u32,
input_index: u64,
first_index: u64,
second_index: u64,
first_value: f64,
second_value: f64,
point_x: f64,
point_y: f64,
message: [c_char; 256],
}
impl Default for NativeObstruction {
fn default() -> Self {
Self {
code: 0,
coordinate_error: 0,
input_index: u64::MAX,
first_index: 0,
second_index: 0,
first_value: 0.0,
second_value: 0.0,
point_x: 0.0,
point_y: 0.0,
message: [0; 256],
}
}
}
#[link(name = "moonlight-triangulation-c")]
unsafe extern "C" {
fn ml_abi_version() -> c_uint;
fn ml_runtime_initialize() -> c_uint;
fn ml_delaunay_f64(
coordinates: *const c_double,
point_count: usize,
result: *mut *mut NativeMesh,
obstruction: *mut NativeObstruction,
) -> c_uint;
fn ml_mesh_insert_many_f64(
mesh: *const NativeMesh,
coordinates: *const c_double,
point_count: usize,
result: *mut *mut NativeMesh,
obstruction: *mut NativeObstruction,
) -> c_uint;
fn ml_mesh_union(
left: *const NativeMesh,
right: *const NativeMesh,
result: *mut *mut NativeMesh,
obstruction: *mut NativeObstruction,
) -> c_uint;
fn ml_mesh_intersection(
left: *const NativeMesh,
right: *const NativeMesh,
result: *mut *mut NativeMesh,
obstruction: *mut NativeObstruction,
) -> c_uint;
fn ml_mesh_difference(
left: *const NativeMesh,
right: *const NativeMesh,
result: *mut *mut NativeMesh,
obstruction: *mut NativeObstruction,
) -> c_uint;
fn ml_mesh_symmetric_difference(
left: *const NativeMesh,
right: *const NativeMesh,
result: *mut *mut NativeMesh,
obstruction: *mut NativeObstruction,
) -> c_uint;
fn ml_mesh_vertex_count(
mesh: *const NativeMesh,
count: *mut usize,
obstruction: *mut NativeObstruction,
) -> c_uint;
fn ml_mesh_triangle_count(
mesh: *const NativeMesh,
count: *mut usize,
obstruction: *mut NativeObstruction,
) -> c_uint;
fn ml_mesh_copy_vertices_f64(
mesh: *const NativeMesh,
coordinates: *mut c_double,
point_capacity: usize,
points_written: *mut usize,
obstruction: *mut NativeObstruction,
) -> c_uint;
fn ml_mesh_copy_triangles_u32(
mesh: *const NativeMesh,
triangles: *mut u32,
triangle_capacity: usize,
triangles_written: *mut usize,
obstruction: *mut NativeObstruction,
) -> c_uint;
fn ml_mesh_free(mesh: *mut NativeMesh);
}
type BinaryMeshOperation = unsafe extern "C" fn(
*const NativeMesh,
*const NativeMesh,
*mut *mut NativeMesh,
*mut NativeObstruction,
) -> c_uint;
#[derive(Debug, Clone, PartialEq)]
pub struct MoonlightError {
pub status: u32,
pub code: u32,
pub coordinate_error: u32,
pub input_index: u64,
pub first_index: u64,
pub second_index: u64,
pub first_value: f64,
pub second_value: f64,
pub point: [f64; 2],
pub message: String,
}
impl Display for MoonlightError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
write!(formatter, "{}", self.message)
}
}
impl Error for MoonlightError {}
pub struct Moonlight;
impl Moonlight {
pub fn initialize() -> Result<Self, MoonlightError> {
let status = unsafe { ml_runtime_initialize() };
if status != STATUS_OK {
return Err(synthetic_error(
status,
"Moonlight runtime initialization failed",
0,
0,
));
}
let version = unsafe { ml_abi_version() };
if version != ABI_VERSION {
return Err(synthetic_error(
5,
format!("unsupported Moonlight ABI version {version}"),
u64::from(version),
u64::from(ABI_VERSION),
));
}
Ok(Self)
}
pub fn delaunay(&self, points: &[[f64; 2]]) -> Result<Mesh, MoonlightError> {
let coordinates = flatten_points(points);
create_mesh(|output, obstruction| unsafe {
ml_delaunay_f64(coordinates.as_ptr(), points.len(), output, obstruction)
})
}
}
pub struct Mesh {
handle: NonNull<NativeMesh>,
_thread_affinity: PhantomData<Rc<()>>,
}
impl Mesh {
pub fn vertex_count(&self) -> Result<usize, MoonlightError> {
self.count(ml_mesh_vertex_count)
}
pub fn triangle_count(&self) -> Result<usize, MoonlightError> {
self.count(ml_mesh_triangle_count)
}
pub fn vertices(&self) -> Result<Vec<[f64; 2]>, MoonlightError> {
let count = self.vertex_count()?;
let mut coordinates = vec![0.0; count * 2];
let mut written = 0;
let mut obstruction = NativeObstruction::default();
let status = unsafe {
ml_mesh_copy_vertices_f64(
self.handle.as_ptr(),
coordinates.as_mut_ptr(),
count,
&mut written,
&mut obstruction,
)
};
status_result(status, obstruction)?;
coordinates.truncate(written * 2);
Ok(coordinates
.chunks_exact(2)
.map(|point| [point[0], point[1]])
.collect())
}
pub fn triangles(&self) -> Result<Vec<[u32; 3]>, MoonlightError> {
let count = self.triangle_count()?;
let mut triangles = vec![0; count * 3];
let mut written = 0;
let mut obstruction = NativeObstruction::default();
let status = unsafe {
ml_mesh_copy_triangles_u32(
self.handle.as_ptr(),
triangles.as_mut_ptr(),
count,
&mut written,
&mut obstruction,
)
};
status_result(status, obstruction)?;
triangles.truncate(written * 3);
Ok(triangles
.chunks_exact(3)
.map(|triangle| [triangle[0], triangle[1], triangle[2]])
.collect())
}
pub fn insert_many(&self, points: &[[f64; 2]]) -> Result<Self, MoonlightError> {
let coordinates = flatten_points(points);
create_mesh(|output, obstruction| unsafe {
ml_mesh_insert_many_f64(
self.handle.as_ptr(),
coordinates.as_ptr(),
points.len(),
output,
obstruction,
)
})
}
pub fn union(&self, other: &Self) -> Result<Self, MoonlightError> {
self.binary(other, ml_mesh_union)
}
pub fn intersection(&self, other: &Self) -> Result<Self, MoonlightError> {
self.binary(other, ml_mesh_intersection)
}
pub fn difference(&self, other: &Self) -> Result<Self, MoonlightError> {
self.binary(other, ml_mesh_difference)
}
pub fn symmetric_difference(&self, other: &Self) -> Result<Self, MoonlightError> {
self.binary(other, ml_mesh_symmetric_difference)
}
fn binary(&self, other: &Self, operation: BinaryMeshOperation) -> Result<Self, MoonlightError> {
create_mesh(|output, obstruction| unsafe {
operation(
self.handle.as_ptr(),
other.handle.as_ptr(),
output,
obstruction,
)
})
}
fn count(
&self,
operation: unsafe extern "C" fn(
*const NativeMesh,
*mut usize,
*mut NativeObstruction,
) -> c_uint,
) -> Result<usize, MoonlightError> {
let mut count = 0;
let mut obstruction = NativeObstruction::default();
let status = unsafe { operation(self.handle.as_ptr(), &mut count, &mut obstruction) };
status_result(status, obstruction)?;
Ok(count)
}
}
impl Drop for Mesh {
fn drop(&mut self) {
unsafe { ml_mesh_free(self.handle.as_ptr()) }
}
}
fn flatten_points(points: &[[f64; 2]]) -> Vec<f64> {
points.iter().flat_map(|[x, y]| [*x, *y]).collect()
}
#[inline]
fn create_mesh(
operation: impl FnOnce(*mut *mut NativeMesh, *mut NativeObstruction) -> u32,
) -> Result<Mesh, MoonlightError> {
let mut output = std::ptr::null_mut();
let mut obstruction = NativeObstruction::default();
let status = operation(&mut output, &mut obstruction);
status_result(status, obstruction)?;
NonNull::new(output)
.map(|handle| Mesh {
handle,
_thread_affinity: PhantomData,
})
.ok_or_else(success_without_handle_error)
}
fn status_result(status: u32, obstruction: NativeObstruction) -> Result<(), MoonlightError> {
if status == STATUS_OK {
Ok(())
} else {
Err(MoonlightError::from_native(status, obstruction))
}
}
impl MoonlightError {
fn from_native(status: u32, obstruction: NativeObstruction) -> Self {
let message_end = obstruction
.message
.iter()
.position(|character| *character == 0)
.unwrap_or(obstruction.message.len());
let message_bytes = obstruction.message[..message_end]
.iter()
.map(|character| *character as u8)
.collect::<Vec<_>>();
Self {
status,
code: obstruction.code,
coordinate_error: obstruction.coordinate_error,
input_index: obstruction.input_index,
first_index: obstruction.first_index,
second_index: obstruction.second_index,
first_value: obstruction.first_value,
second_value: obstruction.second_value,
point: [obstruction.point_x, obstruction.point_y],
message: String::from_utf8_lossy(&message_bytes).into_owned(),
}
}
}
fn success_without_handle_error() -> MoonlightError {
synthetic_error(5, "Moonlight returned success without a mesh handle", 0, 0)
}
fn synthetic_error(
status: u32,
message: impl Into<String>,
first_index: u64,
second_index: u64,
) -> MoonlightError {
MoonlightError {
status,
code: 103,
coordinate_error: 0,
input_index: u64::MAX,
first_index,
second_index,
first_value: 0.0,
second_value: 0.0,
point: [0.0, 0.0],
message: message.into(),
}
}