add background color for color wipe and my first unit test !

This commit is contained in:
Tobias Ollive 2022-03-11 17:10:33 +01:00
parent b718334392
commit ee133379f2
11 changed files with 242 additions and 49 deletions

View File

@ -2,21 +2,21 @@
mac_addresses : mac_addresses :
- C9:81:9C:BA:53:BC - C9:81:9C:BA:53:BC
sequence: sequence:
- pattern: # - pattern:
type: Blink # type: Blink
fade: # fade:
current_iteration: 10 # current_iteration: 10
nbr_iterations: 20 # nbr_iterations: 20
begin_color: &black # begin_color: &black
red: 0 # red: 0
green: 0 # green: 0
blue: 0 # blue: 0
end_color: &purple # end_color: &purple
red: 255 # red: 255
green: 0 # green: 0
blue: 255 # blue: 255
max_iteration: 10 # max_iteration: 10
period: 50 # period: 50
- pattern: - pattern:
type: Fade type: Fade
nbr_iterations: 20 nbr_iterations: 20
@ -36,7 +36,6 @@
red: 0 red: 0
green: 255 green: 255
blue: 0 blue: 0
background_color: *purple
period: 100 period: 100
- pattern: - pattern:
type: FillRandom type: FillRandom
@ -45,17 +44,22 @@
period: 80 period: 80
- name: titi - name: titi
sequence: sequence:
- pattern: # - pattern:
type: Blink # type: Blink
fade : # fade :
nbr_iterations: 20 # nbr_iterations: 20
begin_color: *black # begin_color: *black
end_color: &cyan # end_color: &cyan
red: 0 # red: 0
green: 255 # green: 255
blue: 255 # blue: 255
max_iteration : 10 # max_iteration : 10
period: 50 # period: 50
- pattern: - pattern:
type: Rainbow type: Rainbow
max_iteration: 200
period: 10
- pattern:
type: ColorWipe
color: *blue
period: 100 period: 100

View File

