smart_leds_matrix/
layout.rs

1//! LED layout.
2
3use core::marker::PhantomData;
4
5use embedded_graphics_core::geometry::{Point, Size};
6use invert_axis::{InvertX, InvertXY, InvertY, NoInvert};
7
8/// Trait that represents a certain type of LED matrix.
9///
10/// The map() function shall fix any x y coordinate mismatch. Mismatch means
11/// the matrix might display the result being drawn in mirrored or otherwise
12/// incorrect ways due to the LEDs order on the PCB.
13/// Grid type matrixes (like 2x2  of 1x4 grid of 8x8 matrixes) should be also
14/// handled using this trait.
15pub trait Layout {
16    fn map(&self, p: Point) -> Option<usize>;
17    fn size(&self) -> Size;
18}
19
20/// Rectangular LED matrix.
21pub struct Rectangular<I> {
22    size: Size,
23    invert_axis: PhantomData<I>,
24}
25
26impl<I> Rectangular<I> {
27    const fn new_common(width: u32, height: u32) -> Self {
28        Self {
29            size: Size::new(width, height),
30            invert_axis: PhantomData,
31        }
32    }
33}
34
35impl Rectangular<NoInvert> {
36    /// Creates a new rectangular layout.
37    pub const fn new(width: u32, height: u32) -> Self {
38        Self::new_common(width, height)
39    }
40}
41
42impl Rectangular<InvertX> {
43    /// Creates a new rectangular layout with inverted X axis.
44    pub const fn new_invert_x(width: u32, height: u32) -> Self {
45        Self::new_common(width, height)
46    }
47}
48
49impl Rectangular<InvertY> {
50    /// Creates a new rectangular layout with inverted Y axis.
51    pub const fn new_invert_y(width: u32, height: u32) -> Self {
52        Self::new_common(width, height)
53    }
54}
55
56impl Rectangular<InvertXY> {
57    /// Creates a new rectangular layout with inverted X and Y axis.
58    pub const fn new_invert_xy(width: u32, height: u32) -> Self {
59        Self::new_common(width, height)
60    }
61}
62
63macro_rules! impl_layout {
64    ($invert_type:ty, $invert_x:expr, $invert_y:expr) => {
65        impl Layout for Rectangular<$invert_type> {
66            fn map(&self, mut p: Point) -> Option<usize> {
67                if $invert_x {
68                    p.x = (self.size.width - 1) as i32 - p.x;
69                }
70                if $invert_y {
71                    p.y = (self.size.height - 1) as i32 - p.y;
72                }
73
74                (p.x >= 0
75                    && p.y >= 0
76                    && p.x < self.size.width as i32
77                    && p.y < self.size.height as i32)
78                    .then(|| p.y as usize * self.size.width as usize + p.x as usize)
79            }
80
81            fn size(&self) -> Size {
82                return self.size;
83            }
84        }
85    };
86}
87
88impl_layout!(NoInvert, false, false);
89impl_layout!(InvertX, true, false);
90impl_layout!(InvertY, false, true);
91impl_layout!(InvertXY, true, true);
92
93/// Marker types for axis inversion.
94pub mod invert_axis {
95    /// No inverted axis.
96    pub enum NoInvert {}
97
98    /// Inverted X axis.
99    pub enum InvertX {}
100
101    /// Inverted Y axis.
102    pub enum InvertY {}
103
104    /// Inverted X and Y axis.
105    pub enum InvertXY {}
106}