path_offset/offset/
flo_curves.rs1use flo_curves::{
2 bezier::{
3 curve_is_tiny, fit_curve, offset,
4 path::{path_remove_interior_points, BezierPath, BezierPathFactory, SimpleBezierPath},
5 walk_curve_evenly, Curve,
6 },
7 BezierCurve, Coord2,
8};
9
10use crate::{
11 error::{PathError, Result},
12 offset::Offset,
13 path::Path,
14};
15
16pub struct FloCurvesOffset {
17 curves: Vec<Curve<Coord2>>,
18}
19
20impl FloCurvesOffset {
21 pub fn new(path: &Path, offset_distance: f64) -> Self {
22 FloCurvesOffset {
23 curves: SimpleBezierPath::from(path)
24 .to_curves()
25 .into_iter()
26 .flat_map(|curve| offset(&curve, -offset_distance, -offset_distance))
27 .filter(|curve| !curve_is_tiny(curve))
28 .collect::<Vec<_>>(),
29 }
30 }
31
32 pub fn curves(&self) -> &Vec<Curve<Coord2>> {
33 &self.curves
34 }
35}
36
37impl Offset for FloCurvesOffset {
38 fn offset_path(&self) -> Result<Path> {
39 let offset_points = self
40 .curves
41 .iter()
42 .flat_map(|curve| sample_curve(curve))
43 .collect::<Vec<_>>();
44
45 let fitted_curve =
46 fit_curve::<Curve<Coord2>>(&offset_points, 1.0).ok_or(PathError::FitCurve)?;
47
48 let offset_toolpath = SimpleBezierPath::from_connected_curves(
49 fitted_curve
50 .into_iter()
51 .filter(|curve| !curve_is_tiny(curve)),
52 );
53
54 let clean_offset_toolpath: SimpleBezierPath =
55 path_remove_interior_points(&vec![offset_toolpath], 0.01)
56 .into_iter()
57 .next()
58 .ok_or(PathError::CleanPath)?;
59
60 Ok(Path::from(&clean_offset_toolpath))
61 }
62}
63
64fn sample_curve(curve: &Curve<Coord2>) -> Vec<Coord2> {
66 let max_error = 0.01;
67 let distance = 0.1;
68
69 walk_curve_evenly(curve, distance, max_error)
71 .map(|section| section.point_at_pos(0.5))
72 .collect::<Vec<_>>()
73}