@ -5,6 +5,7 @@ mod rainbow;
mod fill_random; mod fill_random;
mod fill_unstable; mod fill_unstable;
mod blink; mod blink;
mod rain;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fmt; use std::fmt;
@ -29,6 +30,12 @@ pub(crate) enum Patterns<const N: usize> {
Blink(Blink<N>), Blink(Blink<N>),
} }
pub trait Pattern : Iterator {
type Strip;
fn init(&self) -> Option<Self::Strip>;
}
impl<const N: usize> Iterator for Patterns<N> { impl<const N: usize> Iterator for Patterns<N> {
type Item = Strip<N>; type Item = Strip<N>;
@ -45,6 +52,22 @@ impl<const N: usize> Iterator for Patterns<N> {
} }
} }
impl<const N: usize> Pattern for Patterns<N> {
type Strip = Strip<N>;
fn init(&self) -> Option<Self::Strip> {
match self {
Patterns::Rainbow(p) => p.init(),
Patterns::Fade(p) => p.init(),
Patterns::ColorWipe(p) => p.init(),
Patterns::Scanner(p) => p.init(),
Patterns::FillRandom(p) => p.init(),
Patterns::FillUnstable(p) => p.init(),
Patterns::Blink(p) => p.init(),
}
}
}
/// ///
/// a struct for an RGB color /// a struct for an RGB color
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Serialize, Deserialize, Default, Eq, Hash, PartialEq)]
@ -84,17 +107,17 @@ impl SubAssign for PixelColor {
/// ///
/// a basic newtype based on a fix array size. /// a basic newtype based on a fix array size.
/// ///
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct Strip<const N: usize>([PixelColor; N]); pub struct Strip<const N: usize>([Option<PixelColor>; N]);
impl<const N: usize> Default for Strip<N> { impl<const N: usize> Default for Strip<N> {
fn default() -> Self { fn default() -> Self {
Strip([PixelColor::default(); N]) Strip([None; N])
} }
} }
impl<const N: usize> Index<usize> for Strip<N> { impl<const N: usize> Index<usize> for Strip<N> {
type Output = PixelColor; type Output = Option<PixelColor>;
fn index(&self, index: usize) -> &Self::Output { fn index(&self, index: usize) -> &Self::Output {
&self.0[index] &self.0[index]
@ -102,7 +125,7 @@ impl<const N: usize> Index<usize> for Strip<N> {
} }
impl<const N: usize> IndexMut<usize> for Strip<N> { impl<const N: usize> IndexMut<usize> for Strip<N> {
fn index_mut(&mut self, index: usize) -> &mut PixelColor { fn index_mut(&mut self, index: usize) -> &mut Option<PixelColor> {
&mut self.0[index] &mut self.0[index]
} }
} }
@ -112,16 +135,17 @@ impl<const N: usize> Strip<N> {
let mut data: Vec<u8> = vec![]; let mut data: Vec<u8> = vec![];
for i in 0..N { for i in 0..N {
data.append(&mut vec![self[i].green, self[i].red, self[i].blue]); let color = self[i].unwrap_or(PixelColor::default());
data.append(&mut vec![color.green, color.red, color.blue]);
} }
data data
} }
pub fn new(color: PixelColor) -> Strip<N> { pub fn new(color: PixelColor) -> Strip<N> {
Strip { 0: [color; N] } Strip { 0: [Some(color); N] }
} }
pub fn fill(&mut self, color: PixelColor) { pub fn fill(&mut self, color: PixelColor) {
self.0.fill(color); self.0.fill(Some(color));
} }
} }

View File

@ -1,6 +1,7 @@
use crate::patterns::fade::Fade; use crate::patterns::fade::Fade;
use crate::Strip; use crate::Strip;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::patterns::Pattern;
/// # Blink /// # Blink
/// ///
@ -13,6 +14,14 @@ pub struct Blink<const N: usize> {
fade: Fade<N>, fade: Fade<N>,
} }
impl<const N: usize> Pattern for Blink<N> {
type Strip = Strip<N>;
fn init(&self) -> Option<Self::Strip> {
None
}
}
impl<const N: usize> Iterator for Blink<N> { impl<const N: usize> Iterator for Blink<N> {
type Item = Strip<N>; type Item = Strip<N>;

View File

@ -1,4 +1,4 @@
use crate::patterns::PixelColor; use crate::patterns::{Pattern, PixelColor};
use crate::Strip; use crate::Strip;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
@ -20,6 +20,18 @@ pub struct ColorWipe<const N: usize> {
pub(crate) background_color: Option<PixelColor>, pub(crate) background_color: Option<PixelColor>,
} }
impl<const N: usize> Pattern for ColorWipe<N> {
type Strip = Strip<N>;
fn init(&self) -> Option<Self::Strip> {
if let Some(color) = self.background_color {
Some(Strip::<N>::new(color))
} else {
None
}
}
}
impl<const N: usize> Iterator for ColorWipe<N> { impl<const N: usize> Iterator for ColorWipe<N> {
type Item = Strip<N>; type Item = Strip<N>;
@ -36,7 +48,7 @@ impl<const N: usize> Iterator for ColorWipe<N> {
} }
for i in 0..iteration { for i in 0..iteration {
strip[i] = self.color; strip[i] = Some(self.color);
} }
self.current_iteration += 1; self.current_iteration += 1;
Some(strip) Some(strip)

View File

@ -1,4 +1,4 @@
use crate::patterns::PixelColor; use crate::patterns::{Pattern, PixelColor};
use crate::Strip; use crate::Strip;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
@ -17,6 +17,14 @@ fn fade_value(value_start: u8, value_end: u8, index: usize, nbr_iter: usize) ->
((value_start as usize * (nbr_iter - index) + value_end as usize * index) / nbr_iter) as u8 ((value_start as usize * (nbr_iter - index) + value_end as usize * index) / nbr_iter) as u8
} }
impl<const N: usize> Pattern for Fade<N> {
type Strip = Strip<N>;
fn init(&self) -> Option<Self::Strip> {
None
}
}
impl<const N: usize> Iterator for Fade<N> { impl<const N: usize> Iterator for Fade<N> {
type Item = Strip<N>; type Item = Strip<N>;

View File

@ -1,5 +1,5 @@
use rand::Rng; use rand::Rng;
use crate::patterns::PixelColor; use crate::patterns::{Pattern, PixelColor};
use crate::Strip; use crate::Strip;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
@ -13,6 +13,14 @@ pub struct FillRandom<const N: usize> {
pub(crate) max_iteration: Option<usize>, pub(crate) max_iteration: Option<usize>,
} }
impl<const N: usize> Pattern for FillRandom<N> {
type Strip = Strip<N>;
fn init(&self) -> Option<Self::Strip> {
None
}
}
impl<const N: usize> Iterator for FillRandom<N> { impl<const N: usize> Iterator for FillRandom<N> {
type Item = Strip<N>; type Item = Strip<N>;
@ -45,7 +53,7 @@ impl<const N: usize> Iterator for FillRandom<N> {
blue = self.color.blue.saturating_sub(blue_delta); blue = self.color.blue.saturating_sub(blue_delta);
} }
let c = PixelColor { red, green, blue }; let c = PixelColor { red, green, blue };
strip[i] = c; strip[i] = Some(c);
} }
Some(strip) Some(strip)
} }

