use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use tachyonfx::fx::{self, Glitch};
use tachyonfx::{Duration, Effect, Interpolation, IntoEffect, Motion};
use super::fx::PINK;
pub const DISSOLVE_MS: u32 = 700;
#[derive(Clone, Copy)]
enum Spot {
Screen,
Visitors,
Log,
Qr,
}
#[derive(Default)]
pub struct Spots {
pub visitors: Option<Rect>,
pub log: Option<Rect>,
pub qr: Option<Rect>,
}
pub struct Shaders {
calm: bool,
running: Vec<(Spot, Effect)>,
disco: Option<Effect>,
}
impl Shaders {
pub fn new(calm: bool) -> Self {
Self {
calm,
running: Vec::new(),
disco: None,
}
}
fn add(&mut self, spot: Spot, effect: Effect) {
if !self.calm {
self.running.push((spot, effect));
}
}
pub fn entry(&mut self) {
self.add(Spot::Screen, fx::coalesce((700, Interpolation::QuadOut)));
}
pub fn arrival(&mut self) {
let sweep = fx::sweep_in(
Motion::LeftToRight,
12,
0,
PINK,
(900, Interpolation::QuadOut),
);
self.add(Spot::Visitors, sweep);
}
pub fn crash(&mut self) {
let glitch = Glitch::builder()
.cell_glitch_ratio(0.08)
.action_start_delay_ms(0..120)
.action_ms(60..200)
.build()
.into_effect();
let brief = fx::with_duration(Duration::from_millis(600), glitch);
self.add(Spot::Log, brief);
}
pub fn flash_qr(&mut self) {
self.add(
Spot::Qr,
fx::fade_from(PINK, PINK, (600, Interpolation::QuadOut)),
);
}
pub fn dissolve(&mut self) {
self.add(
Spot::Screen,
fx::dissolve((DISSOLVE_MS, Interpolation::Linear)),
);
}
pub fn disco(&mut self, on: bool) {
self.disco = (on && !self.calm).then(|| {
fx::repeating(fx::hsl_shift_fg(
[360.0, 20.0, 0.0],
(2400, Interpolation::Linear),
))
});
}
pub fn busy(&self) -> bool {
!self.running.is_empty() || self.disco.is_some()
}
pub fn render(&mut self, buf: &mut Buffer, screen: Rect, spots: &Spots, dt: f32) {
let tick = Duration::from_millis((dt * 1000.0) as u32);
self.running.retain_mut(|(spot, effect)| {
let area = match spot {
Spot::Screen => Some(screen),
Spot::Visitors => spots.visitors,
Spot::Log => spots.log,
Spot::Qr => spots.qr,
};
let Some(area) = area else {
return false;
};
effect.process(tick, buf, area);
!effect.done()
});
if let Some(disco) = &mut self.disco {
disco.process(tick, buf, screen);
}
}
}