use crate::style::LineStyle;
use embedded_graphics::prelude::*;
#[derive(Debug, Clone)]
pub struct AxisStyle<C: PixelColor> {
pub axis_line: LineStyle<C>,
pub major_ticks: TickStyle<C>,
pub minor_ticks: TickStyle<C>,
pub grid_lines: Option<LineStyle<C>>,
pub labels: LabelStyle<C>,
pub label_offset: u32,
}
#[derive(Debug, Clone)]
pub struct TickStyle<C: PixelColor> {
pub line: LineStyle<C>,
pub length: u32,
pub visible: bool,
}
#[derive(Debug, Clone)]
pub struct LabelStyle<C: PixelColor> {
pub color: C,
pub font_size: u32,
pub visible: bool,
pub alignment: TextAlignment,
pub rotation: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextAlignment {
Start,
Center,
End,
}
impl<C: PixelColor> AxisStyle<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
pub fn new() -> Self {
Self {
axis_line: LineStyle::solid(embedded_graphics::pixelcolor::Rgb565::RED.into()).width(3),
major_ticks: TickStyle::new(embedded_graphics::pixelcolor::Rgb565::RED.into(), 10),
minor_ticks: TickStyle::new(embedded_graphics::pixelcolor::Rgb565::BLUE.into(), 5),
grid_lines: None,
labels: LabelStyle::new(embedded_graphics::pixelcolor::Rgb565::BLACK.into()),
label_offset: 8,
}
}
pub fn with_axis_line(mut self, style: LineStyle<C>) -> Self {
self.axis_line = style;
self
}
pub fn with_major_ticks(mut self, style: TickStyle<C>) -> Self {
self.major_ticks = style;
self
}
pub fn with_minor_ticks(mut self, style: TickStyle<C>) -> Self {
self.minor_ticks = style;
self
}
pub fn with_grid_lines(mut self, style: LineStyle<C>) -> Self {
self.grid_lines = Some(style);
self
}
pub fn without_grid_lines(mut self) -> Self {
self.grid_lines = None;
self
}
pub fn with_labels(mut self, style: LabelStyle<C>) -> Self {
self.labels = style;
self
}
pub fn with_label_offset(mut self, offset: u32) -> Self {
self.label_offset = offset;
self
}
pub fn minimal() -> Self {
Self {
axis_line: LineStyle::solid(embedded_graphics::pixelcolor::Rgb565::BLACK.into()),
major_ticks: TickStyle::new(embedded_graphics::pixelcolor::Rgb565::BLACK.into(), 3),
minor_ticks: TickStyle::new(embedded_graphics::pixelcolor::Rgb565::BLACK.into(), 1)
.hidden(),
grid_lines: None,
labels: LabelStyle::new(embedded_graphics::pixelcolor::Rgb565::BLACK.into())
.with_font_size(8),
label_offset: 4,
}
}
pub fn professional() -> Self {
Self {
axis_line: LineStyle::solid(embedded_graphics::pixelcolor::Rgb565::BLACK.into()),
major_ticks: TickStyle::new(embedded_graphics::pixelcolor::Rgb565::BLACK.into(), 8),
minor_ticks: TickStyle::new(
embedded_graphics::pixelcolor::Rgb565::new(16, 32, 16).into(), 4, )
.with_width(1), grid_lines: Some(LineStyle::solid(
embedded_graphics::pixelcolor::Rgb565::new(25, 50, 25).into(),
)),
labels: LabelStyle::new(embedded_graphics::pixelcolor::Rgb565::BLACK.into()),
label_offset: 10,
}
}
}
impl<C: PixelColor> TickStyle<C> {
pub fn new(color: C, length: u32) -> Self {
Self {
line: LineStyle::solid(color),
length,
visible: true,
}
}
pub fn with_color(mut self, color: C) -> Self {
self.line = self.line.color(color);
self
}
pub fn with_width(mut self, width: u32) -> Self {
self.line = self.line.width(width);
self
}
pub fn with_length(mut self, length: u32) -> Self {
self.length = length;
self
}
pub fn hidden(mut self) -> Self {
self.visible = false;
self
}
pub fn visible(mut self) -> Self {
self.visible = true;
self
}
}
impl<C: PixelColor> LabelStyle<C> {
pub fn new(color: C) -> Self {
Self {
color,
font_size: 12,
visible: true,
alignment: TextAlignment::Center,
rotation: 0,
}
}
pub fn with_color(mut self, color: C) -> Self {
self.color = color;
self
}
pub fn with_font_size(mut self, size: u32) -> Self {
self.font_size = size;
self
}
pub fn with_alignment(mut self, alignment: TextAlignment) -> Self {
self.alignment = alignment;
self
}
pub fn with_rotation(mut self, degrees: u16) -> Self {
self.rotation = match degrees {
0..=45 => 0,
46..=135 => 90,
136..=225 => 180,
226..=315 => 270,
_ => 0,
};
self
}
pub fn hidden(mut self) -> Self {
self.visible = false;
self
}
pub fn visible(mut self) -> Self {
self.visible = true;
self
}
}
impl<C: PixelColor> Default for AxisStyle<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
fn default() -> Self {
Self::new()
}
}
impl<C: PixelColor> Default for TickStyle<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
fn default() -> Self {
Self::new(embedded_graphics::pixelcolor::Rgb565::BLACK.into(), 4)
}
}
impl<C: PixelColor> Default for LabelStyle<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
fn default() -> Self {
Self::new(embedded_graphics::pixelcolor::Rgb565::BLACK.into())
}
}
impl Default for TextAlignment {
fn default() -> Self {
Self::Center
}
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_graphics::pixelcolor::Rgb565;
#[test]
fn test_axis_style_creation() {
let style: AxisStyle<Rgb565> = AxisStyle::new();
assert!(style.major_ticks.visible);
assert!(style.minor_ticks.visible);
assert!(style.grid_lines.is_none());
}
#[test]
fn test_tick_style_builder() {
let style = TickStyle::new(Rgb565::RED, 5).with_width(2).with_length(8);
assert_eq!(style.length, 8);
assert!(style.visible);
}
#[test]
fn test_label_style_builder() {
let style = LabelStyle::new(Rgb565::BLUE)
.with_font_size(14)
.with_alignment(TextAlignment::Start)
.with_rotation(90);
assert_eq!(style.font_size, 14);
assert_eq!(style.alignment, TextAlignment::Start);
assert_eq!(style.rotation, 90);
}
#[test]
fn test_professional_style() {
let style: AxisStyle<Rgb565> = AxisStyle::professional();
assert!(style.grid_lines.is_some());
assert_eq!(style.label_offset, 10);
}
#[test]
fn test_minimal_style() {
let style: AxisStyle<Rgb565> = AxisStyle::minimal();
assert!(!style.minor_ticks.visible);
assert_eq!(style.label_offset, 4);
}
}