View File

@ -1,5 +1,5 @@
use rand::Rng; use rand::Rng;
use crate::patterns::PixelColor; use crate::patterns::{Pattern, PixelColor};
use crate::Strip; use crate::Strip;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
@ -13,6 +13,14 @@ pub struct FillUnstable<const N: usize> {
pub(crate) max_iteration: Option<usize>, pub(crate) max_iteration: Option<usize>,
} }
impl<const N: usize> Pattern for FillUnstable<N> {
type Strip = Strip<N>;
fn init(&self) -> Option<Self::Strip> {
None
}
}
impl<const N: usize> Iterator for FillUnstable<N> { impl<const N: usize> Iterator for FillUnstable<N> {
type Item = Strip<N>; type Item = Strip<N>;

48
src/patterns/rain.rs Normal file
View File

@ -0,0 +1,48 @@
use crate::patterns::PixelColor;
use crate::Strip;
use serde::{Serialize, Deserialize};
/// # scanner pattern
/// color one pixel with a color and leave a train of this color fading to black
///
/// # note
///
/// background_color work only for color not set by scanner pattern
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct Scanner<const N: usize> {
#[serde(default)]
current_iteration: usize,
pub(crate) color: PixelColor,
pub(crate) background_color: Option<PixelColor>,
pub(crate) stability: usize,
#[serde(skip)]
strip : Strip<N>,
}
impl<const N: usize> Iterator for Scanner<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
let mut strip = Strip::<N>::default();
if let Some(c) = self.background_color {
strip.fill(c);
} else {
strip.fill(PixelColor::default());
}
let current_led = self.current_iteration % N;
let min_led;
if current_led < 8 {
min_led = 0;
} else {
min_led = current_led - 8;
}
let mut c = self.color;
for i in min_led..current_led {
strip[i] = Some(c);
c = c / 2;
}
self.current_iteration += 1;
Some(strip)
}
}

View File

