implement basics
parent
a97250d9ac
commit
a999746c69
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "digitals"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Ales Katona <ales@katona.me>"]
|
||||||
|
edition = "2018"
|
||||||
|
license = "Apache-2"
|
||||||
|
repository = "https://git.bitsmart.ltd/almindor/digitals.git"
|
||||||
|
readme = "README.md"
|
||||||
|
documentation = "https://docs.rs/digitals"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embedded-graphics = "0.6.0-beta.1"
|
@ -0,0 +1,280 @@
|
|||||||
|
#![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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue