pub mod builder;
pub mod style;
pub mod traits;
pub mod types;
#[cfg(all(feature = "no_std", not(feature = "std")))]
extern crate alloc;
#[cfg(all(feature = "no_std", not(feature = "std")))]
use alloc::boxed::Box;
#[cfg(not(all(feature = "no_std", not(feature = "std"))))]
use std::boxed::Box;
pub use builder::{CustomGridBuilder, GridBuilder, LinearGridBuilder, TickBasedGridBuilder};
pub use style::{GridLineStyle, GridStyle, GridVisibility, MajorGridStyle, MinorGridStyle};
pub use traits::{DefaultGridRenderer, Grid, GridConfiguration, GridOrientation, GridRenderer};
pub use types::{CustomGrid, GridSpacing, GridType, LinearGrid, TickBasedGrid};
pub use traits::TickAlignedGrid;
use crate::axes::traits::TickGenerator;
use crate::error::{ChartError, ChartResult};
use embedded_graphics::{
prelude::*,
primitives::{Line, PrimitiveStyle, Rectangle},
};
#[derive(Debug)]
pub struct GridSystem<C: PixelColor> {
pub horizontal: Option<GridContainer<C>>,
pub vertical: Option<GridContainer<C>>,
pub style: GridStyle<C>,
pub enabled: bool,
}
#[derive(Debug)]
pub enum GridContainer<C: PixelColor> {
Linear(LinearGrid<C>),
TickBasedF32(TickBasedGrid<f32, C>),
TickBasedI32(TickBasedGrid<i32, C>),
Custom(Box<CustomGrid<C>>),
}
impl<C: PixelColor + 'static> GridContainer<C> {
pub fn draw<D>(&self, viewport: Rectangle, target: &mut D) -> ChartResult<()>
where
D: DrawTarget<Color = C>,
{
match self {
GridContainer::Linear(grid) => grid.draw(viewport, target),
GridContainer::TickBasedF32(grid) => grid.draw(viewport, target),
GridContainer::TickBasedI32(grid) => grid.draw(viewport, target),
GridContainer::Custom(grid) => grid.draw(viewport, target),
}
}
pub fn orientation(&self) -> traits::GridOrientation {
match self {
GridContainer::Linear(grid) => grid.orientation(),
GridContainer::TickBasedF32(grid) => grid.orientation(),
GridContainer::TickBasedI32(grid) => grid.orientation(),
GridContainer::Custom(grid) => grid.orientation(),
}
}
pub fn is_visible(&self) -> bool {
match self {
GridContainer::Linear(grid) => grid.is_visible(),
GridContainer::TickBasedF32(grid) => grid.is_visible(),
GridContainer::TickBasedI32(grid) => grid.is_visible(),
GridContainer::Custom(grid) => grid.is_visible(),
}
}
}
impl<C: PixelColor + 'static> GridSystem<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
pub fn new() -> Self {
Self {
horizontal: None,
vertical: None,
style: GridStyle::default(),
enabled: true,
}
}
pub fn builder() -> GridBuilder<C> {
GridBuilder::new()
}
pub fn set_horizontal_grid(&mut self, grid: GridContainer<C>) {
self.horizontal = Some(grid);
}
pub fn set_vertical_grid(&mut self, grid: GridContainer<C>) {
self.vertical = Some(grid);
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
pub fn draw<D>(&self, viewport: Rectangle, target: &mut D) -> ChartResult<()>
where
D: DrawTarget<Color = C>,
{
if !self.enabled {
return Ok(());
}
if let Some(ref horizontal_grid) = self.horizontal {
horizontal_grid.draw(viewport, target)?;
}
if let Some(ref vertical_grid) = self.vertical {
vertical_grid.draw(viewport, target)?;
}
Ok(())
}
pub fn draw_with_axes<T, D, XA, YA>(
&self,
viewport: Rectangle,
x_axis: Option<&XA>,
y_axis: Option<&YA>,
target: &mut D,
) -> ChartResult<()>
where
T: crate::axes::traits::AxisValue,
D: DrawTarget<Color = C>,
XA: crate::axes::traits::Axis<T, C>,
YA: crate::axes::traits::Axis<T, C>,
{
if !self.enabled {
return Ok(());
}
if let Some(x_axis) = x_axis {
let ticks = TickGenerator::generate_ticks(
x_axis.tick_generator(),
x_axis.min(),
x_axis.max(),
10, );
for tick in &ticks {
let x_pos = x_axis.transform_value(tick.value, viewport);
if x_pos >= viewport.top_left.x
&& x_pos <= viewport.top_left.x + viewport.size.width as i32
{
let start = Point::new(x_pos, viewport.top_left.y);
let end = Point::new(x_pos, viewport.top_left.y + viewport.size.height as i32);
Line::new(start, end)
.into_styled(PrimitiveStyle::with_stroke(
self.style.major.line.line_style.color,
self.style.major.line.line_style.width,
))
.draw(target)
.map_err(|_| ChartError::RenderingError)?;
}
}
}
if let Some(y_axis) = y_axis {
let ticks = TickGenerator::generate_ticks(
y_axis.tick_generator(),
y_axis.min(),
y_axis.max(),
10, );
for tick in &ticks {
let y_pos = y_axis.transform_value(tick.value, viewport);
if y_pos >= viewport.top_left.y
&& y_pos <= viewport.top_left.y + viewport.size.height as i32
{
let start = Point::new(viewport.top_left.x, y_pos);
let end = Point::new(viewport.top_left.x + viewport.size.width as i32, y_pos);
Line::new(start, end)
.into_styled(PrimitiveStyle::with_stroke(
self.style.major.line.line_style.color,
self.style.major.line.line_style.width,
))
.draw(target)
.map_err(|_| ChartError::RenderingError)?;
}
}
}
Ok(())
}
}
impl<C: PixelColor + 'static> Default for GridSystem<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_graphics::pixelcolor::Rgb565;
#[test]
fn test_grid_system_creation() {
let grid: GridSystem<Rgb565> = GridSystem::new();
assert!(grid.is_enabled());
assert!(grid.horizontal.is_none());
assert!(grid.vertical.is_none());
}
#[test]
fn test_grid_system_enable_disable() {
let mut grid: GridSystem<Rgb565> = GridSystem::new();
assert!(grid.is_enabled());
grid.set_enabled(false);
assert!(!grid.is_enabled());
grid.set_enabled(true);
assert!(grid.is_enabled());
}
}