@ -1,4 +1,4 @@
use crate::patterns::PixelColor; use crate::patterns::{Pattern, PixelColor};
use crate::Strip; use crate::Strip;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
@ -20,6 +20,14 @@ pub struct Rainbow<const N: usize> {
pub(crate) step: Option<usize>, pub(crate) step: Option<usize>,
} }
impl<const N: usize> Pattern for Rainbow<N> {
type Strip = Strip<N>;
fn init(&self) -> Option<Self::Strip> {
None
}
}
impl<const N: usize> Iterator for Rainbow<N> { impl<const N: usize> Iterator for Rainbow<N> {
type Item = Strip<N>; type Item = Strip<N>;
@ -33,7 +41,7 @@ impl<const N: usize> Iterator for Rainbow<N> {
let step = self.step.unwrap_or(255 / N); let step = self.step.unwrap_or(255 / N);
for i in 0..N { for i in 0..N {
let pos = (i * step + self.current_iteration) as u8; let pos = (i * step + self.current_iteration) as u8;
strip[i] = wheel(pos) strip[i] = Some(wheel(pos))
} }
self.current_iteration += 1; self.current_iteration += 1;
Some(strip) Some(strip)

View File

@ -1,4 +1,4 @@
use crate::patterns::PixelColor; use crate::patterns::{Pattern, PixelColor};
use crate::Strip; use crate::Strip;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
@ -16,6 +16,18 @@ pub struct Scanner<const N: usize> {
pub(crate) background_color: Option<PixelColor>, pub(crate) background_color: Option<PixelColor>,
} }
impl<const N:usize> Pattern for Scanner<N> {
type Strip = Strip<N>;
fn init(&self) -> Option<Self::Strip> {
if let Some(color) = self.background_color {
Some(Strip::<N>::new(color))
} else {
None
}
}
}
impl<const N: usize> Iterator for Scanner<N> { impl<const N: usize> Iterator for Scanner<N> {
type Item = Strip<N>; type Item = Strip<N>;
@ -36,7 +48,7 @@ impl<const N: usize> Iterator for Scanner<N> {
} }
let mut c = self.color; let mut c = self.color;
for i in min_led..current_led { for i in min_led..current_led {
strip[i] = c; strip[i] = Some(c);
c = c / 2; c = c / 2;
} }
self.current_iteration += 1; self.current_iteration += 1;

View File

@ -1,8 +1,8 @@
use crate::config::Config; use crate::config::Config;
use crate::patterns::{Patterns, PixelColor}; use crate::patterns::{Patterns, PixelColor, Pattern};
use crate::Strip; use crate::Strip;
use bluer::Address; use bluer::Address;
use serde_derive::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::future::Future; use std::future::Future;
use std::str::FromStr; use std::str::FromStr;
@ -41,27 +41,46 @@ pub struct Context<const N: usize> {
pub(crate) type DeviceSequence<const N: usize> = Vec<Context<N>>; pub(crate) type DeviceSequence<const N: usize> = Vec<Context<N>>;
pub async fn runner_loop<const N: usize>(runner: DeviceSequence<N>, tx: Sender<Strip<N>>) { pub async fn runner_loop<const N: usize>(runner: DeviceSequence<N>, tx: Sender<Strip<N>>) {
let mut background = Strip::<N>::default();
for mut context in runner { for mut context in runner {
let mut strip = context.pattern.next().unwrap(); let mut strip = context.pattern.init().unwrap_or(background);
let delay = context.period; let delay = context.period;
while tx.send(strip).is_ok() { while tx.send(strip).is_ok() {
if let Some(value) = context.pattern.next() { if let Some(value) = context.pattern.next() {
strip = value; strip = apply_mask(background, value);
} else { } else {
break; break;
} }
tokio::time::sleep(delay).await; tokio::time::sleep(delay).await;
} }
background = strip;
println!("{:?}", background);
} }
} }
pub fn apply_mask<const N: usize>(previous_strip: Strip<N>, strip: Strip<{ N }>) -> Strip<N> {
let mut new_strip = Strip::default();
for i in 0..N {
if let Some(color) = strip[i] {
new_strip[i] = Some(color);
} else {
new_strip[i] = previous_strip[i];
}
}
new_strip
}
#[derive(Default)] #[derive(Default)]
pub struct Spectacle<const N: usize> { pub struct Spectacle<const N: usize> {
runners: HashMap<DeviceSequence<N>, (Sender<Strip<N>>, Receiver<Strip<N>>)>, runners: HashMap<DeviceSequence<N>, (Sender<Strip<N>>, Receiver<Strip<N>>)>,
channels: HashMap<Address, Receiver<Strip<N>>>, channels: HashMap<Address, Receiver<Strip<N>>>,
default_runner: Option<(DeviceSequence<N>, Sender<Strip<N>>)>, default_runner: Option<(DeviceSequence<N>, Sender<Strip<N>>)>,
default_receiver: Option<Receiver<Strip<N>>>, default_receiver: Option<Receiver<Strip<N>>>,
// #[serde(skip)]
buffered_strip: Strip<N>,
} }
impl<const N: usize> Spectacle<N> { impl<const N: usize> Spectacle<N> {
@ -134,6 +153,7 @@ impl<const N: usize> Spectacle<N> {
} }
joinhandles joinhandles
} }
} }
impl<const N: usize> From<Config<N>> for Spectacle<N> { impl<const N: usize> From<Config<N>> for Spectacle<N> {
@ -159,3 +179,35 @@ impl<const N: usize> From<Config<N>> for Spectacle<N> {
spectacle spectacle
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apply_mask() {
let pixel_entry = PixelColor {
red: 10,
green: 10,
blue: 10
};
let pixel_mask = PixelColor {
red: 20,
green: 20,
blue: 20
};
let mut entry = Strip::<5>::new(pixel_entry) ;
entry[0] = None;
entry[1] = None;
let mut mask = Strip::<5>::new(pixel_mask) ;
mask[0] = None;
let mut expected = entry.clone();
expected[1] = Some(pixel_mask);
assert_eq!(expected, super::apply_mask(mask, entry));
}
}