From ee133379f2473ae744acd5dc9374eb4cddd29b58 Mon Sep 17 00:00:00 2001 From: Tobias Ollive Date: Fri, 11 Mar 2022 17:10:33 +0100 Subject: [PATCH] add background color for color wipe and my first unit test ! --- spectacle.yml | 58 +++++++++++++++++---------------- src/patterns.rs | 40 ++++++++++++++++++----- src/patterns/blink.rs | 9 ++++++ src/patterns/color_wipe.rs | 16 ++++++++-- src/patterns/fade.rs | 10 +++++- src/patterns/fill_random.rs | 12 +++++-- src/patterns/fill_unstable.rs | 10 +++++- src/patterns/rain.rs | 48 ++++++++++++++++++++++++++++ src/patterns/rainbow.rs | 12 +++++-- src/patterns/scanner.rs | 16 ++++++++-- src/runner.rs | 60 ++++++++++++++++++++++++++++++++--- 11 files changed, 242 insertions(+), 49 deletions(-) create mode 100644 src/patterns/rain.rs diff --git a/spectacle.yml b/spectacle.yml index 43689f7..f2e3a6f 100644 --- a/spectacle.yml +++ b/spectacle.yml @@ -2,21 +2,21 @@ mac_addresses : - C9:81:9C:BA:53:BC sequence: - - pattern: - type: Blink - fade: - current_iteration: 10 - nbr_iterations: 20 - begin_color: &black - red: 0 - green: 0 - blue: 0 - end_color: &purple - red: 255 - green: 0 - blue: 255 - max_iteration: 10 - period: 50 +# - pattern: +# type: Blink +# fade: +# current_iteration: 10 +# nbr_iterations: 20 +# begin_color: &black +# red: 0 +# green: 0 +# blue: 0 +# end_color: &purple +# red: 255 +# green: 0 +# blue: 255 +# max_iteration: 10 +# period: 50 - pattern: type: Fade nbr_iterations: 20 @@ -36,7 +36,6 @@ red: 0 green: 255 blue: 0 - background_color: *purple period: 100 - pattern: type: FillRandom @@ -45,17 +44,22 @@ period: 80 - name: titi sequence: - - pattern: - type: Blink - fade : - nbr_iterations: 20 - begin_color: *black - end_color: &cyan - red: 0 - green: 255 - blue: 255 - max_iteration : 10 - period: 50 +# - pattern: +# type: Blink +# fade : +# nbr_iterations: 20 +# begin_color: *black +# end_color: &cyan +# red: 0 +# green: 255 +# blue: 255 +# max_iteration : 10 +# period: 50 - pattern: type: Rainbow + max_iteration: 200 + period: 10 + - pattern: + type: ColorWipe + color: *blue period: 100 \ No newline at end of file diff --git a/src/patterns.rs b/src/patterns.rs index 614669f..c60428f 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -5,6 +5,7 @@ mod rainbow; mod fill_random; mod fill_unstable; mod blink; +mod rain; use serde_derive::{Deserialize, Serialize}; use std::fmt; @@ -29,6 +30,12 @@ pub(crate) enum Patterns { Blink(Blink), } +pub trait Pattern : Iterator { + type Strip; + + fn init(&self) -> Option; +} + impl Iterator for Patterns { type Item = Strip; @@ -45,6 +52,22 @@ impl Iterator for Patterns { } } +impl Pattern for Patterns { + type Strip = Strip; + + fn init(&self) -> Option { + 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 #[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. /// -#[derive(Copy, Clone)] -pub struct Strip([PixelColor; N]); +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub struct Strip([Option; N]); impl Default for Strip { fn default() -> Self { - Strip([PixelColor::default(); N]) + Strip([None; N]) } } impl Index for Strip { - type Output = PixelColor; + type Output = Option; fn index(&self, index: usize) -> &Self::Output { &self.0[index] @@ -102,7 +125,7 @@ impl Index for Strip { } impl IndexMut for Strip { - fn index_mut(&mut self, index: usize) -> &mut PixelColor { + fn index_mut(&mut self, index: usize) -> &mut Option { &mut self.0[index] } } @@ -112,16 +135,17 @@ impl Strip { let mut data: Vec = vec![]; 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 } pub fn new(color: PixelColor) -> Strip { - Strip { 0: [color; N] } + Strip { 0: [Some(color); N] } } pub fn fill(&mut self, color: PixelColor) { - self.0.fill(color); + self.0.fill(Some(color)); } } \ No newline at end of file diff --git a/src/patterns/blink.rs b/src/patterns/blink.rs index 45ffdcf..1df8f3e 100644 --- a/src/patterns/blink.rs +++ b/src/patterns/blink.rs @@ -1,6 +1,7 @@ use crate::patterns::fade::Fade; use crate::Strip; use serde::{Serialize, Deserialize}; +use crate::patterns::Pattern; /// # Blink /// @@ -13,6 +14,14 @@ pub struct Blink { fade: Fade, } +impl Pattern for Blink { + type Strip = Strip; + + fn init(&self) -> Option { + None + } +} + impl Iterator for Blink { type Item = Strip; diff --git a/src/patterns/color_wipe.rs b/src/patterns/color_wipe.rs index 167e96a..af441bd 100644 --- a/src/patterns/color_wipe.rs +++ b/src/patterns/color_wipe.rs @@ -1,4 +1,4 @@ -use crate::patterns::PixelColor; +use crate::patterns::{Pattern, PixelColor}; use crate::Strip; use serde::{Serialize, Deserialize}; @@ -20,6 +20,18 @@ pub struct ColorWipe { pub(crate) background_color: Option, } +impl Pattern for ColorWipe { + type Strip = Strip; + + fn init(&self) -> Option { + if let Some(color) = self.background_color { + Some(Strip::::new(color)) + } else { + None + } + } +} + impl Iterator for ColorWipe { type Item = Strip; @@ -36,7 +48,7 @@ impl Iterator for ColorWipe { } for i in 0..iteration { - strip[i] = self.color; + strip[i] = Some(self.color); } self.current_iteration += 1; Some(strip) diff --git a/src/patterns/fade.rs b/src/patterns/fade.rs index dd490a5..4bfeee6 100644 --- a/src/patterns/fade.rs +++ b/src/patterns/fade.rs @@ -1,4 +1,4 @@ -use crate::patterns::PixelColor; +use crate::patterns::{Pattern, PixelColor}; use crate::Strip; 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 } +impl Pattern for Fade { + type Strip = Strip; + + fn init(&self) -> Option { + None + } +} + impl Iterator for Fade { type Item = Strip; diff --git a/src/patterns/fill_random.rs b/src/patterns/fill_random.rs index 7b9b435..c0d41a4 100644 --- a/src/patterns/fill_random.rs +++ b/src/patterns/fill_random.rs @@ -1,5 +1,5 @@ use rand::Rng; -use crate::patterns::PixelColor; +use crate::patterns::{Pattern, PixelColor}; use crate::Strip; use serde::{Serialize, Deserialize}; @@ -13,6 +13,14 @@ pub struct FillRandom { pub(crate) max_iteration: Option, } +impl Pattern for FillRandom { + type Strip = Strip; + + fn init(&self) -> Option { + None + } +} + impl Iterator for FillRandom { type Item = Strip; @@ -45,7 +53,7 @@ impl Iterator for FillRandom { blue = self.color.blue.saturating_sub(blue_delta); } let c = PixelColor { red, green, blue }; - strip[i] = c; + strip[i] = Some(c); } Some(strip) } diff --git a/src/patterns/fill_unstable.rs b/src/patterns/fill_unstable.rs index e23e493..09331d9 100644 --- a/src/patterns/fill_unstable.rs +++ b/src/patterns/fill_unstable.rs @@ -1,5 +1,5 @@ use rand::Rng; -use crate::patterns::PixelColor; +use crate::patterns::{Pattern, PixelColor}; use crate::Strip; use serde::{Serialize, Deserialize}; @@ -13,6 +13,14 @@ pub struct FillUnstable { pub(crate) max_iteration: Option, } +impl Pattern for FillUnstable { + type Strip = Strip; + + fn init(&self) -> Option { + None + } +} + impl Iterator for FillUnstable { type Item = Strip; diff --git a/src/patterns/rain.rs b/src/patterns/rain.rs new file mode 100644 index 0000000..47a1c18 --- /dev/null +++ b/src/patterns/rain.rs @@ -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 { + #[serde(default)] + current_iteration: usize, + pub(crate) color: PixelColor, + pub(crate) background_color: Option, + pub(crate) stability: usize, + #[serde(skip)] + strip : Strip, +} + +impl Iterator for Scanner { + type Item = Strip; + + fn next(&mut self) -> Option { + let mut strip = Strip::::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) + } +} \ No newline at end of file diff --git a/src/patterns/rainbow.rs b/src/patterns/rainbow.rs index 6b15ee7..629e896 100644 --- a/src/patterns/rainbow.rs +++ b/src/patterns/rainbow.rs @@ -1,4 +1,4 @@ -use crate::patterns::PixelColor; +use crate::patterns::{Pattern, PixelColor}; use crate::Strip; use serde::{Serialize, Deserialize}; @@ -20,6 +20,14 @@ pub struct Rainbow { pub(crate) step: Option, } +impl Pattern for Rainbow { + type Strip = Strip; + + fn init(&self) -> Option { + None + } +} + impl Iterator for Rainbow { type Item = Strip; @@ -33,7 +41,7 @@ impl Iterator for Rainbow { let step = self.step.unwrap_or(255 / N); for i in 0..N { let pos = (i * step + self.current_iteration) as u8; - strip[i] = wheel(pos) + strip[i] = Some(wheel(pos)) } self.current_iteration += 1; Some(strip) diff --git a/src/patterns/scanner.rs b/src/patterns/scanner.rs index 1a2809d..73b74e1 100644 --- a/src/patterns/scanner.rs +++ b/src/patterns/scanner.rs @@ -1,4 +1,4 @@ -use crate::patterns::PixelColor; +use crate::patterns::{Pattern, PixelColor}; use crate::Strip; use serde::{Serialize, Deserialize}; @@ -16,6 +16,18 @@ pub struct Scanner { pub(crate) background_color: Option, } +impl Pattern for Scanner { + type Strip = Strip; + + fn init(&self) -> Option { + if let Some(color) = self.background_color { + Some(Strip::::new(color)) + } else { + None + } + } +} + impl Iterator for Scanner { type Item = Strip; @@ -36,7 +48,7 @@ impl Iterator for Scanner { } let mut c = self.color; for i in min_led..current_led { - strip[i] = c; + strip[i] = Some(c); c = c / 2; } self.current_iteration += 1; diff --git a/src/runner.rs b/src/runner.rs index d08c13f..9f39448 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,8 +1,8 @@ use crate::config::Config; -use crate::patterns::{Patterns, PixelColor}; +use crate::patterns::{Patterns, PixelColor, Pattern}; use crate::Strip; use bluer::Address; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::future::Future; use std::str::FromStr; @@ -41,27 +41,46 @@ pub struct Context { pub(crate) type DeviceSequence = Vec>; + + pub async fn runner_loop(runner: DeviceSequence, tx: Sender>) { + let mut background = Strip::::default(); 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; while tx.send(strip).is_ok() { if let Some(value) = context.pattern.next() { - strip = value; + strip = apply_mask(background, value); } else { break; } tokio::time::sleep(delay).await; } + background = strip; + println!("{:?}", background); } } +pub fn apply_mask(previous_strip: Strip, strip: Strip<{ N }>) -> Strip { + 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)] pub struct Spectacle { runners: HashMap, (Sender>, Receiver>)>, channels: HashMap>>, default_runner: Option<(DeviceSequence, Sender>)>, default_receiver: Option>>, + // #[serde(skip)] + buffered_strip: Strip, } impl Spectacle { @@ -134,6 +153,7 @@ impl Spectacle { } joinhandles } + } impl From> for Spectacle { @@ -159,3 +179,35 @@ impl From> for 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)); + } +} \ No newline at end of file