path_offset/path/conversions/
flo_curves.rs

1use flo_curves::{
2    bezier::{
3        path::{BezierPathBuilder, SimpleBezierPath},
4        Curve,
5    },
6    BezierCurve, Coord2, Coordinate,
7};
8use lyon::path::Event;
9
10use crate::path::point::PointConvert;
11
12impl From<&crate::path::Path> for SimpleBezierPath {
13    fn from(path: &crate::path::Path) -> SimpleBezierPath {
14        let mut builder = BezierPathBuilder::<SimpleBezierPath>::start(Coord2::from((0.0, 0.0)));
15        let mut current_pos = Coord2::from((0.0, 0.0)); // 跟踪当前位置
16
17        for event in path.inner.iter() {
18            match event {
19                Event::Begin { at } => {
20                    let start_point = at.use_as();
21                    builder = BezierPathBuilder::start(start_point);
22                    current_pos = start_point;
23                }
24                Event::Line { to, .. } => {
25                    let to_point = to.use_as();
26                    builder = builder.line_to(to_point);
27                    current_pos = to_point;
28                }
29                Event::Quadratic { ctrl, to, .. } => {
30                    // 二次贝塞尔转换为三次贝塞尔控制点
31                    let cp1: Coord2 =
32                        current_pos + (ctrl.use_as::<Coord2>() - current_pos) * (2.0 / 3.0);
33                    let cp2: Coord2 = to.use_as::<Coord2>()
34                        + (ctrl.use_as::<Coord2>() - to.use_as::<Coord2>()) * (2.0 / 3.0);
35
36                    let to_point = to.use_as();
37                    builder = builder.curve_to((cp1, cp2), to_point);
38                    current_pos = to_point;
39                }
40                Event::Cubic {
41                    ctrl1, ctrl2, to, ..
42                } => {
43                    let to_point = to.use_as();
44                    builder = builder.curve_to((ctrl1.use_as(), ctrl2.use_as()), to_point);
45                    current_pos = to_point;
46                }
47                // --- 这是关键的修改 ---
48                Event::End { first, close, .. } => {
49                    // 只在 lyon 报告路径未闭合时才手动添加闭合线段
50                    if !close {
51                        // 同样检查一下,避免浮点误差导致添加一个极小的线段
52                        if current_pos.distance_to(&first.use_as()) > 1e-6 {
53                            builder = builder.line_to(first.use_as());
54                        }
55                    }
56                    // 如果 close 为 true,我们什么都不做,因为路径已经完美闭合了。
57                }
58            }
59        }
60
61        builder.build()
62    }
63}
64
65impl From<&Vec<Curve<Coord2>>> for crate::path::Path {
66    fn from(value: &Vec<Curve<Coord2>>) -> Self {
67        let mut builder = lyon::path::Path::builder();
68
69        let mut points = vec![];
70        for curve in value {
71            let start_point = curve.start_point();
72            let end_point = curve.end_point();
73            let (ctrl1, ctrl2) = curve.control_points();
74
75            points.push((
76                start_point.use_as(),
77                ctrl1.use_as(),
78                ctrl2.use_as(),
79                end_point.use_as(),
80            ));
81        }
82
83        for (start, ctrl1, ctrl2, end) in points {
84            builder.begin(start);
85            builder.cubic_bezier_to(ctrl1, ctrl2, end);
86            builder.end(false);
87        }
88
89        Self {
90            inner: builder.build(),
91        }
92    }
93}
94
95impl From<&SimpleBezierPath> for crate::path::Path {
96    fn from(value: &SimpleBezierPath) -> Self {
97        let (start_point, segments) = value;
98        let mut builder = lyon::path::Path::builder();
99
100        // Begin path at the start point
101        builder.begin(start_point.use_as());
102
103        // Track last point for later closure
104        let mut last_point = start_point;
105
106        for (ctrl1, ctrl2, to) in segments {
107            if ctrl1.is_nan() || ctrl2.is_nan() || to.is_nan() {
108                continue;
109            }
110
111            let is_line = ctrl1 == last_point && ctrl2 == to;
112
113            if is_line {
114                builder.line_to(to.use_as());
115            } else {
116                builder.cubic_bezier_to(ctrl1.use_as(), ctrl2.use_as(), to.use_as());
117            }
118
119            last_point = to;
120        }
121
122        // 闭合路径:回到起点并调用 close()
123        builder.line_to(start_point.use_as());
124        builder.close();
125
126        Self {
127            inner: builder.build(),
128        }
129    }
130}