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 serde_derive::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fs;
#[derive(Serialize, Deserialize)]
pub struct Config<const N: usize> {
devices: Vec<Device<N>>,

View File

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

View File

@ -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<const N: usize> {
StarsRandom(StarsRandom<N>),
}
/// Pattern that have to be implemented
///
///
///
///
pub trait Pattern: Iterator {
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.
///

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 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 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 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 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 rand::Rng;
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 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 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 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 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 rand::Rng;
use serde::{Deserialize, Serialize};
@ -26,14 +27,13 @@ impl<const N: usize> Pattern for StarsRandom<N> {
fn init(&mut self) -> Option<Self::Strip> {
if let Some(color) = self.background_color {
self.strip = Strip::<N>::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<const N: usize> Iterator for StarsRandom<N> {
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());

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::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<const N: usize> {
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();
for mut context in runner {
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();
for i in 0..N {
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>) {
let (tx, rx) = tokio::sync::watch::channel(Strip::<N>::new(PixelColor {
red: 0,
@ -104,6 +123,15 @@ impl<const N: usize> Spectacle<N> {
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>>> {
let channel = self.channels.get(mac_address);
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 = ()>> {
let mut joinhandles = vec![];