1pub mod builder;
7pub mod style;
8pub mod traits;
9pub mod types;
10
11#[cfg(all(feature = "no_std", not(feature = "std")))]
12extern crate alloc;
13
14#[cfg(all(feature = "no_std", not(feature = "std")))]
15use alloc::boxed::Box;
16
17#[cfg(not(all(feature = "no_std", not(feature = "std"))))]
18use std::boxed::Box;
19
20pub use builder::{CustomGridBuilder, GridBuilder, LinearGridBuilder, TickBasedGridBuilder};
22pub use style::{GridLineStyle, GridStyle, GridVisibility, MajorGridStyle, MinorGridStyle};
23pub use traits::{DefaultGridRenderer, Grid, GridConfiguration, GridOrientation, GridRenderer};
24pub use types::{CustomGrid, GridSpacing, GridType, LinearGrid, TickBasedGrid};
25
26pub use traits::TickAlignedGrid;
27
28use crate::axes::traits::TickGenerator;
29use crate::error::{ChartError, ChartResult};
30use embedded_graphics::{
31 prelude::*,
32 primitives::{Line, PrimitiveStyle, Rectangle},
33};
34
35#[derive(Debug)]
37pub struct GridSystem<C: PixelColor> {
38 pub horizontal: Option<GridContainer<C>>,
40 pub vertical: Option<GridContainer<C>>,
42 pub style: GridStyle<C>,
44 pub enabled: bool,
46}
47
48#[derive(Debug)]
50pub enum GridContainer<C: PixelColor> {
51 Linear(LinearGrid<C>),
53 TickBasedF32(TickBasedGrid<f32, C>),
55 TickBasedI32(TickBasedGrid<i32, C>),
57 Custom(Box<CustomGrid<C>>),
59}
60
61impl<C: PixelColor + 'static> GridContainer<C> {
62 pub fn draw<D>(&self, viewport: Rectangle, target: &mut D) -> ChartResult<()>
64 where
65 D: DrawTarget<Color = C>,
66 {
67 match self {
68 GridContainer::Linear(grid) => grid.draw(viewport, target),
69 GridContainer::TickBasedF32(grid) => grid.draw(viewport, target),
70 GridContainer::TickBasedI32(grid) => grid.draw(viewport, target),
71 GridContainer::Custom(grid) => grid.draw(viewport, target),
72 }
73 }
74
75 pub fn orientation(&self) -> traits::GridOrientation {
77 match self {
78 GridContainer::Linear(grid) => grid.orientation(),
79 GridContainer::TickBasedF32(grid) => grid.orientation(),
80 GridContainer::TickBasedI32(grid) => grid.orientation(),
81 GridContainer::Custom(grid) => grid.orientation(),
82 }
83 }
84
85 pub fn is_visible(&self) -> bool {
87 match self {
88 GridContainer::Linear(grid) => grid.is_visible(),
89 GridContainer::TickBasedF32(grid) => grid.is_visible(),
90 GridContainer::TickBasedI32(grid) => grid.is_visible(),
91 GridContainer::Custom(grid) => grid.is_visible(),
92 }
93 }
94}
95
96impl<C: PixelColor + 'static> GridSystem<C>
97where
98 C: From<embedded_graphics::pixelcolor::Rgb565>,
99{
100 pub fn new() -> Self {
102 Self {
103 horizontal: None,
104 vertical: None,
105 style: GridStyle::default(),
106 enabled: true,
107 }
108 }
109
110 pub fn builder() -> GridBuilder<C> {
112 GridBuilder::new()
113 }
114
115 pub fn set_horizontal_grid(&mut self, grid: GridContainer<C>) {
117 self.horizontal = Some(grid);
118 }
119
120 pub fn set_vertical_grid(&mut self, grid: GridContainer<C>) {
122 self.vertical = Some(grid);
123 }
124
125 pub fn set_enabled(&mut self, enabled: bool) {
127 self.enabled = enabled;
128 }
129
130 pub fn is_enabled(&self) -> bool {
132 self.enabled
133 }
134
135 pub fn draw<D>(&self, viewport: Rectangle, target: &mut D) -> ChartResult<()>
137 where
138 D: DrawTarget<Color = C>,
139 {
140 if !self.enabled {
141 return Ok(());
142 }
143
144 if let Some(ref horizontal_grid) = self.horizontal {
146 horizontal_grid.draw(viewport, target)?;
147 }
148
149 if let Some(ref vertical_grid) = self.vertical {
151 vertical_grid.draw(viewport, target)?;
152 }
153
154 Ok(())
155 }
156
157 pub fn draw_with_axes<T, D, XA, YA>(
159 &self,
160 viewport: Rectangle,
161 x_axis: Option<&XA>,
162 y_axis: Option<&YA>,
163 target: &mut D,
164 ) -> ChartResult<()>
165 where
166 T: crate::axes::traits::AxisValue,
167 D: DrawTarget<Color = C>,
168 XA: crate::axes::traits::Axis<T, C>,
169 YA: crate::axes::traits::Axis<T, C>,
170 {
171 if !self.enabled {
172 return Ok(());
173 }
174
175 if let Some(x_axis) = x_axis {
177 let ticks = TickGenerator::generate_ticks(
179 x_axis.tick_generator(),
180 x_axis.min(),
181 x_axis.max(),
182 10, );
184
185 for tick in &ticks {
186 let x_pos = x_axis.transform_value(tick.value, viewport);
187 if x_pos >= viewport.top_left.x
188 && x_pos <= viewport.top_left.x + viewport.size.width as i32
189 {
190 let start = Point::new(x_pos, viewport.top_left.y);
191 let end = Point::new(x_pos, viewport.top_left.y + viewport.size.height as i32);
192
193 Line::new(start, end)
194 .into_styled(PrimitiveStyle::with_stroke(
195 self.style.major.line.line_style.color,
196 self.style.major.line.line_style.width,
197 ))
198 .draw(target)
199 .map_err(|_| ChartError::RenderingError)?;
200 }
201 }
202 }
203
204 if let Some(y_axis) = y_axis {
205 let ticks = TickGenerator::generate_ticks(
207 y_axis.tick_generator(),
208 y_axis.min(),
209 y_axis.max(),
210 10, );
212
213 for tick in &ticks {
214 let y_pos = y_axis.transform_value(tick.value, viewport);
215 if y_pos >= viewport.top_left.y
216 && y_pos <= viewport.top_left.y + viewport.size.height as i32
217 {
218 let start = Point::new(viewport.top_left.x, y_pos);
219 let end = Point::new(viewport.top_left.x + viewport.size.width as i32, y_pos);
220
221 Line::new(start, end)
222 .into_styled(PrimitiveStyle::with_stroke(
223 self.style.major.line.line_style.color,
224 self.style.major.line.line_style.width,
225 ))
226 .draw(target)
227 .map_err(|_| ChartError::RenderingError)?;
228 }
229 }
230 }
231
232 Ok(())
233 }
234}
235
236impl<C: PixelColor + 'static> Default for GridSystem<C>
237where
238 C: From<embedded_graphics::pixelcolor::Rgb565>,
239{
240 fn default() -> Self {
241 Self::new()
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248 use embedded_graphics::pixelcolor::Rgb565;
249
250 #[test]
251 fn test_grid_system_creation() {
252 let grid: GridSystem<Rgb565> = GridSystem::new();
253 assert!(grid.is_enabled());
254 assert!(grid.horizontal.is_none());
255 assert!(grid.vertical.is_none());
256 }
257
258 #[test]
259 fn test_grid_system_enable_disable() {
260 let mut grid: GridSystem<Rgb565> = GridSystem::new();
261 assert!(grid.is_enabled());
262
263 grid.set_enabled(false);
264 assert!(!grid.is_enabled());
265
266 grid.set_enabled(true);
267 assert!(grid.is_enabled());
268 }
269}