Skip to main content

easyofd_core/graph/tight/method/
line.rs

1//! 线段路径方法。
2//!
3//! 对应 Java: org.ofdrw.core.graph.tight.method.Line
4
5use crate::xml_element::{XmlElement, XmlElementError, XmlNode};
6
7/// 线段路径方法。
8///
9/// 图 51 线段结构。从当前点绘制直线到指定结束点。
10///
11/// 对应 Java: org.ofdrw.core.graph.tight.method.Line
12#[derive(Debug, Clone, PartialEq)]
13pub struct Line {
14    /// 线段的结束点 (x, y)。
15    pub point: (f64, f64),
16}
17
18impl Line {
19    /// 创建线段。
20    #[must_use]
21    pub fn new(x: f64, y: f64) -> Self {
22        Self { point: (x, y) }
23    }
24
25    /// 序列化为缩写数据字符串(L 命令格式)。
26    #[must_use]
27    pub fn to_abbreviated_string(&self) -> String {
28        format!("L {} {}", self.point.0, self.point.1)
29    }
30}
31
32impl std::fmt::Display for Line {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        f.write_str(&self.to_abbreviated_string())
35    }
36}
37
38impl XmlElement for Line {
39    /// 对应 Java: Line 元素名 "Line"。
40    fn element_name(&self) -> &'static str {
41        "Line"
42    }
43
44    fn attributes(&self) -> Vec<(String, String)> {
45        Vec::new()
46    }
47
48    /// 覆写 write_xml:文本内容为 L 命令格式。
49    fn write_xml(&self, out: &mut String) {
50        out.push_str("<Line>");
51        out.push_str(&crate::xml_element::xml_escape(
52            &self.to_abbreviated_string(),
53        ));
54        out.push_str("</Line>");
55    }
56
57    fn from_xml(node: &XmlNode) -> Result<Self, XmlElementError> {
58        let text = node
59            .text
60            .as_deref()
61            .ok_or_else(|| XmlElementError("Line 缺少文本内容".to_string()))?;
62        let parts: Vec<&str> = text.split_whitespace().collect();
63        if parts.len() < 3 || parts[0] != "L" {
64            return Err(XmlElementError(format!("Line 格式错误: {text}")));
65        }
66        let x: f64 = parts[1]
67            .parse()
68            .map_err(|e| XmlElementError(format!("解析 Line.x 失败: {e}")))?;
69        let y: f64 = parts[2]
70            .parse()
71            .map_err(|e| XmlElementError(format!("解析 Line.y 失败: {e}")))?;
72        Ok(Self::new(x, y))
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::xml_parse::parse_xml_to_nodes;
80
81    #[test]
82    fn line_new() {
83        let l = Line::new(10.0, 20.0);
84        assert_eq!(l.point, (10.0, 20.0));
85    }
86
87    #[test]
88    fn line_to_string() {
89        let l = Line::new(100.5, 50.3);
90        assert_eq!(l.to_abbreviated_string(), "L 100.5 50.3");
91    }
92
93    #[test]
94    fn line_display() {
95        let l = Line::new(1.0, 2.0);
96        assert_eq!(format!("{l}"), "L 1 2");
97    }
98
99    #[test]
100    fn line_clone_eq() {
101        let l = Line::new(3.0, 4.0);
102        let l2 = l.clone();
103        assert_eq!(l, l2);
104    }
105
106    #[test]
107    fn test_xml_element_name() {
108        let l = Line::new(1.0, 2.0);
109        assert_eq!(l.element_name(), "Line");
110    }
111
112    #[test]
113    fn test_xml_element_roundtrip() {
114        let l = Line::new(10.5, 20.5);
115        let xml = l.to_xml();
116        assert!(xml.contains("<Line>"));
117        assert!(xml.contains("L 10.5 20.5"));
118        let node = parse_xml_to_nodes(&xml).unwrap();
119        let l2 = Line::from_xml(&node).unwrap();
120        assert!((l.point.0 - l2.point.0).abs() < f64::EPSILON);
121        assert!((l.point.1 - l2.point.1).abs() < f64::EPSILON);
122    }
123}