use crate::style::{LinePattern, LineStyle};
use embedded_graphics::prelude::*;
#[derive(Debug, Clone)]
pub struct GridStyle<C: PixelColor> {
pub major: MajorGridStyle<C>,
pub minor: MinorGridStyle<C>,
pub visibility: GridVisibility,
pub opacity: f32,
}
#[derive(Debug, Clone)]
pub struct MajorGridStyle<C: PixelColor> {
pub line: GridLineStyle<C>,
pub enabled: bool,
pub spacing: f32,
}
#[derive(Debug, Clone)]
pub struct MinorGridStyle<C: PixelColor> {
pub line: GridLineStyle<C>,
pub enabled: bool,
pub spacing: f32,
pub subdivisions: u32,
}
#[derive(Debug, Clone)]
pub struct GridLineStyle<C: PixelColor> {
pub line_style: LineStyle<C>,
pub anti_alias: bool,
pub opacity: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GridVisibility {
pub horizontal: bool,
pub vertical: bool,
pub major: bool,
pub minor: bool,
}
impl<C: PixelColor> GridStyle<C> {
pub fn new() -> Self
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
Self {
major: MajorGridStyle::default(),
minor: MinorGridStyle::default(),
visibility: GridVisibility::default(),
opacity: 1.0,
}
}
pub fn professional() -> Self
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
let major_color = embedded_graphics::pixelcolor::Rgb565::new(20, 40, 20).into();
let minor_color = embedded_graphics::pixelcolor::Rgb565::new(10, 20, 10).into();
Self {
major: MajorGridStyle {
line: GridLineStyle {
line_style: LineStyle::solid(major_color).width(1),
anti_alias: true,
opacity: 0.8,
},
enabled: true,
spacing: 1.0,
},
minor: MinorGridStyle {
line: GridLineStyle {
line_style: LineStyle::solid(minor_color).width(1),
anti_alias: true,
opacity: 0.4,
},
enabled: true,
spacing: 0.2,
subdivisions: 5,
},
visibility: GridVisibility::all(),
opacity: 1.0,
}
}
pub fn minimal() -> Self
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
let grid_color = embedded_graphics::pixelcolor::Rgb565::new(15, 30, 15).into();
Self {
major: MajorGridStyle {
line: GridLineStyle {
line_style: LineStyle::solid(grid_color).width(1),
anti_alias: false,
opacity: 0.6,
},
enabled: true,
spacing: 1.0,
},
minor: MinorGridStyle {
line: GridLineStyle {
line_style: LineStyle::solid(grid_color).width(1),
anti_alias: false,
opacity: 0.3,
},
enabled: false,
spacing: 0.2,
subdivisions: 5,
},
visibility: GridVisibility {
horizontal: true,
vertical: true,
major: true,
minor: false,
},
opacity: 1.0,
}
}
pub fn dashed() -> Self
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
let major_color = embedded_graphics::pixelcolor::Rgb565::new(25, 50, 25).into();
let minor_color = embedded_graphics::pixelcolor::Rgb565::new(12, 25, 12).into();
Self {
major: MajorGridStyle {
line: GridLineStyle {
line_style: LineStyle::dashed(major_color).width(1),
anti_alias: true,
opacity: 0.7,
},
enabled: true,
spacing: 1.0,
},
minor: MinorGridStyle {
line: GridLineStyle {
line_style: LineStyle::dotted(minor_color).width(1),
anti_alias: true,
opacity: 0.4,
},
enabled: true,
spacing: 0.25,
subdivisions: 4,
},
visibility: GridVisibility::all(),
opacity: 1.0,
}
}
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.opacity = opacity.clamp(0.0, 1.0);
self
}
pub fn with_visibility(mut self, visibility: GridVisibility) -> Self {
self.visibility = visibility;
self
}
pub fn with_major_enabled(mut self, enabled: bool) -> Self {
self.major.enabled = enabled;
self
}
pub fn with_minor_enabled(mut self, enabled: bool) -> Self {
self.minor.enabled = enabled;
self
}
pub fn with_major_spacing(mut self, spacing: f32) -> Self {
self.major.spacing = spacing;
self
}
pub fn with_minor_spacing(mut self, spacing: f32) -> Self {
self.minor.spacing = spacing;
self
}
}
impl<C: PixelColor> Default for GridStyle<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
fn default() -> Self {
Self::new()
}
}
impl<C: PixelColor> MajorGridStyle<C> {
pub fn new(line_style: LineStyle<C>) -> Self {
Self {
line: GridLineStyle {
line_style,
anti_alias: false,
opacity: 1.0,
},
enabled: true,
spacing: 1.0,
}
}
pub fn with_color(mut self, color: C) -> Self {
self.line.line_style.color = color;
self
}
pub fn with_width(mut self, width: u32) -> Self {
self.line.line_style.width = width;
self
}
pub fn with_pattern(mut self, pattern: LinePattern) -> Self {
self.line.line_style.pattern = pattern;
self
}
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.line.opacity = opacity.clamp(0.0, 1.0);
self
}
pub fn with_spacing(mut self, spacing: f32) -> Self {
self.spacing = spacing;
self
}
}
impl<C: PixelColor> Default for MajorGridStyle<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
fn default() -> Self {
let color = embedded_graphics::pixelcolor::Rgb565::new(20, 40, 20).into();
Self::new(LineStyle::solid(color))
}
}
impl<C: PixelColor> MinorGridStyle<C> {
pub fn new(line_style: LineStyle<C>) -> Self {
Self {
line: GridLineStyle {
line_style,
anti_alias: false,
opacity: 0.5,
},
enabled: true,
spacing: 0.2,
subdivisions: 5,
}
}
pub fn with_color(mut self, color: C) -> Self {
self.line.line_style.color = color;
self
}
pub fn with_width(mut self, width: u32) -> Self {
self.line.line_style.width = width;
self
}
pub fn with_pattern(mut self, pattern: LinePattern) -> Self {
self.line.line_style.pattern = pattern;
self
}
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.line.opacity = opacity.clamp(0.0, 1.0);
self
}
pub fn with_spacing(mut self, spacing: f32) -> Self {
self.spacing = spacing;
self
}
pub fn with_subdivisions(mut self, subdivisions: u32) -> Self {
self.subdivisions = subdivisions;
self
}
}
impl<C: PixelColor> Default for MinorGridStyle<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
fn default() -> Self {
let color = embedded_graphics::pixelcolor::Rgb565::new(10, 20, 10).into();
Self::new(LineStyle::solid(color))
}
}
impl<C: PixelColor> GridLineStyle<C> {
pub fn new(line_style: LineStyle<C>) -> Self {
Self {
line_style,
anti_alias: false,
opacity: 1.0,
}
}
pub fn with_anti_alias(mut self, anti_alias: bool) -> Self {
self.anti_alias = anti_alias;
self
}
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.opacity = opacity.clamp(0.0, 1.0);
self
}
}
impl GridVisibility {
pub const fn all() -> Self {
Self {
horizontal: true,
vertical: true,
major: true,
minor: true,
}
}
pub const fn none() -> Self {
Self {
horizontal: false,
vertical: false,
major: false,
minor: false,
}
}
pub const fn major_only() -> Self {
Self {
horizontal: true,
vertical: true,
major: true,
minor: false,
}
}
pub const fn horizontal_only() -> Self {
Self {
horizontal: true,
vertical: false,
major: true,
minor: true,
}
}
pub const fn vertical_only() -> Self {
Self {
horizontal: false,
vertical: true,
major: true,
minor: true,
}
}
pub const fn any_visible(&self) -> bool {
(self.horizontal || self.vertical) && (self.major || self.minor)
}
pub const fn major_visible(&self) -> bool {
(self.horizontal || self.vertical) && self.major
}
pub const fn minor_visible(&self) -> bool {
(self.horizontal || self.vertical) && self.minor
}
}
impl Default for GridVisibility {
fn default() -> Self {
Self::major_only()
}
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_graphics::pixelcolor::Rgb565;
#[test]
fn test_grid_style_creation() {
let style: GridStyle<Rgb565> = GridStyle::new();
assert!(style.major.enabled);
assert_eq!(style.opacity, 1.0);
}
#[test]
fn test_grid_style_professional() {
let style: GridStyle<Rgb565> = GridStyle::professional();
assert!(style.major.enabled);
assert!(style.minor.enabled);
assert!(style.visibility.any_visible());
}
#[test]
fn test_grid_style_minimal() {
let style: GridStyle<Rgb565> = GridStyle::minimal();
assert!(style.major.enabled);
assert!(!style.minor.enabled);
}
#[test]
fn test_grid_visibility() {
let vis = GridVisibility::all();
assert!(vis.any_visible());
assert!(vis.major_visible());
assert!(vis.minor_visible());
let vis = GridVisibility::none();
assert!(!vis.any_visible());
let vis = GridVisibility::major_only();
assert!(vis.major_visible());
assert!(!vis.minor_visible());
}
#[test]
fn test_major_grid_style_builder() {
let style: MajorGridStyle<Rgb565> = MajorGridStyle::default()
.with_width(2)
.with_opacity(0.8)
.with_spacing(2.0);
assert_eq!(style.line.line_style.width, 2);
assert_eq!(style.line.opacity, 0.8);
assert_eq!(style.spacing, 2.0);
}
#[test]
fn test_minor_grid_style_builder() {
let style: MinorGridStyle<Rgb565> = MinorGridStyle::default()
.with_subdivisions(10)
.with_spacing(0.1);
assert_eq!(style.subdivisions, 10);
assert_eq!(style.spacing, 0.1);
}
}