#![no_std] use embedded_graphics::prelude::*; use embedded_graphics::primitives::Rectangle; use embedded_graphics::style::PrimitiveStyle; pub struct Digitals 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, } #[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 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 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(self, digitals: &Digitals) -> 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 Digitals where C: PixelColor, { pub fn new(height: i32, style: PrimitiveStyle) -> 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>( &self, line: &str, mut offset: Point, display: &mut D, ) -> Result { 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>( &self, previous_line: &str, line: &str, mut offset: Point, display: &mut D, ) -> Result { 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>(&self, offset: Point, display: &mut D) -> Result { 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>( &self, offset: Point, display: &mut D, ) -> Result { 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>( &self, offset: Point, display: &mut D, ) -> Result { 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>( &self, offset: Point, display: &mut D, ) -> Result { Ok(false) // TODO } fn draw_segment>( &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>( &self, segments: u8, offset: Point, display: &mut D, ) -> Result { 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>( &self, symbol: Symbol, offset: Point, display: &mut D, ) -> Result { 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), } } }