use std::marker::PhantomData;
use clipper2c_sys::{
clipper_clipper64, clipper_clipper64_add_clip, clipper_clipper64_add_open_subject,
clipper_clipper64_add_subject, clipper_clipper64_execute, clipper_clipper64_size,
clipper_delete_clipper64, clipper_delete_paths64, ClipperClipper64,
};
use crate::{malloc, Centi, ClipType, FillRule, Paths, PointScaler};
pub trait ClipperState {}
#[derive(Debug)]
pub struct NoSubjects {}
impl ClipperState for NoSubjects {}
#[derive(Debug)]
pub struct WithSubjects {}
impl ClipperState for WithSubjects {}
#[derive(Debug)]
pub struct WithClips {}
impl ClipperState for WithClips {}
#[derive(Debug)]
pub struct Clipper<S: ClipperState = NoSubjects, P: PointScaler = Centi> {
ptr: *mut ClipperClipper64,
keep_ptr_on_drop: bool,
_marker: PhantomData<P>,
_state: S,
}
impl<P: PointScaler> Clipper<NoSubjects, P> {
pub fn new() -> Clipper<NoSubjects, P> {
let ptr = unsafe {
let mem = malloc(clipper_clipper64_size());
clipper_clipper64(mem)
};
Clipper::<NoSubjects, P> {
ptr,
keep_ptr_on_drop: false,
_marker: PhantomData,
_state: NoSubjects {},
}
}
}
impl<P: PointScaler> Clipper<NoSubjects, P> {
pub fn add_subject(mut self, subject: impl Into<Paths<P>>) -> Clipper<WithSubjects, P> {
self.keep_ptr_on_drop = true;
let clipper = Clipper::<WithSubjects, P> {
ptr: self.ptr,
keep_ptr_on_drop: false,
_marker: PhantomData,
_state: WithSubjects {},
};
drop(self);
clipper.add_subject(subject)
}
pub fn add_open_subject(mut self, subject: impl Into<Paths<P>>) -> Clipper<WithSubjects, P> {
self.keep_ptr_on_drop = true;
let clipper = Clipper::<WithSubjects, P> {
ptr: self.ptr,
keep_ptr_on_drop: false,
_marker: PhantomData,
_state: WithSubjects {},
};
drop(self);
clipper.add_open_subject(subject)
}
}
impl<P: PointScaler> Clipper<WithSubjects, P> {
pub fn add_subject(self, subject: impl Into<Paths<P>>) -> Self {
unsafe {
let subject_ptr = subject.into().to_clipperpaths64();
clipper_clipper64_add_subject(self.ptr, subject_ptr);
clipper_delete_paths64(subject_ptr);
}
self
}
pub fn add_open_subject(self, subject: impl Into<Paths<P>>) -> Self {
unsafe {
let subject_ptr = subject.into().to_clipperpaths64();
clipper_clipper64_add_open_subject(self.ptr, subject_ptr);
clipper_delete_paths64(subject_ptr);
}
self
}
pub fn add_clip(mut self, clip: impl Into<Paths<P>>) -> Clipper<WithClips, P> {
self.keep_ptr_on_drop = true;
let clipper = Clipper::<WithClips, P> {
ptr: self.ptr,
keep_ptr_on_drop: false,
_marker: PhantomData,
_state: WithClips {},
};
drop(self);
clipper.add_clip(clip)
}
}
impl<P: PointScaler> Clipper<WithClips, P> {
pub fn add_clip(self, clip: impl Into<Paths<P>>) -> Self {
unsafe {
let clip_ptr = clip.into().to_clipperpaths64();
clipper_clipper64_add_clip(self.ptr, clip_ptr);
clipper_delete_paths64(clip_ptr);
}
self
}
pub fn union(self, fill_rule: FillRule) -> Result<Paths<P>, ClipperError> {
self.boolean_operation(ClipType::Union, fill_rule)
}
pub fn difference(self, fill_rule: FillRule) -> Result<Paths<P>, ClipperError> {
self.boolean_operation(ClipType::Difference, fill_rule)
}
pub fn intersect(self, fill_rule: FillRule) -> Result<Paths<P>, ClipperError> {
self.boolean_operation(ClipType::Intersection, fill_rule)
}
pub fn xor(self, fill_rule: FillRule) -> Result<Paths<P>, ClipperError> {
self.boolean_operation(ClipType::Xor, fill_rule)
}
fn boolean_operation(
self,
clip_type: ClipType,
fill_rule: FillRule,
) -> Result<Paths<P>, ClipperError> {
let closed_path = unsafe { Paths::<P>::new(Vec::new()).to_clipperpaths64() };
let open_path = unsafe { Paths::<P>::new(Vec::new()).to_clipperpaths64() };
let result = unsafe {
let success = clipper_clipper64_execute(
self.ptr,
clip_type.into(),
fill_rule.into(),
closed_path,
open_path,
);
if success != 1 {
clipper_delete_paths64(closed_path);
clipper_delete_paths64(open_path);
return Err(ClipperError::FailedBooleanOperation);
}
let path = Paths::from_clipperpaths64(closed_path);
clipper_delete_paths64(closed_path);
clipper_delete_paths64(open_path);
Ok(path)
};
drop(self);
result
}
}
impl Default for Clipper<NoSubjects, Centi> {
fn default() -> Self {
Self::new()
}
}
impl<S: ClipperState, P: PointScaler> Drop for Clipper<S, P> {
fn drop(&mut self) {
if !self.keep_ptr_on_drop {
unsafe { clipper_delete_clipper64(self.ptr) }
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ClipperError {
#[error("Failed boolean operation")]
FailedBooleanOperation,
}