easyofd_core/graph/tight/method/
cubic_bezier.rs1use crate::xml_element::{XmlElement, XmlElementError, XmlNode};
6
7#[derive(Debug, Clone, PartialEq)]
14pub struct CubicBezier {
15 pub point1: (f64, f64),
17 pub point2: (f64, f64),
19 pub point3: (f64, f64),
21}
22
23impl CubicBezier {
24 #[must_use]
26 pub fn new(x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) -> Self {
27 Self {
28 point1: (x1, y1),
29 point2: (x2, y2),
30 point3: (x3, y3),
31 }
32 }
33
34 #[must_use]
36 pub fn to_abbreviated_string(&self) -> String {
37 format!(
38 "B {} {} {} {} {} {}",
39 self.point1.0,
40 self.point1.1,
41 self.point2.0,
42 self.point2.1,
43 self.point3.0,
44 self.point3.1
45 )
46 }
47}
48
49impl std::fmt::Display for CubicBezier {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 f.write_str(&self.to_abbreviated_string())
52 }
53}
54
55impl XmlElement for CubicBezier {
56 fn element_name(&self) -> &'static str {
58 "CubicBezier"
59 }
60
61 fn attributes(&self) -> Vec<(String, String)> {
62 Vec::new()
63 }
64
65 fn write_xml(&self, out: &mut String) {
67 out.push_str("<CubicBezier>");
68 out.push_str(&crate::xml_element::xml_escape(
69 &self.to_abbreviated_string(),
70 ));
71 out.push_str("</CubicBezier>");
72 }
73
74 fn from_xml(node: &XmlNode) -> Result<Self, XmlElementError> {
75 let text = node
76 .text
77 .as_deref()
78 .ok_or_else(|| XmlElementError("CubicBezier 缺少文本内容".to_string()))?;
79 let parts: Vec<&str> = text.split_whitespace().collect();
80 if parts.len() < 7 || parts[0] != "B" {
81 return Err(XmlElementError(format!("CubicBezier 格式错误: {text}")));
82 }
83 let x1: f64 = parts[1]
84 .parse()
85 .map_err(|e| XmlElementError(format!("解析 CubicBezier.x1 失败: {e}")))?;
86 let y1: f64 = parts[2]
87 .parse()
88 .map_err(|e| XmlElementError(format!("解析 CubicBezier.y1 失败: {e}")))?;
89 let x2: f64 = parts[3]
90 .parse()
91 .map_err(|e| XmlElementError(format!("解析 CubicBezier.x2 失败: {e}")))?;
92 let y2: f64 = parts[4]
93 .parse()
94 .map_err(|e| XmlElementError(format!("解析 CubicBezier.y2 失败: {e}")))?;
95 let x3: f64 = parts[5]
96 .parse()
97 .map_err(|e| XmlElementError(format!("解析 CubicBezier.x3 失败: {e}")))?;
98 let y3: f64 = parts[6]
99 .parse()
100 .map_err(|e| XmlElementError(format!("解析 CubicBezier.y3 失败: {e}")))?;
101 Ok(Self::new(x1, y1, x2, y2, x3, y3))
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use crate::xml_parse::parse_xml_to_nodes;
109
110 #[test]
111 fn cubic_bezier_new() {
112 let cb = CubicBezier::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
113 assert_eq!(cb.point1, (1.0, 2.0));
114 assert_eq!(cb.point2, (3.0, 4.0));
115 assert_eq!(cb.point3, (5.0, 6.0));
116 }
117
118 #[test]
119 fn cubic_bezier_to_string() {
120 let cb = CubicBezier::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
121 assert_eq!(cb.to_abbreviated_string(), "B 1 2 3 4 5 6");
122 }
123
124 #[test]
125 fn cubic_bezier_display() {
126 let cb = CubicBezier::new(0.0, 0.0, 10.0, 10.0, 20.0, 0.0);
127 let s = format!("{cb}");
128 assert!(s.starts_with("B 0 0 10 10 20 0"));
129 }
130
131 #[test]
132 fn cubic_bezier_clone_eq() {
133 let cb = CubicBezier::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
134 let cb2 = cb.clone();
135 assert_eq!(cb, cb2);
136 }
137
138 #[test]
139 fn test_xml_element_name() {
140 let cb = CubicBezier::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
141 assert_eq!(cb.element_name(), "CubicBezier");
142 }
143
144 #[test]
145 fn test_xml_element_roundtrip() {
146 let cb = CubicBezier::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
147 let xml = cb.to_xml();
148 assert!(xml.contains("<CubicBezier>"));
149 assert!(xml.contains("B 1 2 3 4 5 6"));
150 let node = parse_xml_to_nodes(&xml).unwrap();
151 let cb2 = CubicBezier::from_xml(&node).unwrap();
152 assert_eq!(cb, cb2);
153 }
154}