monorepo/bike-lights/core/src/lib.rs

406 lines
12 KiB
Rust
Raw Normal View History

#![no_std]
extern crate alloc;
use alloc::boxed::Box;
use core::{clone::Clone, cmp::PartialEq, ops::Sub, option::Option};
2023-11-27 01:57:28 +00:00
mod patterns;
pub use patterns::*;
mod types;
pub use types::{DashboardPattern, Pattern, RGB};
#[derive(Clone, Copy)]
pub struct Instant(pub u128);
impl Sub for Instant {
type Output = Self;
fn sub(self, r: Self) -> Self::Output {
Self(self.0 - r.0)
}
}
pub const FPS: u8 = 30;
2023-11-27 01:57:28 +00:00
pub trait UI {
fn check_event(&self) -> Option<Event>;
2023-11-27 01:57:28 +00:00
fn update_lights(&self, dashboard_lights: DashboardPattern, lights: Pattern);
}
pub trait Animation {
fn tick(&mut self, time: Instant) -> (DashboardPattern, Pattern);
2023-11-27 01:57:28 +00:00
}
pub struct DefaultAnimation {}
impl Animation for DefaultAnimation {
fn tick(&mut self, _: Instant) -> (DashboardPattern, Pattern) {
2023-11-27 01:57:28 +00:00
(PRIDE_DASHBOARD, PRIDE)
}
}
pub struct Fade {
starting_dashboard: DashboardPattern,
starting_lights: Pattern,
start_time: Instant,
dashboard_slope: [RGB<f64>; 3],
body_slope: [RGB<f64>; 60],
frames: u8,
}
impl Fade {
fn new(
dashboard: DashboardPattern,
lights: Pattern,
ending_dashboard: DashboardPattern,
ending_lights: Pattern,
frames: u8,
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: (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[i] = 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[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, Pattern) {
let mut frames: u8 = ((time - self.start_time).0 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 enum BlinkerDirection {
Left,
Right,
}
pub struct Blinker {
fade_in: Fade,
fade_out: Fade,
direction: FadeDirection,
start_time: Instant,
frames: u8,
}
impl Blinker {
fn new(
starting_dashboard: DashboardPattern,
starting_body: Pattern,
direction: BlinkerDirection,
time: Instant,
) -> Self {
let mut ending_dashboard = starting_dashboard.clone();
match direction {
BlinkerDirection::Left => {
ending_dashboard[0].r = LEFT_DASHBOARD[0].r;
ending_dashboard[0].g = LEFT_DASHBOARD[0].g;
ending_dashboard[0].b = LEFT_DASHBOARD[0].b;
}
BlinkerDirection::Right => {
ending_dashboard[2].r = RIGHT_DASHBOARD[2].r;
ending_dashboard[2].g = RIGHT_DASHBOARD[2].g;
ending_dashboard[2].b = RIGHT_DASHBOARD[2].b;
}
}
let mut ending_body = starting_body.clone();
match direction {
BlinkerDirection::Left => {
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;
}
}
BlinkerDirection::Right => {
for i in 30..60 {
ending_body[i].r = RIGHT[i].r;
ending_body[i].g = RIGHT[i].g;
ending_body[i].b = RIGHT[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, Pattern) {
let frames: u8 = ((time - self.start_time).0 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;
}
match self.direction {
FadeDirection::FadeIn => self.fade_in.tick(time),
FadeDirection::FadeOut => self.fade_out.tick(time),
}
}
}
#[derive(Clone, Debug)]
2023-11-27 01:57:28 +00:00
pub enum Event {
Brake,
BrakeRelease,
LeftBlinker,
NextPattern,
PreviousPattern,
RightBlinker,
}
#[derive(Clone, PartialEq)]
2023-11-27 01:57:28 +00:00
pub enum State {
Pattern(u8),
Brake,
LeftBlinker,
RightBlinker,
BrakeLeftBlinker,
BrakeRightBlinker,
}
pub struct App {
ui: Box<dyn UI>,
state: State,
home_state: State,
2023-11-27 01:57:28 +00:00
current_animation: Box<dyn Animation>,
dashboard_lights: DashboardPattern,
lights: Pattern,
}
2023-11-27 01:57:28 +00:00
impl App {
pub fn new(ui: Box<dyn UI>) -> Self {
Self {
ui,
state: State::Pattern(0),
home_state: State::Pattern(0),
2023-11-27 01:57:28 +00:00
current_animation: Box::new(DefaultAnimation {}),
dashboard_lights: OFF_DASHBOARD,
lights: OFF,
}
}
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,
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(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);
2023-11-27 01:57:28 +00:00
self.dashboard_lights = dashboard.clone();
self.lights = lights.clone();
self.ui.update_lights(dashboard, lights);
}
}