2023-12-29 22:36:27 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate derive_builder;
|
|
|
|
extern crate triangle_sys as sys;
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
2023-12-26 01:40:37 +00:00
|
|
|
use std::{ffi::CString, mem::MaybeUninit, ptr};
|
|
|
|
|
2023-12-29 22:36:27 +00:00
|
|
|
use anyhow::Result;
|
2023-12-26 01:40:37 +00:00
|
|
|
|
2023-12-29 22:36:27 +00:00
|
|
|
#[derive(Builder)]
|
2023-12-30 00:46:18 +00:00
|
|
|
pub struct TrianglulateOpts<P = Vec<Point>> {
|
2023-12-29 22:36:27 +00:00
|
|
|
point_list: P,
|
|
|
|
|
2023-12-30 07:36:34 +00:00
|
|
|
/// Imposes a maximum triangle area constraint. A fixed area constraint (that
|
|
|
|
/// applies to every triangle) may be specified after the `a', or varying area
|
|
|
|
/// constraints may be read from a .poly file or .area file.
|
|
|
|
#[builder(default, setter(strip_option))]
|
|
|
|
maximum_triangle_area: Option<f64>,
|
|
|
|
|
2023-12-29 22:36:27 +00:00
|
|
|
/// Generates a Voronoi diagram
|
|
|
|
#[builder(default)]
|
|
|
|
voronoi: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<P: Clone> TrianglulateOpts<P> {
|
|
|
|
pub fn builder() -> TrianglulateOptsBuilder<P> {
|
|
|
|
TrianglulateOptsBuilder::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
pub struct Point {
|
2023-12-30 00:46:18 +00:00
|
|
|
pub x: f64,
|
|
|
|
pub y: f64,
|
2023-12-29 22:36:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct TriangulateResult {
|
|
|
|
pub point_list: Vec<Point>,
|
2023-12-30 07:36:34 +00:00
|
|
|
|
|
|
|
/// A list of triangles
|
|
|
|
pub triangle_list: Vec<Vec<usize>>,
|
2023-12-30 05:40:38 +00:00
|
|
|
pub voronoi_point_list: Vec<Point>,
|
2023-12-29 22:36:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn triangulate<P>(opts: TrianglulateOpts<P>) -> Result<TriangulateResult>
|
|
|
|
where
|
|
|
|
P: IntoIterator<Item = Point>,
|
|
|
|
{
|
|
|
|
let mut switches = Vec::new();
|
2023-12-30 07:36:34 +00:00
|
|
|
switches.push("p".to_owned());
|
|
|
|
switches.push("c".to_owned());
|
|
|
|
switches.push("z".to_owned());
|
|
|
|
switches.push("A".to_owned());
|
|
|
|
if let Some(maximum_triangle_area) = opts.maximum_triangle_area {
|
|
|
|
switches.push(format!("a{maximum_triangle_area}"));
|
|
|
|
}
|
|
|
|
switches.push("q".to_owned());
|
|
|
|
switches.push("e".to_owned());
|
|
|
|
switches.push("n".to_owned());
|
2023-12-29 22:36:27 +00:00
|
|
|
if opts.voronoi {
|
2023-12-30 07:36:34 +00:00
|
|
|
switches.push("v".to_owned());
|
2023-12-29 22:36:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let switches = CString::new(switches.join(""))?;
|
|
|
|
|
|
|
|
let point_list = opts.point_list.into_iter().collect::<Vec<_>>();
|
|
|
|
|
|
|
|
let mut flat_point_list = point_list
|
|
|
|
.iter()
|
|
|
|
.flat_map(|point| [point.x, point.y])
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2023-12-30 00:46:18 +00:00
|
|
|
let input = MaybeUninit::<sys::triangulateio>::zeroed();
|
|
|
|
let output = MaybeUninit::<sys::triangulateio>::zeroed();
|
|
|
|
let vorout = MaybeUninit::<sys::triangulateio>::zeroed();
|
2023-12-26 01:40:37 +00:00
|
|
|
|
2023-12-29 22:36:27 +00:00
|
|
|
let mut input = unsafe { input.assume_init() };
|
|
|
|
let mut output = unsafe { output.assume_init() };
|
2023-12-30 00:46:18 +00:00
|
|
|
let mut vorout = unsafe { vorout.assume_init() };
|
|
|
|
|
2023-12-29 22:36:27 +00:00
|
|
|
input.pointlist = flat_point_list.as_mut_ptr();
|
|
|
|
input.numberofpoints = point_list.len() as i32;
|
|
|
|
|
|
|
|
// TODO: Implement point attributes
|
|
|
|
input.numberofpointattributes = 0;
|
|
|
|
|
2023-12-30 00:46:18 +00:00
|
|
|
println!("Going to triangulate...");
|
|
|
|
|
2023-12-26 01:40:37 +00:00
|
|
|
unsafe {
|
|
|
|
sys::triangulate(
|
|
|
|
switches.as_ptr() as *mut _,
|
|
|
|
&mut input as *mut _,
|
2023-12-29 22:36:27 +00:00
|
|
|
&mut output as *mut _,
|
2023-12-30 00:46:18 +00:00
|
|
|
&mut vorout as *mut _,
|
2023-12-26 01:40:37 +00:00
|
|
|
)
|
|
|
|
};
|
|
|
|
|
2023-12-30 05:40:38 +00:00
|
|
|
println!("Triangulated {}.", output.numberoftriangles);
|
2023-12-30 00:46:18 +00:00
|
|
|
|
2023-12-30 05:40:38 +00:00
|
|
|
let point_list = point_list_from_flat_point_list(
|
|
|
|
output.pointlist,
|
|
|
|
output.numberofpoints as usize * 2,
|
|
|
|
);
|
|
|
|
|
2023-12-30 07:36:34 +00:00
|
|
|
let triangle_list = triangle_list_from_flat_point_list(
|
|
|
|
output.trianglelist,
|
|
|
|
output.numberofcorners as usize,
|
|
|
|
output.numberoftriangles as usize * output.numberofcorners as usize,
|
|
|
|
);
|
|
|
|
println!("aa {}", triangle_list.len());
|
|
|
|
|
2023-12-30 05:40:38 +00:00
|
|
|
let voronoi_point_list = point_list_from_flat_point_list(
|
|
|
|
vorout.pointlist,
|
|
|
|
vorout.numberofpoints as usize * 2,
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(TriangulateResult {
|
|
|
|
point_list,
|
2023-12-30 07:36:34 +00:00
|
|
|
triangle_list,
|
2023-12-30 05:40:38 +00:00
|
|
|
voronoi_point_list,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn point_list_from_flat_point_list(ptr: *mut f64, len: usize) -> Vec<Point> {
|
|
|
|
let flat_point_list = unsafe { Vec::from_raw_parts(ptr, len, len) };
|
|
|
|
flat_point_list
|
2023-12-30 07:36:34 +00:00
|
|
|
.chunks_exact(2)
|
2023-12-29 22:36:27 +00:00
|
|
|
.map(|points| Point {
|
|
|
|
x: points[0],
|
|
|
|
y: points[1],
|
|
|
|
})
|
2023-12-30 05:40:38 +00:00
|
|
|
.collect::<Vec<_>>()
|
2023-12-26 01:40:37 +00:00
|
|
|
}
|
2023-12-30 07:36:34 +00:00
|
|
|
|
|
|
|
fn triangle_list_from_flat_point_list(
|
|
|
|
ptr: *mut i32,
|
|
|
|
num_corners: usize,
|
|
|
|
len: usize,
|
|
|
|
) -> Vec<Vec<usize>> {
|
|
|
|
let flat_point_list = unsafe { Vec::from_raw_parts(ptr, len, len) };
|
|
|
|
flat_point_list
|
|
|
|
.chunks_exact(num_corners)
|
|
|
|
.map(|points| points.iter().map(|point| *point as usize).collect())
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
}
|