diff --git a/src/config.rs b/src/config.rs index e962e4d..ac685d2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,10 +1,9 @@ -use crate::patterns::PixelColor; +use crate::pixel_color::PixelColor; use crate::runner::DeviceSequence; use serde_derive::{Deserialize, Serialize}; use std::collections::HashSet; use std::fs; - #[derive(Serialize, Deserialize)] pub struct Config { devices: Vec>, diff --git a/src/main.rs b/src/main.rs index 98618f5..911e409 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod audio; mod bluetooth; mod config; mod patterns; +mod pixel_color; mod runner; mod spectacle; diff --git a/src/patterns.rs b/src/patterns.rs index cf3e76b..d99377f 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -25,11 +25,9 @@ use crate::patterns::ring::Ring; use crate::patterns::ring_scanner::RingScanner; use crate::patterns::scanner::Scanner; use crate::patterns::stars_random::StarsRandom; -use rand::rngs::ThreadRng; -use rand::Rng; +use crate::pixel_color::PixelColor; use serde_derive::{Deserialize, Serialize}; -use std::fmt; -use std::ops::{Add, AddAssign, Div, Index, IndexMut, Sub, SubAssign}; +use std::ops::{Index, IndexMut}; #[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)] #[serde(tag = "type")] @@ -49,6 +47,11 @@ pub(crate) enum Patterns { StarsRandom(StarsRandom), } +/// Pattern that have to be implemented +/// +/// +/// +/// pub trait Pattern: Iterator { type Strip; @@ -151,108 +154,6 @@ impl Pattern for Patterns { } } -/// -/// a struct for an RGB color -#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default, Eq, Hash, PartialEq)] -pub struct PixelColor { - pub(crate) red: u8, - pub(crate) green: u8, - pub(crate) blue: u8, -} - -impl fmt::Display for PixelColor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "[{},{},{}]", self.red, self.green, self.blue) - } -} - -impl Div for PixelColor { - type Output = PixelColor; - - fn div(self, rhs: u8) -> Self::Output { - PixelColor { - red: self.red / rhs, - green: self.green / rhs, - blue: self.blue / rhs, - } - } -} - -impl Add for PixelColor { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - PixelColor { - red: self.red.saturating_add(rhs.red), - green: self.green.saturating_add(rhs.green), - blue: self.blue.saturating_add(rhs.blue), - } - } -} - -impl Sub for PixelColor { - type Output = Self; - - fn sub(self, rhs: u8) -> Self::Output { - PixelColor { - red: self.red.saturating_sub(rhs), - green: self.green.saturating_sub(rhs), - blue: self.blue.saturating_sub(rhs), - } - } -} - -impl SubAssign for PixelColor { - fn sub_assign(&mut self, rhs: Self) { - self.red = self.red.saturating_sub(rhs.red); - self.green = self.red.saturating_sub(rhs.green); - self.blue = self.red.saturating_sub(rhs.blue); - } -} - -impl AddAssign for PixelColor { - fn add_assign(&mut self, rhs: Self) { - self.red = self.red.saturating_add(rhs.red); - self.green = self.red.saturating_add(rhs.green); - self.blue = self.red.saturating_add(rhs.blue); - } -} - -impl PixelColor { - /// apply random delta to pixel and return computed value - /// - /// # Arguments - /// - /// * `pixel`: pixel to apply delta - /// * `stability`: amplitude of delta between 0 and `stability` - /// - /// returns: PixelColor - /// - pub fn random_delta(&self, stability: usize, rng: &mut ThreadRng) -> PixelColor { - let minus_interval = -(stability as i8); - let max_interval = stability as i8; - let red_delta = rng.gen_range(minus_interval..max_interval) as i8; - let green_delta = rng.gen_range(minus_interval..max_interval) as i8; - let blue_delta = rng.gen_range(minus_interval..max_interval) as i8; - self.delta((red_delta, green_delta, blue_delta)) - } - - pub fn delta(&self, delta: (i8, i8, i8)) -> PixelColor { - let (red, green, blue) = delta; - PixelColor { - red: (self.red as i16 + red as i16) as u8, - green: (self.green as i16 + green as i16) as u8, - blue: (self.blue as i16 + blue as i16) as u8, - } - } - - pub fn is_closer(&self, pixel: PixelColor, distance: PixelColor) -> bool { - (self.red as i16 - pixel.red as i16).abs() <= distance.red as i16 - && (self.green as i16 - pixel.green as i16).abs() <= distance.green as i16 - && (self.blue as i16 - pixel.blue as i16).abs() <= distance.blue as i16 - } -} - /// /// a basic newtype based on a fix array size. /// diff --git a/src/patterns/color_wipe.rs b/src/patterns/color_wipe.rs index 279c050..73b9bd6 100644 --- a/src/patterns/color_wipe.rs +++ b/src/patterns/color_wipe.rs @@ -1,4 +1,5 @@ -use crate::patterns::{Pattern, PixelColor}; +use crate::patterns::Pattern; +use crate::pixel_color::PixelColor; use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip}; use serde::{Deserialize, Serialize}; diff --git a/src/patterns/fade.rs b/src/patterns/fade.rs index b928697..c3873f9 100644 --- a/src/patterns/fade.rs +++ b/src/patterns/fade.rs @@ -1,4 +1,5 @@ -use crate::patterns::{Pattern, PixelColor}; +use crate::patterns::Pattern; +use crate::pixel_color::PixelColor; use crate::Strip; use serde::{Deserialize, Serialize}; diff --git a/src/patterns/fill_random.rs b/src/patterns/fill_random.rs index f159a9d..305c325 100644 --- a/src/patterns/fill_random.rs +++ b/src/patterns/fill_random.rs @@ -1,4 +1,5 @@ -use crate::patterns::{Pattern, PixelColor}; +use crate::patterns::Pattern; +use crate::pixel_color::PixelColor; use crate::{impl_pattern_last_iteration, Strip}; use serde::{Deserialize, Serialize}; diff --git a/src/patterns/fill_unstable.rs b/src/patterns/fill_unstable.rs index 1b77fe6..a87b8b3 100644 --- a/src/patterns/fill_unstable.rs +++ b/src/patterns/fill_unstable.rs @@ -1,4 +1,5 @@ -use crate::patterns::{Pattern, PixelColor}; +use crate::patterns::Pattern; +use crate::pixel_color::PixelColor; use crate::{impl_pattern_last_iteration, Strip}; use serde::{Deserialize, Serialize}; diff --git a/src/patterns/rain.rs b/src/patterns/rain.rs index e564906..d8d9350 100644 --- a/src/patterns/rain.rs +++ b/src/patterns/rain.rs @@ -1,4 +1,5 @@ -use crate::patterns::{Pattern, PixelColor}; +use crate::patterns::Pattern; +use crate::pixel_color::PixelColor; use crate::{impl_pattern_last_iteration, Strip}; use rand::Rng; use serde::{Deserialize, Serialize}; diff --git a/src/patterns/rainbow.rs b/src/patterns/rainbow.rs index d3bec86..70d98a1 100644 --- a/src/patterns/rainbow.rs +++ b/src/patterns/rainbow.rs @@ -1,4 +1,5 @@ -use crate::patterns::{Pattern, PixelColor}; +use crate::patterns::Pattern; +use crate::pixel_color::PixelColor; use crate::{impl_pattern_last_iteration, Strip}; use serde::{Deserialize, Serialize}; diff --git a/src/patterns/ring.rs b/src/patterns/ring.rs index d8b58cb..03cfef9 100644 --- a/src/patterns/ring.rs +++ b/src/patterns/ring.rs @@ -1,4 +1,5 @@ -use crate::patterns::{Pattern, PixelColor}; +use crate::patterns::Pattern; +use crate::pixel_color::PixelColor; use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip}; use serde::{Deserialize, Serialize}; diff --git a/src/patterns/ring_scanner.rs b/src/patterns/ring_scanner.rs index f533413..804c8bd 100644 --- a/src/patterns/ring_scanner.rs +++ b/src/patterns/ring_scanner.rs @@ -1,4 +1,5 @@ -use crate::patterns::{Pattern, PixelColor}; +use crate::patterns::Pattern; +use crate::pixel_color::PixelColor; use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip}; use serde::{Deserialize, Serialize}; diff --git a/src/patterns/scanner.rs b/src/patterns/scanner.rs index cbf72cd..e5a9088 100644 --- a/src/patterns/scanner.rs +++ b/src/patterns/scanner.rs @@ -1,4 +1,5 @@ -use crate::patterns::{Pattern, PixelColor}; +use crate::patterns::Pattern; +use crate::pixel_color::PixelColor; use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip}; use serde::{Deserialize, Serialize}; diff --git a/src/patterns/stars_random.rs b/src/patterns/stars_random.rs index 30c4f30..75c77f9 100644 --- a/src/patterns/stars_random.rs +++ b/src/patterns/stars_random.rs @@ -1,4 +1,5 @@ -use crate::patterns::{Pattern, PixelColor}; +use crate::patterns::Pattern; +use crate::pixel_color::PixelColor; use crate::{impl_pattern_last_iteration, Strip}; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -26,14 +27,13 @@ impl Pattern for StarsRandom { fn init(&mut self) -> Option { if let Some(color) = self.background_color { self.strip = Strip::::new(color); - let red = (self.color.red as i16 - color.red as i16) as i8; - let green = (color.green as i16 - self.color.green as i16) as i8; - let blue = (self.color.blue as i16 - color.blue as i16) as i8; - println!("{} {} {}", red, green, blue); + let red = (self.color.red as i16 - color.red as i16); + let green = (color.green as i16 - self.color.green as i16); + let blue = (self.color.blue as i16 - color.blue as i16); self.step = ( - red / self.steps as i8, - green / self.steps as i8, - blue / self.steps as i8, + -(red / self.steps as i16) as i8, + -(green / self.steps as i16) as i8, + -(blue / self.steps as i16) as i8, ); Some(self.strip) } else { @@ -59,10 +59,8 @@ impl Iterator for StarsRandom { let mut rng = rand::thread_rng(); for i in 0..N { let current_color = self.strip[i]; - if current_color == self.background_color { - if rng.gen_bool(1.0 / self.ratio as f64) { - self.strip[i] = Some(self.color); - } + if rng.gen_bool(1.0 / self.ratio as f64) { + self.strip[i] = Some(self.color); } else { let color = current_color.unwrap(); let background = self.background_color.unwrap_or(PixelColor::default()); diff --git a/src/pixel_color.rs b/src/pixel_color.rs new file mode 100644 index 0000000..69501e5 --- /dev/null +++ b/src/pixel_color.rs @@ -0,0 +1,122 @@ +use rand::prelude::ThreadRng; +use rand::Rng; +use serde_derive::{Deserialize, Serialize}; +use std::fmt; +use std::ops::{Add, AddAssign, Div, Sub, SubAssign}; + +/// +/// a struct for an RGB color +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default, Eq, Hash, PartialEq)] +pub struct PixelColor { + pub(crate) red: u8, + pub(crate) green: u8, + pub(crate) blue: u8, +} + +impl fmt::Display for PixelColor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{},{},{}]", self.red, self.green, self.blue) + } +} + +impl Div for PixelColor { + type Output = PixelColor; + + fn div(self, rhs: u8) -> Self::Output { + PixelColor { + red: self.red / rhs, + green: self.green / rhs, + blue: self.blue / rhs, + } + } +} + +impl Add for PixelColor { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + PixelColor { + red: self.red.saturating_add(rhs.red), + green: self.green.saturating_add(rhs.green), + blue: self.blue.saturating_add(rhs.blue), + } + } +} + +impl Sub for PixelColor { + type Output = Self; + + fn sub(self, rhs: u8) -> Self::Output { + PixelColor { + red: self.red.saturating_sub(rhs), + green: self.green.saturating_sub(rhs), + blue: self.blue.saturating_sub(rhs), + } + } +} + +impl SubAssign for PixelColor { + fn sub_assign(&mut self, rhs: Self) { + self.red = self.red.saturating_sub(rhs.red); + self.green = self.red.saturating_sub(rhs.green); + self.blue = self.red.saturating_sub(rhs.blue); + } +} + +impl AddAssign for PixelColor { + fn add_assign(&mut self, rhs: Self) { + self.red = self.red.saturating_add(rhs.red); + self.green = self.red.saturating_add(rhs.green); + self.blue = self.red.saturating_add(rhs.blue); + } +} + +impl PixelColor { + /// apply random delta to pixel and return computed value + /// + /// # Arguments + /// + /// * `pixel`: pixel to apply delta + /// * `stability`: amplitude of delta between 0 and `stability` + /// + /// returns: PixelColor + /// + pub fn random_delta(&self, stability: usize, rng: &mut ThreadRng) -> PixelColor { + let minus_interval = -(stability as i8); + let max_interval = stability as i8; + let red_delta = rng.gen_range(minus_interval..max_interval) as i8; + let green_delta = rng.gen_range(minus_interval..max_interval) as i8; + let blue_delta = rng.gen_range(minus_interval..max_interval) as i8; + self.delta((red_delta, green_delta, blue_delta)) + } + + pub fn delta(&self, delta: (i8, i8, i8)) -> PixelColor { + let (red, green, blue) = delta; + + let red = self.red as i16 + red as i16; + let green = self.green as i16 + green as i16; + let blue = self.blue as i16 + blue as i16; + + let bound = |color: i16| { + if color < 0 { + 0 + } else if color > 255 { + 255 + } else { + color as u8 + } + }; + + PixelColor { + red: bound(red), + green: bound(green), + blue: bound(blue), + } + } + + pub fn is_closer(&self, pixel: PixelColor, distance: PixelColor) -> bool { + (self.red as i16 - pixel.red as i16).abs() <= distance.red as i16 + && (self.green as i16 - pixel.green as i16).abs() <= distance.green as i16 + && (self.blue as i16 - pixel.blue as i16).abs() <= distance.blue as i16 + } +} diff --git a/src/runner.rs b/src/runner.rs index 2db4758..386c826 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,5 +1,6 @@ use crate::config::Device; -use crate::patterns::{Pattern, Patterns, PixelColor}; +use crate::patterns::{Pattern, Patterns}; +use crate::pixel_color::PixelColor; use crate::Strip; use bluer::Address; use serde::{Deserialize, Serialize}; @@ -41,7 +42,16 @@ pub struct Context { pub(crate) type DeviceSequence = Vec>; -pub async fn runner_loop(runner: DeviceSequence, tx: Sender>) { +/// iterate over `runner` and stream strip data to `tx` channel +/// +/// # Arguments +/// +/// * `runner`: Sequence to stream +/// * `tx`: channel to stream data to +/// +/// returns: () +/// +async fn runner_loop(runner: DeviceSequence, tx: Sender>) { let mut background = Strip::::default(); for mut context in runner { let mut strip = context.pattern.init().unwrap_or(background); @@ -59,7 +69,7 @@ pub async fn runner_loop(runner: DeviceSequence, tx: Sender(previous_strip: Strip, strip: Strip<{ N }>) -> Strip { +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] { @@ -94,6 +104,15 @@ impl Spectacle { } } + /// add default sequence for any device implementing **adafruit neopixel BLE** + /// not specified in an other sequence. + /// + /// # Arguments + /// + /// * `runner`: sequence to stream + /// + /// returns: () + /// pub fn add_default(&mut self, runner: DeviceSequence) { let (tx, rx) = tokio::sync::watch::channel(Strip::::new(PixelColor { red: 0, @@ -104,6 +123,15 @@ impl Spectacle { self.default_receiver = Some(rx); } + /// Get channel associated with the mac address passed in parameters. If no channel found, + /// give back the default channel if existing. + /// + /// # Arguments + /// + /// * `mac_address`: mac address to look for + /// + /// returns: Option>> + /// pub fn get_channel(&self, mac_address: &Address) -> Option>> { let channel = self.channels.get(mac_address); match channel { @@ -124,6 +152,12 @@ impl Spectacle { } } + /// start to stream configured sequences. Returns the joinhandle of task to wait for + /// + /// + /// + /// returns: Vec> + /// pub fn run(&mut self) -> Vec> { let mut joinhandles = vec![];