You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
8.1 KiB
Rust

#![no_std]
use embedded_graphics::prelude::*;
use embedded_graphics::primitives::Rectangle;
use embedded_graphics::style::PrimitiveStyle;
pub struct Digitals<C>
where
C: PixelColor,
{
width: i32, // pixel width (calculated from height)
height: i32, // pixel height (set by user)
stroke: i32, // pixel stroke size for segments, calculated from height
style: PrimitiveStyle<C>,
}
#[derive(Clone, Copy)]
enum Symbol {
Dot,
Colon,
Exclamation,
Question,
Segments(u8),
Invalid,
}
enum Segment {
Top,
Bottom,
Middle,
TopLeft,
BottomLeft,
TopRight,
BottomRight,
}
// u8 single bit to single Segment enum
impl From<u8> for Segment {
fn from(bit: u8) -> Self {
match bit {
1 => Segment::Top,
2 => Segment::Bottom,
4 => Segment::Middle,
8 => Segment::TopLeft,
16 => Segment::BottomLeft,
32 => Segment::TopRight,
64 => Segment::BottomRight,
_ => panic!("Invalid segment bit"), // this is used only internally, shouldn't be possible
}
}
}
impl From<char> for Symbol {
fn from(value: char) -> Self {
match value {
' ' => Self::Segments(0b0000_0000),
'0' | 'D' | 'O' => Self::Segments(0b0111_1011),
'1' => Self::Segments(0b0001_1000),
'2' => Self::Segments(0b0011_0111),
'3' => Self::Segments(0b0110_0111),
'4' => Self::Segments(0b0110_1100),
'5' | 'S' => Self::Segments(0b0100_1111),
'6' => Self::Segments(0b0101_1111),
'7' => Self::Segments(0b0110_0001),
'8' | 'B' => Self::Segments(0b0111_1111),
'9' => Self::Segments(0b0110_1111),
'-' => Self::Segments(0b0000_0100),
'_' => Self::Segments(0b0000_0010),
'A' => Self::Segments(0b0111_1101),
'C' => Self::Segments(0b0001_1011),
'E' => Self::Segments(0b0001_1111),
'F' => Self::Segments(0b0001_1101),
'H' => Self::Segments(0b0111_1100),
'I' => Self::Segments(0b0110_0000),
'L' => Self::Segments(0b0001_1010),
'P' => Self::Segments(0b0011_1101),
'U' => Self::Segments(0b0111_1010),
'Ξ' => Self::Segments(0b0000_0111),
'.' => Self::Dot,
':' => Self::Colon,
'!' => Self::Exclamation,
'?' => Self::Question,
_ => Self::Invalid,
}
}
}
impl Symbol {
fn width<C: PixelColor>(self, digitals: &Digitals<C>) -> i32 {
match self {
Self::Dot => digitals.width / 2,
Self::Colon | Self::Exclamation => digitals.width + digitals.stroke + 1,
Self::Question => 0, // TODO
Self::Segments(_) => digitals.width + digitals.stroke + 1,
Self::Invalid => panic!("Invalid symbol encountered"),
}
}
}
impl<C> Digitals<C>
where
C: PixelColor,
{
pub fn new(height: i32, style: PrimitiveStyle<C>) -> Self {
assert!(height > 7); // ensure we don't divide to 0 and have width / 2 > 1
Self {
width: height / 2,
height,
stroke: height / 16,
style,
}
}
pub fn draw_str<D: DrawTarget<C>>(
&self,
line: &str,
mut offset: Point,
display: &mut D,
) -> Result<u8, D::Error> {
let mut count = 0;
for c in line.chars() {
let symbol = Symbol::from(c);
if self.draw_symbol(symbol, offset, display)? {
count += 1; // keep count of symbols that made it
}
offset.x += symbol.width(&self)
}
Ok(count)
}
pub fn draw_str_diff<D: DrawTarget<C>>(
&self,
previous_line: &str,
line: &str,
mut offset: Point,
display: &mut D,
) -> Result<u8, D::Error> {
assert!(previous_line.len() == line.len());
let mut count = 0;
for pair in line.chars().zip(previous_line.chars()) {
let symbol = Symbol::from(pair.0);
if pair.0 != pair.1 && self.draw_symbol(symbol, offset, display)? {
count += 1; // keep count of symbols that made it
}
offset.x += symbol.width(&self)
}
Ok(count)
}
fn draw_dot<D: DrawTarget<C>>(&self, offset: Point, display: &mut D) -> Result<bool, D::Error> {
let x = self.width / 4 - self.stroke;
let y = self.height - self.stroke;
let s = self.stroke;
let mut rect = Rectangle::new(Point::new(x, y), Point::new(x + s, y + s));
rect.translate_mut(offset);
display.draw_rectangle(&rect.into_styled(self.style))?;
Ok(true)
}
fn draw_colon<D: DrawTarget<C>>(
&self,
offset: Point,
display: &mut D,
) -> Result<bool, D::Error> {
let mut draw_dot = |x, y, s| -> Result<(), D::Error> {
let mut rect = Rectangle::new(Point::new(x - s, y - s), Point::new(x + s, y + s));
rect.translate_mut(offset);
display.draw_rectangle(&rect.into_styled(self.style))
};
draw_dot(self.width / 2, self.height / 3, self.stroke / 2)?;
draw_dot(self.width / 2, self.height / 3 * 2, self.stroke / 2)?;
Ok(true)
}
fn draw_exclamation<D: DrawTarget<C>>(
&self,
offset: Point,
display: &mut D,
) -> Result<bool, D::Error> {
let mut rect = Rectangle::new(
Point::new(self.width / 2 - self.stroke / 2, 0),
Point::new(self.width / 2 + self.stroke / 2, self.height / 3 * 2),
);
rect.translate_mut(offset);
display.draw_rectangle(&rect.into_styled(self.style))?;
let mut rect = Rectangle::new(
Point::new(self.width / 2 - self.stroke / 2, self.height - self.stroke),
Point::new(self.width / 2 + self.stroke / 2, self.height),
);
rect.translate_mut(offset);
display.draw_rectangle(&rect.into_styled(self.style))?;
Ok(true)
}
fn draw_question<D: DrawTarget<C>>(
&self,
offset: Point,
display: &mut D,
) -> Result<bool, D::Error> {
Ok(false) // TODO
}
fn draw_segment<D: DrawTarget<C>>(
&self,
segment: Segment,
offset: Point,
display: &mut D,
) -> Result<(), D::Error> {
let w = self.width;
let h = self.height;
let s = self.stroke;
let (sx, sy, ex, ey) = match segment {
Segment::Top => (0, 0, w, s),
Segment::Bottom => (0, h - s, w, h),
Segment::Middle => (0, h / 2 - s / 2, w, h / 2 + s / 2),
Segment::TopLeft => (0, 0, s, h / 2),
Segment::TopRight => (w - s, 0, w, h / 2),
Segment::BottomLeft => (0, h / 2, s, h),
Segment::BottomRight => (w - s, h / 2, w, h),
};
let mut rect = Rectangle::new(Point::new(sx, sy), Point::new(ex, ey));
rect.translate_mut(offset);
display.draw_rectangle(&rect.into_styled(self.style))
}
fn draw_segments<D: DrawTarget<C>>(
&self,
segments: u8,
offset: Point,
display: &mut D,
) -> Result<bool, D::Error> {
let mut mask = 0b0000_0001;
for _ in 0..8 {
// loop all 8 bits of u8/segments
if segments & mask > 0 {
// if i_th bit is set, draw given segment
let segment = Segment::from(mask);
self.draw_segment(segment, offset, display)?;
}
mask <<= 1;
}
Ok(true)
}
fn draw_symbol<D: DrawTarget<C>>(
&self,
symbol: Symbol,
offset: Point,
display: &mut D,
) -> Result<bool, D::Error> {
match symbol {
Symbol::Dot => self.draw_dot(offset, display),
Symbol::Colon => self.draw_colon(offset, display),
Symbol::Exclamation => self.draw_exclamation(offset, display),
Symbol::Question => self.draw_question(offset, display),
Symbol::Segments(segments) => self.draw_segments(segments, offset, display),
Symbol::Invalid => Ok(false),
}
}
}