use embedded_graphics::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LineStyle<C: PixelColor> {
pub color: C,
pub width: u32,
pub pattern: LinePattern,
pub cap: LineCap,
pub join: LineJoin,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LinePattern {
Solid,
Dashed,
Dotted,
DashDot,
Custom,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LineCap {
Butt,
Round,
Square,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LineJoin {
Miter,
Round,
Bevel,
}
impl<C: PixelColor> LineStyle<C> {
pub const fn solid(color: C) -> Self {
Self {
color,
width: 1,
pattern: LinePattern::Solid,
cap: LineCap::Butt,
join: LineJoin::Miter,
}
}
pub const fn dashed(color: C) -> Self {
Self {
color,
width: 1,
pattern: LinePattern::Dashed,
cap: LineCap::Butt,
join: LineJoin::Miter,
}
}
pub const fn dotted(color: C) -> Self {
Self {
color,
width: 1,
pattern: LinePattern::Dotted,
cap: LineCap::Round,
join: LineJoin::Round,
}
}
pub const fn width(mut self, width: u32) -> Self {
self.width = width;
self
}
pub const fn color(mut self, color: C) -> Self {
self.color = color;
self
}
pub const fn pattern(mut self, pattern: LinePattern) -> Self {
self.pattern = pattern;
self
}
pub const fn cap(mut self, cap: LineCap) -> Self {
self.cap = cap;
self
}
pub const fn join(mut self, join: LineJoin) -> Self {
self.join = join;
self
}
}
impl<C: PixelColor> Default for LineStyle<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
fn default() -> Self {
Self::solid(embedded_graphics::pixelcolor::Rgb565::WHITE.into())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BorderStyle<C: PixelColor> {
pub line: LineStyle<C>,
pub radius: u32,
pub visible: bool,
}
impl<C: PixelColor> BorderStyle<C> {
pub const fn new(line: LineStyle<C>) -> Self {
Self {
line,
radius: 0,
visible: true,
}
}
pub const fn rounded(line: LineStyle<C>, radius: u32) -> Self {
Self {
line,
radius,
visible: true,
}
}
pub const fn radius(mut self, radius: u32) -> Self {
self.radius = radius;
self
}
pub const fn visible(mut self, visible: bool) -> Self {
self.visible = visible;
self
}
}
impl<C: PixelColor> Default for BorderStyle<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
fn default() -> Self {
Self::new(LineStyle::default())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StrokeStyle<C: PixelColor> {
pub color: C,
pub width: u32,
}
impl<C: PixelColor> StrokeStyle<C> {
pub const fn new(color: C, width: u32) -> Self {
Self { color, width }
}
}
impl<C: PixelColor> From<LineStyle<C>> for StrokeStyle<C> {
fn from(line_style: LineStyle<C>) -> Self {
Self {
color: line_style.color,
width: line_style.width,
}
}
}
use super::gradient::{LinearGradient, PatternFill, RadialGradient, MAX_GRADIENT_STOPS};
#[derive(Debug, Clone)]
pub struct FillStyle<C: PixelColor> {
pub pattern: FillPattern<C>,
}
#[derive(Debug, Clone)]
pub enum FillPattern<C: PixelColor> {
Solid(C),
LinearGradient(LinearGradient<C, MAX_GRADIENT_STOPS>),
RadialGradient(RadialGradient<C, MAX_GRADIENT_STOPS>),
Pattern(PatternFill<C>),
}
impl<C: PixelColor> FillStyle<C> {
pub const fn solid(color: C) -> Self {
Self {
pattern: FillPattern::Solid(color),
}
}
pub fn linear_gradient(gradient: LinearGradient<C, MAX_GRADIENT_STOPS>) -> Self {
Self {
pattern: FillPattern::LinearGradient(gradient),
}
}
pub fn radial_gradient(gradient: RadialGradient<C, MAX_GRADIENT_STOPS>) -> Self {
Self {
pattern: FillPattern::RadialGradient(gradient),
}
}
pub const fn pattern(pattern: PatternFill<C>) -> Self {
Self {
pattern: FillPattern::Pattern(pattern),
}
}
pub fn solid_color(&self) -> Option<C> {
match &self.pattern {
FillPattern::Solid(color) => Some(*color),
_ => None,
}
}
}
impl<C: PixelColor> Default for FillStyle<C>
where
C: From<embedded_graphics::pixelcolor::Rgb565>,
{
fn default() -> Self {
Self::solid(embedded_graphics::pixelcolor::Rgb565::WHITE.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_graphics::pixelcolor::Rgb565;
#[test]
fn test_line_style_creation() {
let style = LineStyle::solid(Rgb565::RED);
assert_eq!(style.color, Rgb565::RED);
assert_eq!(style.width, 1);
assert_eq!(style.pattern, LinePattern::Solid);
}
#[test]
fn test_line_style_builder() {
let style = LineStyle::solid(Rgb565::BLUE)
.width(3)
.pattern(LinePattern::Dashed)
.cap(LineCap::Round);
assert_eq!(style.color, Rgb565::BLUE);
assert_eq!(style.width, 3);
assert_eq!(style.pattern, LinePattern::Dashed);
assert_eq!(style.cap, LineCap::Round);
}
#[test]
fn test_border_style() {
let line = LineStyle::solid(Rgb565::BLACK);
let border = BorderStyle::rounded(line, 5);
assert_eq!(border.radius, 5);
assert!(border.visible);
assert_eq!(border.line.color, Rgb565::BLACK);
}
#[test]
fn test_stroke_style_from_line_style() {
let line_style = LineStyle::solid(Rgb565::GREEN).width(2);
let stroke_style: StrokeStyle<Rgb565> = line_style.into();
assert_eq!(stroke_style.color, Rgb565::GREEN);
assert_eq!(stroke_style.width, 2);
}
#[test]
fn test_fill_style() {
let fill = FillStyle::solid(Rgb565::YELLOW);
assert_eq!(fill.solid_color(), Some(Rgb565::YELLOW));
assert!(matches!(fill.pattern, FillPattern::Solid(_)));
}
}