embedded_charts/style/
line.rs

1//! Line styling utilities for charts.
2
3use embedded_graphics::prelude::*;
4
5/// Line style configuration
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct LineStyle<C: PixelColor> {
8    /// Color of the line
9    pub color: C,
10    /// Width of the line in pixels
11    pub width: u32,
12    /// Line pattern
13    pub pattern: LinePattern,
14    /// Line cap style
15    pub cap: LineCap,
16    /// Line join style
17    pub join: LineJoin,
18}
19
20/// Line pattern types
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum LinePattern {
23    /// Solid line
24    Solid,
25    /// Dashed line
26    Dashed,
27    /// Dotted line
28    Dotted,
29    /// Dash-dot pattern
30    DashDot,
31    /// Custom pattern (not implemented in basic version)
32    Custom,
33}
34
35/// Line cap styles
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum LineCap {
38    /// Flat cap
39    Butt,
40    /// Rounded cap
41    Round,
42    /// Square cap
43    Square,
44}
45
46/// Line join styles
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum LineJoin {
49    /// Miter join
50    Miter,
51    /// Round join
52    Round,
53    /// Bevel join
54    Bevel,
55}
56
57impl<C: PixelColor> LineStyle<C> {
58    /// Create a new solid line style
59    pub const fn solid(color: C) -> Self {
60        Self {
61            color,
62            width: 1,
63            pattern: LinePattern::Solid,
64            cap: LineCap::Butt,
65            join: LineJoin::Miter,
66        }
67    }
68
69    /// Create a new dashed line style
70    pub const fn dashed(color: C) -> Self {
71        Self {
72            color,
73            width: 1,
74            pattern: LinePattern::Dashed,
75            cap: LineCap::Butt,
76            join: LineJoin::Miter,
77        }
78    }
79
80    /// Create a new dotted line style
81    pub const fn dotted(color: C) -> Self {
82        Self {
83            color,
84            width: 1,
85            pattern: LinePattern::Dotted,
86            cap: LineCap::Round,
87            join: LineJoin::Round,
88        }
89    }
90
91    /// Set the line width
92    pub const fn width(mut self, width: u32) -> Self {
93        self.width = width;
94        self
95    }
96
97    /// Set the line color
98    pub const fn color(mut self, color: C) -> Self {
99        self.color = color;
100        self
101    }
102
103    /// Set the line pattern
104    pub const fn pattern(mut self, pattern: LinePattern) -> Self {
105        self.pattern = pattern;
106        self
107    }
108
109    /// Set the line cap style
110    pub const fn cap(mut self, cap: LineCap) -> Self {
111        self.cap = cap;
112        self
113    }
114
115    /// Set the line join style
116    pub const fn join(mut self, join: LineJoin) -> Self {
117        self.join = join;
118        self
119    }
120}
121
122impl<C: PixelColor> Default for LineStyle<C>
123where
124    C: From<embedded_graphics::pixelcolor::Rgb565>,
125{
126    fn default() -> Self {
127        Self::solid(embedded_graphics::pixelcolor::Rgb565::WHITE.into())
128    }
129}
130
131/// Border style for chart elements
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133pub struct BorderStyle<C: PixelColor> {
134    /// Line style for the border
135    pub line: LineStyle<C>,
136    /// Border radius (for rounded corners)
137    pub radius: u32,
138    /// Whether the border is visible
139    pub visible: bool,
140}
141
142impl<C: PixelColor> BorderStyle<C> {
143    /// Create a new border style
144    pub const fn new(line: LineStyle<C>) -> Self {
145        Self {
146            line,
147            radius: 0,
148            visible: true,
149        }
150    }
151
152    /// Create a border with rounded corners
153    pub const fn rounded(line: LineStyle<C>, radius: u32) -> Self {
154        Self {
155            line,
156            radius,
157            visible: true,
158        }
159    }
160
161    /// Set the border radius
162    pub const fn radius(mut self, radius: u32) -> Self {
163        self.radius = radius;
164        self
165    }
166
167    /// Set border visibility
168    pub const fn visible(mut self, visible: bool) -> Self {
169        self.visible = visible;
170        self
171    }
172}
173
174impl<C: PixelColor> Default for BorderStyle<C>
175where
176    C: From<embedded_graphics::pixelcolor::Rgb565>,
177{
178    fn default() -> Self {
179        Self::new(LineStyle::default())
180    }
181}
182
183/// Stroke style for drawing operations
184#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185pub struct StrokeStyle<C: PixelColor> {
186    /// Color of the stroke
187    pub color: C,
188    /// Width of the stroke
189    pub width: u32,
190}
191
192impl<C: PixelColor> StrokeStyle<C> {
193    /// Create a new stroke style
194    pub const fn new(color: C, width: u32) -> Self {
195        Self { color, width }
196    }
197}
198
199impl<C: PixelColor> From<LineStyle<C>> for StrokeStyle<C> {
200    fn from(line_style: LineStyle<C>) -> Self {
201        Self {
202            color: line_style.color,
203            width: line_style.width,
204        }
205    }
206}
207
208use super::gradient::{LinearGradient, PatternFill, RadialGradient, MAX_GRADIENT_STOPS};
209
210/// Fill style for drawing operations
211#[derive(Debug, Clone)]
212pub struct FillStyle<C: PixelColor> {
213    /// Fill pattern
214    pub pattern: FillPattern<C>,
215}
216
217/// Fill pattern types
218#[derive(Debug, Clone)]
219pub enum FillPattern<C: PixelColor> {
220    /// Solid fill with a single color
221    Solid(C),
222    /// Linear gradient fill
223    LinearGradient(LinearGradient<C, MAX_GRADIENT_STOPS>),
224    /// Radial gradient fill
225    RadialGradient(RadialGradient<C, MAX_GRADIENT_STOPS>),
226    /// Pattern fill
227    Pattern(PatternFill<C>),
228}
229
230impl<C: PixelColor> FillStyle<C> {
231    /// Create a solid fill style
232    pub const fn solid(color: C) -> Self {
233        Self {
234            pattern: FillPattern::Solid(color),
235        }
236    }
237
238    /// Create a linear gradient fill style
239    pub fn linear_gradient(gradient: LinearGradient<C, MAX_GRADIENT_STOPS>) -> Self {
240        Self {
241            pattern: FillPattern::LinearGradient(gradient),
242        }
243    }
244
245    /// Create a radial gradient fill style
246    pub fn radial_gradient(gradient: RadialGradient<C, MAX_GRADIENT_STOPS>) -> Self {
247        Self {
248            pattern: FillPattern::RadialGradient(gradient),
249        }
250    }
251
252    /// Create a pattern fill style
253    pub const fn pattern(pattern: PatternFill<C>) -> Self {
254        Self {
255            pattern: FillPattern::Pattern(pattern),
256        }
257    }
258
259    /// Get the solid color if this is a solid fill
260    pub fn solid_color(&self) -> Option<C> {
261        match &self.pattern {
262            FillPattern::Solid(color) => Some(*color),
263            _ => None,
264        }
265    }
266}
267
268impl<C: PixelColor> Default for FillStyle<C>
269where
270    C: From<embedded_graphics::pixelcolor::Rgb565>,
271{
272    fn default() -> Self {
273        Self::solid(embedded_graphics::pixelcolor::Rgb565::WHITE.into())
274    }
275}
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280    use embedded_graphics::pixelcolor::Rgb565;
281
282    #[test]
283    fn test_line_style_creation() {
284        let style = LineStyle::solid(Rgb565::RED);
285        assert_eq!(style.color, Rgb565::RED);
286        assert_eq!(style.width, 1);
287        assert_eq!(style.pattern, LinePattern::Solid);
288    }
289
290    #[test]
291    fn test_line_style_builder() {
292        let style = LineStyle::solid(Rgb565::BLUE)
293            .width(3)
294            .pattern(LinePattern::Dashed)
295            .cap(LineCap::Round);
296
297        assert_eq!(style.color, Rgb565::BLUE);
298        assert_eq!(style.width, 3);
299        assert_eq!(style.pattern, LinePattern::Dashed);
300        assert_eq!(style.cap, LineCap::Round);
301    }
302
303    #[test]
304    fn test_border_style() {
305        let line = LineStyle::solid(Rgb565::BLACK);
306        let border = BorderStyle::rounded(line, 5);
307
308        assert_eq!(border.radius, 5);
309        assert!(border.visible);
310        assert_eq!(border.line.color, Rgb565::BLACK);
311    }
312
313    #[test]
314    fn test_stroke_style_from_line_style() {
315        let line_style = LineStyle::solid(Rgb565::GREEN).width(2);
316        let stroke_style: StrokeStyle<Rgb565> = line_style.into();
317
318        assert_eq!(stroke_style.color, Rgb565::GREEN);
319        assert_eq!(stroke_style.width, 2);
320    }
321
322    #[test]
323    fn test_fill_style() {
324        let fill = FillStyle::solid(Rgb565::YELLOW);
325        assert_eq!(fill.solid_color(), Some(Rgb565::YELLOW));
326        assert!(matches!(fill.pattern, FillPattern::Solid(_)));
327    }
328}