diff --git a/bike-lights/core/src/animations/blinker.rs b/bike-lights/core/src/animations/blinker.rs new file mode 100644 index 0000000..b42ccb9 --- /dev/null +++ b/bike-lights/core/src/animations/blinker.rs @@ -0,0 +1,121 @@ +use fixed::types::U16F0; + +use crate::{ + calculate_frames, Animation, BodyPattern, DashboardPattern, Fade, FadeDirection, Instant, BLINKER_FRAMES, LEFT_BLINKER_BODY, LEFT_BLINKER_DASHBOARD, OFF_BODY, OFF_DASHBOARD, RIGHT_BLINKER_BODY, RIGHT_BLINKER_DASHBOARD +}; + +pub enum BlinkerDirection { + Left, + Right, +} + +pub struct Blinker { + transition: Fade, + fade_in: Fade, + fade_out: Fade, + direction: FadeDirection, + + start_time: Instant, + frames: U16F0, +} + +impl Blinker { + pub fn new( + starting_dashboard: DashboardPattern, + starting_body: BodyPattern, + direction: BlinkerDirection, + time: Instant, + ) -> Self { + let mut ending_dashboard = OFF_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 = OFF_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 { + transition: Fade::new( + starting_dashboard.clone(), + starting_body.clone(), + ending_dashboard.clone(), + ending_body.clone(), + BLINKER_FRAMES, + time, + ), + fade_in: Fade::new( + OFF_DASHBOARD.clone(), + OFF_BODY.clone(), + ending_dashboard.clone(), + ending_body.clone(), + BLINKER_FRAMES, + time, + ), + fade_out: Fade::new( + ending_dashboard.clone(), + ending_body.clone(), + OFF_DASHBOARD.clone(), + OFF_BODY.clone(), + BLINKER_FRAMES, + time, + ), + direction: FadeDirection::Transition, + 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::Transition => { + self.direction = FadeDirection::FadeOut; + self.fade_out.start_time = time; + } + 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::Transition => self.transition.tick(time), + FadeDirection::FadeIn => self.fade_in.tick(time), + FadeDirection::FadeOut => self.fade_out.tick(time), + } + } +} diff --git a/bike-lights/core/src/animations/fade.rs b/bike-lights/core/src/animations/fade.rs new file mode 100644 index 0000000..b6aef74 --- /dev/null +++ b/bike-lights/core/src/animations/fade.rs @@ -0,0 +1,100 @@ +use fixed::types::{I8F8, U16F0}; + +use crate::{calculate_frames, calculate_slope, linear_ease, Animation, BodyPattern, DashboardPattern, Instant, OFF_BODY, OFF_DASHBOARD, RGB}; + +pub struct Fade { + starting_dashboard: DashboardPattern, + starting_lights: BodyPattern, + + pub start_time: Instant, + dashboard_slope: [RGB; 3], + body_slope: [RGB; 60], + frames: U16F0, +} + +impl Fade { + pub 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 { + Transition, + FadeIn, + FadeOut, +} diff --git a/bike-lights/core/src/animations/flag_ripple.rs b/bike-lights/core/src/animations/flag_ripple.rs new file mode 100644 index 0000000..a58ae5c --- /dev/null +++ b/bike-lights/core/src/animations/flag_ripple.rs @@ -0,0 +1,43 @@ +use crate::{Animation, BodyPattern, DashboardPattern, Instant}; + +pub struct FlagRipple { + dashboard: DashboardPattern, + body: BodyPattern, + + centers: [usize; 12], +} + +impl FlagRipple { + fn new(dashboard: DashboardPattern, body: BodyPattern) -> Self { + Self { + dashboard, + body, + centers: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60], + } + } +} + +impl Animation for FlagRipple { + fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern, Option) { + // How does a flag ripple work? I think have some center points that are darker and have a + // bit of a darker pattern around them. + + let mut body_pattern = self.body.clone(); + + for i in 0..60 { + if self.centers.contains(&i) { + body_pattern[i].r = self.body[i].r / 2; + body_pattern[i].g = self.body[i].g / 2; + body_pattern[i].b = self.body[i].b / 2; + } else { + body_pattern[i].r = self.body[i].r; + body_pattern[i].g = self.body[i].g; + body_pattern[i].b = self.body[i].b; + } + } + + (self.dashboard, body_pattern, None) + } +} + + diff --git a/bike-lights/core/src/animations/mod.rs b/bike-lights/core/src/animations/mod.rs new file mode 100644 index 0000000..b878412 --- /dev/null +++ b/bike-lights/core/src/animations/mod.rs @@ -0,0 +1,31 @@ +use fixed::types::{I48F16, I8F8, U128F0, U16F0}; +use az::*; + +use crate::{BodyPattern, DashboardPattern, Instant, FPS}; + +mod blinker; +pub use blinker::*; + +mod fade; +pub use fade::*; + +pub trait Animation { + fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern); +} + +pub 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() +} + +pub 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() +} + +pub 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() +} + + diff --git a/bike-lights/core/src/lib.rs b/bike-lights/core/src/lib.rs index 824f1ca..ec7484a 100644 --- a/bike-lights/core/src/lib.rs +++ b/bike-lights/core/src/lib.rs @@ -12,27 +12,15 @@ use core::{ }; use fixed::types::{I48F16, I8F8, U128F0, U16F0}; +mod animations; +pub use animations::*; + 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, PartialOrd, Ord, PartialEq, Eq)] pub struct Instant(pub U128F0); @@ -65,233 +53,6 @@ pub trait UI { 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) { - (WATER_DASHBOARD, WATER_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 { - Transition, - FadeIn, - FadeOut, -} - -pub enum BlinkerDirection { - Left, - Right, -} - -pub struct Blinker { - transition: Fade, - 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 = OFF_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 = OFF_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 { - transition: Fade::new( - starting_dashboard.clone(), - starting_body.clone(), - ending_dashboard.clone(), - ending_body.clone(), - BLINKER_FRAMES, - time, - ), - fade_in: Fade::new( - OFF_DASHBOARD.clone(), - OFF_BODY.clone(), - ending_dashboard.clone(), - ending_body.clone(), - BLINKER_FRAMES, - time, - ), - fade_out: Fade::new( - ending_dashboard.clone(), - ending_body.clone(), - OFF_DASHBOARD.clone(), - OFF_BODY.clone(), - BLINKER_FRAMES, - time, - ), - direction: FadeDirection::Transition, - 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::Transition => { - self.direction = FadeDirection::FadeOut; - self.fade_out.start_time = time; - } - 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::Transition => self.transition.tick(time), - FadeDirection::FadeIn => self.fade_in.tick(time), - FadeDirection::FadeOut => self.fade_out.tick(time), - } - } -} - #[derive(Clone, Debug)] pub enum Event { Brake,