src: implement simulation data sources
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
use embassy_sync::channel::DynamicReceiver;
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
use figments::{liber8tion::interpolate::{ease_in_out_quad, Fract8}, prelude::*};
|
||||
use figments::{liber8tion::interpolate::Fract8, prelude::*};
|
||||
use rgb::{Rgb, Rgba};
|
||||
use log::*;
|
||||
use nalgebra::ComplexField;
|
||||
|
||||
use crate::{display::BikeSpace, events::{DisplayControls, Notification, Scene}, shaders::*, tasks::render::Uniforms};
|
||||
|
||||
@@ -79,10 +80,29 @@ impl<S: Surface<Uniforms = Uniforms, CoordinateSpace = BikeSpace, Pixel = Rgba<u
|
||||
|
||||
pub async fn flash_notification_color(&mut self, color: Rgb<u8>) {
|
||||
self.notification.set_shader(Background::from_color(color));
|
||||
self.notification.set_opacity(255);
|
||||
self.notification.set_opacity(0);
|
||||
self.notification.set_visible(true);
|
||||
// Fade in to full on over 1s
|
||||
Self::animate_duration(Duration::from_secs(1), |pct| {
|
||||
self.notification.set_opacity(pct);
|
||||
}).await;
|
||||
|
||||
// Pulse quickly 3 times
|
||||
for _ in 0..3 {
|
||||
// Brief dimming back to half brightness for half a sec
|
||||
Self::animate_duration(Duration::from_millis(100), |pct| {
|
||||
self.notification.set_opacity(100 + pct/2);
|
||||
}).await;
|
||||
|
||||
// Back to full
|
||||
Self::animate_duration(Duration::from_millis(100), |pct| {
|
||||
self.notification.set_opacity(100 + (255 - pct) / 2);
|
||||
}).await;
|
||||
}
|
||||
|
||||
// Back to off
|
||||
Self::animate_duration(Duration::from_secs(3), |pct| {
|
||||
self.notification.set_opacity(255 * pct);
|
||||
self.notification.set_opacity(255 - pct);
|
||||
}).await;
|
||||
self.notification.set_visible(false);
|
||||
}
|
||||
@@ -104,19 +124,36 @@ impl<S: Surface<Uniforms = Uniforms, CoordinateSpace = BikeSpace, Pixel = Rgba<u
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expo_in(t: f32) -> f32 {
|
||||
if t <= 0.0 {
|
||||
0.0
|
||||
} else {
|
||||
2f32.powf(10.0 * t - 10.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expo_out(t: f32) -> f32 {
|
||||
if 1.0 <= t {
|
||||
1.0
|
||||
} else {
|
||||
1.0 - 2f32.powf(-10.0 * t)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn startup_fade_sequence(&mut self) {
|
||||
info!("Running startup fade sequence");
|
||||
// Start with a completely transparent overlay, which then fades in over 1 second
|
||||
self.overlay.set_opacity(0);
|
||||
self.overlay.set_visible(true);
|
||||
Self::animate_duration(Duration::from_secs(1), |pct| {
|
||||
self.overlay.set_opacity(ease_in_out_quad(pct));
|
||||
Self::animate_duration(Duration::from_secs(2), |pct| {
|
||||
self.overlay.set_opacity((Self::expo_in(pct as f32 / 255.0) * 255.0) as u8);
|
||||
}).await;
|
||||
// When the overlay is fully opaque and all lower layers will be hidden, turn them on
|
||||
self.apply_scene(Scene::Startup).await;
|
||||
Timer::after_secs(1).await;
|
||||
// Then fade out the overlay over 3 seconds, allowing the base animations to be shown
|
||||
Self::animate_duration(Duration::from_secs(3), |pct| {
|
||||
self.overlay.set_opacity(ease_in_out_quad(255 - pct));
|
||||
Self::animate_duration(Duration::from_secs(1), |pct| {
|
||||
self.overlay.set_opacity((Self::expo_in((255 - pct) as f32 / 255.0) * 255.0) as u8);
|
||||
}).await;
|
||||
self.overlay.set_visible(false);
|
||||
info!("Fade sequence completed");
|
||||
@@ -178,13 +215,13 @@ impl<S: Surface<Uniforms = Uniforms, CoordinateSpace = BikeSpace, Pixel = Rgba<u
|
||||
Scene::ParkedIdle => {
|
||||
// Ensure the display is on
|
||||
self.display.on.store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
Self::animate_duration(Duration::from_secs(1), |pct| {
|
||||
self.display.brightness.store(128.scale8(255 - pct), core::sync::atomic::Ordering::Relaxed);
|
||||
}).await;
|
||||
// Hide the content; only notifications will remain
|
||||
self.background.set_visible(true);
|
||||
self.tail.set_visible(false);
|
||||
self.panels.set_visible(false);
|
||||
Self::animate_duration(Duration::from_secs(1), |pct| {
|
||||
self.display.brightness.store(128.scale8(255 - pct), core::sync::atomic::Ordering::Relaxed);
|
||||
}).await;
|
||||
},
|
||||
Scene::ParkedLongTerm => {
|
||||
// For long-term parking, we turn off the safety lights
|
||||
@@ -195,12 +232,27 @@ impl<S: Surface<Uniforms = Uniforms, CoordinateSpace = BikeSpace, Pixel = Rgba<u
|
||||
self.display.brightness.store(255 - pct, core::sync::atomic::Ordering::Relaxed);
|
||||
}).await;
|
||||
self.display.on.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
},
|
||||
Scene::Accelerating => {
|
||||
self.display.on.store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
self.display.brightness.store(255, core::sync::atomic::Ordering::Relaxed);
|
||||
self.tail.set_shader(Thinking::default());
|
||||
self.panels.set_shader(Panel::default());
|
||||
},
|
||||
Scene::Decelerating => {
|
||||
self.display.on.store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
self.display.brightness.store(255, core::sync::atomic::Ordering::Relaxed);
|
||||
self.panels.set_shader(Thinking::default());
|
||||
self.tail.set_shader(Tail::default());
|
||||
}
|
||||
_ => {
|
||||
warn!("Unimplemented scene {next_scene:?}!");
|
||||
}
|
||||
_ => unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn on_event(&mut self, event: Notification) {
|
||||
info!("UI Event: {event:?}");
|
||||
match event {
|
||||
// Apply the scene when it is changed
|
||||
Notification::SceneChange(scene) => self.apply_scene(scene).await,
|
||||
@@ -231,6 +283,9 @@ impl<S: Surface<Uniforms = Uniforms, CoordinateSpace = BikeSpace, Pixel = Rgba<u
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn ui_main(events: DynamicReceiver<'static, Notification>, mut ui: Ui<BufferedSurface<Uniforms, BikeSpace, Rgba<u8>>>) {
|
||||
// Wait for the renderer to start running
|
||||
ui.display.render_is_running.wait().await;
|
||||
|
||||
// Run the fade sequence
|
||||
ui.startup_fade_sequence().await;
|
||||
|
||||
@@ -240,6 +295,9 @@ pub async fn ui_main(events: DynamicReceiver<'static, Notification>, mut ui: Ui<
|
||||
ui.set_headlight_on(true).await;
|
||||
// Turn off the brakelight
|
||||
ui.set_brakelight_on(false).await;
|
||||
|
||||
// Flash white to indicate we are ready
|
||||
ui.flash_notification_color(Rgb::new(255, 255, 255)).await;
|
||||
loop {
|
||||
ui.on_event(events.receive().await).await;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user