diff --git a/src/pattern.rs b/src/pattern.rs index 02a4f3c..2a8185d 100644 --- a/src/pattern.rs +++ b/src/pattern.rs @@ -1,323 +1,58 @@ -use crate::color::{self, RAINBOW, RGB}; +use crate::color::RGB; + +pub mod collide; +pub mod fade; +pub mod moving_pixel; +pub mod moving_rainbow; +pub mod solid; + +pub use collide::Collide; +pub use fade::Fade; +pub use moving_pixel::MovingPixel; +pub use moving_rainbow::MovingRainbow; +pub use solid::Solid; + 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(()) - } -} +// #[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(); -#[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() {} -} +// 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() {} +// } diff --git a/src/pattern/collide.rs b/src/pattern/collide.rs new file mode 100644 index 0000000..e20a7be --- /dev/null +++ b/src/pattern/collide.rs @@ -0,0 +1,135 @@ +use super::Pattern; +use crate::color::{self, RGB}; + +#[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(()) + } +} diff --git a/src/pattern/fade.rs b/src/pattern/fade.rs new file mode 100644 index 0000000..6ff4a0f --- /dev/null +++ b/src/pattern/fade.rs @@ -0,0 +1,47 @@ +use super::Pattern; +use crate::color::{self, RGB}; + +#[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(()) + } +} diff --git a/src/pattern/moving_pixel.rs b/src/pattern/moving_pixel.rs new file mode 100644 index 0000000..9d1b7c3 --- /dev/null +++ b/src/pattern/moving_pixel.rs @@ -0,0 +1,40 @@ +use super::Pattern; +use crate::color::{self, RGB}; + +#[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(()) + } +} diff --git a/src/pattern/moving_rainbow.rs b/src/pattern/moving_rainbow.rs new file mode 100644 index 0000000..88fa84c --- /dev/null +++ b/src/pattern/moving_rainbow.rs @@ -0,0 +1,35 @@ +use super::Pattern; +use crate::color::{RAINBOW, RGB}; + +#[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(()) + } +} diff --git a/src/pattern/solid.rs b/src/pattern/solid.rs new file mode 100644 index 0000000..c707238 --- /dev/null +++ b/src/pattern/solid.rs @@ -0,0 +1,31 @@ +use super::Pattern; +use crate::color::RGB; + +#[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(()) + } +}