refactor patterns
This commit is contained in:
parent
c2b2517661
commit
b718334392
|
@ -7,83 +7,55 @@
|
||||||
fade:
|
fade:
|
||||||
current_iteration: 10
|
current_iteration: 10
|
||||||
nbr_iterations: 20
|
nbr_iterations: 20
|
||||||
begin_color:
|
begin_color: &black
|
||||||
red: 0
|
red: 0
|
||||||
green: 0
|
green: 0
|
||||||
blue: 0
|
blue: 0
|
||||||
end_color:
|
end_color: &purple
|
||||||
red: 255
|
red: 255
|
||||||
green: 0
|
green: 0
|
||||||
blue: 255
|
blue: 255
|
||||||
current_iteration: 0
|
|
||||||
max_iteration: 10
|
max_iteration: 10
|
||||||
period:
|
period: 50
|
||||||
secs: 0
|
|
||||||
nanos: 50000000
|
|
||||||
- pattern:
|
- pattern:
|
||||||
type: Fade
|
type: Fade
|
||||||
current_iteration: 0
|
|
||||||
nbr_iterations: 20
|
nbr_iterations: 20
|
||||||
begin_color:
|
begin_color: &red
|
||||||
red: 255
|
red: 255
|
||||||
green: 0
|
green: 0
|
||||||
blue: 0
|
blue: 0
|
||||||
end_color:
|
end_color: &blue
|
||||||
red: 0
|
red: 0
|
||||||
green: 0
|
green: 0
|
||||||
blue: 255
|
blue: 255
|
||||||
period:
|
period: 200
|
||||||
secs: 0
|
|
||||||
nanos: 200000000
|
|
||||||
- pattern:
|
- pattern:
|
||||||
type: ColorWipe
|
type: ColorWipe
|
||||||
max_iteration: 25
|
max_iteration: 25
|
||||||
current_iteration: 0
|
color: &green
|
||||||
color:
|
|
||||||
red: 0
|
red: 0
|
||||||
green: 255
|
green: 255
|
||||||
blue: 0
|
blue: 0
|
||||||
background_color:
|
background_color: *purple
|
||||||
red: 255
|
period: 100
|
||||||
green: 0
|
|
||||||
blue: 255
|
|
||||||
period:
|
|
||||||
secs: 0
|
|
||||||
nanos: 100000000
|
|
||||||
- pattern:
|
- pattern:
|
||||||
type: FillRandom
|
type: FillRandom
|
||||||
color:
|
color: *blue
|
||||||
red: 0
|
|
||||||
green: 0
|
|
||||||
blue: 255
|
|
||||||
stability: 40
|
stability: 40
|
||||||
period:
|
period: 80
|
||||||
secs: 0
|
|
||||||
nanos: 800000000
|
|
||||||
|
|
||||||
- name: titi
|
- name: titi
|
||||||
sequence:
|
sequence:
|
||||||
- pattern:
|
- pattern:
|
||||||
type: Blink
|
type: Blink
|
||||||
fade :
|
fade :
|
||||||
current_iteration: 0
|
|
||||||
nbr_iterations: 20
|
nbr_iterations: 20
|
||||||
begin_color:
|
begin_color: *black
|
||||||
red: 0
|
end_color: &cyan
|
||||||
green: 0
|
|
||||||
blue: 0
|
|
||||||
end_color:
|
|
||||||
red: 0
|
red: 0
|
||||||
green: 255
|
green: 255
|
||||||
blue: 255
|
blue: 255
|
||||||
current_iteration: 0
|
|
||||||
max_iteration : 10
|
max_iteration : 10
|
||||||
period:
|
period: 50
|
||||||
secs: 0
|
|
||||||
nanos: 100000000
|
|
||||||
- pattern:
|
- pattern:
|
||||||
type: Rainbow
|
type: Rainbow
|
||||||
current_iteration: 0
|
period: 100
|
||||||
period:
|
|
||||||
secs: 0
|
|
||||||
nanos: 100000000
|
|
|
@ -19,7 +19,6 @@ use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
use tokio::sync::Notify;
|
|
||||||
|
|
||||||
const SERVICE_UUID: bluer::Uuid = bluer::Uuid::from_u128(0xadaf0900c33242a893bd25e905756cb8);
|
const SERVICE_UUID: bluer::Uuid = bluer::Uuid::from_u128(0xadaf0900c33242a893bd25e905756cb8);
|
||||||
const PIXEL_DATA_UUID: bluer::Uuid = bluer::Uuid::from_u128(0xadaf0903c33242a893bd25e905756cb8);
|
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 = Spectacle::from(config);
|
||||||
let spectacle = Arc::new(Mutex::new(spectacle));
|
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 adapter = bluetooth::create_session().await?;
|
||||||
let (tx_scan, rx_scan) = mpsc::channel(3);
|
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::spawn(connect_device(spectacle.clone(), rx_scan));
|
||||||
|
|
||||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
|
||||||
|
|
||||||
println!("starting");
|
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 futures;
|
||||||
{
|
{
|
||||||
let mut spectacle = spectacle.lock().unwrap();
|
let mut spectacle = spectacle.lock().unwrap();
|
||||||
|
|
367
src/patterns.rs
367
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 serde_derive::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Div, Index, IndexMut, SubAssign};
|
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)]
|
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
|
@ -110,353 +124,4 @@ impl<const N: usize> Strip<N> {
|
||||||
pub fn fill(&mut self, color: PixelColor) {
|
pub fn fill(&mut self, color: PixelColor) {
|
||||||
self.0.fill(color);
|
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 std::time::Duration;
|
||||||
use tokio::sync::watch::{Receiver, Sender};
|
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)]
|
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
|
||||||
pub struct Context<const N: usize> {
|
pub struct Context<const N: usize> {
|
||||||
pub(crate) pattern: Patterns<N>,
|
pub(crate) pattern: Patterns<N>,
|
||||||
|
#[serde(deserialize_with = "period::deserialize")]
|
||||||
pub period: Duration,
|
pub period: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +66,7 @@ pub struct Spectacle<const N: usize> {
|
||||||
|
|
||||||
impl<const N: usize> Spectacle<N> {
|
impl<const N: usize> Spectacle<N> {
|
||||||
pub fn add(&mut self, mac_address: Address, runner: &DeviceSequence<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());
|
self.channels.insert(mac_address, rx.clone());
|
||||||
} else {
|
} else {
|
||||||
let (tx, rx) = tokio::sync::watch::channel(Strip::<N>::new(PixelColor {
|
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 = ()>> {
|
pub fn run(&mut self) -> Vec<impl Future<Output = ()>> {
|
||||||
let mut joinhandles = vec![];
|
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();
|
let d = std::mem::take(&mut self.default_runner).unwrap();
|
||||||
joinhandles.push(runner_loop(d.0.clone(), d.1));
|
joinhandles.push(runner_loop(d.0.clone(), d.1));
|
||||||
println!("add default runner loop");
|
println!("add default runner loop");
|
||||||
|
|
Loading…
Reference in New Issue
Block a user