chartml_core/shapes/
area.rs1use super::line::{CurveType, LineGenerator};
4
5pub struct AreaGenerator {
7 curve: CurveType,
8}
9
10impl AreaGenerator {
11 pub fn new() -> Self {
13 Self {
14 curve: CurveType::Linear,
15 }
16 }
17
18 pub fn curve(mut self, curve: CurveType) -> Self {
20 self.curve = curve;
21 self
22 }
23
24 pub fn generate_line(&self, points: &[(f64, f64, f64)]) -> String {
29 if points.is_empty() {
30 return String::new();
31 }
32 let top_points: Vec<(f64, f64)> = points.iter().map(|&(x, _y0, y1)| (x, y1)).collect();
33 let line_gen = LineGenerator::new().curve(self.curve);
34 line_gen.generate(&top_points)
35 }
36
37 pub fn generate(&self, points: &[(f64, f64, f64)]) -> String {
46 if points.is_empty() {
47 return String::new();
48 }
49
50 let top_points: Vec<(f64, f64)> = points.iter().map(|&(x, _y0, y1)| (x, y1)).collect();
52 let bottom_points: Vec<(f64, f64)> = points.iter().rev().map(|&(x, y0, _y1)| (x, y0)).collect();
54
55 let line_gen = LineGenerator::new().curve(self.curve);
56 let top_path = line_gen.generate(&top_points);
57 let bottom_path = line_gen.generate(&bottom_points);
58
59 let bottom_continuation = if bottom_path.len() > 1 {
61 format!("L{}", &bottom_path[1..])
62 } else {
63 String::new()
64 };
65
66 format!("{}{}Z", top_path, bottom_continuation)
67 }
68}
69
70impl Default for AreaGenerator {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn area_basic() {
82 let gen = AreaGenerator::new();
83 let path = gen.generate(&[
84 (0.0, 100.0, 10.0),
85 (50.0, 100.0, 20.0),
86 (100.0, 100.0, 5.0),
87 ]);
88 assert!(path.starts_with("M"), "Path should start with M, got: {}", path);
89 assert!(path.contains("L"), "Path should contain L commands, got: {}", path);
90 assert!(path.ends_with("Z"), "Path should end with Z, got: {}", path);
91 }
92
93 #[test]
94 fn area_empty() {
95 let gen = AreaGenerator::new();
96 let path = gen.generate(&[]);
97 assert_eq!(path, "");
98 }
99}