Skip to main content

smooth_frame/output/
format.rs

1use crate::output::path::PathCommand;
2use crate::utils::{MAX_FORMAT_PRECISION, bounded_precision, format_point};
3
4/// 将通用路径命令转换成具体后端输出的扩展契约。
5///
6/// 库内置 SVG path data formatter;Godot、Canvas、函数调用列表等格式可以在库外实现
7/// 这个 trait,并通过 [`crate::SmoothPath::export_with`] 复用同一份路径中间表示。
8pub trait PathFormatter {
9    /// formatter 生成的目标输出类型。
10    type Output;
11
12    /// 将路径命令格式化为目标输出。
13    fn format(&self, commands: &[PathCommand]) -> Self::Output;
14}
15
16/// SVG path data 输出格式。
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct SvgPathFormat {
19    precision: usize,
20}
21
22impl SvgPathFormat {
23    /// 创建 SVG path data formatter。
24    ///
25    /// 小数位数最多保留 12 位,超过该上限时会自动 clamp。
26    #[must_use]
27    pub fn new(precision: usize) -> Self {
28        Self {
29            precision: bounded_precision(precision),
30        }
31    }
32
33    /// SVG path data 输出支持的最大小数位数。
34    pub const MAX_PRECISION: usize = MAX_FORMAT_PRECISION;
35
36    /// 返回坐标小数位数。
37    #[must_use]
38    pub fn precision(&self) -> usize {
39        self.precision
40    }
41}
42
43impl Default for SvgPathFormat {
44    fn default() -> Self {
45        Self::new(6)
46    }
47}
48
49impl PathFormatter for SvgPathFormat {
50    type Output = String;
51
52    fn format(&self, commands: &[PathCommand]) -> Self::Output {
53        let mut parts = Vec::with_capacity(commands.len());
54        for command in commands {
55            match *command {
56                PathCommand::MoveTo(point) => {
57                    parts.push(format!("M {}", format_point(point, self.precision)));
58                }
59                PathCommand::LineTo(point) => {
60                    parts.push(format!("L {}", format_point(point, self.precision)));
61                }
62                PathCommand::CubicTo { ctrl1, ctrl2, to } => {
63                    parts.push(format!(
64                        "C {} {} {}",
65                        format_point(ctrl1, self.precision),
66                        format_point(ctrl2, self.precision),
67                        format_point(to, self.precision)
68                    ));
69                }
70                PathCommand::Close => parts.push("Z".to_owned()),
71            }
72        }
73        parts.join(" ")
74    }
75}