#![no_std] extern crate alloc; use alloc::boxed::Box; use az::*; use core::{ clone::Clone, cmp::PartialEq, default::Default, ops::{Add, Sub}, option::Option, }; use fixed::types::{I48F16, I8F8, U128F0, U16F0}; mod patterns; pub use patterns::*; mod types; pub use types::{BodyPattern, DashboardPattern, RGB}; fn calculate_frames(starting_time: U128F0, now: U128F0) -> U16F0 { let frames_128 = (now - starting_time) / U128F0::from(FPS); (frames_128 % U128F0::from(U16F0::MAX)).cast() } fn calculate_slope(start: I8F8, end: I8F8, frames: U16F0) -> I8F8 { let slope_i16f16 = (I48F16::from(end) - I48F16::from(start)) / I48F16::from(frames); slope_i16f16.saturating_as() } fn linear_ease(value: I8F8, frames: U16F0, slope: I8F8) -> I8F8 { let value_i16f16 = I48F16::from(value) + I48F16::from(frames) * I48F16::from(slope); value_i16f16.saturating_as() } #[derive(Clone, Copy)] pub struct Instant(pub U128F0); impl Default for Instant { fn default() -> Self { Self(U128F0::from(0 as u8)) } } impl Add for Instant { type Output = Self; fn add(self, r: Self) -> Self::Output { Self(self.0 + r.0) } } impl Sub for Instant { type Output = Self; fn sub(self, r: Self) -> Self::Output { Self(self.0 - r.0) } } pub const FPS: u8 = 30; pub trait UI { fn check_event(&self) -> Option; fn update_lights(&self, dashboard_lights: DashboardPattern, body_lights: BodyPattern); } pub trait Animation { fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern); } pub struct DefaultAnimation {} impl Animation for DefaultAnimation { fn tick(&mut self, _: Instant) -> (DashboardPattern, BodyPattern) { (PRIDE_DASHBOARD, PRIDE_BODY) } } pub struct Fade { starting_dashboard: DashboardPattern, starting_lights: BodyPattern, start_time: Instant, dashboard_slope: [RGB; 3], body_slope: [RGB; 60], frames: U16F0, } impl Fade { fn new( dashboard: DashboardPattern, lights: BodyPattern, ending_dashboard: DashboardPattern, ending_lights: BodyPattern, frames: U16F0, time: Instant, ) -> Self { let mut dashboard_slope = [Default::default(); 3]; let mut body_slope = [Default::default(); 60]; for i in 0..3 { let slope = RGB { r: calculate_slope(dashboard[i].r, ending_dashboard[i].r, frames), g: calculate_slope(dashboard[i].g, ending_dashboard[i].g, frames), b: calculate_slope(dashboard[i].b, ending_dashboard[i].b, frames), }; dashboard_slope[i] = slope; } for i in 0..60 { let slope = RGB { r: calculate_slope(lights[i].r, ending_lights[i].r, frames), g: calculate_slope(lights[i].g, ending_lights[i].g, frames), b: calculate_slope(lights[i].b, ending_lights[i].b, frames), }; body_slope[i] = slope; } Self { starting_dashboard: dashboard, starting_lights: lights, start_time: time, dashboard_slope, body_slope, frames, } } } impl Animation for Fade { fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern) { let mut frames = calculate_frames(self.start_time.0, time.0); if frames > self.frames { frames = self.frames } let mut dashboard_pattern: DashboardPattern = OFF_DASHBOARD; let mut body_pattern: BodyPattern = OFF_BODY; for i in 0..3 { dashboard_pattern[i].r = linear_ease( self.starting_dashboard[i].r, frames, self.dashboard_slope[i].r, ); dashboard_pattern[i].g = linear_ease( self.starting_dashboard[i].g, frames, self.dashboard_slope[i].g, ); dashboard_pattern[i].b = linear_ease( self.starting_dashboard[i].b, frames, self.dashboard_slope[i].b, ); } for i in 0..60 { body_pattern[i].r = linear_ease(self.starting_lights[i].r, frames, self.body_slope[i].r); body_pattern[i].g = linear_ease(self.starting_lights[i].g, frames, self.body_slope[i].g); body_pattern[i].b = linear_ease(self.starting_lights[i].b, frames, self.body_slope[i].b); } (dashboard_pattern, body_pattern) } } #[derive(Debug)] pub enum FadeDirection { FadeIn, FadeOut, } pub enum BlinkerDirection { Left, Right, } pub struct Blinker { fade_in: Fade, fade_out: Fade, direction: FadeDirection, start_time: Instant, frames: U16F0, } impl Blinker { fn new( starting_dashboard: DashboardPattern, starting_body: BodyPattern, direction: BlinkerDirection, time: Instant, ) -> Self { let mut ending_dashboard = starting_dashboard.clone(); match direction { BlinkerDirection::Left => { ending_dashboard[0].r = LEFT_BLINKER_DASHBOARD[0].r; ending_dashboard[0].g = LEFT_BLINKER_DASHBOARD[0].g; ending_dashboard[0].b = LEFT_BLINKER_DASHBOARD[0].b; } BlinkerDirection::Right => { ending_dashboard[2].r = RIGHT_BLINKER_DASHBOARD[2].r; ending_dashboard[2].g = RIGHT_BLINKER_DASHBOARD[2].g; ending_dashboard[2].b = RIGHT_BLINKER_DASHBOARD[2].b; } } let mut ending_body = starting_body.clone(); match direction { BlinkerDirection::Left => { for i in 0..30 { ending_body[i].r = LEFT_BLINKER_BODY[i].r; ending_body[i].g = LEFT_BLINKER_BODY[i].g; ending_body[i].b = LEFT_BLINKER_BODY[i].b; } } BlinkerDirection::Right => { for i in 30..60 { ending_body[i].r = RIGHT_BLINKER_BODY[i].r; ending_body[i].g = RIGHT_BLINKER_BODY[i].g; ending_body[i].b = RIGHT_BLINKER_BODY[i].b; } } } Blinker { fade_in: Fade::new( starting_dashboard.clone(), starting_body.clone(), ending_dashboard.clone(), ending_body.clone(), BLINKER_FRAMES, time, ), fade_out: Fade::new( ending_dashboard.clone(), ending_body.clone(), starting_dashboard.clone(), starting_body.clone(), BLINKER_FRAMES, time, ), direction: FadeDirection::FadeIn, start_time: time, frames: BLINKER_FRAMES, } } } impl Animation for Blinker { fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern) { let frames = calculate_frames(self.start_time.0, time.0); if frames > self.frames { match self.direction { FadeDirection::FadeIn => { self.direction = FadeDirection::FadeOut; self.fade_out.start_time = time; } FadeDirection::FadeOut => { self.direction = FadeDirection::FadeIn; self.fade_in.start_time = time; } } self.start_time = time; } match self.direction { FadeDirection::FadeIn => self.fade_in.tick(time), FadeDirection::FadeOut => self.fade_out.tick(time), } } } #[derive(Clone, Debug)] pub enum Event { Brake, BrakeRelease, LeftBlinker, NextPattern, PreviousPattern, RightBlinker, } #[derive(Clone, PartialEq)] pub enum State { Pattern(u8), Brake, LeftBlinker, RightBlinker, BrakeLeftBlinker, BrakeRightBlinker, } pub struct App { ui: Box, state: State, home_state: State, current_animation: Box, dashboard_lights: DashboardPattern, lights: BodyPattern, } impl App { pub fn new(ui: Box) -> Self { Self { ui, state: State::Pattern(0), home_state: State::Pattern(0), current_animation: Box::new(DefaultAnimation {}), dashboard_lights: OFF_DASHBOARD, lights: OFF_BODY, } } fn update_animation(&mut self, time: Instant) { match self.state { State::Pattern(0) => { self.current_animation = Box::new(Fade::new( self.dashboard_lights.clone(), self.lights.clone(), PRIDE_DASHBOARD, PRIDE_BODY, DEFAULT_FRAMES, time, )) } State::Pattern(1) => { self.current_animation = Box::new(Fade::new( self.dashboard_lights.clone(), self.lights.clone(), TRANS_PRIDE_DASHBOARD, TRANS_PRIDE_BODY, DEFAULT_FRAMES, time, )) } State::Pattern(_) => {} State::Brake => { self.current_animation = Box::new(Fade::new( self.dashboard_lights.clone(), self.lights.clone(), BRAKES_DASHBOARD, BRAKES_BODY, BRAKES_FRAMES, time, )); } State::LeftBlinker => { self.current_animation = Box::new(Blinker::new( self.dashboard_lights.clone(), self.lights.clone(), BlinkerDirection::Left, time, )); } State::RightBlinker => { self.current_animation = Box::new(Blinker::new( self.dashboard_lights.clone(), self.lights.clone(), BlinkerDirection::Right, time, )); } State::BrakeLeftBlinker => (), State::BrakeRightBlinker => (), } } fn update_state(&mut self, event: Event) { match event { Event::Brake => { if self.state == State::Brake { self.state = self.home_state.clone(); } else { self.state = State::Brake; } } Event::BrakeRelease => self.state = self.home_state.clone(), Event::LeftBlinker => match self.state { State::Brake => self.state = State::BrakeLeftBlinker, State::BrakeLeftBlinker => self.state = State::Brake, State::LeftBlinker => self.state = self.home_state.clone(), _ => self.state = State::LeftBlinker, }, Event::NextPattern => match self.state { State::Pattern(i) => { let next = i + 1; self.state = State::Pattern(if next > 1 { 0 } else { next }); self.home_state = self.state.clone(); } _ => (), }, Event::PreviousPattern => match self.state { State::Pattern(i) => { if i == 0 { self.state = State::Pattern(1); } else { self.state = State::Pattern(i - 1); } self.home_state = self.state.clone(); } _ => (), }, Event::RightBlinker => match self.state { State::Brake => self.state = State::BrakeRightBlinker, State::BrakeRightBlinker => self.state = State::Brake, State::RightBlinker => self.state = self.home_state.clone(), _ => self.state = State::RightBlinker, }, } } pub fn tick(&mut self, time: Instant) { match self.ui.check_event() { Some(event) => { self.update_state(event); self.update_animation(time); } None => {} }; let (dashboard, lights) = self.current_animation.tick(time); self.dashboard_lights = dashboard.clone(); self.lights = lights.clone(); self.ui.update_lights(dashboard, lights); } }