mod patterns; pub use patterns::*; mod types; pub use types::{DashboardPattern, Pattern, RGB}; pub const FPS: u8 = 30; pub trait UI { fn check_event(&self) -> Option; fn update_lights(&self, dashboard_lights: DashboardPattern, lights: Pattern); } pub trait Animation { fn tick(&mut self, time: std::time::Instant) -> (DashboardPattern, Pattern); } pub struct DefaultAnimation {} impl Animation for DefaultAnimation { fn tick(&mut self, _: std::time::Instant) -> (DashboardPattern, Pattern) { (PRIDE_DASHBOARD, PRIDE) } } pub struct Fade { starting_dashboard: DashboardPattern, starting_lights: Pattern, start_time: std::time::Instant, dashboard_slope: Vec>, body_slope: Vec>, frames: u8, } impl Fade { fn new( dashboard: DashboardPattern, lights: Pattern, ending_dashboard: DashboardPattern, ending_lights: Pattern, frames: u8, time: std::time::Instant, ) -> Self { let mut dashboard_slope = Vec::new(); let mut body_slope = Vec::new(); for i in 0..3 { let slope = RGB { r: (ending_dashboard[i].r as f64 - dashboard[i].r as f64) / frames as f64, g: (ending_dashboard[i].g as f64 - dashboard[i].g as f64) / frames as f64, b: (ending_dashboard[i].b as f64 - dashboard[i].b as f64) / frames as f64, }; dashboard_slope.push(slope); } for i in 0..60 { let slope = RGB { r: (ending_lights[i].r as f64 - lights[i].r as f64) / frames as f64, g: (ending_lights[i].g as f64 - lights[i].g as f64) / frames as f64, b: (ending_lights[i].b as f64 - lights[i].b as f64) / frames as f64, }; body_slope.push(slope); } println!("dashboard slope: {:?}", dashboard_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: std::time::Instant) -> (DashboardPattern, Pattern) { let mut frames: u8 = ((time - self.start_time).as_millis() as f64 / FPS as f64) as u8; if frames > self.frames { frames = self.frames } let mut dashboard_pattern: DashboardPattern = OFF_DASHBOARD; let mut body_pattern: Pattern = OFF; let apply = |value: f64, frames: f64, slope: f64| -> f64 { value + frames * slope }; for i in 0..3 { dashboard_pattern[i].r = apply( self.starting_dashboard[i].r as f64, frames as f64, self.dashboard_slope[i].r as f64, ) as u8; dashboard_pattern[i].g = apply( self.starting_dashboard[i].g as f64, frames as f64, self.dashboard_slope[i].g as f64, ) as u8; dashboard_pattern[i].b = apply( self.starting_dashboard[i].b as f64, frames as f64, self.dashboard_slope[i].b as f64, ) as u8; } for i in 0..60 { body_pattern[i].r = apply( self.starting_lights[i].r as f64, frames as f64, self.body_slope[i].r as f64, ) as u8; body_pattern[i].g = apply( self.starting_lights[i].g as f64, frames as f64, self.body_slope[i].g as f64, ) as u8; body_pattern[i].b = apply( self.starting_lights[i].b as f64, frames as f64, self.body_slope[i].b as f64, ) as u8; } (dashboard_pattern, body_pattern) } } #[derive(Debug)] pub enum FadeDirection { FadeIn, FadeOut, } pub struct LeftBlinker { fade_in: Fade, fade_out: Fade, direction: FadeDirection, start_time: std::time::Instant, frames: u8, } impl LeftBlinker { fn new( starting_dashboard: DashboardPattern, starting_body: Pattern, time: std::time::Instant, ) -> Self { let mut ending_dashboard = starting_dashboard.clone(); ending_dashboard[0].r = LEFT_DASHBOARD[0].r; ending_dashboard[0].g = LEFT_DASHBOARD[0].g; ending_dashboard[0].b = LEFT_DASHBOARD[0].b; let mut ending_body = starting_body.clone(); for i in 0..30 { ending_body[i].r = LEFT[i].r; ending_body[i].g = LEFT[i].g; ending_body[i].b = LEFT[i].b; } LeftBlinker { fade_in: Fade::new( starting_dashboard.clone(), starting_body.clone(), ending_dashboard.clone(), ending_body.clone(), LEFT_FRAMES, time, ), fade_out: Fade::new( ending_dashboard.clone(), ending_body.clone(), starting_dashboard.clone(), starting_body.clone(), LEFT_FRAMES, time, ), direction: FadeDirection::FadeIn, start_time: time, frames: LEFT_FRAMES, } } } impl Animation for LeftBlinker { fn tick(&mut self, time: std::time::Instant) -> (DashboardPattern, Pattern) { let frames: u8 = ((time - self.start_time).as_millis() as f64 / FPS as f64) as u8; 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; } println!("anim: {:?} {}", self.direction, frames); 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: Pattern, } 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, } } fn update_state(&mut self, time: std::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, 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, DEFAULT_FRAMES, time, )) } State::Pattern(_) => {} State::Brake => { self.current_animation = Box::new(Fade::new( self.dashboard_lights.clone(), self.lights.clone(), BRAKE_DASHBOARD, BRAKES, BRAKE_FRAMES, time, )); } State::LeftBlinker => { self.current_animation = Box::new(LeftBlinker::new( self.dashboard_lights.clone(), self.lights.clone(), time, )); } State::RightBlinker => (), State::BrakeLeftBlinker => (), State::BrakeRightBlinker => (), } } pub fn tick(&mut self, time: std::time::Instant) { match self.ui.check_event() { Some(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 = State::Pattern(0), 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 => {} } self.update_state(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); } }