use manifold_csg_sys::*;
use std::ops;
use std::sync::Mutex;
use crate::manifold::{read_polygons, with_quality_lock_if_auto_segments};
use crate::rect::Rect;
use crate::types::{OpType, PanicPayload, store_panic, take_stored_panic};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum JoinType {
Square,
Round,
Miter,
Bevel,
}
impl JoinType {
const fn to_ffi(self) -> ManifoldJoinType {
match self {
JoinType::Square => ManifoldJoinType::Square,
JoinType::Round => ManifoldJoinType::Round,
JoinType::Miter => ManifoldJoinType::Miter,
JoinType::Bevel => ManifoldJoinType::Bevel,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum FillRule {
EvenOdd,
NonZero,
Positive,
Negative,
}
impl FillRule {
const fn to_ffi(self) -> ManifoldFillRule {
match self {
FillRule::EvenOdd => ManifoldFillRule::EvenOdd,
FillRule::NonZero => ManifoldFillRule::NonZero,
FillRule::Positive => ManifoldFillRule::Positive,
FillRule::Negative => ManifoldFillRule::Negative,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rect2 {
pub min_x: f64,
pub min_y: f64,
pub max_x: f64,
pub max_y: f64,
}
impl Default for Rect2 {
fn default() -> Self {
Self {
min_x: 0.0,
min_y: 0.0,
max_x: 0.0,
max_y: 0.0,
}
}
}
pub struct CrossSection {
pub(crate) ptr: *mut ManifoldCrossSection,
}
impl std::fmt::Debug for CrossSection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CrossSection")
.field("ptr", &self.ptr)
.finish_non_exhaustive()
}
}
unsafe impl Send for CrossSection {}
unsafe impl Sync for CrossSection {}
impl Clone for CrossSection {
fn clone(&self) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_copy(ptr, self.ptr) };
Self { ptr }
}
}
impl Drop for CrossSection {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { manifold_delete_cross_section(self.ptr) };
}
}
}
impl Default for CrossSection {
fn default() -> Self {
Self::empty()
}
}
impl CrossSection {
#[must_use]
pub fn empty() -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_empty(ptr) };
Self { ptr }
}
#[must_use]
pub fn square(x: f64, y: f64, center: bool) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_square(ptr, x, y, i32::from(center)) };
Self { ptr }
}
#[must_use]
pub fn circle(radius: f64, segments: i32) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
with_quality_lock_if_auto_segments(segments, || {
unsafe { manifold_cross_section_circle(ptr, radius, segments) };
});
Self { ptr }
}
#[must_use]
pub fn from_polygons(polygons: &[Vec<[f64; 2]>]) -> Self {
Self::from_polygons_with_fill_rule(polygons, FillRule::Positive)
}
#[must_use]
pub fn from_polygons_with_fill_rule(polygons: &[Vec<[f64; 2]>], fill_rule: FillRule) -> Self {
if polygons.is_empty() {
return Self::empty();
}
let (polys_ptr, simple_ptrs) = build_polygons_ffi(polygons);
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe {
manifold_cross_section_of_polygons(ptr, polys_ptr, fill_rule.to_ffi());
}
unsafe { manifold_delete_polygons(polys_ptr) };
for sp in simple_ptrs {
unsafe { manifold_delete_simple_polygon(sp) };
}
Self { ptr }
}
#[must_use]
pub fn from_simple_polygon(points: &[[f64; 2]]) -> Self {
Self::from_simple_polygon_with_fill_rule(points, FillRule::Positive)
}
#[must_use]
pub fn from_simple_polygon_with_fill_rule(points: &[[f64; 2]], fill_rule: FillRule) -> Self {
if points.is_empty() {
return Self::empty();
}
let vec2s: Vec<ManifoldVec2> = points
.iter()
.map(|p| ManifoldVec2 { x: p[0], y: p[1] })
.collect();
let sp = unsafe { manifold_alloc_simple_polygon() };
unsafe { manifold_simple_polygon(sp, vec2s.as_ptr(), vec2s.len()) };
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_of_simple_polygon(ptr, sp, fill_rule.to_ffi()) };
unsafe { manifold_delete_simple_polygon(sp) };
Self { ptr }
}
#[must_use]
pub fn hull_simple_polygon(points: &[[f64; 2]]) -> Self {
if points.is_empty() {
return Self::empty();
}
let vec2s: Vec<ManifoldVec2> = points
.iter()
.map(|p| ManifoldVec2 { x: p[0], y: p[1] })
.collect();
let sp = unsafe { manifold_alloc_simple_polygon() };
unsafe { manifold_simple_polygon(sp, vec2s.as_ptr(), vec2s.len()) };
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_hull_simple_polygon(ptr, sp) };
unsafe { manifold_delete_simple_polygon(sp) };
Self { ptr }
}
#[must_use]
pub fn hull_polygons(polygons: &[Vec<[f64; 2]>]) -> Self {
if polygons.is_empty() {
return Self::empty();
}
let (polys_ptr, simple_ptrs) = build_polygons_ffi(polygons);
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_hull_polygons(ptr, polys_ptr) };
unsafe { manifold_delete_polygons(polys_ptr) };
for sp in simple_ptrs {
unsafe { manifold_delete_simple_polygon(sp) };
}
Self { ptr }
}
#[must_use]
pub fn union(&self, other: &Self) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_union(ptr, self.ptr, other.ptr) };
Self { ptr }
}
#[must_use]
pub fn difference(&self, other: &Self) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_difference(ptr, self.ptr, other.ptr) };
Self { ptr }
}
#[must_use]
pub fn intersection(&self, other: &Self) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_intersection(ptr, self.ptr, other.ptr) };
Self { ptr }
}
#[must_use]
pub fn boolean(&self, other: &Self, op: OpType) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_boolean(ptr, self.ptr, other.ptr, op.to_ffi()) };
Self { ptr }
}
#[must_use]
pub fn offset(
&self,
delta: f64,
join_type: JoinType,
miter_limit: f64,
circular_segments: i32,
) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
with_quality_lock_if_auto_segments(circular_segments, || {
unsafe {
manifold_cross_section_offset(
ptr,
self.ptr,
delta,
join_type.to_ffi(),
miter_limit,
circular_segments,
);
}
});
Self { ptr }
}
#[must_use]
pub fn hull(&self) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_hull(ptr, self.ptr) };
Self { ptr }
}
#[must_use]
pub fn translate(&self, x: f64, y: f64) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_translate(ptr, self.ptr, x, y) };
Self { ptr }
}
#[must_use]
pub fn rotate(&self, degrees: f64) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_rotate(ptr, self.ptr, degrees) };
Self { ptr }
}
#[must_use]
pub fn scale(&self, x: f64, y: f64) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_scale(ptr, self.ptr, x, y) };
Self { ptr }
}
#[must_use]
pub fn mirror(&self, ax_x: f64, ax_y: f64) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_mirror(ptr, self.ptr, ax_x, ax_y) };
Self { ptr }
}
#[must_use]
pub fn transform(&self, m: &[f64; 6]) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe {
manifold_cross_section_transform(ptr, self.ptr, m[0], m[1], m[2], m[3], m[4], m[5]);
}
Self { ptr }
}
#[must_use]
pub fn decompose(&self) -> Vec<Self> {
let vec_ptr = unsafe { manifold_alloc_cross_section_vec() };
unsafe { manifold_cross_section_decompose(vec_ptr, self.ptr) };
let n = unsafe { manifold_cross_section_vec_length(vec_ptr) };
let mut result = Vec::with_capacity(n);
for i in 0..n {
let cs_ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_vec_get(cs_ptr, vec_ptr, i) };
result.push(Self { ptr: cs_ptr });
}
unsafe { manifold_delete_cross_section_vec(vec_ptr) };
result
}
#[must_use]
pub fn area(&self) -> f64 {
unsafe { manifold_cross_section_area(self.ptr) }
}
#[must_use]
pub fn num_vert(&self) -> usize {
unsafe { manifold_cross_section_num_vert(self.ptr) }
}
#[must_use]
pub fn num_contour(&self) -> usize {
unsafe { manifold_cross_section_num_contour(self.ptr) }
}
#[must_use]
pub fn is_empty(&self) -> bool {
unsafe { manifold_cross_section_is_empty(self.ptr) != 0 }
}
#[must_use]
pub fn bounds(&self) -> Rect {
let rect_ptr = unsafe { manifold_alloc_rect() };
unsafe { manifold_cross_section_bounds(rect_ptr, self.ptr) };
Rect::from_ptr(rect_ptr)
}
#[must_use]
pub fn bounds_rect2(&self) -> Rect2 {
let r = self.bounds();
let lo = r.min();
let hi = r.max();
Rect2 {
min_x: lo[0],
min_y: lo[1],
max_x: hi[0],
max_y: hi[1],
}
}
#[must_use]
pub fn simplify(&self, epsilon: f64) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_simplify(ptr, self.ptr, epsilon) };
Self { ptr }
}
#[must_use]
pub fn batch_boolean(sections: &[Self], op: OpType) -> Self {
if sections.is_empty() {
return Self::empty();
}
let vec_ptr = unsafe { manifold_alloc_cross_section_vec() };
unsafe { manifold_cross_section_empty_vec(vec_ptr) };
for cs in sections {
let copy_ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_copy(copy_ptr, cs.ptr) };
unsafe { manifold_cross_section_vec_push_back(vec_ptr, copy_ptr) };
unsafe { manifold_delete_cross_section(copy_ptr) };
}
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_batch_boolean(ptr, vec_ptr, op.to_ffi()) };
unsafe { manifold_delete_cross_section_vec(vec_ptr) };
Self { ptr }
}
#[must_use]
pub fn batch_union(sections: &[Self]) -> Self {
Self::batch_boolean(sections, OpType::Add)
}
#[must_use]
pub fn batch_hull(sections: &[Self]) -> Self {
if sections.is_empty() {
return Self::empty();
}
let vec_ptr = unsafe { manifold_alloc_cross_section_vec() };
unsafe { manifold_cross_section_empty_vec(vec_ptr) };
for cs in sections {
let copy_ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_copy(copy_ptr, cs.ptr) };
unsafe { manifold_cross_section_vec_push_back(vec_ptr, copy_ptr) };
unsafe { manifold_delete_cross_section(copy_ptr) };
}
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_batch_hull(ptr, vec_ptr) };
unsafe { manifold_delete_cross_section_vec(vec_ptr) };
Self { ptr }
}
#[must_use]
pub fn compose(sections: &[Self]) -> Self {
let vec_ptr = unsafe { manifold_alloc_cross_section_vec() };
unsafe { manifold_cross_section_empty_vec(vec_ptr) };
for cs in sections {
let copy_ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_copy(copy_ptr, cs.ptr) };
unsafe { manifold_cross_section_vec_push_back(vec_ptr, copy_ptr) };
unsafe { manifold_delete_cross_section(copy_ptr) };
}
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_compose(ptr, vec_ptr) };
unsafe { manifold_delete_cross_section_vec(vec_ptr) };
Self { ptr }
}
#[must_use]
pub fn extrude(&self, height: f64) -> crate::Manifold {
crate::Manifold::extrude(self, height)
}
#[must_use]
pub fn warp<F>(&self, f: F) -> Self
where
F: FnMut(f64, f64) -> [f64; 2],
{
struct Context<'a, F> {
f: &'a mut F,
panic: Mutex<Option<PanicPayload>>,
}
unsafe extern "C" fn trampoline<F>(
x: f64,
y: f64,
ctx: *mut std::ffi::c_void,
) -> ManifoldVec2
where
F: FnMut(f64, f64) -> [f64; 2],
{
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let ctx = unsafe { &mut *(ctx as *mut Context<'_, F>) };
(ctx.f)(x, y)
}));
match result {
Ok([rx, ry]) => ManifoldVec2 { x: rx, y: ry },
Err(payload) => {
let ctx = unsafe { &*(ctx as *const Context<'_, F>) };
store_panic(&ctx.panic, payload);
ManifoldVec2 { x, y }
}
}
}
let mut closure = f;
let mut ctx = Context {
f: &mut closure,
panic: Mutex::new(None),
};
let ctx_ptr = &mut ctx as *mut Context<'_, F> as *mut std::ffi::c_void;
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe {
manifold_cross_section_warp_context(ptr, self.ptr, Some(trampoline::<F>), ctx_ptr)
};
if let Some(payload) = take_stored_panic(&ctx.panic) {
unsafe { manifold_delete_cross_section(ptr) };
std::panic::resume_unwind(payload);
}
Self { ptr }
}
#[must_use]
pub fn to_polygons(&self) -> Vec<Vec<[f64; 2]>> {
let poly_ptr = unsafe { manifold_alloc_polygons() };
unsafe { manifold_cross_section_to_polygons(poly_ptr, self.ptr) };
let result = read_polygons(poly_ptr);
unsafe { manifold_delete_polygons(poly_ptr) };
result
}
}
impl ops::Add for &CrossSection {
type Output = CrossSection;
fn add(self, rhs: &CrossSection) -> CrossSection {
self.union(rhs)
}
}
impl ops::Sub for &CrossSection {
type Output = CrossSection;
fn sub(self, rhs: &CrossSection) -> CrossSection {
self.difference(rhs)
}
}
impl ops::BitXor for &CrossSection {
type Output = CrossSection;
fn bitxor(self, rhs: &CrossSection) -> CrossSection {
self.intersection(rhs)
}
}
pub(crate) fn build_polygons_ffi(
polygons: &[Vec<[f64; 2]>],
) -> (*mut ManifoldPolygons, Vec<*mut ManifoldSimplePolygon>) {
let mut simple_ptrs: Vec<*mut ManifoldSimplePolygon> = Vec::with_capacity(polygons.len());
for ring in polygons {
let vec2s: Vec<ManifoldVec2> = ring
.iter()
.map(|p| ManifoldVec2 { x: p[0], y: p[1] })
.collect();
let sp = unsafe { manifold_alloc_simple_polygon() };
unsafe { manifold_simple_polygon(sp, vec2s.as_ptr(), vec2s.len()) };
simple_ptrs.push(sp);
}
let polys_ptr = unsafe { manifold_alloc_polygons() };
unsafe { manifold_polygons(polys_ptr, simple_ptrs.as_ptr(), simple_ptrs.len()) };
(polys_ptr, simple_ptrs)
}