diff --git a/Cargo.lock b/Cargo.lock index 2d83d2e..5eec9cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -482,6 +482,12 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + [[package]] name = "proc-macro2" version = "1.0.36" @@ -500,6 +506,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.2.10" @@ -515,6 +551,7 @@ version = "0.1.0" dependencies = [ "bluer", "futures", + "rand", "serde", "serde_derive", "serde_yaml", diff --git a/Cargo.toml b/Cargo.toml index 382813a..ba4e65f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" bluer = "0.13.1" tokio = {version = "1.15.0", features = ["full"]} futures = "0.3.19" +rand = "0.8.5" serde_derive = "1.0.136" serde = "1.0.136" diff --git a/spectacle.yml b/spectacle.yml index a168422..8d14055 100644 --- a/spectacle.yml +++ b/spectacle.yml @@ -13,6 +13,16 @@ period: secs: 0 nanos: 200000000 +- pattern: + type: FillRandom + color: + red: 0 + green: 0 + blue: 255 + stability: 40 + period: + secs: 0 + nanos: 100000000 - pattern: type: ColorWipe current_iteration: 0 @@ -20,17 +30,11 @@ red: 0 green: 255 blue: 0 + background_color: + red: 255 + green: 0 + blue: 255 infinite: false period: secs: 0 nanos: 100000000 -- pattern: - type: Scanner - current_iteration: 0 - color: - red: 0 - green: 0 - blue: 255 - period: - secs: 0 - nanos: 100000000 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 363a95a..194381d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ mod patterns; mod runner; mod spectacle; -use crate::patterns::{ColorWipe, Fade, Patterns, PixelColor}; use crate::runner::Runner; use bluer::gatt::remote::Characteristic; use patterns::Strip; @@ -19,44 +18,8 @@ const BASE_STRIP_DATA: [u8; 3] = [0x00, 0x00, 0x01]; #[tokio::main(flavor = "current_thread")] async fn main() -> bluer::Result<()> { - let pixel = PixelColor { - red: 231, - green: 124, - blue: 0, - }; - - let fade = Fade::<25> { - current_iteration: 0, - nbr_iterations: 0, - begin_color: pixel, - end_color: Default::default(), - }; - - let context = runner::Context { - pattern: Patterns::Fade(fade), - period: Duration::from_millis(300), - }; - - let mut runner = runner::Runner::<25>::new(); - runner.push(context); - - let context = runner::Context { - pattern: Patterns::ColorWipe(ColorWipe { - current_iteration: 0, - color: Default::default(), - infinite: false, - }), - period: Default::default(), - }; - - runner.push(context); - - let yaml = serde_yaml::to_string(&runner).unwrap(); - println!("{}", yaml); - let file = Path::new("spectacle.yml"); let config: Runner<25> = Runner::load_from_file(file); - println!("{:?}", config); let adapter = bluetooth::create_session().await?; diff --git a/src/patterns.rs b/src/patterns.rs index 9f7de08..3afd81e 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -1,8 +1,7 @@ +use rand::Rng; use serde_derive::{Deserialize, Serialize}; -use std::cmp::{max, min, Ordering}; -use std::convert::Infallible; use std::fmt; -use std::ops::Div; +use std::ops::{Div, Index, IndexMut, SubAssign}; #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "type")] @@ -11,6 +10,7 @@ pub(crate) enum Patterns { Fade(Fade), ColorWipe(ColorWipe), Scanner(Scanner), + FillRandom(FillRandom), } impl Iterator for Patterns { @@ -22,23 +22,20 @@ impl Iterator for Patterns { Patterns::Fade(p) => p.next(), Patterns::ColorWipe(p) => p.next(), Patterns::Scanner(p) => p.next(), + Patterns::FillRandom(p) => p.next(), } } } -#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)] +/// +/// a struct for an RGB color +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default)] pub struct PixelColor { pub(crate) red: u8, pub(crate) green: u8, pub(crate) blue: u8, } -impl PixelColor { - fn new() -> Self { - Default::default() - } -} - impl fmt::Display for PixelColor { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "[{},{},{}]", self.red, self.green, self.blue) @@ -57,9 +54,39 @@ impl Div for PixelColor { } } +impl SubAssign for PixelColor { + fn sub_assign(&mut self, rhs: Self) { + println!("self.red {}, red {}", self.red, rhs.red); + self.red = self.red.saturating_sub(rhs.red); + self.green = self.red.saturating_sub(rhs.green); + self.blue = self.red.saturating_sub(rhs.blue); + } +} + +/// +/// a basic newtype based on a fix array size. +/// #[derive(Copy, Clone)] -pub struct Strip { - pub strip: [PixelColor; N], +pub struct Strip([PixelColor; N]); + +impl Default for Strip { + fn default() -> Self { + Strip([PixelColor::default(); N]) + } +} + +impl Index for Strip { + type Output = PixelColor; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl IndexMut for Strip { + fn index_mut(&mut self, index: usize) -> &mut PixelColor { + &mut self.0[index] + } } impl Strip { @@ -67,30 +94,26 @@ impl Strip { let mut data: Vec = vec![]; for i in 0..N { - data.append(&mut vec![ - self.strip[i].green, - self.strip[i].red, - self.strip[i].blue, - ]); + data.append(&mut vec![self[i].green, self[i].red, self[i].blue]); } data } pub fn fill(&mut self, color: PixelColor) { - self.strip = [color; N]; + self.0.fill(color); } } -impl Default for Strip { - fn default() -> Self { - Strip { - strip: [PixelColor::new(); N], - } - } -} - -/// Rainbow pattern ///////////////////////////////////////////////// - +/// # Rainbow pattern +/// +/// every pattern implement the iterator trait +/// +/// This pattern display a moving rainbow over the strip +/// +/// ### Note +/// +/// If max iteration is let to None, it's an infinite pattern. +/// #[derive(Serialize, Deserialize, Debug)] pub struct Rainbow { pub(crate) current_iteration: usize, @@ -111,13 +134,21 @@ 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.strip[i] = wheel(pos) + strip[i] = wheel(pos) } self.current_iteration += 1; Some(strip) } } +/// compute **rgb** pixel color according to the **hsv** wheel +/// +/// # Arguments +/// +/// * `index`: position in the hsv wheel +/// +/// returns: PixelColor +/// fn wheel(index: u8) -> PixelColor { let pos = 255 - index; match pos { @@ -145,15 +176,21 @@ fn wheel(index: u8) -> PixelColor { } } -////////////////////////////////////////////////////////////////////////// -/// Colorwipe pattern //////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - +/// # Colorwipe pattern +/// every pattern implement the iterator trait +/// +/// This pattern fill the strip with a specific color, one by one. +/// +/// ### Note +/// +/// setting max_iteration to None lead the pattern to loop infinitely +/// #[derive(Serialize, Deserialize, Debug)] pub struct ColorWipe { pub(crate) current_iteration: usize, + pub(crate) max_iteration: Option, pub(crate) color: PixelColor, - pub(crate) infinite: bool, + pub(crate) background_color: Option, } impl Iterator for ColorWipe { @@ -161,24 +198,26 @@ impl Iterator for ColorWipe { fn next(&mut self) -> Option { let mut strip = Strip::default(); - if self.infinite { - self.current_iteration %= N; - } else if self.current_iteration >= N { - return None; + if let Some(c) = self.background_color { + strip.fill(c); + } + let iteration = self.current_iteration % N; + if let Some(max_iteration) = self.max_iteration { + if self.current_iteration >= max_iteration { + return None; + } } - for i in 0..self.current_iteration { - strip.strip[i] = self.color; + for i in 0..iteration { + strip[i] = self.color; } self.current_iteration += 1; Some(strip) } } -////////////////////////////////////////////////////////////////////////// -/// fade pattern //////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - +/// # fade pattern +/// fade from one color to an other #[derive(Serialize, Deserialize, Debug)] pub struct Fade { pub(crate) current_iteration: usize, @@ -229,10 +268,12 @@ impl Iterator for Fade { } } -////////////////////////////////////////////////////////////////////////// -/// scanner pattern //////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - +/// # 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)] pub struct Scanner { pub(crate) current_iteration: usize, @@ -260,11 +301,58 @@ impl Iterator for Scanner { } let mut c = self.color; for i in min_led..current_led { - strip.strip[i] = c; + strip[i] = c; c = c / 2; - println!("{}", c); } self.current_iteration += 1; Some(strip) } } + +/// # FillRandom +/// +/// fill strip with color then apply random variation +#[derive(Serialize, Deserialize, Debug)] +pub struct FillRandom { + pub(crate) color: PixelColor, + pub(crate) stability: usize, + pub(crate) max_iteration: Option, +} + +impl Iterator for FillRandom { + type Item = Strip; + + fn next(&mut self) -> Option { + if let Some(iteration) = self.max_iteration { + if iteration == 0 { + return None; + } else { + self.max_iteration = Some(iteration - 1); + } + } + + let mut strip = Strip::::default(); + let mut rng = rand::thread_rng(); + for i in 0..N { + let red_delta = rng.gen_range(0..self.stability) as u8; + let green_delta = rng.gen_range(0..self.stability) as u8; + let blue_delta = rng.gen_range(0..self.stability) as u8; + let operation = rng.gen_bool(0.5); + let red; + let green; + let blue; + if operation { + red = self.color.red.saturating_add(red_delta); + green = self.color.green.saturating_add(green_delta); + blue = self.color.blue.saturating_add(blue_delta); + } else { + red = self.color.red.saturating_sub(red_delta); + green = self.color.green.saturating_sub(green_delta); + blue = self.color.blue.saturating_sub(blue_delta); + } + let c = PixelColor { red, green, blue }; + strip[i] = c; + } + Some(strip) + } +} diff --git a/src/spectacle.rs b/src/spectacle.rs index e69de29..8b13789 100644 --- a/src/spectacle.rs +++ b/src/spectacle.rs @@ -0,0 +1 @@ +