use palette::Hsv; use rgb::RGB8; use crate::events::{Event, EventBus}; use crate::time::Periodically; use crate::geometry::*; use crate::render::{Shader, Surface, Surfaces}; use crate::task::Task; use crate::lib8::{trig::{sin8, cos8}, interpolate::scale8, noise::inoise8, IntoRgb8}; #[derive(Debug)] pub struct IdleTask { solid: T, surface: T, shimmer: T, } #[derive(Debug)] struct SolidShader {} impl Shader for SolidShader { fn draw(&self, _coords: &VirtualCoordinates, frame: usize) -> RGB8 { Hsv::new_srgb((frame % 255) as u8, 255, sin8((frame % 64) as u8)).into_rgb8() } } #[derive(Debug)] struct ShimmerShader {} impl Shader for ShimmerShader { fn draw(&self, coords: &VirtualCoordinates, frame: usize) -> RGB8 { Hsv::new_srgb(((coords.x as usize).wrapping_add(frame / 3) % 255) as u8, coords.y, sin8(frame).max(75).min(64)).into_rgb8() } } #[derive(Debug)] struct ThinkingShader {} impl Shader for ThinkingShader { fn draw(&self, coords: &VirtualCoordinates, frame: usize) -> RGB8 { //let noise_x = sin8(sin8((frame % 255) as u8).wrapping_add(coords.x)); //let noise_y = cos8(cos8((frame % 255) as u8).wrapping_add(coords.y)); let offset_x = sin8(frame.wrapping_add(coords.x as usize)); let offset_y = cos8(frame.wrapping_add(coords.y as usize)); let noise_x = offset_x / 2; let noise_y = offset_y / 2; //let noise_x = coords.x.wrapping_add(offset_x); //let noise_y = coords.y.wrapping_add(offset_y); Hsv::new_srgb( inoise8(offset_x as i16, offset_y as i16), 128_u8.saturating_add(inoise8(noise_y as i16, noise_x as i16)), 255 ).into_rgb8() } } impl IdleTask { pub fn new>(surfaces: &mut S) -> Self { IdleTask { solid: surfaces.new_surface(Rectangle::everything()).unwrap(), surface: surfaces.new_surface(Rectangle::everything()).unwrap(), shimmer: surfaces.new_surface(Rectangle::everything()).unwrap(), } } } impl Task for IdleTask { fn name(&self) -> &'static str { "Idle" } fn start(&mut self, _bus: &mut EventBus) { self.solid.set_shader(Box::new(SolidShader { })); self.surface.set_shader(Box::new(ThinkingShader { })); self.shimmer.set_shader(Box::new(ShimmerShader { })); self.solid.set_opacity(64); self.surface.set_opacity(128); self.shimmer.set_opacity(64); } fn stop(&mut self, _bus: &mut EventBus) { self.solid.clear_shader(); self.surface.clear_shader(); self.shimmer.clear_shader(); } } #[derive(Clone, Copy, Debug)] enum TestShader { Red, Green, Blue, White, RGB, HSV, Outline, SweepX, SweepY, SinX, SinY, Metaballs } impl TestShader { pub fn next(self) -> Self { match self { Self::Red => Self::Green, Self::Green => Self::Blue, Self::Blue => Self::White, Self::White => Self::RGB, Self::RGB => Self::HSV, Self::HSV => Self::Outline, Self::Outline => Self::SweepX, Self::SweepX => Self::SweepY, Self::SweepY => Self::SinX, Self::SinX => Self::SinY, Self::SinY => Self::Metaballs, Self::Metaballs => Self::Red } } } impl Shader for TestShader { fn draw(&self, coords: &VirtualCoordinates, frame: usize) -> RGB8 { match self { Self::Red => RGB8::new(255, 0, 0), Self::Green => RGB8::new(0, 255, 0), Self::Blue => RGB8::new(0, 0, 255), Self::White => RGB8::new(255, 255, 255), Self::RGB => RGB8::new(coords.x, coords.y, 255 - (coords.x / 2 + coords.y / 2)), Self::HSV => Hsv::new_srgb(coords.x, coords.y, 255).into_rgb8(), Self::Outline => match (coords.x, coords.y) { (0, 0) => RGB8::new(255, 255, 255), (0, _) => RGB8::new(255, 0, 0), (_, 0) => RGB8::new(0, 255, 0), (255, _) => RGB8::new(0, 0, 255), (_, 255) => RGB8::new(0, 0, 255), _ => RGB8::new(0, 0, 0) }, Self::SweepX => RGB8::new(255, 0, 0), Self::SweepY => RGB8::new(255, 0, 0), Self::SinX => RGB8::new(sin8(coords.x.wrapping_add((frame % 255) as u8)), 0, 0), Self::SinY => RGB8::new(sin8(coords.y.wrapping_add((frame % 255) as u8)), 0, 0), Self::Metaballs => { let ball_center = Coordinates::new( scale8(64, sin8(frame)) + 128, scale8(64, cos8(frame)) + 128 ); let dist = 255 - coords.distance_to(&ball_center); RGB8::new(dist, 0, 0) } } } } #[derive(Debug)] pub struct TestPattern { surface: T, updater: Periodically, stepper: Periodically, pattern: TestShader, frame: u8 } impl TestPattern { pub fn new(surface: T) -> Self { TestPattern { surface, updater: Periodically::new_every_n_seconds(10), stepper: Periodically::new_every_n_ms(60), pattern: TestShader::HSV, frame: 0 } } } impl Task for TestPattern { fn name(&self) -> &'static str { "TestPattern" } fn start(&mut self, _bus: &mut EventBus) { self.surface.set_shader(Box::new(self.pattern)); } fn on_tick(&mut self, bus: &mut EventBus) { self.updater.run(|| { self.pattern = self.pattern.next(); log::info!("Test pattern: {:?}", self.pattern); self.frame = 0; self.surface.set_shader(Box::new(self.pattern)); bus.push(Event::new_property_change("animations.test.pattern", format!("{:?}", self.pattern))); }); self.stepper.run(|| { self.frame = self.frame.wrapping_add(1); self.surface.set_opacity(sin8(self.frame)); self.surface.set_rect( match self.pattern { TestShader::SweepX => Rectangle::new( Coordinates::new(self.frame, 0), Coordinates::new(self.frame, 255) ), TestShader::SweepY => Rectangle::new( Coordinates::new(0, self.frame), Coordinates::new(255, self.frame) ), _ => Rectangle::everything() }); }); } fn stop(&mut self, _bus: &mut EventBus) { self.surface.clear_shader(); } }