clipper2_sys/
paths_blob.rs1use crate::cxx_bridge::clipper2_sys_cxx::{P64, PD, PathsBlob64, PathsBlobD};
6use crate::{Path64, PathD, Paths64, PathsD};
7
8pub(crate) const DEFAULT_PATHD_PRECISION: i32 = 4;
12
13#[inline]
15pub(crate) fn path64_from_p64_slice(points: &[P64]) -> Path64 {
16 Path64::from_vec(points.to_vec())
17}
18
19#[inline]
21pub(crate) fn pathd_from_pd_slice(points: &[PD]) -> PathD {
22 PathD::from_vec(points.to_vec())
23}
24
25define_paths_blob_iter! {
26 pub PathsBlob64Iter,
30 point = P64,
31 path = Path64,
32 blob = PathsBlob64,
33 conv_slice = path64_from_p64_slice,
34}
35
36define_paths_blob_iter! {
37 pub PathsBlobDIter,
39 point = PD,
40 path = PathD,
41 blob = PathsBlobD,
42 conv_slice = pathd_from_pd_slice,
43}
44
45#[inline]
47pub(crate) fn path64_to_blob(p: &Path64) -> PathsBlob64 {
48 let n = p.0.len();
49 PathsBlob64 {
50 points: p.0.clone(),
51 path_starts: vec![0, n],
52 }
53}
54
55pub(crate) fn paths64_to_blob(p: &Paths64) -> PathsBlob64 {
57 let n_paths = p.0.len();
58 let total_pts: usize = p.0.iter().map(|path| path.0.len()).sum();
59 let mut points = Vec::with_capacity(total_pts);
60 let mut path_starts = Vec::with_capacity(n_paths + 1);
61 path_starts.push(0);
62 for path in &p.0 {
63 points.extend_from_slice(&path.0);
64 path_starts.push(points.len());
65 }
66 PathsBlob64 { points, path_starts }
67}
68
69pub(crate) fn path64_from_single_paths_blob(b: PathsBlob64) -> Path64 {
71 if b.path_starts.len() < 2 {
72 return Path64::default();
73 }
74 let a = b.path_starts[0];
75 let e = b.path_starts[1];
76 path64_from_p64_slice(&b.points[a..e])
77}
78
79pub(crate) fn paths64_from_blob(b: PathsBlob64) -> Paths64 {
81 if b.path_starts.len() < 2 {
82 return Paths64::default();
83 }
84 let n_paths = b.path_starts.len() - 1;
85 let mut paths = Vec::with_capacity(n_paths);
86 for w in b.path_starts.windows(2) {
87 let (a, e) = (w[0], w[1]);
88 paths.push(path64_from_p64_slice(&b.points[a..e]));
89 }
90 Paths64::from_vec(paths)
91}
92
93#[inline]
94pub(crate) fn pathd_to_blob(p: &PathD) -> PathsBlobD {
95 let n = p.0.len();
96 PathsBlobD {
97 points: p.0.clone(),
98 path_starts: vec![0, n],
99 }
100}
101
102pub(crate) fn pathsd_to_blob(p: &PathsD) -> PathsBlobD {
103 let n_paths = p.0.len();
104 let total_pts: usize = p.0.iter().map(|path| path.0.len()).sum();
105 let mut points = Vec::with_capacity(total_pts);
106 let mut path_starts = Vec::with_capacity(n_paths + 1);
107 path_starts.push(0);
108 for path in &p.0 {
109 points.extend_from_slice(&path.0);
110 path_starts.push(points.len());
111 }
112 PathsBlobD { points, path_starts }
113}
114
115pub(crate) fn pathd_from_single_paths_blob(b: PathsBlobD) -> PathD {
116 if b.path_starts.len() < 2 {
117 return PathD::default();
118 }
119 let a = b.path_starts[0];
120 let e = b.path_starts[1];
121 pathd_from_pd_slice(&b.points[a..e])
122}
123
124pub(crate) fn pathsd_from_blob(b: PathsBlobD) -> PathsD {
125 if b.path_starts.len() < 2 {
126 return PathsD::default();
127 }
128 let n_paths = b.path_starts.len() - 1;
129 let mut paths = Vec::with_capacity(n_paths);
130 for w in b.path_starts.windows(2) {
131 let (a, e) = (w[0], w[1]);
132 paths.push(pathd_from_pd_slice(&b.points[a..e]));
133 }
134 PathsD::from_vec(paths)
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use crate::{Path64, PathD, Paths64, PathsD, Point64, PointD};
141
142 #[test]
143 fn path64_single_blob_roundtrip() {
144 let p = Path64::new(vec![
145 Point64::new(0, 0),
146 Point64::new(10, 0),
147 Point64::new(10, 10),
148 ]);
149 let blob = path64_to_blob(&p);
150 let q = path64_from_single_paths_blob(blob);
151 assert_eq!(q.len(), p.len());
152 assert_eq!(q.get_point(0), p.get_point(0));
153 assert_eq!(q.get_point(2), p.get_point(2));
154 }
155
156 #[test]
157 fn paths64_multi_blob_roundtrip() {
158 let a = Path64::new(vec![Point64::new(0, 0), Point64::new(1, 0)]);
159 let b = Path64::new(vec![
160 Point64::new(5, 5),
161 Point64::new(6, 5),
162 Point64::new(6, 6),
163 ]);
164 let paths = Paths64::new(vec![a, b]);
165 let blob = paths64_to_blob(&paths);
166 let out = paths64_from_blob(blob);
167 assert_eq!(out.len(), 2);
168 assert_eq!(out.path(0).len(), 2);
169 assert_eq!(out.path(1).len(), 3);
170 }
171
172 #[test]
173 fn paths64_from_blob_short_starts_is_empty() {
174 let b = PathsBlob64 {
175 points: vec![],
176 path_starts: vec![0],
177 };
178 assert!(paths64_from_blob(b).is_empty());
179 let b2 = PathsBlob64 {
180 points: vec![],
181 path_starts: vec![],
182 };
183 assert!(paths64_from_blob(b2).is_empty());
184 }
185
186 #[test]
187 fn path64_from_single_paths_blob_short_starts_is_default() {
188 let b = PathsBlob64 {
189 points: vec![],
190 path_starts: vec![0],
191 };
192 assert!(path64_from_single_paths_blob(b).is_empty());
193 }
194
195 #[test]
196 fn paths_blob64_iter_exact_len_and_fused() {
197 let paths = Paths64::new(vec![
198 Path64::new(vec![Point64::new(0, 0), Point64::new(1, 0)]),
199 Path64::new(vec![Point64::new(2, 2)]),
200 ]);
201 let blob = paths64_to_blob(&paths);
202 let mut it = PathsBlob64Iter::new(&blob);
203 assert_eq!(it.len(), 2);
204 assert_eq!(it.next().map(|p| p.len()), Some(2));
205 assert_eq!(it.len(), 1);
206 assert_eq!(it.next().map(|p| p.len()), Some(1));
207 assert!(it.next().is_none());
208 assert!(it.next().is_none());
209 }
210
211 #[test]
212 fn pathd_single_and_paths_roundtrip() {
213 let p = PathD::new(vec![
214 PointD::new(0.0, 0.0),
215 PointD::new(3.0, 0.0),
216 PointD::new(3.0, 4.0),
217 ]);
218 let blob = pathd_to_blob(&p);
219 let q = pathd_from_single_paths_blob(blob);
220 assert_eq!(q.len(), 3);
221
222 let paths = PathsD::new(vec![p]);
223 let blob2 = pathsd_to_blob(&paths);
224 let out = pathsd_from_blob(blob2);
225 assert_eq!(out.len(), 1);
226 assert_eq!(out.path(0).len(), 3);
227 }
228}