use manifold_csg_sys::*;
use std::ops;
use crate::manifold::read_polygons;
use crate::rect::Rect;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
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)]
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("is_empty", &self.is_empty())
.field("area", &self.area())
.field("num_vert", &self.num_vert())
.finish()
}
}
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 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() };
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::EvenOdd)
}
#[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]], 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: ManifoldOpType) -> Self {
let ptr = unsafe { manifold_alloc_cross_section() };
unsafe { manifold_cross_section_boolean(ptr, self.ptr, other.ptr, op) };
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() };
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: crate::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) };
unsafe { manifold_delete_cross_section_vec(vec_ptr) };
Self { ptr }
}
#[must_use]
pub fn batch_union(sections: &[Self]) -> Self {
Self::batch_boolean(sections, crate::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],
{
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 f = unsafe { &mut *(ctx as *mut F) };
f(x, y)
}));
match result {
Ok([rx, ry]) => ManifoldVec2 { x: rx, y: ry },
Err(_) => ManifoldVec2 { x, y },
}
}
let mut closure = f;
let ctx = &mut closure as *mut 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) };
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)
}