refactor and fix starrandom error

This commit is contained in:
Tobias Ollive 2022-03-16 18:44:14 +01:00
parent 6deb31836f
commit b332d75c7f
15 changed files with 196 additions and 132 deletions

View File

@ -1,10 +1,9 @@
use crate::patterns::PixelColor; use crate::pixel_color::PixelColor;
use crate::runner::DeviceSequence; use crate::runner::DeviceSequence;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::collections::HashSet; use std::collections::HashSet;
use std::fs; use std::fs;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Config<const N: usize> { pub struct Config<const N: usize> {
devices: Vec<Device<N>>, devices: Vec<Device<N>>,

View File

@ -3,6 +3,7 @@ mod audio;
mod bluetooth; mod bluetooth;
mod config; mod config;
mod patterns; mod patterns;
mod pixel_color;
mod runner; mod runner;
mod spectacle; mod spectacle;

View File

@ -25,11 +25,9 @@ use crate::patterns::ring::Ring;
use crate::patterns::ring_scanner::RingScanner; use crate::patterns::ring_scanner::RingScanner;
use crate::patterns::scanner::Scanner; use crate::patterns::scanner::Scanner;
use crate::patterns::stars_random::StarsRandom; use crate::patterns::stars_random::StarsRandom;
use rand::rngs::ThreadRng; use crate::pixel_color::PixelColor;
use rand::Rng;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::fmt; use std::ops::{Index, IndexMut};
use std::ops::{Add, AddAssign, Div, Index, IndexMut, Sub, SubAssign};
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)] #[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
#[serde(tag = "type")] #[serde(tag = "type")]
@ -49,6 +47,11 @@ pub(crate) enum Patterns<const N: usize> {
StarsRandom(StarsRandom<N>), StarsRandom(StarsRandom<N>),
} }
/// Pattern that have to be implemented
///
///
///
///
pub trait Pattern: Iterator { pub trait Pattern: Iterator {
type Strip; type Strip;
@ -151,108 +154,6 @@ impl<const N: usize> Pattern for Patterns<N> {
} }
} }
///
/// 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<u8> 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<u8> 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. /// a basic newtype based on a fix array size.
/// ///

View File

@ -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 crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -1,4 +1,5 @@
use crate::patterns::{Pattern, PixelColor}; use crate::patterns::Pattern;
use crate::pixel_color::PixelColor;
use crate::Strip; use crate::Strip;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -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 crate::{impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -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 crate::{impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -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 crate::{impl_pattern_last_iteration, Strip};
use rand::Rng; use rand::Rng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -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 crate::{impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -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 crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -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 crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -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 crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -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 crate::{impl_pattern_last_iteration, Strip};
use rand::Rng; use rand::Rng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -26,14 +27,13 @@ impl<const N: usize> Pattern for StarsRandom<N> {
fn init(&mut self) -> Option<Self::Strip> { fn init(&mut self) -> Option<Self::Strip> {
if let Some(color) = self.background_color { if let Some(color) = self.background_color {
self.strip = Strip::<N>::new(color); self.strip = Strip::<N>::new(color);
let red = (self.color.red as i16 - color.red as i16) as i8; let red = (self.color.red as i16 - color.red as i16);
let green = (color.green as i16 - self.color.green as i16) as i8; let green = (color.green as i16 - self.color.green as i16);
let blue = (self.color.blue as i16 - color.blue as i16) as i8; let blue = (self.color.blue as i16 - color.blue as i16);
println!("{} {} {}", red, green, blue);
self.step = ( self.step = (
red / self.steps as i8, -(red / self.steps as i16) as i8,
green / self.steps as i8, -(green / self.steps as i16) as i8,
blue / self.steps as i8, -(blue / self.steps as i16) as i8,
); );
Some(self.strip) Some(self.strip)
} else { } else {
@ -59,10 +59,8 @@ impl<const N: usize> Iterator for StarsRandom<N> {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
for i in 0..N { for i in 0..N {
let current_color = self.strip[i]; let current_color = self.strip[i];
if current_color == self.background_color { if rng.gen_bool(1.0 / self.ratio as f64) {
if rng.gen_bool(1.0 / self.ratio as f64) { self.strip[i] = Some(self.color);
self.strip[i] = Some(self.color);
}
} else { } else {
let color = current_color.unwrap(); let color = current_color.unwrap();
let background = self.background_color.unwrap_or(PixelColor::default()); let background = self.background_color.unwrap_or(PixelColor::default());

122
src/pixel_color.rs Normal file
View File

@ -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<u8> 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<u8> 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
}
}

View File

@ -1,5 +1,6 @@
use crate::config::Device; use crate::config::Device;
use crate::patterns::{Pattern, Patterns, PixelColor}; use crate::patterns::{Pattern, Patterns};
use crate::pixel_color::PixelColor;
use crate::Strip; use crate::Strip;
use bluer::Address; use bluer::Address;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -41,7 +42,16 @@ 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>>) { /// 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<const N: usize>(runner: DeviceSequence<N>, tx: Sender<Strip<N>>) {
let mut background = Strip::<N>::default(); let mut background = Strip::<N>::default();
for mut context in runner { for mut context in runner {
let mut strip = context.pattern.init().unwrap_or(background); let mut strip = context.pattern.init().unwrap_or(background);
@ -59,7 +69,7 @@ pub async fn runner_loop<const N: usize>(runner: DeviceSequence<N>, tx: Sender<S
} }
} }
pub fn apply_mask<const N: usize>(previous_strip: Strip<N>, strip: Strip<{ N }>) -> Strip<N> { fn apply_mask<const N: usize>(previous_strip: Strip<N>, strip: Strip<{ N }>) -> Strip<N> {
let mut new_strip = Strip::default(); let mut new_strip = Strip::default();
for i in 0..N { for i in 0..N {
if let Some(color) = strip[i] { if let Some(color) = strip[i] {
@ -94,6 +104,15 @@ impl<const N: usize> Spectacle<N> {
} }
} }
/// 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<N>) { pub fn add_default(&mut self, runner: DeviceSequence<N>) {
let (tx, rx) = tokio::sync::watch::channel(Strip::<N>::new(PixelColor { let (tx, rx) = tokio::sync::watch::channel(Strip::<N>::new(PixelColor {
red: 0, red: 0,
@ -104,6 +123,15 @@ impl<const N: usize> Spectacle<N> {
self.default_receiver = Some(rx); 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<Receiver<Strip<{ N }>>>
///
pub fn get_channel(&self, mac_address: &Address) -> Option<Receiver<Strip<N>>> { pub fn get_channel(&self, mac_address: &Address) -> Option<Receiver<Strip<N>>> {
let channel = self.channels.get(mac_address); let channel = self.channels.get(mac_address);
match channel { match channel {
@ -124,6 +152,12 @@ impl<const N: usize> Spectacle<N> {
} }
} }
/// start to stream configured sequences. Returns the joinhandle of task to wait for
///
///
///
/// returns: Vec<impl Future<Output = ()>>
///
pub fn run(&mut self) -> Vec<impl Future<Output = ()>> { pub fn run(&mut self) -> Vec<impl Future<Output = ()>> {
let mut joinhandles = vec![]; let mut joinhandles = vec![];