Compare commits
2 Commits
911bc97b69
...
f4d990546b
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | f4d990546b | |
Savanni D'Gerinel | 288cecc92f |
File diff suppressed because it is too large
Load Diff
|
@ -32,5 +32,5 @@ members = [
|
||||||
"sgf",
|
"sgf",
|
||||||
"timezone-testing",
|
"timezone-testing",
|
||||||
"tree",
|
"tree",
|
||||||
"visions/server", "gm-dash/server", "halloween-leds"
|
"visions/server", "gm-dash/server"
|
||||||
]
|
]
|
||||||
|
|
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<I8F8>; 3],
|
||||||
|
body_slope: [RGB<I8F8>; 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,
|
||||||
|
}
|
|
@ -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<Animation>) {
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
use fixed::types::{I16F0, I8F0, I8F8, U16F0, U8F0};
|
||||||
|
use az::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
calculate_frames, calculate_slope, linear_ease, sin, Animation, BodyPattern, DashboardPattern, Instant, OFF_BODY, OFF_DASHBOARD, RGB, WATER_1, WATER_BODY
|
||||||
|
};
|
||||||
|
|
||||||
|
const DROPLET_DIMMING: u8 = 30;
|
||||||
|
|
||||||
|
pub struct Ripple {
|
||||||
|
dashboard: DashboardPattern,
|
||||||
|
base: BodyPattern,
|
||||||
|
|
||||||
|
focii: [usize; 2],
|
||||||
|
dimming: I8F8,
|
||||||
|
slope: [RGB<I8F8>; 2],
|
||||||
|
|
||||||
|
pub start_time: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ripple {
|
||||||
|
pub fn new(dashboard: DashboardPattern, base: BodyPattern, time: Instant) -> Self {
|
||||||
|
let mut slope = [Default::default(); 2];
|
||||||
|
let dimming = I8F8::lit("0.5");
|
||||||
|
let focii = [15, 45];
|
||||||
|
|
||||||
|
for (idx, focus) in focii.iter().enumerate() {
|
||||||
|
slope[idx] = RGB {
|
||||||
|
r: calculate_slope(
|
||||||
|
base[*focus].r,
|
||||||
|
base[*focus].r * dimming,
|
||||||
|
U16F0::from(DROPLET_DIMMING),
|
||||||
|
),
|
||||||
|
g: calculate_slope(
|
||||||
|
base[*focus].g,
|
||||||
|
base[*focus].g * dimming,
|
||||||
|
U16F0::from(DROPLET_DIMMING),
|
||||||
|
),
|
||||||
|
b: calculate_slope(
|
||||||
|
base[*focus].b,
|
||||||
|
base[*focus].b * dimming,
|
||||||
|
U16F0::from(DROPLET_DIMMING),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
dashboard,
|
||||||
|
base,
|
||||||
|
focii,
|
||||||
|
dimming,
|
||||||
|
slope,
|
||||||
|
start_time: time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pixel(&self, pos: U8F0, frames: U16F0) -> RGB<I8F8> {
|
||||||
|
let focus: I16F0 = if pos <= 30 { I16F0::from(15 as i16) } else { I16F0::from(45 as i16) };
|
||||||
|
let distance_from_focus: I8F0 = (focus - I16F0::from(pos)).saturating_as();
|
||||||
|
|
||||||
|
let pos: usize = pos.into();
|
||||||
|
|
||||||
|
/*
|
||||||
|
let target = RGB {
|
||||||
|
r: WATER_BODY[pos].r * self.dimming,
|
||||||
|
g: WATER_BODY[pos].g * self.dimming,
|
||||||
|
b: WATER_BODY[pos].b * self.dimming,
|
||||||
|
};
|
||||||
|
let slope = RGB{
|
||||||
|
r: calculate_slope(WATER_BODY[pos].r, target.r, U16F0::from(DROPLET_DIMMING)),
|
||||||
|
g: calculate_slope(WATER_BODY[pos].g, target.g, U16F0::from(DROPLET_DIMMING)),
|
||||||
|
b: calculate_slope(WATER_BODY[pos].b, target.b, U16F0::from(DROPLET_DIMMING)),
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
RGB {
|
||||||
|
r: WATER_BODY[pos].r + sin(frames * 5) * I8F8::from_num(0.5),
|
||||||
|
g: WATER_BODY[pos].g + sin(frames * 6) * I8F8::from_num(0.5),
|
||||||
|
b: WATER_BODY[pos].b + sin(frames * 7) * I8F8::from_num(0.5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Animation for Ripple {
|
||||||
|
fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern) {
|
||||||
|
let frames = calculate_frames(self.start_time.0, time.0);
|
||||||
|
let mut body_pattern: BodyPattern = OFF_BODY;
|
||||||
|
|
||||||
|
for i in 0..60 {
|
||||||
|
body_pattern[i] = self.pixel(U8F0::from(i as u8), frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
(self.dashboard.clone(), body_pattern)
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,27 +12,15 @@ use core::{
|
||||||
};
|
};
|
||||||
use fixed::types::{I48F16, I8F8, U128F0, U16F0};
|
use fixed::types::{I48F16, I8F8, U128F0, U16F0};
|
||||||
|
|
||||||
|
mod animations;
|
||||||
|
pub use animations::*;
|
||||||
|
|
||||||
mod patterns;
|
mod patterns;
|
||||||
pub use patterns::*;
|
pub use patterns::*;
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
pub use types::{BodyPattern, DashboardPattern, RGB};
|
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)]
|
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
|
||||||
pub struct Instant(pub U128F0);
|
pub struct Instant(pub U128F0);
|
||||||
|
|
||||||
|
@ -65,233 +53,6 @@ pub trait UI {
|
||||||
fn update_lights(&self, dashboard_lights: DashboardPattern, body_lights: BodyPattern);
|
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<I8F8>; 3],
|
|
||||||
body_slope: [RGB<I8F8>; 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)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Brake,
|
Brake,
|
||||||
|
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] }
|
adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] }
|
||||||
|
async-channel = "2.3.1"
|
||||||
cairo-rs = { version = "0.18" }
|
cairo-rs = { version = "0.18" }
|
||||||
fixed = { version = "1" }
|
fixed = { version = "1" }
|
||||||
gio = { version = "0.18" }
|
gio = { version = "0.18" }
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
|
use std::{cell::RefCell, env, rc::Rc};
|
||||||
|
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
|
use async_channel::{unbounded, Receiver, Sender, TryRecvError};
|
||||||
use fixed::types::{I8F8, U128F0};
|
use fixed::types::{I8F8, U128F0};
|
||||||
use glib::{Object, Sender};
|
use glib::Object;
|
||||||
use gtk::subclass::prelude::*;
|
use gtk::subclass::prelude::*;
|
||||||
use lights_core::{
|
use lights_core::{
|
||||||
App, BodyPattern, DashboardPattern, Event, Instant, FPS, OFF_BODY, OFF_DASHBOARD, RGB, UI,
|
App, BodyPattern, DashboardPattern, Event, Instant, FPS, OFF_BODY, OFF_DASHBOARD, RGB, UI,
|
||||||
};
|
};
|
||||||
use std::{
|
|
||||||
cell::RefCell,
|
|
||||||
env,
|
|
||||||
rc::Rc,
|
|
||||||
sync::mpsc::{Receiver, TryRecvError},
|
|
||||||
};
|
|
||||||
|
|
||||||
const WIDTH: i32 = 640;
|
const WIDTH: i32 = 640;
|
||||||
const HEIGHT: i32 = 480;
|
const HEIGHT: i32 = 480;
|
||||||
|
@ -156,13 +153,13 @@ impl UI for GTKUI {
|
||||||
match self.rx.try_recv() {
|
match self.rx.try_recv() {
|
||||||
Ok(event) => Some(event),
|
Ok(event) => Some(event),
|
||||||
Err(TryRecvError::Empty) => None,
|
Err(TryRecvError::Empty) => None,
|
||||||
Err(TryRecvError::Disconnected) => None,
|
Err(TryRecvError::Closed) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_lights(&self, dashboard_lights: DashboardPattern, lights: BodyPattern) {
|
fn update_lights(&self, dashboard_lights: DashboardPattern, lights: BodyPattern) {
|
||||||
self.tx
|
self.tx
|
||||||
.send(Update {
|
.send_blocking(Update {
|
||||||
dashboard: dashboard_lights,
|
dashboard: dashboard_lights,
|
||||||
lights,
|
lights,
|
||||||
})
|
})
|
||||||
|
@ -176,9 +173,8 @@ fn main() {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
adw_app.connect_activate(move |adw_app| {
|
adw_app.connect_activate(move |adw_app| {
|
||||||
let (update_tx, update_rx) =
|
let (update_tx, update_rx) = unbounded();
|
||||||
gtk::glib::MainContext::channel::<Update>(gtk::glib::Priority::DEFAULT);
|
let (event_tx, event_rx) = unbounded();
|
||||||
let (event_tx, event_rx) = std::sync::mpsc::channel();
|
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let mut bike_app = App::new(Box::new(GTKUI {
|
let mut bike_app = App::new(Box::new(GTKUI {
|
||||||
|
@ -218,21 +214,21 @@ fn main() {
|
||||||
left_button.connect_clicked({
|
left_button.connect_clicked({
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
move |_| {
|
move |_| {
|
||||||
let _ = event_tx.send(Event::LeftBlinker);
|
let _ = event_tx.send_blocking(Event::LeftBlinker);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
brake_button.connect_clicked({
|
brake_button.connect_clicked({
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
move |_| {
|
move |_| {
|
||||||
let _ = event_tx.send(Event::Brake);
|
let _ = event_tx.send_blocking(Event::Brake);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
right_button.connect_clicked({
|
right_button.connect_clicked({
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
move |_| {
|
move |_| {
|
||||||
let _ = event_tx.send(Event::RightBlinker);
|
let _ = event_tx.send_blocking(Event::RightBlinker);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -251,14 +247,14 @@ fn main() {
|
||||||
previous_pattern.connect_clicked({
|
previous_pattern.connect_clicked({
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
move |_| {
|
move |_| {
|
||||||
let _ = event_tx.send(Event::PreviousPattern);
|
let _ = event_tx.send_blocking(Event::PreviousPattern);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
next_pattern.connect_clicked({
|
next_pattern.connect_clicked({
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
move |_| {
|
move |_| {
|
||||||
let _ = event_tx.send(Event::NextPattern);
|
let _ = event_tx.send_blocking(Event::NextPattern);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -269,6 +265,7 @@ fn main() {
|
||||||
layout.append(&dashboard_lights);
|
layout.append(&dashboard_lights);
|
||||||
layout.append(&bike_lights);
|
layout.append(&bike_lights);
|
||||||
|
|
||||||
|
/*
|
||||||
update_rx.attach(None, {
|
update_rx.attach(None, {
|
||||||
let dashboard_lights = dashboard_lights.clone();
|
let dashboard_lights = dashboard_lights.clone();
|
||||||
let bike_lights = bike_lights.clone();
|
let bike_lights = bike_lights.clone();
|
||||||
|
@ -278,6 +275,19 @@ fn main() {
|
||||||
glib::ControlFlow::Continue
|
glib::ControlFlow::Continue
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
glib::spawn_future_local({
|
||||||
|
let dashboard_lights = dashboard_lights.clone();
|
||||||
|
let bike_lights = bike_lights.clone();
|
||||||
|
|
||||||
|
async move {
|
||||||
|
while let Ok(Update { dashboard, lights }) = update_rx.recv().await {
|
||||||
|
dashboard_lights.set_lights(dashboard);
|
||||||
|
bike_lights.set_lights(lights);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
window.set_content(Some(&layout));
|
window.set_content(Some(&layout));
|
||||||
window.present();
|
window.present();
|
||||||
|
|
Loading…
Reference in New Issue