Set up a bunch of animations and some state transitions!

This commit is contained in:
Savanni D'Gerinel 2023-11-26 23:30:45 -05:00
parent 782c442a21
commit 7d6413c91d
4 changed files with 580 additions and 26 deletions

View File

@ -1,28 +1,213 @@
use std::time::Duration;
mod patterns; mod patterns;
pub use patterns::*; pub use patterns::*;
mod types; mod types;
pub use types::{DashboardPattern, Pattern, RGB}; pub use types::{DashboardPattern, Pattern, RGB};
pub const FPS: u8 = 30;
pub trait UI { pub trait UI {
fn check_event(&self) -> Option<Event>; fn check_event(&self) -> Option<Event>;
fn update_lights(&self, dashboard_lights: DashboardPattern, lights: Pattern); fn update_lights(&self, dashboard_lights: DashboardPattern, lights: Pattern);
} }
pub trait Animation { pub trait Animation {
fn tick(&self, time: std::time::Instant) -> (DashboardPattern, Pattern); fn tick(&mut self, time: std::time::Instant) -> (DashboardPattern, Pattern);
} }
pub struct DefaultAnimation {} pub struct DefaultAnimation {}
impl Animation for DefaultAnimation { impl Animation for DefaultAnimation {
fn tick(&self, time: std::time::Instant) -> (DashboardPattern, Pattern) { fn tick(&mut self, _: std::time::Instant) -> (DashboardPattern, Pattern) {
(PRIDE_DASHBOARD, PRIDE) (PRIDE_DASHBOARD, PRIDE)
} }
} }
pub struct Fade {
starting_dashboard: DashboardPattern,
starting_lights: Pattern,
start_time: std::time::Instant,
dashboard_slope: Vec<RGB<f64>>,
body_slope: Vec<RGB<f64>>,
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)] #[derive(Clone, Debug)]
pub enum Event { pub enum Event {
Brake, Brake,
@ -33,7 +218,7 @@ pub enum Event {
RightBlinker, RightBlinker,
} }
#[derive(Clone)] #[derive(Clone, PartialEq)]
pub enum State { pub enum State {
Pattern(u8), Pattern(u8),
Brake, Brake,
@ -46,6 +231,7 @@ pub enum State {
pub struct App { pub struct App {
ui: Box<dyn UI>, ui: Box<dyn UI>,
state: State, state: State,
home_state: State,
current_animation: Box<dyn Animation>, current_animation: Box<dyn Animation>,
dashboard_lights: DashboardPattern, dashboard_lights: DashboardPattern,
lights: Pattern, lights: Pattern,
@ -56,17 +242,103 @@ impl App {
Self { Self {
ui, ui,
state: State::Pattern(0), state: State::Pattern(0),
home_state: State::Pattern(0),
current_animation: Box::new(DefaultAnimation {}), current_animation: Box::new(DefaultAnimation {}),
dashboard_lights: OFF_DASHBOARD, dashboard_lights: OFF_DASHBOARD,
lights: OFF, 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) { pub fn tick(&mut self, time: std::time::Instant) {
match self.ui.check_event() { match self.ui.check_event() {
Some(event) => println!("event received: {:?}", event), Some(event) => {
None => (), 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); let (dashboard, lights) = self.current_animation.tick(time);
self.dashboard_lights = dashboard.clone(); self.dashboard_lights = dashboard.clone();
self.lights = lights.clone(); self.lights = lights.clone();

View File

@ -1,7 +1,7 @@
use crate::RGB; use crate::RGB;
pub type DashboardPattern = [RGB; 3]; pub type DashboardPattern = [RGB<u8>; 3];
pub type Pattern = [RGB; 60]; pub type Pattern = [RGB<u8>; 60];
pub const OFF_DASHBOARD: DashboardPattern = [ pub const OFF_DASHBOARD: DashboardPattern = [
RGB { r: 0, g: 0, b: 0 }, RGB { r: 0, g: 0, b: 0 },
@ -72,17 +72,19 @@ pub const OFF: Pattern = [
RGB { r: 0, g: 0, b: 0 }, RGB { r: 0, g: 0, b: 0 },
]; ];
pub const DEFAULT_FRAMES: u8 = 30;
pub const PRIDE_DASHBOARD: DashboardPattern = [ pub const PRIDE_DASHBOARD: DashboardPattern = [
RGB { r: 228, g: 3, b: 3 }, RGB { r: 228, g: 3, b: 3 },
RGB { RGB {
r: 255, r: 0,
g: 237, g: 128,
b: 0, b: 38,
}, },
RGB { RGB {
r: 115, r: 36,
g: 41, g: 64,
b: 130, b: 142,
}, },
]; ];
@ -669,3 +671,269 @@ pub const TRANS_PRIDE: Pattern = [
b: 250, b: 250,
}, },
]; ];
pub const BRAKE_FRAMES: u8 = 15;
pub const BRAKE_DASHBOARD: DashboardPattern = [
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
];
pub const BRAKES: Pattern = [
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
RGB { r: 128, g: 0, b: 0 },
];
pub const LEFT_FRAMES: u8 = 15;
pub const LEFT_DASHBOARD: DashboardPattern = [
RGB {
r: 255,
g: 191,
b: 0,
},
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
];
pub const LEFT: Pattern = [
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB {
r: 255,
g: 191,
b: 0,
},
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
RGB { r: 0, g: 0, b: 0 },
];

View File

@ -1,9 +1,9 @@
#[derive(Clone)] #[derive(Clone, Default, Debug)]
pub struct RGB { pub struct RGB<T> {
pub r: u8, pub r: T,
pub g: u8, pub g: T,
pub b: u8, pub b: T,
} }
pub type DashboardPattern = [RGB; 3]; pub type DashboardPattern = [RGB<u8>; 3];
pub type Pattern = [RGB; 60]; pub type Pattern = [RGB<u8>; 60];

View File

@ -1,7 +1,7 @@
use adw::prelude::*; use adw::prelude::*;
use glib::{Object, Sender}; use glib::{Object, Sender};
use gtk::subclass::prelude::*; use gtk::subclass::prelude::*;
use lights_core::{App, DashboardPattern, Event, Pattern, RGB, UI}; use lights_core::{App, DashboardPattern, Event, Pattern, FPS, RGB, UI};
use std::{ use std::{
cell::RefCell, cell::RefCell,
env, env,
@ -73,7 +73,7 @@ impl DashboardLights {
s s
} }
pub fn set_lights(&self, lights: [RGB; 3]) { pub fn set_lights(&self, lights: [RGB<u8>; 3]) {
*self.imp().lights.borrow_mut() = lights; *self.imp().lights.borrow_mut() = lights;
self.queue_draw(); self.queue_draw();
} }
@ -200,7 +200,7 @@ impl BikeLights {
s s
} }
pub fn set_lights(&self, lights: [RGB; 60]) { pub fn set_lights(&self, lights: [RGB<u8>; 60]) {
*self.imp().lights.borrow_mut() = lights; *self.imp().lights.borrow_mut() = lights;
self.queue_draw(); self.queue_draw();
} }
@ -247,7 +247,7 @@ fn main() {
})); }));
loop { loop {
bike_app.tick(std::time::Instant::now()); bike_app.tick(std::time::Instant::now());
std::thread::sleep(std::time::Duration::from_millis(100)); std::thread::sleep(std::time::Duration::from_millis(1000 / (FPS as u64)));
} }
}); });
@ -303,6 +303,20 @@ fn main() {
let previous_pattern = gtk::Button::builder().label("Previous").build(); let previous_pattern = gtk::Button::builder().label("Previous").build();
let next_pattern = gtk::Button::builder().label("Next").build(); let next_pattern = gtk::Button::builder().label("Next").build();
previous_pattern.connect_clicked({
let event_tx = event_tx.clone();
move |_| {
let _ = event_tx.send(Event::PreviousPattern);
}
});
next_pattern.connect_clicked({
let event_tx = event_tx.clone();
move |_| {
let _ = event_tx.send(Event::NextPattern);
}
});
pattern_controls.append(&previous_pattern); pattern_controls.append(&previous_pattern);
pattern_controls.append(&next_pattern); pattern_controls.append(&next_pattern);
layout.append(&pattern_controls); layout.append(&pattern_controls);