1use crate::{Point, Polyline, DEFAULT_TOLERANCE};
4
5use kurbo::{BezPath, PathEl, PathSeg};
6
7pub trait IntoBezPathTolerance {
9 fn into_bezpath_with_tolerance(self, tolerance: f64) -> BezPath;
10}
11
12pub trait IntoBezPath {
17 fn into_bezpath(self) -> BezPath;
18}
19
20impl<T: IntoBezPathTolerance> IntoBezPath for T {
21 fn into_bezpath(self) -> BezPath {
22 <Self as IntoBezPathTolerance>::into_bezpath_with_tolerance(self, DEFAULT_TOLERANCE)
23 }
24}
25
26impl IntoBezPathTolerance for &[(f64, f64)] {
27 fn into_bezpath_with_tolerance(self, _tolerance: f64) -> BezPath {
28 points_to_bezpath(self.iter().copied())
29 }
30}
31
32impl IntoBezPathTolerance for Vec<(f64, f64)> {
33 fn into_bezpath_with_tolerance(self, _tolerance: f64) -> BezPath {
34 points_to_bezpath(self)
35 }
36}
37
38impl IntoBezPathTolerance for &[Point] {
39 fn into_bezpath_with_tolerance(self, _tolerance: f64) -> BezPath {
40 points_to_bezpath(self.iter().copied())
41 }
42}
43
44impl<const N: usize> IntoBezPathTolerance for [Point; N] {
45 fn into_bezpath_with_tolerance(self, _tolerance: f64) -> BezPath {
46 points_to_bezpath(self.iter().copied())
47 }
48}
49
50impl IntoBezPathTolerance for &Vec<Point> {
51 fn into_bezpath_with_tolerance(self, _tolerance: f64) -> BezPath {
52 points_to_bezpath(self.iter().copied())
53 }
54}
55
56impl IntoBezPathTolerance for &[(Point, Point)] {
57 fn into_bezpath_with_tolerance(self, _tolerance: f64) -> BezPath {
58 line_segment_to_bezpath(self.iter().copied())
59 }
60}
61
62impl<const N: usize> IntoBezPathTolerance for [(Point, Point); N] {
63 fn into_bezpath_with_tolerance(self, _tolerance: f64) -> BezPath {
64 line_segment_to_bezpath(self)
65 }
66}
67
68macro_rules! kurbo_shape_into_bezpath {
72 ($t:ty) => {
73 impl IntoBezPathTolerance for $t {
74 fn into_bezpath_with_tolerance(self, tolerance: f64) -> BezPath {
75 <$t as kurbo::Shape>::into_path(self, tolerance)
76 }
77 }
78 };
79}
80
81kurbo_shape_into_bezpath!(kurbo::PathSeg);
82kurbo_shape_into_bezpath!(kurbo::Arc);
83kurbo_shape_into_bezpath!(kurbo::BezPath);
84kurbo_shape_into_bezpath!(kurbo::Circle);
85kurbo_shape_into_bezpath!(kurbo::CircleSegment);
86kurbo_shape_into_bezpath!(kurbo::CubicBez);
87kurbo_shape_into_bezpath!(kurbo::Ellipse);
88kurbo_shape_into_bezpath!(kurbo::Line);
89kurbo_shape_into_bezpath!(kurbo::QuadBez);
90kurbo_shape_into_bezpath!(kurbo::Rect);
91kurbo_shape_into_bezpath!(kurbo::RoundedRect);
92
93impl IntoBezPathTolerance for Polyline {
94 fn into_bezpath_with_tolerance(self, tolerance: f64) -> BezPath {
95 self.points().into_bezpath_with_tolerance(tolerance)
96 }
97}
98
99#[cfg(feature = "geo")]
100pub mod geo_impl {
101 #[allow(clippy::wildcard_imports)]
102 use super::*;
103
104 impl IntoBezPathTolerance for geo::Geometry<f64> {
105 fn into_bezpath_with_tolerance(self, _tolerance: f64) -> BezPath {
106 match self {
107 geo::Geometry::Point(pt) => BezPath::from_vec(vec![
108 PathEl::MoveTo((pt.x(), pt.y()).into()),
109 PathEl::LineTo((pt.x(), pt.y()).into()),
110 ]),
111 geo::Geometry::MultiPoint(mp) => BezPath::from_vec(
112 mp.into_iter()
113 .flat_map(|pt| {
114 [
115 PathEl::MoveTo((pt.x(), pt.y()).into()),
116 PathEl::LineTo((pt.x(), pt.y()).into()),
117 ]
118 })
119 .collect(),
120 ),
121 geo::Geometry::Line(line) => {
122 BezPath::from_path_segments(std::iter::once(PathSeg::Line(kurbo::Line::new(
123 (line.start.x, line.start.y),
124 (line.end.x, line.end.y),
125 ))))
126 }
127
128 geo::Geometry::LineString(pts) => linestring_to_path_el(pts).collect::<BezPath>(),
129 geo::Geometry::MultiLineString(mls) => mls
130 .into_iter()
131 .flat_map(linestring_to_path_el)
132 .collect::<BezPath>(),
133
134 geo::Geometry::Polygon(poly) => {
135 let (exterior, interiors) = poly.into_inner();
136
137 linestring_to_path_el(exterior)
138 .chain(interiors.into_iter().flat_map(linestring_to_path_el))
139 .collect::<BezPath>()
140 }
141
142 geo::Geometry::MultiPolygon(mp) => mp
143 .into_iter()
144 .flat_map(|poly| {
145 let (exterior, interiors) = poly.into_inner();
146
147 linestring_to_path_el(exterior)
148 .chain(interiors.into_iter().flat_map(linestring_to_path_el))
149 })
150 .collect::<BezPath>(),
151
152 geo::Geometry::GeometryCollection(coll) => coll
153 .into_iter()
154 .flat_map(IntoBezPath::into_bezpath)
155 .collect(),
156 geo::Geometry::Rect(rect) => BezPath::from_vec(vec![
157 PathEl::MoveTo((rect.min().x, rect.min().y).into()),
158 PathEl::LineTo((rect.min().x, rect.max().y).into()),
159 PathEl::LineTo((rect.max().x, rect.max().y).into()),
160 PathEl::LineTo((rect.max().x, rect.min().y).into()),
161 PathEl::ClosePath,
162 ]),
163 geo::Geometry::Triangle(tri) => BezPath::from_vec(vec![
164 PathEl::MoveTo((tri.0.x, tri.0.y).into()),
165 PathEl::LineTo((tri.1.x, tri.1.y).into()),
166 PathEl::LineTo((tri.2.x, tri.2.y).into()),
167 PathEl::ClosePath,
168 ]),
169 }
170 }
171 }
172
173 macro_rules! geo_object_into_bezpath {
174 ( $ t: ty) => {
175 impl IntoBezPathTolerance for $t {
176 fn into_bezpath_with_tolerance(self, tolerance: f64) -> BezPath {
177 let geom: ::geo::Geometry = self.into();
178 geom.into_bezpath_with_tolerance(tolerance)
179 }
180 }
181 };
182 }
183
184 geo_object_into_bezpath!(geo::Point<f64>);
185 geo_object_into_bezpath!(geo::Line<f64>);
186 geo_object_into_bezpath!(geo::LineString<f64>);
187 geo_object_into_bezpath!(geo::Polygon<f64>);
188 geo_object_into_bezpath!(geo::MultiPoint<f64>);
189 geo_object_into_bezpath!(geo::MultiLineString<f64>);
190 geo_object_into_bezpath!(geo::MultiPolygon<f64>);
191 geo_object_into_bezpath!(geo::Rect<f64>);
192 geo_object_into_bezpath!(geo::Triangle<f64>);
193
194 pub(super) fn linestring_to_path_el(ls: geo::LineString<f64>) -> impl Iterator<Item = PathEl> {
195 let closed = ls.is_closed();
196 let len = ls.0.len();
197
198 ls.into_iter().enumerate().map(move |(i, pt)| {
199 if i == 0 {
200 PathEl::MoveTo(coord_to_point(pt).into())
201 } else if i == len - 1 && closed {
202 PathEl::ClosePath
203 } else {
204 PathEl::LineTo(coord_to_point(pt).into())
205 }
206 })
207 }
208
209 #[inline]
210 pub(super) fn coord_to_point(c: geo::Coord<f64>) -> Point {
211 (c.x, c.y).into()
212 }
213}
214
215pub(crate) fn points_to_bezpath(
216 points: impl IntoIterator<Item = impl Into<Point>>,
217) -> kurbo::BezPath {
218 let mut bezpath = kurbo::BezPath::new();
219
220 let mut points = points.into_iter().map(Into::into);
221
222 if let Some(pt) = points.next() {
223 bezpath.move_to(pt);
224 }
225
226 for pt in points {
227 bezpath.line_to(pt);
228 }
229
230 bezpath
231}
232
233pub(crate) fn line_segment_to_bezpath(
234 segments: impl IntoIterator<Item = impl Into<(Point, Point)>>,
235) -> BezPath {
236 let segments = segments
237 .into_iter()
238 .map(Into::into)
239 .map(|(a, b)| kurbo::PathSeg::Line(kurbo::Line::new(a, b)));
240
241 BezPath::from_path_segments(segments)
242}
243#[cfg(test)]
244mod test {
245 use super::geo_impl::*;
246 use super::*;
247
248 #[test]
249 fn test_linestring_to_path_el() {
250 assert_eq!(
252 linestring_to_path_el(geo::LineString(vec![])).collect::<Vec<_>>(),
253 vec![]
254 );
255
256 assert_eq!(
258 linestring_to_path_el(geo::LineString(vec![(0., 0.).into()])).collect::<Vec<_>>(),
259 vec![PathEl::MoveTo((0., 0.).into())]
260 );
261
262 assert_eq!(
264 linestring_to_path_el(geo::LineString(vec![
265 (0., 0.).into(),
266 (1., 1.).into(),
267 (2., 2.).into()
268 ]))
269 .collect::<Vec<_>>(),
270 vec![
271 PathEl::MoveTo((0., 0.).into()),
272 PathEl::LineTo((1., 1.).into()),
273 PathEl::LineTo((2., 2.).into()),
274 ]
275 );
276
277 assert_eq!(
279 linestring_to_path_el(geo::LineString(vec![
280 (0., 0.).into(),
281 (1., 1.).into(),
282 (2., 2.).into(),
283 (0., 0.).into(),
284 ]))
285 .collect::<Vec<_>>(),
286 vec![
287 PathEl::MoveTo((0., 0.).into()),
288 PathEl::LineTo((1., 1.).into()),
289 PathEl::LineTo((2., 2.).into()),
290 PathEl::ClosePath,
291 ]
292 );
293
294 let mut ls = geo::LineString(vec![(0., 0.).into(), (1., 1.).into(), (2., 2.).into()]);
296 ls.close();
297
298 assert_eq!(
299 linestring_to_path_el(ls).collect::<Vec<_>>(),
300 vec![
301 PathEl::MoveTo((0., 0.).into()),
302 PathEl::LineTo((1., 1.).into()),
303 PathEl::LineTo((2., 2.).into()),
304 PathEl::ClosePath,
305 ]
306 );
307 }
308
309 #[test]
310 fn test_points_to_bezpath() {
311 let points = vec![[0.0, 0.0], [10.0, 12.0], [1.0, 2.0]];
312
313 assert_eq!(
314 points_to_bezpath(points),
315 BezPath::from_vec(vec![
316 PathEl::MoveTo(kurbo::Point::new(0.0, 0.0)),
317 PathEl::LineTo(kurbo::Point::new(10.0, 12.0)),
318 PathEl::LineTo(kurbo::Point::new(1.0, 2.0))
319 ])
320 );
321 }
322
323 #[test]
324 fn test_points_to_bezpath_empty() {
325 let points: [Point; 0] = [];
326 assert!(points_to_bezpath(points).is_empty());
327
328 let points = [Point::new(0.0, 0.0)];
329 assert!(points_to_bezpath(points).is_empty());
330 }
331
332 #[test]
333 fn test_line_segments_to_bezpath() {
334 let segs = [
335 (Point::new(0.0, 0.0), Point::new(10.0, 12.0)),
336 (Point::new(1.0, 2.0), Point::new(3.0, 4.0)),
337 ];
338 let bezpath = line_segment_to_bezpath(segs);
339
340 assert_eq!(
341 bezpath,
342 BezPath::from_vec(vec![
343 PathEl::MoveTo(kurbo::Point::new(0.0, 0.0)),
344 PathEl::LineTo(kurbo::Point::new(10.0, 12.0)),
345 PathEl::MoveTo(kurbo::Point::new(1.0, 2.0)),
346 PathEl::LineTo(kurbo::Point::new(3.0, 4.0))
347 ])
348 );
349 }
350}