1use crate::style::{LinePattern, LineStyle};
4use embedded_graphics::prelude::*;
5
6#[derive(Debug, Clone)]
8pub struct GridStyle<C: PixelColor> {
9 pub major: MajorGridStyle<C>,
11 pub minor: MinorGridStyle<C>,
13 pub visibility: GridVisibility,
15 pub opacity: f32,
17}
18
19#[derive(Debug, Clone)]
21pub struct MajorGridStyle<C: PixelColor> {
22 pub line: GridLineStyle<C>,
24 pub enabled: bool,
26 pub spacing: f32,
28}
29
30#[derive(Debug, Clone)]
32pub struct MinorGridStyle<C: PixelColor> {
33 pub line: GridLineStyle<C>,
35 pub enabled: bool,
37 pub spacing: f32,
39 pub subdivisions: u32,
41}
42
43#[derive(Debug, Clone)]
45pub struct GridLineStyle<C: PixelColor> {
46 pub line_style: LineStyle<C>,
48 pub anti_alias: bool,
50 pub opacity: f32,
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub struct GridVisibility {
57 pub horizontal: bool,
59 pub vertical: bool,
61 pub major: bool,
63 pub minor: bool,
65}
66
67impl<C: PixelColor> GridStyle<C> {
68 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 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 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 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 pub fn with_opacity(mut self, opacity: f32) -> Self {
186 self.opacity = opacity.clamp(0.0, 1.0);
187 self
188 }
189
190 pub fn with_visibility(mut self, visibility: GridVisibility) -> Self {
192 self.visibility = visibility;
193 self
194 }
195
196 pub fn with_major_enabled(mut self, enabled: bool) -> Self {
198 self.major.enabled = enabled;
199 self
200 }
201
202 pub fn with_minor_enabled(mut self, enabled: bool) -> Self {
204 self.minor.enabled = enabled;
205 self
206 }
207
208 pub fn with_major_spacing(mut self, spacing: f32) -> Self {
210 self.major.spacing = spacing;
211 self
212 }
213
214 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 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 pub fn with_color(mut self, color: C) -> Self {
246 self.line.line_style.color = color;
247 self
248 }
249
250 pub fn with_width(mut self, width: u32) -> Self {
252 self.line.line_style.width = width;
253 self
254 }
255
256 pub fn with_pattern(mut self, pattern: LinePattern) -> Self {
258 self.line.line_style.pattern = pattern;
259 self
260 }
261
262 pub fn with_opacity(mut self, opacity: f32) -> Self {
264 self.line.opacity = opacity.clamp(0.0, 1.0);
265 self
266 }
267
268 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 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 pub fn with_color(mut self, color: C) -> Self {
302 self.line.line_style.color = color;
303 self
304 }
305
306 pub fn with_width(mut self, width: u32) -> Self {
308 self.line.line_style.width = width;
309 self
310 }
311
312 pub fn with_pattern(mut self, pattern: LinePattern) -> Self {
314 self.line.line_style.pattern = pattern;
315 self
316 }
317
318 pub fn with_opacity(mut self, opacity: f32) -> Self {
320 self.line.opacity = opacity.clamp(0.0, 1.0);
321 self
322 }
323
324 pub fn with_spacing(mut self, spacing: f32) -> Self {
326 self.spacing = spacing;
327 self
328 }
329
330 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 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 pub fn with_anti_alias(mut self, anti_alias: bool) -> Self {
359 self.anti_alias = anti_alias;
360 self
361 }
362
363 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 pub const fn all() -> Self {
373 Self {
374 horizontal: true,
375 vertical: true,
376 major: true,
377 minor: true,
378 }
379 }
380
381 pub const fn none() -> Self {
383 Self {
384 horizontal: false,
385 vertical: false,
386 major: false,
387 minor: false,
388 }
389 }
390
391 pub const fn major_only() -> Self {
393 Self {
394 horizontal: true,
395 vertical: true,
396 major: true,
397 minor: false,
398 }
399 }
400
401 pub const fn horizontal_only() -> Self {
403 Self {
404 horizontal: true,
405 vertical: false,
406 major: true,
407 minor: true,
408 }
409 }
410
411 pub const fn vertical_only() -> Self {
413 Self {
414 horizontal: false,
415 vertical: true,
416 major: true,
417 minor: true,
418 }
419 }
420
421 pub const fn any_visible(&self) -> bool {
423 (self.horizontal || self.vertical) && (self.major || self.minor)
424 }
425
426 pub const fn major_visible(&self) -> bool {
428 (self.horizontal || self.vertical) && self.major
429 }
430
431 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}