use crate::color::{self, RAINBOW, RGB}; pub trait Pattern: std::fmt::Debug { fn init(&mut self, lights_buf: &mut Vec, num_lights: u16) -> Result<(), ()>; fn step(&mut self, lights_buf: &mut Vec) -> bool; } #[derive(Clone, Debug)] pub struct MovingPixel { color: RGB, num_lights: u16, step: u16, } impl MovingPixel { pub const fn new(color: RGB) -> Self { Self { color, step: 0, num_lights: 1, } } } impl Pattern for MovingPixel { fn step(&mut self, lights_buf: &mut Vec) -> bool { let len = self.num_lights; lights_buf.swap( self.step.rem_euclid(len).into(), self.step.saturating_add(1).saturating_mul(3).into(), ); self.step = self.step.wrapping_add(1); true } fn init(&mut self, lights_buf: &mut Vec, num_lights: u16) -> Result<(), ()> { if num_lights < 1 { return Err(()); } self.step = 0; self.num_lights = num_lights; // Set the strip to black except for one pixel *lights_buf = vec![color::BLACK; num_lights.into()]; lights_buf[0] = self.color; Ok(()) } } #[derive(Clone, Debug)] pub struct Solid { color: RGB, has_run: bool, } impl Solid { pub const fn new(color: RGB) -> Self { Self { color, has_run: false, } } } impl Pattern for Solid { fn step(&mut self, _lights_buf: &mut Vec) -> bool { let ret = !self.has_run; self.has_run = true; ret } fn init(&mut self, lights_buf: &mut Vec, num_lights: u16) -> Result<(), ()> { if num_lights < 1 { return Err(()); } self.has_run = false; *lights_buf = vec![self.color; num_lights.into()]; Ok(()) } } #[derive(Clone, Debug)] pub struct MovingRainbow {} impl MovingRainbow { pub const fn new() -> Self { Self {} } } impl Pattern for MovingRainbow { fn step(&mut self, lights_buf: &mut Vec) -> bool { lights_buf.rotate_right(1); true } fn init(&mut self, lights_buf: &mut Vec, num_lights: u16) -> Result<(), ()> { if num_lights < 1 { return Err(()); } *lights_buf = RAINBOW .iter() .cycle() .take( num_lights .saturating_sub(1) .div_euclid(7) .saturating_add(1) .saturating_mul(7) .into(), ) .copied() .collect::>(); Ok(()) } } #[derive(Clone, Debug)] pub struct Fade { color: RGB, step: u8, direction: bool, num_lights: u16, } impl Fade { pub const fn new(color: RGB) -> Self { Self { color, step: 0, direction: true, num_lights: 1, } } } impl Pattern for Fade { fn step(&mut self, lights_buf: &mut Vec) -> bool { if self.direction { if self.step == 254 { self.direction = !self.direction; } self.step = self.step.saturating_add(1); } else { if self.step == 1 { self.direction = !self.direction; } self.step = self.step.saturating_sub(1); } *lights_buf = vec![RGB(self.step, self.step, self.step); self.num_lights.into()]; true } fn init(&mut self, lights_buf: &mut Vec, num_lights: u16) -> Result<(), ()> { if num_lights < 1 { return Err(()); } self.step = 0; self.direction = true; self.num_lights = num_lights; *lights_buf = vec![color::BLACK; self.num_lights.into()]; Ok(()) } } #[derive(Clone, Debug)] pub struct Collide { num_lights: u16, left_color: RGB, right_color: RGB, conjoined_color: RGB, step: u16, step_max: u16, conjoined_bounds: (u16, u16), offset_max: u16, offset: u16, previous_offset: u16, increase_offset: bool, } impl Collide { pub const fn new(left_color: RGB, right_color: RGB, conjoined_color: RGB) -> Self { Self { num_lights: 0, left_color, right_color, conjoined_color, step: 0, step_max: 0, conjoined_bounds: (0, 0), offset_max: 0, previous_offset: 0, offset: 0, increase_offset: true, } } } impl Pattern for Collide { fn step(&mut self, lights_buf: &mut Vec) -> bool { // TODO: Better range storage // Set the left and right color let colors = if ((self.conjoined_bounds.0)..=(self.conjoined_bounds.1)).contains(&(self.step)) { (self.conjoined_color, self.conjoined_color) } else { (self.left_color, self.right_color) }; // Turn off the previous LED lights_buf[usize::from(self.previous_offset)] = color::BLACK; if self.previous_offset != self .num_lights .saturating_sub(1) .saturating_sub(self.previous_offset) { lights_buf[usize::from( self.num_lights .saturating_sub(1) .saturating_sub(self.previous_offset), )] = color::BLACK; } // Set the color of the current offset lights_buf[usize::from(self.offset)] = colors.0; if self.offset != self .num_lights .saturating_sub(1) .saturating_sub(self.offset) { lights_buf[usize::from( self.num_lights .saturating_sub(1) .saturating_sub(self.offset), )] = colors.1; } self.previous_offset = self.offset; if self.offset == 0 { self.increase_offset = true; self.offset = self.offset.saturating_add(1); } else if self.offset == self.offset_max { self.increase_offset = false; self.offset = self.offset.saturating_sub(1); } else if self.increase_offset { self.offset = self.offset.saturating_add(1); // The previous condition was false, so subtract } else { self.offset = self.offset.saturating_sub(1); } self.step = self.step.saturating_add(1).rem_euclid(self.step_max); true } fn init(&mut self, lights_buf: &mut Vec, num_lights: u16) -> Result<(), ()> { // Reset changing parameters self.step = 0; self.offset = 0; self.previous_offset = 0; self.num_lights = num_lights; self.increase_offset = true; if self.num_lights < 3 { return Err(()); } *lights_buf = vec![color::BLACK; self.num_lights.into()]; if self.num_lights.rem_euclid(2) == 0 { self.conjoined_bounds = ( self.num_lights .div_euclid(2) .saturating_add(1_u16.saturating_sub(1)), self.num_lights .saturating_add(1) .saturating_mul(3) .div_euclid(2) .saturating_sub(4), ); } else { self.conjoined_bounds = ( self.num_lights .div_euclid(2) .saturating_add(1_u16.saturating_sub(1)), self.num_lights .saturating_add(1) .saturating_mul(3) .div_euclid(2) .saturating_sub(3), ); } self.offset_max = self.num_lights.saturating_sub(1).div_euclid(2); self.step_max = self .num_lights .saturating_sub(1) .div_euclid(2) .saturating_mul(4); Ok(()) } } #[cfg(test)] mod tests { use super::*; const NUM_LIGHTS: u16 = 10; fn test_strip() -> Vec { vec![color::BLACK; NUM_LIGHTS.into()] } #[test] fn moving_pixel() { let color = RGB(123, 152, 89); let mut pat = MovingPixel::new(color.clone()); let mut strip = test_strip(); assert!(pat.init(&mut strip, NUM_LIGHTS).is_ok()); // One is my color assert_eq!(strip.iter().filter(|c| **c == color).count(), 1); // The rest are off assert_eq!( strip.iter().filter(|c| **c == color::BLACK).count(), (NUM_LIGHTS - 1).into() ); pat.step(&mut strip); // One is my color assert_eq!(strip.iter().filter(|c| **c == color).count(), 1); // The rest are off assert_eq!( strip.iter().filter(|c| **c == color::BLACK).count(), (NUM_LIGHTS - 1).into() ); } #[test] fn solid() {} #[test] fn moving_rainbow() {} #[test] fn fade() {} #[test] fn collide() {} }