use crate::control::DisplayControl;
use crate::timer::DisplayTimer;
use crate::render::{Render, BRIGHTNESSES, MAX_BRIGHTNESS};
#[derive(Copy, Clone, Debug)]
struct ColumnSet (u16);
impl ColumnSet {
const fn empty() -> ColumnSet {
ColumnSet(0)
}
fn set(&mut self, col: usize) {
self.0 |= 1<<col;
}
fn as_u32(&self) -> u32 {
self.0 as u32
}
fn is_empty(&self) -> bool {
self.0 == 0
}
}
#[derive(Copy, Clone, Debug)]
pub struct RowPlan (
[ColumnSet; BRIGHTNESSES],
);
impl RowPlan {
pub const fn default() -> RowPlan {
RowPlan([ColumnSet::empty(); BRIGHTNESSES])
}
fn clear(&mut self) {
self.0 = RowPlan::default().0;
}
fn lit_cols(&self, brightness: u8) -> ColumnSet {
self.0[brightness as usize]
}
fn light_col(&mut self, brightness: u8, col: usize) {
self.0[brightness as usize].set(col);
}
}
pub trait Matrix {
const MATRIX_COLS: usize;
const MATRIX_ROWS: usize;
const IMAGE_COLS: usize;
const IMAGE_ROWS: usize;
fn image_coordinates(col: usize, row: usize) -> Option<(usize, usize)>;
}
pub trait Frame: Copy + Default {
type Mtx: Matrix;
const COLS: usize = Self::Mtx::MATRIX_COLS;
const ROWS: usize = Self::Mtx::MATRIX_ROWS;
fn row_plan(&self, row: usize) -> &RowPlan;
fn row_plan_mut(&mut self, row: usize) -> &mut RowPlan;
fn set<T>(&mut self, image: &T) where T: Render + ?Sized {
for row in 0..Self::ROWS {
let plan = self.row_plan_mut(row);
plan.clear();
for col in 0..Self::COLS {
if let Some((x, y)) = Self::Mtx::image_coordinates(col, row) {
let brightness = image.brightness_at(x, y);
plan.light_col(brightness, col);
}
}
}
}
}
const CYCLE_TICKS: u16 = 375;
const GREYSCALE_TIMINGS: [u16; BRIGHTNESSES-2] = [
373, 371, 367, 360, 347, 322, 273, 176, ];
#[derive(PartialEq, Eq, Debug)]
pub enum Event {
SwitchedRow,
UpdatedRow,
Unknown,
}
impl Event {
pub fn is_new_row(self) -> bool {
self == Event::SwitchedRow
}
}
pub fn initialise_timer(timer: &mut impl DisplayTimer) {
timer.initialise_cycle(CYCLE_TICKS);
}
pub fn initialise_control(control: &mut impl DisplayControl) {
control.initialise_for_display();
}
pub struct Display<F: Frame> {
row_strobe : usize,
next_brightness : u8,
frame : F,
current_plan : RowPlan
}
impl<F: Frame> Display<F> {
pub fn new() -> Display<F> {
Display {
row_strobe: 0,
next_brightness: 0,
frame: F::default(),
current_plan: RowPlan::default(),
}
}
pub fn set_frame(&mut self, frame: &F) {
self.frame = *frame;
}
fn render_row(&mut self,
control: &mut impl DisplayControl,
timer: &mut impl DisplayTimer) {
assert! (self.row_strobe < F::ROWS);
self.row_strobe += 1;
if self.row_strobe == F::ROWS {self.row_strobe = 0};
let plan = self.frame.row_plan(self.row_strobe);
let lit_cols = plan.lit_cols(MAX_BRIGHTNESS);
control.display_row_leds(self.row_strobe, lit_cols.as_u32());
self.current_plan = *plan;
self.next_brightness = MAX_BRIGHTNESS;
self.program_next_brightness(timer);
if self.next_brightness != 0 {
timer.enable_secondary();
}
}
fn render_subrow(&mut self,
control: &mut impl DisplayControl,
timer: &mut impl DisplayTimer) {
let additional_cols = self.current_plan.lit_cols(self.next_brightness);
control.light_current_row_leds(additional_cols.as_u32());
self.program_next_brightness(timer);
}
fn program_next_brightness(&mut self, timer: &mut impl DisplayTimer) {
loop {
self.next_brightness -= 1;
if self.next_brightness == 0 {
timer.disable_secondary();
break;
}
if !self.current_plan.lit_cols(self.next_brightness).is_empty() {
timer.program_secondary(
GREYSCALE_TIMINGS[(self.next_brightness-1) as usize]
);
break;
}
}
}
pub fn handle_event(&mut self,
timer: &mut impl DisplayTimer,
control: &mut impl DisplayControl) -> Event {
let row_timer_fired = timer.check_primary();
let brightness_timer_fired = timer.check_secondary();
if row_timer_fired {
self.render_row(control, timer);
Event::SwitchedRow
} else if brightness_timer_fired {
self.render_subrow(control, timer);
Event::UpdatedRow
} else {
Event::Unknown
}
}
}