refactor patterns
This commit is contained in:
parent
c2b2517661
commit
b718334392
|
@ -7,83 +7,55 @@
|
|||
fade:
|
||||
current_iteration: 10
|
||||
nbr_iterations: 20
|
||||
begin_color:
|
||||
begin_color: &black
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 0
|
||||
end_color:
|
||||
end_color: &purple
|
||||
red: 255
|
||||
green: 0
|
||||
blue: 255
|
||||
current_iteration: 0
|
||||
max_iteration: 10
|
||||
period:
|
||||
secs: 0
|
||||
nanos: 50000000
|
||||
period: 50
|
||||
- pattern:
|
||||
type: Fade
|
||||
current_iteration: 0
|
||||
nbr_iterations: 20
|
||||
begin_color:
|
||||
begin_color: &red
|
||||
red: 255
|
||||
green: 0
|
||||
blue: 0
|
||||
end_color:
|
||||
end_color: &blue
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 255
|
||||
period:
|
||||
secs: 0
|
||||
nanos: 200000000
|
||||
period: 200
|
||||
- pattern:
|
||||
type: ColorWipe
|
||||
max_iteration: 25
|
||||
current_iteration: 0
|
||||
color:
|
||||
color: &green
|
||||
red: 0
|
||||
green: 255
|
||||
blue: 0
|
||||
background_color:
|
||||
red: 255
|
||||
green: 0
|
||||
blue: 255
|
||||
period:
|
||||
secs: 0
|
||||
nanos: 100000000
|
||||
background_color: *purple
|
||||
period: 100
|
||||
- pattern:
|
||||
type: FillRandom
|
||||
color:
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 255
|
||||
color: *blue
|
||||
stability: 40
|
||||
period:
|
||||
secs: 0
|
||||
nanos: 800000000
|
||||
|
||||
period: 80
|
||||
- name: titi
|
||||
sequence:
|
||||
- pattern:
|
||||
type: Blink
|
||||
fade :
|
||||
current_iteration: 0
|
||||
nbr_iterations: 20
|
||||
begin_color:
|
||||
red: 0
|
||||
green: 0
|
||||
blue: 0
|
||||
end_color:
|
||||
begin_color: *black
|
||||
end_color: &cyan
|
||||
red: 0
|
||||
green: 255
|
||||
blue: 255
|
||||
current_iteration: 0
|
||||
max_iteration : 10
|
||||
period:
|
||||
secs: 0
|
||||
nanos: 100000000
|
||||
period: 50
|
||||
- pattern:
|
||||
type: Rainbow
|
||||
current_iteration: 0
|
||||
period:
|
||||
secs: 0
|
||||
nanos: 100000000
|
||||
period: 100
|
|
@ -19,7 +19,6 @@ use std::sync::{Arc, Mutex};
|
|||
use std::time::Duration;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::watch;
|
||||
use tokio::sync::Notify;
|
||||
|
||||
const SERVICE_UUID: bluer::Uuid = bluer::Uuid::from_u128(0xadaf0900c33242a893bd25e905756cb8);
|
||||
const PIXEL_DATA_UUID: bluer::Uuid = bluer::Uuid::from_u128(0xadaf0903c33242a893bd25e905756cb8);
|
||||
|
@ -35,8 +34,6 @@ async fn main() -> bluer::Result<()> {
|
|||
let spectacle = Spectacle::from(config);
|
||||
let spectacle = Arc::new(Mutex::new(spectacle));
|
||||
|
||||
let musique_file = command_line.musique_file;
|
||||
let musique = tokio::task::spawn_blocking(move || play_sound(Path::new(&musique_file)));
|
||||
|
||||
let adapter = bluetooth::create_session().await?;
|
||||
let (tx_scan, rx_scan) = mpsc::channel(3);
|
||||
|
@ -49,9 +46,9 @@ async fn main() -> bluer::Result<()> {
|
|||
|
||||
tokio::spawn(connect_device(spectacle.clone(), rx_scan));
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
|
||||
println!("starting");
|
||||
let musique_file = command_line.musique_file;
|
||||
let musique = tokio::task::spawn_blocking(move || play_sound(Path::new(&musique_file)));
|
||||
let futures;
|
||||
{
|
||||
let mut spectacle = spectacle.lock().unwrap();
|
||||
|
|
365
src/patterns.rs
365
src/patterns.rs
|
@ -1,7 +1,21 @@
|
|||
use rand::Rng;
|
||||
mod scanner;
|
||||
mod fade;
|
||||
mod color_wipe;
|
||||
mod rainbow;
|
||||
mod fill_random;
|
||||
mod fill_unstable;
|
||||
mod blink;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::ops::{Div, Index, IndexMut, SubAssign};
|
||||
use crate::patterns::blink::Blink;
|
||||
use crate::patterns::color_wipe::ColorWipe;
|
||||
use crate::patterns::fade::Fade;
|
||||
use crate::patterns::fill_random::FillRandom;
|
||||
use crate::patterns::fill_unstable::FillUnstable;
|
||||
use crate::patterns::rainbow::Rainbow;
|
||||
use crate::patterns::scanner::Scanner;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
#[serde(tag = "type")]
|
||||
|
@ -111,352 +125,3 @@ impl<const N: usize> Strip<N> {
|
|||
self.0.fill(color);
|
||||
}
|
||||
}
|
||||
|
||||
/// # 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, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Rainbow<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) step: Option<usize>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Rainbow<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(nbr_iteration) = self.max_iteration {
|
||||
if nbr_iteration == self.current_iteration {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let mut strip = Strip::default();
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
0..=85 => PixelColor {
|
||||
red: 255 - (pos * 3),
|
||||
green: 0,
|
||||
blue: pos * 3,
|
||||
},
|
||||
86..=170 => {
|
||||
let pos = pos - 85;
|
||||
PixelColor {
|
||||
red: 0,
|
||||
green: pos * 3,
|
||||
blue: 255 - (pos * 3),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let pos = pos - 170;
|
||||
PixelColor {
|
||||
red: pos * 3,
|
||||
green: 255 - (pos * 3),
|
||||
blue: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # 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, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct ColorWipe<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) background_color: Option<PixelColor>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for ColorWipe<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut strip = Strip::default();
|
||||
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..iteration {
|
||||
strip[i] = self.color;
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
|
||||
/// # fade pattern
|
||||
/// fade from one color to an other
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Fade<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) nbr_iterations: usize,
|
||||
pub(crate) begin_color: PixelColor,
|
||||
pub(crate) end_color: PixelColor,
|
||||
}
|
||||
|
||||
fn fade_value(value_start: u8, value_end: u8, index: usize, nbr_iter: usize) -> u8 {
|
||||
((value_start as usize * (nbr_iter - index) + value_end as usize * index) / nbr_iter) as u8
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Fade<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut strip = Strip::default();
|
||||
|
||||
if self.current_iteration >= self.nbr_iterations {
|
||||
return None;
|
||||
}
|
||||
|
||||
let red = fade_value(
|
||||
self.begin_color.red,
|
||||
self.end_color.red,
|
||||
self.current_iteration,
|
||||
self.nbr_iterations,
|
||||
);
|
||||
let green = fade_value(
|
||||
self.begin_color.green,
|
||||
self.end_color.green,
|
||||
self.current_iteration,
|
||||
self.nbr_iterations,
|
||||
);
|
||||
let blue = fade_value(
|
||||
self.begin_color.blue,
|
||||
self.end_color.blue,
|
||||
self.current_iteration,
|
||||
self.nbr_iterations,
|
||||
);
|
||||
|
||||
let current_color = PixelColor { red, green, blue };
|
||||
|
||||
strip.fill(current_color);
|
||||
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
|
||||
/// # 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<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) background_color: Option<PixelColor>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Scanner<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut strip = Strip::<N>::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] = c;
|
||||
c = c / 2;
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
|
||||
/// # FillRandom
|
||||
///
|
||||
/// fill strip with color then apply random variation to each pixel
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct FillRandom<const N: usize> {
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) stability: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for FillRandom<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(iteration) = self.max_iteration {
|
||||
if iteration == 0 {
|
||||
return None;
|
||||
} else {
|
||||
self.max_iteration = Some(iteration - 1);
|
||||
}
|
||||
}
|
||||
|
||||
let mut strip = Strip::<N>::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)
|
||||
}
|
||||
}
|
||||
|
||||
/// # FillUnstable
|
||||
///
|
||||
/// fill strip with color then apply same random variation to all pixel
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct FillUnstable<const N: usize> {
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) stability: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for FillUnstable<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(iteration) = self.max_iteration {
|
||||
if iteration == 0 {
|
||||
return None;
|
||||
} else {
|
||||
self.max_iteration = Some(iteration - 1);
|
||||
}
|
||||
}
|
||||
|
||||
let mut strip = Strip::<N>::default();
|
||||
let mut rng = rand::thread_rng();
|
||||
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);
|
||||
}
|
||||
strip.fill(PixelColor { red, green, blue });
|
||||
Some(strip)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
enum Direction {
|
||||
there,
|
||||
back,
|
||||
}
|
||||
|
||||
impl Default for Direction {
|
||||
fn default() -> Self {
|
||||
Direction::there
|
||||
}
|
||||
}
|
||||
|
||||
/// # Blink
|
||||
///
|
||||
/// fade from one color to an other then go back several times
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Blink<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
fade: Fade<N>,
|
||||
#[serde(default)]
|
||||
direction: Direction,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Blink<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let value = self.fade.next();
|
||||
match value {
|
||||
None => {
|
||||
if let Some(iter_max) = self.max_iteration {
|
||||
if self.current_iteration >= iter_max {
|
||||
return None;
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
}
|
||||
self.fade.current_iteration = 0;
|
||||
std::mem::swap(&mut self.fade.begin_color, &mut self.fade.end_color);
|
||||
self.fade.next()
|
||||
}
|
||||
Some(value) => Some(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
36
src/patterns/blink.rs
Normal file
36
src/patterns/blink.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use crate::patterns::fade::Fade;
|
||||
use crate::Strip;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// # Blink
|
||||
///
|
||||
/// fade from one color to an other then go back several times
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Blink<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
fade: Fade<N>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Blink<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let value = self.fade.next();
|
||||
match value {
|
||||
None => {
|
||||
if let Some(iter_max) = self.max_iteration {
|
||||
if self.current_iteration >= iter_max {
|
||||
return None;
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
}
|
||||
self.fade.current_iteration = 0;
|
||||
std::mem::swap(&mut self.fade.begin_color, &mut self.fade.end_color);
|
||||
self.fade.next()
|
||||
}
|
||||
Some(value) => Some(value),
|
||||
}
|
||||
}
|
||||
}
|
44
src/patterns/color_wipe.rs
Normal file
44
src/patterns/color_wipe.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use crate::patterns::PixelColor;
|
||||
use crate::Strip;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// # 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, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct ColorWipe<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) background_color: Option<PixelColor>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for ColorWipe<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut strip = Strip::default();
|
||||
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..iteration {
|
||||
strip[i] = self.color;
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
56
src/patterns/fade.rs
Normal file
56
src/patterns/fade.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use crate::patterns::PixelColor;
|
||||
use crate::Strip;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// # fade pattern
|
||||
/// fade from one color to an other
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Fade<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) nbr_iterations: usize,
|
||||
pub(crate) begin_color: PixelColor,
|
||||
pub(crate) end_color: PixelColor,
|
||||
}
|
||||
|
||||
fn fade_value(value_start: u8, value_end: u8, index: usize, nbr_iter: usize) -> u8 {
|
||||
((value_start as usize * (nbr_iter - index) + value_end as usize * index) / nbr_iter) as u8
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Fade<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut strip = Strip::default();
|
||||
|
||||
if self.current_iteration >= self.nbr_iterations {
|
||||
return None;
|
||||
}
|
||||
|
||||
let red = fade_value(
|
||||
self.begin_color.red,
|
||||
self.end_color.red,
|
||||
self.current_iteration,
|
||||
self.nbr_iterations,
|
||||
);
|
||||
let green = fade_value(
|
||||
self.begin_color.green,
|
||||
self.end_color.green,
|
||||
self.current_iteration,
|
||||
self.nbr_iterations,
|
||||
);
|
||||
let blue = fade_value(
|
||||
self.begin_color.blue,
|
||||
self.end_color.blue,
|
||||
self.current_iteration,
|
||||
self.nbr_iterations,
|
||||
);
|
||||
|
||||
let current_color = PixelColor { red, green, blue };
|
||||
|
||||
strip.fill(current_color);
|
||||
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
52
src/patterns/fill_random.rs
Normal file
52
src/patterns/fill_random.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use rand::Rng;
|
||||
use crate::patterns::PixelColor;
|
||||
use crate::Strip;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// # FillRandom
|
||||
///
|
||||
/// fill strip with color then apply random variation to each pixel
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct FillRandom<const N: usize> {
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) stability: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for FillRandom<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(iteration) = self.max_iteration {
|
||||
if iteration == 0 {
|
||||
return None;
|
||||
} else {
|
||||
self.max_iteration = Some(iteration - 1);
|
||||
}
|
||||
}
|
||||
|
||||
let mut strip = Strip::<N>::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)
|
||||
}
|
||||
}
|
49
src/patterns/fill_unstable.rs
Normal file
49
src/patterns/fill_unstable.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use rand::Rng;
|
||||
use crate::patterns::PixelColor;
|
||||
use crate::Strip;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// # FillUnstable
|
||||
///
|
||||
/// fill strip with color then apply same random variation to all pixel
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct FillUnstable<const N: usize> {
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) stability: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for FillUnstable<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(iteration) = self.max_iteration {
|
||||
if iteration == 0 {
|
||||
return None;
|
||||
} else {
|
||||
self.max_iteration = Some(iteration - 1);
|
||||
}
|
||||
}
|
||||
|
||||
let mut strip = Strip::<N>::default();
|
||||
let mut rng = rand::thread_rng();
|
||||
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);
|
||||
}
|
||||
strip.fill(PixelColor { red, green, blue });
|
||||
Some(strip)
|
||||
}
|
||||
}
|
76
src/patterns/rainbow.rs
Normal file
76
src/patterns/rainbow.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
use crate::patterns::PixelColor;
|
||||
use crate::Strip;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// # 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, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Rainbow<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) max_iteration: Option<usize>,
|
||||
pub(crate) step: Option<usize>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Rainbow<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(nbr_iteration) = self.max_iteration {
|
||||
if nbr_iteration == self.current_iteration {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let mut strip = Strip::default();
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
0..=85 => PixelColor {
|
||||
red: 255 - (pos * 3),
|
||||
green: 0,
|
||||
blue: pos * 3,
|
||||
},
|
||||
86..=170 => {
|
||||
let pos = pos - 85;
|
||||
PixelColor {
|
||||
red: 0,
|
||||
green: pos * 3,
|
||||
blue: 255 - (pos * 3),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let pos = pos - 170;
|
||||
PixelColor {
|
||||
red: pos * 3,
|
||||
green: 255 - (pos * 3),
|
||||
blue: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
src/patterns/scanner.rs
Normal file
45
src/patterns/scanner.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
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<const N: usize> {
|
||||
#[serde(default)]
|
||||
pub(crate) current_iteration: usize,
|
||||
pub(crate) color: PixelColor,
|
||||
pub(crate) background_color: Option<PixelColor>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for Scanner<N> {
|
||||
type Item = Strip<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut strip = Strip::<N>::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] = c;
|
||||
c = c / 2;
|
||||
}
|
||||
self.current_iteration += 1;
|
||||
Some(strip)
|
||||
}
|
||||
}
|
|
@ -9,9 +9,33 @@ use std::str::FromStr;
|
|||
use std::time::Duration;
|
||||
use tokio::sync::watch::{Receiver, Sender};
|
||||
|
||||
pub(crate) mod period {
|
||||
use std::time::Duration;
|
||||
use serde::{de::Error as _, Deserialize, Deserializer};
|
||||
|
||||
|
||||
/// Custom deserializer for period from millisecond value
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `deserializer`:
|
||||
///
|
||||
/// returns: Result<Duration, <D as Deserializer>::Error>
|
||||
///
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
String::deserialize(deserializer)?
|
||||
.parse::<u64>().and_then(|d| Ok(Duration::from_millis(d)))
|
||||
.map_err(|e| D::Error::custom(format!("{}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||
pub struct Context<const N: usize> {
|
||||
pub(crate) pattern: Patterns<N>,
|
||||
#[serde(deserialize_with = "period::deserialize")]
|
||||
pub period: Duration,
|
||||
}
|
||||
|
||||
|
@ -42,7 +66,7 @@ pub struct Spectacle<const N: usize> {
|
|||
|
||||
impl<const N: usize> Spectacle<N> {
|
||||
pub fn add(&mut self, mac_address: Address, runner: &DeviceSequence<N>) {
|
||||
if let Some((_, rx)) = self.runners.get(&runner as &DeviceSequence<N>) {
|
||||
if let Some((_, rx)) = self.runners.get(runner as &DeviceSequence<N>) {
|
||||
self.channels.insert(mac_address, rx.clone());
|
||||
} else {
|
||||
let (tx, rx) = tokio::sync::watch::channel(Strip::<N>::new(PixelColor {
|
||||
|
@ -98,7 +122,7 @@ impl<const N: usize> Spectacle<N> {
|
|||
pub fn run(&mut self) -> Vec<impl Future<Output = ()>> {
|
||||
let mut joinhandles = vec![];
|
||||
|
||||
if let Some(_) = &self.default_runner {
|
||||
if self.default_runner.is_some() {
|
||||
let d = std::mem::take(&mut self.default_runner).unwrap();
|
||||
joinhandles.push(runner_loop(d.0.clone(), d.1));
|
||||
println!("add default runner loop");
|
||||
|
|
Loading…
Reference in New Issue
Block a user