use std::fmt;
use crate::cxx_bridge::clipper2_sys_cxx;
use crate::paths_blob::{pathsd_to_blob, PathsBlobDIter};
use crate::poly_path::PolyCxxPreorderIterD;
use crate::{ClipType, FillRule, PathsD};
use super::LazyPathsD;
#[derive(Clone, Debug)]
pub struct ClipSolutionD {
closed: clipper2_sys_cxx::PathsBlobD,
open: clipper2_sys_cxx::PathsBlobD,
}
impl ClipSolutionD {
#[inline]
pub fn closed_is_empty(&self) -> bool {
self.closed.path_starts.len() < 2
}
#[inline]
pub fn open_is_empty(&self) -> bool {
self.open.path_starts.len() < 2
}
#[inline]
pub fn iter_closed(&self) -> PathsBlobDIter<'_> {
PathsBlobDIter::new(&self.closed)
}
#[inline]
pub fn iter_open(&self) -> PathsBlobDIter<'_> {
PathsBlobDIter::new(&self.open)
}
pub fn to_closed(&self) -> PathsD {
self.iter_closed().collect()
}
pub fn to_open(&self) -> PathsD {
self.iter_open().collect()
}
pub fn into_lazy(self) -> (LazyPathsD, LazyPathsD) {
(
LazyPathsD::from_blob(self.closed),
LazyPathsD::from_blob(self.open),
)
}
}
pub struct ClipTreeSolutionD {
poly_root: Option<usize>,
open: clipper2_sys_cxx::PathsBlobD,
}
impl fmt::Debug for ClipTreeSolutionD {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ClipTreeSolutionD")
.field("has_poly_root", &self.poly_root.map(|r| r != 0))
.field("open_paths", &(self.open.path_starts.len().saturating_sub(1)))
.finish()
}
}
impl Drop for ClipTreeSolutionD {
fn drop(&mut self) {
if let Some(r) = self.poly_root.take() {
if r != 0 {
clipper2_sys_cxx::cxx_polyd_delete(r);
}
}
}
}
impl ClipTreeSolutionD {
#[inline]
pub fn has_poly_tree(&self) -> bool {
matches!(self.poly_root, Some(r) if r != 0)
}
#[inline]
pub fn open_is_empty(&self) -> bool {
self.open.path_starts.len() < 2
}
#[inline]
pub fn iter_open(&self) -> PathsBlobDIter<'_> {
PathsBlobDIter::new(&self.open)
}
pub fn to_open(&self) -> PathsD {
self.iter_open().collect()
}
pub fn into_open_lazy(mut self) -> LazyPathsD {
if let Some(r) = self.poly_root.take() {
if r != 0 {
clipper2_sys_cxx::cxx_polyd_delete(r);
}
}
LazyPathsD::from_blob(std::mem::take(&mut self.open))
}
pub fn into_open_and_poly_preorder(mut self) -> (LazyPathsD, PolyCxxPreorderIterD) {
let root = self.poly_root.take().unwrap_or(0);
let open = std::mem::take(&mut self.open);
let iter = PolyCxxPreorderIterD::new(root);
(LazyPathsD::from_blob(open), iter)
}
}
pub struct ClipperD {
inner: cxx::UniquePtr<clipper2_sys_cxx::ClipperDBox>,
}
impl ClipperD {
pub fn new(precision: i32) -> Self {
Self {
inner: clipper2_sys_cxx::cxx_clipperd_new(precision),
}
}
pub fn set_preserve_collinear(&mut self, value: bool) {
clipper2_sys_cxx::cxx_clipperd_set_preserve_collinear(self.inner.pin_mut(), value);
}
pub fn get_preserve_collinear(&self) -> bool {
clipper2_sys_cxx::cxx_clipperd_get_preserve_collinear(&self.inner)
}
pub fn set_reverse_solution(&mut self, value: bool) {
clipper2_sys_cxx::cxx_clipperd_set_reverse_solution(self.inner.pin_mut(), value);
}
pub fn get_reverse_solution(&self) -> bool {
clipper2_sys_cxx::cxx_clipperd_get_reverse_solution(&self.inner)
}
pub fn clear(&mut self) {
clipper2_sys_cxx::cxx_clipperd_clear(self.inner.pin_mut());
}
pub fn add_open_subject(&mut self, open_subject: &PathsD) {
clipper2_sys_cxx::cxx_clipperd_add_open_subject(
self.inner.pin_mut(),
&pathsd_to_blob(open_subject),
);
}
pub fn add_subject(&mut self, subject: &PathsD) {
clipper2_sys_cxx::cxx_clipperd_add_subject(self.inner.pin_mut(), &pathsd_to_blob(subject));
}
pub fn add_clip(&mut self, clip: &PathsD) {
clipper2_sys_cxx::cxx_clipperd_add_clip(self.inner.pin_mut(), &pathsd_to_blob(clip));
}
pub fn execute(&mut self, clip_type: ClipType, fill_rule: FillRule) -> ClipSolutionD {
let out = clipper2_sys_cxx::cxx_clipperd_execute(
self.inner.pin_mut(),
clip_type.into(),
fill_rule.into(),
);
ClipSolutionD {
closed: out.closed,
open: out.open,
}
}
pub fn execute_tree(&mut self, clip_type: ClipType, fill_rule: FillRule) -> ClipTreeSolutionD {
let te = clipper2_sys_cxx::cxx_clipperd_execute_tree(
self.inner.pin_mut(),
clip_type.into(),
fill_rule.into(),
);
ClipTreeSolutionD {
poly_root: if te.root == 0 { None } else { Some(te.root) },
open: te.open,
}
}
}
#[cfg(test)]
mod tests {
use crate::{ClipType, ClipperD, FillRule, PathD, PathsD, PointD};
fn square(x0: f64, y0: f64, s: f64) -> PathD {
PathD::new(vec![
PointD::new(x0, y0),
PointD::new(x0 + s, y0),
PointD::new(x0 + s, y0 + s),
PointD::new(x0, y0 + s),
])
}
#[test]
fn union_overlapping_squares_non_empty() {
let mut c = ClipperD::new(4);
c.add_subject(&PathsD::new(vec![square(0.0, 0.0, 100.0)]));
c.add_clip(&PathsD::new(vec![square(50.0, 50.0, 100.0)]));
let sol = c.execute(ClipType::Union, FillRule::NonZero);
assert!(!sol.closed_is_empty() || !sol.open_is_empty());
}
#[test]
fn execute_tree_poly_preorder_d_count_stable() {
let mut c = ClipperD::new(4);
c.add_subject(&PathsD::new(vec![square(0.0, 0.0, 100.0)]));
c.add_clip(&PathsD::new(vec![square(50.0, 50.0, 100.0)]));
let sol = c.execute_tree(ClipType::Union, FillRule::NonZero);
let (_, iter_a) = sol.into_open_and_poly_preorder();
let n_a = iter_a.count();
let mut c2 = ClipperD::new(4);
c2.add_subject(&PathsD::new(vec![square(0.0, 0.0, 100.0)]));
c2.add_clip(&PathsD::new(vec![square(50.0, 50.0, 100.0)]));
let sol2 = c2.execute_tree(ClipType::Union, FillRule::NonZero);
let (_, iter_b) = sol2.into_open_and_poly_preorder();
let n_b = iter_b.count();
assert_eq!(n_a, n_b);
}
#[test]
fn execute_tree_union_d_has_poly_or_open() {
let mut c = ClipperD::new(4);
c.add_subject(&PathsD::new(vec![square(0.0, 0.0, 100.0)]));
c.add_clip(&PathsD::new(vec![square(50.0, 50.0, 100.0)]));
let sol = c.execute_tree(ClipType::Union, FillRule::NonZero);
assert!(sol.has_poly_tree() || !sol.open_is_empty());
}
}