aw-lights/src/pattern.rs

324 lines
9.0 KiB
Rust
Raw Normal View History

2021-07-31 14:29:21 -04:00
use crate::color::{self, RAINBOW, RGB};
pub trait Pattern: std::fmt::Debug {
fn init(&mut self, lights_buf: &mut Vec<RGB>, num_lights: u16) -> Result<(), ()>;
fn step(&mut self, lights_buf: &mut Vec<RGB>) -> 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<RGB>) -> 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<RGB>, 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<RGB>) -> bool {
let ret = !self.has_run;
self.has_run = true;
ret
}
fn init(&mut self, lights_buf: &mut Vec<RGB>, 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<RGB>) -> bool {
lights_buf.rotate_right(1);
true
}
fn init(&mut self, lights_buf: &mut Vec<RGB>, 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::<Vec<RGB>>();
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<RGB>) -> 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<RGB>, 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<RGB>) -> 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<RGB>, 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<RGB> {
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() {}
}