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
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),
|
|
}
|
|
}
|
|
}
|