clipper2_sys/clipperd/
clipper.rs1use std::fmt;
2
3use crate::cxx_bridge::clipper2_sys_cxx;
4use crate::paths_blob::{pathsd_to_blob, PathsBlobDIter};
5use crate::poly_path::PolyCxxPreorderIterD;
6use crate::{ClipType, FillRule, PathsD};
7
8use super::LazyPathsD;
9
10#[derive(Clone, Debug)]
12pub struct ClipSolutionD {
13 closed: clipper2_sys_cxx::PathsBlobD,
14 open: clipper2_sys_cxx::PathsBlobD,
15}
16
17impl ClipSolutionD {
18 #[inline]
20 pub fn closed_is_empty(&self) -> bool {
21 self.closed.path_starts.len() < 2
22 }
23
24 #[inline]
26 pub fn open_is_empty(&self) -> bool {
27 self.open.path_starts.len() < 2
28 }
29
30 #[inline]
32 pub fn iter_closed(&self) -> PathsBlobDIter<'_> {
33 PathsBlobDIter::new(&self.closed)
34 }
35
36 #[inline]
38 pub fn iter_open(&self) -> PathsBlobDIter<'_> {
39 PathsBlobDIter::new(&self.open)
40 }
41
42 pub fn to_closed(&self) -> PathsD {
44 self.iter_closed().collect()
45 }
46
47 pub fn to_open(&self) -> PathsD {
49 self.iter_open().collect()
50 }
51
52 pub fn into_lazy(self) -> (LazyPathsD, LazyPathsD) {
54 (
55 LazyPathsD::from_blob(self.closed),
56 LazyPathsD::from_blob(self.open),
57 )
58 }
59}
60
61pub struct ClipTreeSolutionD {
63 poly_root: Option<usize>,
64 open: clipper2_sys_cxx::PathsBlobD,
65}
66
67impl fmt::Debug for ClipTreeSolutionD {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 f.debug_struct("ClipTreeSolutionD")
70 .field("has_poly_root", &self.poly_root.map(|r| r != 0))
71 .field("open_paths", &(self.open.path_starts.len().saturating_sub(1)))
72 .finish()
73 }
74}
75
76impl Drop for ClipTreeSolutionD {
77 fn drop(&mut self) {
78 if let Some(r) = self.poly_root.take() {
79 if r != 0 {
80 clipper2_sys_cxx::cxx_polyd_delete(r);
81 }
82 }
83 }
84}
85
86impl ClipTreeSolutionD {
87 #[inline]
89 pub fn has_poly_tree(&self) -> bool {
90 matches!(self.poly_root, Some(r) if r != 0)
91 }
92
93 #[inline]
95 pub fn open_is_empty(&self) -> bool {
96 self.open.path_starts.len() < 2
97 }
98
99 #[inline]
101 pub fn iter_open(&self) -> PathsBlobDIter<'_> {
102 PathsBlobDIter::new(&self.open)
103 }
104
105 pub fn to_open(&self) -> PathsD {
107 self.iter_open().collect()
108 }
109
110 pub fn into_open_lazy(mut self) -> LazyPathsD {
112 if let Some(r) = self.poly_root.take() {
113 if r != 0 {
114 clipper2_sys_cxx::cxx_polyd_delete(r);
115 }
116 }
117 LazyPathsD::from_blob(std::mem::take(&mut self.open))
118 }
119
120 pub fn into_open_and_poly_preorder(mut self) -> (LazyPathsD, PolyCxxPreorderIterD) {
122 let root = self.poly_root.take().unwrap_or(0);
123 let open = std::mem::take(&mut self.open);
124 let iter = PolyCxxPreorderIterD::new(root);
125 (LazyPathsD::from_blob(open), iter)
126 }
127}
128
129pub struct ClipperD {
133 inner: cxx::UniquePtr<clipper2_sys_cxx::ClipperDBox>,
134}
135
136impl ClipperD {
137 pub fn new(precision: i32) -> Self {
139 Self {
140 inner: clipper2_sys_cxx::cxx_clipperd_new(precision),
141 }
142 }
143
144 pub fn set_preserve_collinear(&mut self, value: bool) {
146 clipper2_sys_cxx::cxx_clipperd_set_preserve_collinear(self.inner.pin_mut(), value);
147 }
148
149 pub fn get_preserve_collinear(&self) -> bool {
151 clipper2_sys_cxx::cxx_clipperd_get_preserve_collinear(&self.inner)
152 }
153
154 pub fn set_reverse_solution(&mut self, value: bool) {
156 clipper2_sys_cxx::cxx_clipperd_set_reverse_solution(self.inner.pin_mut(), value);
157 }
158
159 pub fn get_reverse_solution(&self) -> bool {
161 clipper2_sys_cxx::cxx_clipperd_get_reverse_solution(&self.inner)
162 }
163
164 pub fn clear(&mut self) {
166 clipper2_sys_cxx::cxx_clipperd_clear(self.inner.pin_mut());
167 }
168
169 pub fn add_open_subject(&mut self, open_subject: &PathsD) {
171 clipper2_sys_cxx::cxx_clipperd_add_open_subject(
172 self.inner.pin_mut(),
173 &pathsd_to_blob(open_subject),
174 );
175 }
176
177 pub fn add_subject(&mut self, subject: &PathsD) {
179 clipper2_sys_cxx::cxx_clipperd_add_subject(self.inner.pin_mut(), &pathsd_to_blob(subject));
180 }
181
182 pub fn add_clip(&mut self, clip: &PathsD) {
184 clipper2_sys_cxx::cxx_clipperd_add_clip(self.inner.pin_mut(), &pathsd_to_blob(clip));
185 }
186
187 pub fn execute(&mut self, clip_type: ClipType, fill_rule: FillRule) -> ClipSolutionD {
189 let out = clipper2_sys_cxx::cxx_clipperd_execute(
190 self.inner.pin_mut(),
191 clip_type.into(),
192 fill_rule.into(),
193 );
194 ClipSolutionD {
195 closed: out.closed,
196 open: out.open,
197 }
198 }
199
200 pub fn execute_tree(&mut self, clip_type: ClipType, fill_rule: FillRule) -> ClipTreeSolutionD {
202 let te = clipper2_sys_cxx::cxx_clipperd_execute_tree(
203 self.inner.pin_mut(),
204 clip_type.into(),
205 fill_rule.into(),
206 );
207 ClipTreeSolutionD {
208 poly_root: if te.root == 0 { None } else { Some(te.root) },
209 open: te.open,
210 }
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use crate::{ClipType, ClipperD, FillRule, PathD, PathsD, PointD};
217
218 fn square(x0: f64, y0: f64, s: f64) -> PathD {
219 PathD::new(vec![
220 PointD::new(x0, y0),
221 PointD::new(x0 + s, y0),
222 PointD::new(x0 + s, y0 + s),
223 PointD::new(x0, y0 + s),
224 ])
225 }
226
227 #[test]
228 fn union_overlapping_squares_non_empty() {
229 let mut c = ClipperD::new(4);
230 c.add_subject(&PathsD::new(vec![square(0.0, 0.0, 100.0)]));
231 c.add_clip(&PathsD::new(vec![square(50.0, 50.0, 100.0)]));
232 let sol = c.execute(ClipType::Union, FillRule::NonZero);
233 assert!(!sol.closed_is_empty() || !sol.open_is_empty());
234 }
235
236 #[test]
237 fn execute_tree_poly_preorder_d_count_stable() {
238 let mut c = ClipperD::new(4);
239 c.add_subject(&PathsD::new(vec![square(0.0, 0.0, 100.0)]));
240 c.add_clip(&PathsD::new(vec![square(50.0, 50.0, 100.0)]));
241 let sol = c.execute_tree(ClipType::Union, FillRule::NonZero);
242 let (_, iter_a) = sol.into_open_and_poly_preorder();
243 let n_a = iter_a.count();
244
245 let mut c2 = ClipperD::new(4);
246 c2.add_subject(&PathsD::new(vec![square(0.0, 0.0, 100.0)]));
247 c2.add_clip(&PathsD::new(vec![square(50.0, 50.0, 100.0)]));
248 let sol2 = c2.execute_tree(ClipType::Union, FillRule::NonZero);
249 let (_, iter_b) = sol2.into_open_and_poly_preorder();
250 let n_b = iter_b.count();
251
252 assert_eq!(n_a, n_b);
253 }
254
255 #[test]
256 fn execute_tree_union_d_has_poly_or_open() {
257 let mut c = ClipperD::new(4);
258 c.add_subject(&PathsD::new(vec![square(0.0, 0.0, 100.0)]));
259 c.add_clip(&PathsD::new(vec![square(50.0, 50.0, 100.0)]));
260 let sol = c.execute_tree(ClipType::Union, FillRule::NonZero);
261 assert!(sol.has_poly_tree() || !sol.open_is_empty());
262 }
263}