embedded_charts/grid/
style.rs

1//! Grid styling configuration.
2
3use crate::style::{LinePattern, LineStyle};
4use embedded_graphics::prelude::*;
5
6/// Overall grid style configuration
7#[derive(Debug, Clone)]
8pub struct GridStyle<C: PixelColor> {
9    /// Major grid line style
10    pub major: MajorGridStyle<C>,
11    /// Minor grid line style
12    pub minor: MinorGridStyle<C>,
13    /// Grid visibility settings
14    pub visibility: GridVisibility,
15    /// Grid opacity (0.0 = transparent, 1.0 = opaque)
16    pub opacity: f32,
17}
18
19/// Style configuration for major grid lines
20#[derive(Debug, Clone)]
21pub struct MajorGridStyle<C: PixelColor> {
22    /// Line style for major grid lines
23    pub line: GridLineStyle<C>,
24    /// Whether major grid lines are enabled
25    pub enabled: bool,
26    /// Spacing between major grid lines (in data units)
27    pub spacing: f32,
28}
29
30/// Style configuration for minor grid lines
31#[derive(Debug, Clone)]
32pub struct MinorGridStyle<C: PixelColor> {
33    /// Line style for minor grid lines
34    pub line: GridLineStyle<C>,
35    /// Whether minor grid lines are enabled
36    pub enabled: bool,
37    /// Spacing between minor grid lines (in data units)
38    pub spacing: f32,
39    /// Number of minor divisions between major grid lines
40    pub subdivisions: u32,
41}
42
43/// Line style specifically for grid lines
44#[derive(Debug, Clone)]
45pub struct GridLineStyle<C: PixelColor> {
46    /// Base line style
47    pub line_style: LineStyle<C>,
48    /// Whether to use anti-aliasing (if supported)
49    pub anti_alias: bool,
50    /// Line opacity (0.0 = transparent, 1.0 = opaque)
51    pub opacity: f32,
52}
53
54/// Grid visibility configuration
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub struct GridVisibility {
57    /// Whether horizontal grid lines are visible
58    pub horizontal: bool,
59    /// Whether vertical grid lines are visible
60    pub vertical: bool,
61    /// Whether major grid lines are visible
62    pub major: bool,
63    /// Whether minor grid lines are visible
64    pub minor: bool,
65}
66
67impl<C: PixelColor> GridStyle<C> {
68    /// Create a new grid style with default settings
69    pub fn new() -> Self
70    where
71        C: From<embedded_graphics::pixelcolor::Rgb565>,
72    {
73        Self {
74            major: MajorGridStyle::default(),
75            minor: MinorGridStyle::default(),
76            visibility: GridVisibility::default(),
77            opacity: 1.0,
78        }
79    }
80
81    /// Create a professional grid style
82    pub fn professional() -> Self
83    where
84        C: From<embedded_graphics::pixelcolor::Rgb565>,
85    {
86        let major_color = embedded_graphics::pixelcolor::Rgb565::new(20, 40, 20).into();
87        let minor_color = embedded_graphics::pixelcolor::Rgb565::new(10, 20, 10).into();
88
89        Self {
90            major: MajorGridStyle {
91                line: GridLineStyle {
92                    line_style: LineStyle::solid(major_color).width(1),
93                    anti_alias: true,
94                    opacity: 0.8,
95                },
96                enabled: true,
97                spacing: 1.0,
98            },
99            minor: MinorGridStyle {
100                line: GridLineStyle {
101                    line_style: LineStyle::solid(minor_color).width(1),
102                    anti_alias: true,
103                    opacity: 0.4,
104                },
105                enabled: true,
106                spacing: 0.2,
107                subdivisions: 5,
108            },
109            visibility: GridVisibility::all(),
110            opacity: 1.0,
111        }
112    }
113
114    /// Create a minimal grid style (major lines only)
115    pub fn minimal() -> Self
116    where
117        C: From<embedded_graphics::pixelcolor::Rgb565>,
118    {
119        let grid_color = embedded_graphics::pixelcolor::Rgb565::new(15, 30, 15).into();
120
121        Self {
122            major: MajorGridStyle {
123                line: GridLineStyle {
124                    line_style: LineStyle::solid(grid_color).width(1),
125                    anti_alias: false,
126                    opacity: 0.6,
127                },
128                enabled: true,
129                spacing: 1.0,
130            },
131            minor: MinorGridStyle {
132                line: GridLineStyle {
133                    line_style: LineStyle::solid(grid_color).width(1),
134                    anti_alias: false,
135                    opacity: 0.3,
136                },
137                enabled: false,
138                spacing: 0.2,
139                subdivisions: 5,
140            },
141            visibility: GridVisibility {
142                horizontal: true,
143                vertical: true,
144                major: true,
145                minor: false,
146            },
147            opacity: 1.0,
148        }
149    }
150
151    /// Create a dashed grid style
152    pub fn dashed() -> Self
153    where
154        C: From<embedded_graphics::pixelcolor::Rgb565>,
155    {
156        let major_color = embedded_graphics::pixelcolor::Rgb565::new(25, 50, 25).into();
157        let minor_color = embedded_graphics::pixelcolor::Rgb565::new(12, 25, 12).into();
158
159        Self {
160            major: MajorGridStyle {
161                line: GridLineStyle {
162                    line_style: LineStyle::dashed(major_color).width(1),
163                    anti_alias: true,
164                    opacity: 0.7,
165                },
166                enabled: true,
167                spacing: 1.0,
168            },
169            minor: MinorGridStyle {
170                line: GridLineStyle {
171                    line_style: LineStyle::dotted(minor_color).width(1),
172                    anti_alias: true,
173                    opacity: 0.4,
174                },
175                enabled: true,
176                spacing: 0.25,
177                subdivisions: 4,
178            },
179            visibility: GridVisibility::all(),
180            opacity: 1.0,
181        }
182    }
183
184    /// Set the overall grid opacity
185    pub fn with_opacity(mut self, opacity: f32) -> Self {
186        self.opacity = opacity.clamp(0.0, 1.0);
187        self
188    }
189
190    /// Set grid visibility
191    pub fn with_visibility(mut self, visibility: GridVisibility) -> Self {
192        self.visibility = visibility;
193        self
194    }
195
196    /// Enable or disable major grid lines
197    pub fn with_major_enabled(mut self, enabled: bool) -> Self {
198        self.major.enabled = enabled;
199        self
200    }
201
202    /// Enable or disable minor grid lines
203    pub fn with_minor_enabled(mut self, enabled: bool) -> Self {
204        self.minor.enabled = enabled;
205        self
206    }
207
208    /// Set major grid spacing
209    pub fn with_major_spacing(mut self, spacing: f32) -> Self {
210        self.major.spacing = spacing;
211        self
212    }
213
214    /// Set minor grid spacing
215    pub fn with_minor_spacing(mut self, spacing: f32) -> Self {
216        self.minor.spacing = spacing;
217        self
218    }
219}
220
221impl<C: PixelColor> Default for GridStyle<C>
222where
223    C: From<embedded_graphics::pixelcolor::Rgb565>,
224{
225    fn default() -> Self {
226        Self::new()
227    }
228}
229
230impl<C: PixelColor> MajorGridStyle<C> {
231    /// Create a new major grid style
232    pub fn new(line_style: LineStyle<C>) -> Self {
233        Self {
234            line: GridLineStyle {
235                line_style,
236                anti_alias: false,
237                opacity: 1.0,
238            },
239            enabled: true,
240            spacing: 1.0,
241        }
242    }
243
244    /// Set the line color
245    pub fn with_color(mut self, color: C) -> Self {
246        self.line.line_style.color = color;
247        self
248    }
249
250    /// Set the line width
251    pub fn with_width(mut self, width: u32) -> Self {
252        self.line.line_style.width = width;
253        self
254    }
255
256    /// Set the line pattern
257    pub fn with_pattern(mut self, pattern: LinePattern) -> Self {
258        self.line.line_style.pattern = pattern;
259        self
260    }
261
262    /// Set the opacity
263    pub fn with_opacity(mut self, opacity: f32) -> Self {
264        self.line.opacity = opacity.clamp(0.0, 1.0);
265        self
266    }
267
268    /// Set the spacing
269    pub fn with_spacing(mut self, spacing: f32) -> Self {
270        self.spacing = spacing;
271        self
272    }
273}
274
275impl<C: PixelColor> Default for MajorGridStyle<C>
276where
277    C: From<embedded_graphics::pixelcolor::Rgb565>,
278{
279    fn default() -> Self {
280        let color = embedded_graphics::pixelcolor::Rgb565::new(20, 40, 20).into();
281        Self::new(LineStyle::solid(color))
282    }
283}
284
285impl<C: PixelColor> MinorGridStyle<C> {
286    /// Create a new minor grid style
287    pub fn new(line_style: LineStyle<C>) -> Self {
288        Self {
289            line: GridLineStyle {
290                line_style,
291                anti_alias: false,
292                opacity: 0.5,
293            },
294            enabled: true,
295            spacing: 0.2,
296            subdivisions: 5,
297        }
298    }
299
300    /// Set the line color
301    pub fn with_color(mut self, color: C) -> Self {
302        self.line.line_style.color = color;
303        self
304    }
305
306    /// Set the line width
307    pub fn with_width(mut self, width: u32) -> Self {
308        self.line.line_style.width = width;
309        self
310    }
311
312    /// Set the line pattern
313    pub fn with_pattern(mut self, pattern: LinePattern) -> Self {
314        self.line.line_style.pattern = pattern;
315        self
316    }
317
318    /// Set the opacity
319    pub fn with_opacity(mut self, opacity: f32) -> Self {
320        self.line.opacity = opacity.clamp(0.0, 1.0);
321        self
322    }
323
324    /// Set the spacing
325    pub fn with_spacing(mut self, spacing: f32) -> Self {
326        self.spacing = spacing;
327        self
328    }
329
330    /// Set the number of subdivisions
331    pub fn with_subdivisions(mut self, subdivisions: u32) -> Self {
332        self.subdivisions = subdivisions;
333        self
334    }
335}
336
337impl<C: PixelColor> Default for MinorGridStyle<C>
338where
339    C: From<embedded_graphics::pixelcolor::Rgb565>,
340{
341    fn default() -> Self {
342        let color = embedded_graphics::pixelcolor::Rgb565::new(10, 20, 10).into();
343        Self::new(LineStyle::solid(color))
344    }
345}
346
347impl<C: PixelColor> GridLineStyle<C> {
348    /// Create a new grid line style
349    pub fn new(line_style: LineStyle<C>) -> Self {
350        Self {
351            line_style,
352            anti_alias: false,
353            opacity: 1.0,
354        }
355    }
356
357    /// Enable or disable anti-aliasing
358    pub fn with_anti_alias(mut self, anti_alias: bool) -> Self {
359        self.anti_alias = anti_alias;
360        self
361    }
362
363    /// Set the opacity
364    pub fn with_opacity(mut self, opacity: f32) -> Self {
365        self.opacity = opacity.clamp(0.0, 1.0);
366        self
367    }
368}
369
370impl GridVisibility {
371    /// Create visibility settings with all grids enabled
372    pub const fn all() -> Self {
373        Self {
374            horizontal: true,
375            vertical: true,
376            major: true,
377            minor: true,
378        }
379    }
380
381    /// Create visibility settings with no grids enabled
382    pub const fn none() -> Self {
383        Self {
384            horizontal: false,
385            vertical: false,
386            major: false,
387            minor: false,
388        }
389    }
390
391    /// Create visibility settings with only major grids enabled
392    pub const fn major_only() -> Self {
393        Self {
394            horizontal: true,
395            vertical: true,
396            major: true,
397            minor: false,
398        }
399    }
400
401    /// Create visibility settings with only horizontal grids enabled
402    pub const fn horizontal_only() -> Self {
403        Self {
404            horizontal: true,
405            vertical: false,
406            major: true,
407            minor: true,
408        }
409    }
410
411    /// Create visibility settings with only vertical grids enabled
412    pub const fn vertical_only() -> Self {
413        Self {
414            horizontal: false,
415            vertical: true,
416            major: true,
417            minor: true,
418        }
419    }
420
421    /// Check if any grid lines are visible
422    pub const fn any_visible(&self) -> bool {
423        (self.horizontal || self.vertical) && (self.major || self.minor)
424    }
425
426    /// Check if major grid lines are visible
427    pub const fn major_visible(&self) -> bool {
428        (self.horizontal || self.vertical) && self.major
429    }
430
431    /// Check if minor grid lines are visible
432    pub const fn minor_visible(&self) -> bool {
433        (self.horizontal || self.vertical) && self.minor
434    }
435}
436
437impl Default for GridVisibility {
438    fn default() -> Self {
439        Self::major_only()
440    }
441}
442
443#[cfg(test)]
444mod tests {
445    use super::*;
446    use embedded_graphics::pixelcolor::Rgb565;
447
448    #[test]
449    fn test_grid_style_creation() {
450        let style: GridStyle<Rgb565> = GridStyle::new();
451        assert!(style.major.enabled);
452        assert_eq!(style.opacity, 1.0);
453    }
454
455    #[test]
456    fn test_grid_style_professional() {
457        let style: GridStyle<Rgb565> = GridStyle::professional();
458        assert!(style.major.enabled);
459        assert!(style.minor.enabled);
460        assert!(style.visibility.any_visible());
461    }
462
463    #[test]
464    fn test_grid_style_minimal() {
465        let style: GridStyle<Rgb565> = GridStyle::minimal();
466        assert!(style.major.enabled);
467        assert!(!style.minor.enabled);
468    }
469
470    #[test]
471    fn test_grid_visibility() {
472        let vis = GridVisibility::all();
473        assert!(vis.any_visible());
474        assert!(vis.major_visible());
475        assert!(vis.minor_visible());
476
477        let vis = GridVisibility::none();
478        assert!(!vis.any_visible());
479
480        let vis = GridVisibility::major_only();
481        assert!(vis.major_visible());
482        assert!(!vis.minor_visible());
483    }
484
485    #[test]
486    fn test_major_grid_style_builder() {
487        let style: MajorGridStyle<Rgb565> = MajorGridStyle::default()
488            .with_width(2)
489            .with_opacity(0.8)
490            .with_spacing(2.0);
491
492        assert_eq!(style.line.line_style.width, 2);
493        assert_eq!(style.line.opacity, 0.8);
494        assert_eq!(style.spacing, 2.0);
495    }
496
497    #[test]
498    fn test_minor_grid_style_builder() {
499        let style: MinorGridStyle<Rgb565> = MinorGridStyle::default()
500            .with_subdivisions(10)
501            .with_spacing(0.1);
502
503        assert_eq!(style.subdivisions, 10);
504        assert_eq!(style.spacing, 0.1);
505    }
506}