1#[cfg(all(feature = "no_std", not(feature = "std")))]
4extern crate alloc;
5
6#[cfg(all(feature = "no_std", not(feature = "std")))]
7use alloc::boxed::Box;
8
9#[cfg(not(all(feature = "no_std", not(feature = "std"))))]
10use std::boxed::Box;
11
12use crate::grid::{
13 style::{GridStyle, GridVisibility, MajorGridStyle, MinorGridStyle},
14 traits::{Grid, GridOrientation},
15 types::{CustomGrid, GridSpacing, LinearGrid, TickBasedGrid},
16 GridContainer, GridSystem,
17};
18use embedded_graphics::prelude::*;
19
20#[derive(Debug)]
22pub struct GridBuilder<C: PixelColor> {
23 horizontal_grid: Option<GridContainer<C>>,
24 vertical_grid: Option<GridContainer<C>>,
25 style: GridStyle<C>,
26 enabled: bool,
27}
28
29impl<C: PixelColor + 'static> GridBuilder<C>
30where
31 C: From<embedded_graphics::pixelcolor::Rgb565>,
32{
33 pub fn new() -> Self {
35 Self {
36 horizontal_grid: None,
37 vertical_grid: None,
38 style: GridStyle::default(),
39 enabled: true,
40 }
41 }
42
43 pub fn horizontal_linear(mut self, spacing: GridSpacing) -> Self {
45 self.horizontal_grid = Some(GridContainer::Linear(LinearGrid::horizontal(spacing)));
46 self
47 }
48
49 pub fn vertical_linear(mut self, spacing: GridSpacing) -> Self {
51 self.vertical_grid = Some(GridContainer::Linear(LinearGrid::vertical(spacing)));
52 self
53 }
54
55 pub fn horizontal_tick_based_f32(mut self) -> Self {
57 self.horizontal_grid = Some(GridContainer::TickBasedF32(
58 TickBasedGrid::<f32, C>::horizontal(),
59 ));
60 self
61 }
62
63 pub fn vertical_tick_based_f32(mut self) -> Self {
65 self.vertical_grid = Some(GridContainer::TickBasedF32(
66 TickBasedGrid::<f32, C>::vertical(),
67 ));
68 self
69 }
70
71 pub fn horizontal_tick_based_i32(mut self) -> Self {
73 self.horizontal_grid = Some(GridContainer::TickBasedI32(
74 TickBasedGrid::<i32, C>::horizontal(),
75 ));
76 self
77 }
78
79 pub fn vertical_tick_based_i32(mut self) -> Self {
81 self.vertical_grid = Some(GridContainer::TickBasedI32(
82 TickBasedGrid::<i32, C>::vertical(),
83 ));
84 self
85 }
86
87 pub fn horizontal_custom(mut self, positions: &[i32]) -> Self {
89 let mut grid = CustomGrid::horizontal();
90 grid.add_lines(positions);
91 self.horizontal_grid = Some(GridContainer::Custom(Box::new(grid)));
92 self
93 }
94
95 pub fn vertical_custom(mut self, positions: &[i32]) -> Self {
97 let mut grid = CustomGrid::vertical();
98 grid.add_lines(positions);
99 self.vertical_grid = Some(GridContainer::Custom(Box::new(grid)));
100 self
101 }
102
103 pub fn style(mut self, style: GridStyle<C>) -> Self {
105 self.style = style;
106 self
107 }
108
109 pub fn professional(mut self) -> Self {
111 self.style = GridStyle::professional();
112 self
113 }
114
115 pub fn minimal(mut self) -> Self {
117 self.style = GridStyle::minimal();
118 self
119 }
120
121 pub fn dashed(mut self) -> Self {
123 self.style = GridStyle::dashed();
124 self
125 }
126
127 pub fn visibility(mut self, visibility: GridVisibility) -> Self {
129 self.style.visibility = visibility;
130 self
131 }
132
133 pub fn enabled(mut self, enabled: bool) -> Self {
135 self.enabled = enabled;
136 self
137 }
138
139 pub fn opacity(mut self, opacity: f32) -> Self {
141 self.style.opacity = opacity.clamp(0.0, 1.0);
142 self
143 }
144
145 pub fn major_grid(mut self, style: MajorGridStyle<C>) -> Self {
147 self.style.major = style;
148 self
149 }
150
151 pub fn minor_grid(mut self, style: MinorGridStyle<C>) -> Self {
153 self.style.minor = style;
154 self
155 }
156
157 pub fn build(self) -> GridSystem<C> {
159 let mut grid_system = GridSystem::new();
160 grid_system.style = self.style;
161 grid_system.enabled = self.enabled;
162
163 if let Some(horizontal) = self.horizontal_grid {
164 grid_system.horizontal = Some(horizontal);
165 }
166
167 if let Some(vertical) = self.vertical_grid {
168 grid_system.vertical = Some(vertical);
169 }
170
171 grid_system
172 }
173}
174
175impl<C: PixelColor + 'static> Default for GridBuilder<C>
176where
177 C: From<embedded_graphics::pixelcolor::Rgb565>,
178{
179 fn default() -> Self {
180 Self::new()
181 }
182}
183
184#[derive(Debug)]
186pub struct LinearGridBuilder<C: PixelColor> {
187 orientation: GridOrientation,
188 spacing: GridSpacing,
189 style: GridStyle<C>,
190 visible: bool,
191}
192
193impl<C: PixelColor> LinearGridBuilder<C>
194where
195 C: From<embedded_graphics::pixelcolor::Rgb565>,
196{
197 pub fn new(orientation: GridOrientation) -> Self {
199 Self {
200 orientation,
201 spacing: GridSpacing::Auto,
202 style: GridStyle::default(),
203 visible: true,
204 }
205 }
206
207 pub fn horizontal() -> Self {
209 Self::new(GridOrientation::Horizontal)
210 }
211
212 pub fn vertical() -> Self {
214 Self::new(GridOrientation::Vertical)
215 }
216
217 pub fn spacing(mut self, spacing: GridSpacing) -> Self {
219 self.spacing = spacing;
220 self
221 }
222
223 pub fn spacing_pixels(mut self, pixels: u32) -> Self {
225 self.spacing = GridSpacing::Pixels(pixels);
226 self
227 }
228
229 pub fn spacing_data_units(mut self, units: f32) -> Self {
231 self.spacing = GridSpacing::DataUnits(units);
232 self
233 }
234
235 pub fn spacing_auto(mut self) -> Self {
237 self.spacing = GridSpacing::Auto;
238 self
239 }
240
241 pub fn style(mut self, style: GridStyle<C>) -> Self {
243 self.style = style;
244 self
245 }
246
247 pub fn visible(mut self, visible: bool) -> Self {
249 self.visible = visible;
250 self
251 }
252
253 pub fn build(self) -> LinearGrid<C> {
255 LinearGrid::new(self.orientation, self.spacing)
256 .with_style(self.style)
257 .with_visibility(self.visible)
258 }
259}
260
261#[derive(Debug)]
263pub struct TickBasedGridBuilder<T, C>
264where
265 T: Copy + PartialOrd + core::fmt::Display,
266 C: PixelColor,
267{
268 orientation: GridOrientation,
269 style: GridStyle<C>,
270 visible: bool,
271 major_ticks_only: bool,
272 _phantom: core::marker::PhantomData<T>,
273}
274
275impl<T, C> TickBasedGridBuilder<T, C>
276where
277 T: Copy + PartialOrd + core::fmt::Display,
278 C: PixelColor,
279{
280 pub fn new(orientation: GridOrientation) -> Self
282 where
283 C: From<embedded_graphics::pixelcolor::Rgb565>,
284 {
285 Self {
286 orientation,
287 style: GridStyle::default(),
288 visible: true,
289 major_ticks_only: false,
290 _phantom: core::marker::PhantomData,
291 }
292 }
293
294 pub fn horizontal() -> Self
296 where
297 C: From<embedded_graphics::pixelcolor::Rgb565>,
298 {
299 Self::new(GridOrientation::Horizontal)
300 }
301
302 pub fn vertical() -> Self
304 where
305 C: From<embedded_graphics::pixelcolor::Rgb565>,
306 {
307 Self::new(GridOrientation::Vertical)
308 }
309
310 pub fn style(mut self, style: GridStyle<C>) -> Self {
312 self.style = style;
313 self
314 }
315
316 pub fn visible(mut self, visible: bool) -> Self {
318 self.visible = visible;
319 self
320 }
321
322 pub fn major_ticks_only(mut self, major_only: bool) -> Self {
324 self.major_ticks_only = major_only;
325 self
326 }
327
328 pub fn build(self) -> TickBasedGrid<T, C>
330 where
331 C: From<embedded_graphics::pixelcolor::Rgb565>,
332 {
333 TickBasedGrid::new(self.orientation)
334 .with_style(self.style)
335 .with_major_ticks_only(self.major_ticks_only)
336 }
337}
338
339#[derive(Debug)]
341pub struct CustomGridBuilder<C: PixelColor> {
342 orientation: GridOrientation,
343 positions: heapless::Vec<i32, 64>,
344 style: GridStyle<C>,
345 visible: bool,
346}
347
348impl<C: PixelColor + 'static> CustomGridBuilder<C>
349where
350 C: From<embedded_graphics::pixelcolor::Rgb565>,
351{
352 pub fn new(orientation: GridOrientation) -> Self {
354 Self {
355 orientation,
356 positions: heapless::Vec::new(),
357 style: GridStyle::default(),
358 visible: true,
359 }
360 }
361
362 pub fn horizontal() -> Self {
364 Self::new(GridOrientation::Horizontal)
365 }
366
367 pub fn vertical() -> Self {
369 Self::new(GridOrientation::Vertical)
370 }
371
372 pub fn add_line(mut self, position: i32) -> Self {
374 let _ = self.positions.push(position);
375 self
376 }
377
378 pub fn add_lines(mut self, positions: &[i32]) -> Self {
380 for &pos in positions {
381 let _ = self.positions.push(pos);
382 }
383 self
384 }
385
386 pub fn evenly_spaced(mut self, start: i32, end: i32, count: usize) -> Self {
388 if count > 1 {
389 let step = (end - start) / (count - 1) as i32;
390 for i in 0..count {
391 let pos = start + i as i32 * step;
392 let _ = self.positions.push(pos);
393 }
394 }
395 self
396 }
397
398 pub fn style(mut self, style: GridStyle<C>) -> Self {
400 self.style = style;
401 self
402 }
403
404 pub fn visible(mut self, visible: bool) -> Self {
406 self.visible = visible;
407 self
408 }
409
410 pub fn build(self) -> CustomGrid<C> {
412 let mut grid = CustomGrid::new(self.orientation).with_style(self.style);
413 grid.set_visible(self.visible);
414
415 for &pos in self.positions.iter() {
416 let _ = grid.add_line(pos);
417 }
418
419 grid
420 }
421}
422
423pub mod presets {
425 use super::*;
426 use embedded_graphics::pixelcolor::Rgb565;
427
428 pub fn professional_grid() -> GridSystem<Rgb565> {
430 GridBuilder::new()
431 .horizontal_linear(GridSpacing::Auto)
432 .vertical_linear(GridSpacing::Auto)
433 .professional()
434 .build()
435 }
436
437 pub fn minimal_grid() -> GridSystem<Rgb565> {
439 GridBuilder::new()
440 .horizontal_linear(GridSpacing::Auto)
441 .vertical_linear(GridSpacing::Auto)
442 .minimal()
443 .build()
444 }
445
446 pub fn dashed_grid() -> GridSystem<Rgb565> {
448 GridBuilder::new()
449 .horizontal_linear(GridSpacing::Auto)
450 .vertical_linear(GridSpacing::Auto)
451 .dashed()
452 .build()
453 }
454
455 pub fn tick_aligned_grid_f32() -> GridSystem<Rgb565> {
457 GridBuilder::new()
458 .horizontal_tick_based_f32()
459 .vertical_tick_based_f32()
460 .professional()
461 .build()
462 }
463
464 pub fn tick_aligned_grid_i32() -> GridSystem<Rgb565> {
466 GridBuilder::new()
467 .horizontal_tick_based_i32()
468 .vertical_tick_based_i32()
469 .professional()
470 .build()
471 }
472
473 pub fn horizontal_only_grid() -> GridSystem<Rgb565> {
475 GridBuilder::new()
476 .horizontal_linear(GridSpacing::Auto)
477 .visibility(GridVisibility::horizontal_only())
478 .professional()
479 .build()
480 }
481
482 pub fn vertical_only_grid() -> GridSystem<Rgb565> {
484 GridBuilder::new()
485 .vertical_linear(GridSpacing::Auto)
486 .visibility(GridVisibility::vertical_only())
487 .professional()
488 .build()
489 }
490}
491
492#[cfg(test)]
493mod tests {
494 use super::*;
495 use embedded_graphics::pixelcolor::Rgb565;
496
497 #[test]
498 fn test_grid_builder() {
499 let grid = GridBuilder::<Rgb565>::new()
500 .horizontal_linear(GridSpacing::Pixels(20))
501 .vertical_linear(GridSpacing::Pixels(30))
502 .professional()
503 .build();
504
505 assert!(grid.is_enabled());
506 assert!(grid.horizontal.is_some());
507 assert!(grid.vertical.is_some());
508 }
509
510 #[test]
511 fn test_linear_grid_builder() {
512 let grid = LinearGridBuilder::<Rgb565>::horizontal()
513 .spacing_pixels(25)
514 .visible(true)
515 .build();
516
517 assert_eq!(grid.orientation(), GridOrientation::Horizontal);
518 assert!(grid.is_visible());
519 }
520
521 #[test]
522 fn test_tick_based_grid_builder() {
523 let grid = TickBasedGridBuilder::<f32, Rgb565>::vertical()
524 .major_ticks_only(true)
525 .build();
526
527 assert_eq!(grid.orientation(), GridOrientation::Vertical);
528 assert!(grid.is_major_ticks_only());
529 }
530
531 #[test]
532 fn test_custom_grid_builder() {
533 let grid = CustomGridBuilder::<Rgb565>::horizontal()
534 .add_line(100)
535 .add_line(200)
536 .add_line(300)
537 .build();
538
539 assert_eq!(grid.orientation(), GridOrientation::Horizontal);
540 let positions = grid.calculate_positions(embedded_graphics::primitives::Rectangle::new(
541 embedded_graphics::prelude::Point::zero(),
542 embedded_graphics::prelude::Size::new(400, 300),
543 ));
544 assert_eq!(positions.len(), 3);
545 }
546
547 #[test]
548 fn test_preset_grids() {
549 let professional = presets::professional_grid();
550 assert!(professional.is_enabled());
551
552 let minimal = presets::minimal_grid();
553 assert!(minimal.is_enabled());
554
555 let dashed = presets::dashed_grid();
556 assert!(dashed.is_enabled());
557 }
558}