Added new "racers" animation
This commit is contained in:
parent
ec904b45f0
commit
fb1eae119e
|
@ -13,6 +13,7 @@ type Result<T> = std::result::Result<T, AnimationError>;
|
||||||
|
|
||||||
pub mod particles;
|
pub mod particles;
|
||||||
pub mod sparkles;
|
pub mod sparkles;
|
||||||
|
pub mod racers;
|
||||||
|
|
||||||
/////////// Error Type and Implementation ////////////
|
/////////// Error Type and Implementation ////////////
|
||||||
|
|
||||||
|
|
303
src/animation/racers.rs
Normal file
303
src/animation/racers.rs
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
// vim: noet
|
||||||
|
|
||||||
|
use crate::animation::{Color, Animation, Result};
|
||||||
|
use crate::signal_processing::SignalProcessing;
|
||||||
|
use crate::config;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
const COOLDOWN_FACTOR : f32 = 0.99980;
|
||||||
|
const RGB_EXPONENT : f32 = 1.5;
|
||||||
|
const W_EXPONENT : f32 = 2.2;
|
||||||
|
const W_SCALE : f32 = 0.5;
|
||||||
|
const BRIGHTNESS_FILTER_ALPHA : f32 = 0.1;
|
||||||
|
|
||||||
|
const NUM_RACERS_R : usize = 10;
|
||||||
|
const NUM_RACERS_G : usize = 10;
|
||||||
|
const NUM_RACERS_B : usize = 10;
|
||||||
|
|
||||||
|
const RACER_MIN_SPEED_R : f32 = 0.5 / config::FPS_ANIMATION;
|
||||||
|
const RACER_MAX_SPEED_R : f32 = 30.0 / config::FPS_ANIMATION;
|
||||||
|
const RACER_MIN_BRIGHTNESS_R : f32 = 0.01;
|
||||||
|
const RACER_MAX_BRIGHTNESS_R : f32 = 1.00;
|
||||||
|
|
||||||
|
const RACER_MIN_SPEED_G : f32 = 0.5 / config::FPS_ANIMATION;
|
||||||
|
const RACER_MAX_SPEED_G : f32 = 30.0 / config::FPS_ANIMATION;
|
||||||
|
const RACER_MIN_BRIGHTNESS_G : f32 = 0.01;
|
||||||
|
const RACER_MAX_BRIGHTNESS_G : f32 = 1.00;
|
||||||
|
|
||||||
|
const RACER_MIN_SPEED_B : f32 = 0.5 / config::FPS_ANIMATION;
|
||||||
|
const RACER_MAX_SPEED_B : f32 = 30.0 / config::FPS_ANIMATION;
|
||||||
|
const RACER_MIN_BRIGHTNESS_B : f32 = 0.01;
|
||||||
|
const RACER_MAX_BRIGHTNESS_B : f32 = 1.00;
|
||||||
|
|
||||||
|
const SPEED_SCALE_RANGE : f32 = 0.10;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A racer is a point of light that can move along the LED strips.
|
||||||
|
*/
|
||||||
|
struct Racer
|
||||||
|
{
|
||||||
|
min_speed: f32, // LEDs per frame
|
||||||
|
max_speed: f32, // LEDs per frame
|
||||||
|
direction: i8, // either +1 or -1
|
||||||
|
|
||||||
|
min_brightness: f32,
|
||||||
|
max_brightness: f32,
|
||||||
|
|
||||||
|
color: Color,
|
||||||
|
|
||||||
|
pos: f32,
|
||||||
|
|
||||||
|
brightness: f32,
|
||||||
|
flare_brightness: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Racer
|
||||||
|
{
|
||||||
|
pub fn new(min_speed: f32, max_speed: f32, min_brightness: f32, max_brightness: f32, color: Color, start_pos: f32) -> Racer
|
||||||
|
{
|
||||||
|
Racer {
|
||||||
|
min_speed: min_speed,
|
||||||
|
max_speed: max_speed,
|
||||||
|
min_brightness: min_brightness,
|
||||||
|
max_brightness: max_brightness,
|
||||||
|
direction: 1,
|
||||||
|
color: color,
|
||||||
|
pos: start_pos,
|
||||||
|
brightness: min_brightness,
|
||||||
|
flare_brightness: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _pos2ledstrip(pos: i32) -> (i32, i32)
|
||||||
|
{
|
||||||
|
let strip = pos / (config::NUM_LEDS_PER_STRIP as i32);
|
||||||
|
|
||||||
|
let mut led = pos % (config::NUM_LEDS_PER_STRIP as i32);
|
||||||
|
|
||||||
|
if (strip % 2) == 1 {
|
||||||
|
led = (config::NUM_LEDS_PER_STRIP as i32) - led - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
(strip, led)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, brightness: f32, flare_brightness: f32)
|
||||||
|
{
|
||||||
|
// move along the strip
|
||||||
|
let cur_speed = self.min_speed + brightness * (self.max_speed - self.min_speed);
|
||||||
|
|
||||||
|
self.pos += (self.direction as f32) * cur_speed;
|
||||||
|
|
||||||
|
let maxpos = config::NUM_LEDS_TOTAL as f32;
|
||||||
|
|
||||||
|
// if the end is reached, reverse the direction
|
||||||
|
if self.pos >= maxpos {
|
||||||
|
self.direction = -1;
|
||||||
|
self.pos = 2.0 * maxpos - self.pos;
|
||||||
|
} else if self.pos <= 0.0 {
|
||||||
|
self.direction = 1;
|
||||||
|
self.pos = -self.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.brightness = brightness;
|
||||||
|
self.flare_brightness = flare_brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, colorlists: &mut [ [Color; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS])
|
||||||
|
{
|
||||||
|
let brightness = self.min_brightness + self.brightness * (self.max_brightness - self.min_brightness);
|
||||||
|
|
||||||
|
let fract_led = self.pos - self.pos.floor();
|
||||||
|
|
||||||
|
let led1_idx = self.pos.floor() as i32;
|
||||||
|
let led2_idx = self.pos.ceil() as i32;
|
||||||
|
|
||||||
|
let mut color = self.color;
|
||||||
|
color.w += self.flare_brightness;
|
||||||
|
|
||||||
|
let led1_color = color.scaled_copy((1.0 - fract_led) * brightness);
|
||||||
|
let led2_color = color.scaled_copy(fract_led * brightness);
|
||||||
|
|
||||||
|
if led1_idx >= 0 {
|
||||||
|
let (strip, led) = Racer::_pos2ledstrip(led1_idx);
|
||||||
|
|
||||||
|
colorlists[strip as usize][led as usize].add(&led1_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if led2_idx < (config::NUM_LEDS_TOTAL as i32) {
|
||||||
|
let (strip, led) = Racer::_pos2ledstrip(led2_idx);
|
||||||
|
|
||||||
|
colorlists[strip as usize][led as usize].add(&led2_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Racers
|
||||||
|
{
|
||||||
|
max_energy : Color,
|
||||||
|
filtered_energy : Color,
|
||||||
|
|
||||||
|
racers_r : Vec<Racer>,
|
||||||
|
racers_g : Vec<Racer>,
|
||||||
|
racers_b : Vec<Racer>,
|
||||||
|
|
||||||
|
colorlists : [ [Color; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS],
|
||||||
|
|
||||||
|
sigproc: Rc<RefCell<SignalProcessing>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Animation for Racers
|
||||||
|
{
|
||||||
|
fn new(sigproc: Rc<RefCell<SignalProcessing>>) -> Racers
|
||||||
|
{
|
||||||
|
Racers {
|
||||||
|
max_energy: Color{r: 1.0, g: 1.0, b: 1.0, w: 1.0},
|
||||||
|
filtered_energy: Color{r: 0.0, g: 0.0, b: 0.0, w: 0.0},
|
||||||
|
racers_r: Vec::with_capacity(NUM_RACERS_R),
|
||||||
|
racers_g: Vec::with_capacity(NUM_RACERS_G),
|
||||||
|
racers_b: Vec::with_capacity(NUM_RACERS_B),
|
||||||
|
colorlists: [ [Color{r: 0.0, g: 0.0, b: 0.0, w: 0.0}; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS],
|
||||||
|
sigproc: sigproc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self) -> Result<()>
|
||||||
|
{
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
for _i in 0 .. NUM_RACERS_R {
|
||||||
|
let start_pos = rng.gen::<f32>() * (config::NUM_LEDS_TOTAL as f32);
|
||||||
|
let speed_scale = 1.0 + SPEED_SCALE_RANGE * (rng.gen::<f32>() - 0.5);
|
||||||
|
|
||||||
|
self.racers_r.push(Racer::new(
|
||||||
|
RACER_MIN_SPEED_R * speed_scale,
|
||||||
|
RACER_MAX_SPEED_R * speed_scale,
|
||||||
|
RACER_MIN_BRIGHTNESS_R,
|
||||||
|
RACER_MAX_BRIGHTNESS_R,
|
||||||
|
Color{r: 1.0, g: 0.0, b: 0.0, w: 0.0},
|
||||||
|
start_pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
for _i in 0 .. NUM_RACERS_G {
|
||||||
|
let start_pos = rng.gen::<f32>() * (config::NUM_LEDS_TOTAL as f32);
|
||||||
|
let speed_scale = 1.0 + SPEED_SCALE_RANGE * (rng.gen::<f32>() - 0.5);
|
||||||
|
|
||||||
|
self.racers_g.push(Racer::new(
|
||||||
|
RACER_MIN_SPEED_G * speed_scale,
|
||||||
|
RACER_MAX_SPEED_G * speed_scale,
|
||||||
|
RACER_MIN_BRIGHTNESS_G,
|
||||||
|
RACER_MAX_BRIGHTNESS_G,
|
||||||
|
Color{r: 0.0, g: 1.0, b: 0.0, w: 0.0},
|
||||||
|
start_pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
for _i in 0 .. NUM_RACERS_B {
|
||||||
|
let start_pos = rng.gen::<f32>() * (config::NUM_LEDS_TOTAL as f32);
|
||||||
|
let speed_scale = 1.0 + SPEED_SCALE_RANGE * (rng.gen::<f32>() - 0.5);
|
||||||
|
|
||||||
|
self.racers_b.push(Racer::new(
|
||||||
|
RACER_MIN_SPEED_B * speed_scale,
|
||||||
|
RACER_MAX_SPEED_B * speed_scale,
|
||||||
|
RACER_MIN_BRIGHTNESS_B,
|
||||||
|
RACER_MAX_BRIGHTNESS_B,
|
||||||
|
Color{r: 0.0, g: 0.0, b: 1.0, w: 0.0},
|
||||||
|
start_pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn periodic(&mut self) -> Result<()>
|
||||||
|
{
|
||||||
|
let sigproc = self.sigproc.borrow();
|
||||||
|
|
||||||
|
// extract frequency band energies
|
||||||
|
let cur_energy = Color{
|
||||||
|
r: sigproc.get_energy_in_band( 0.0, 400.0),
|
||||||
|
g: sigproc.get_energy_in_band( 400.0, 4000.0),
|
||||||
|
b: sigproc.get_energy_in_band( 4000.0, 12000.0),
|
||||||
|
w: sigproc.get_energy_in_band(12000.0, 22000.0)};
|
||||||
|
|
||||||
|
// track the maximum energy with cooldown
|
||||||
|
self.max_energy.r *= COOLDOWN_FACTOR;
|
||||||
|
if cur_energy.r > self.max_energy.r {
|
||||||
|
self.max_energy.r = cur_energy.r;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.max_energy.g *= COOLDOWN_FACTOR;
|
||||||
|
if cur_energy.g > self.max_energy.g {
|
||||||
|
self.max_energy.g = cur_energy.g;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.max_energy.b *= COOLDOWN_FACTOR;
|
||||||
|
if cur_energy.b > self.max_energy.b {
|
||||||
|
self.max_energy.b = cur_energy.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.max_energy.w *= COOLDOWN_FACTOR;
|
||||||
|
if cur_energy.w > self.max_energy.w {
|
||||||
|
self.max_energy.w = cur_energy.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set all LEDs initially to black
|
||||||
|
for strip in 0..config::NUM_STRIPS {
|
||||||
|
for led in 0..config::NUM_LEDS_PER_STRIP {
|
||||||
|
//self.colorlists[strip][led].scale(FADE_FACTOR);
|
||||||
|
self.colorlists[strip][led] = Color{r: 0.0, g: 0.0, b: 0.0, w: 0.0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rescaling and normalization of the energies
|
||||||
|
let scaled_energy = Color{
|
||||||
|
r: (cur_energy.r / self.max_energy.r).powf(RGB_EXPONENT),
|
||||||
|
g: (cur_energy.g / self.max_energy.g).powf(RGB_EXPONENT),
|
||||||
|
b: (cur_energy.b / self.max_energy.b).powf(RGB_EXPONENT),
|
||||||
|
w: (cur_energy.w / self.max_energy.w).powf(W_EXPONENT) * W_SCALE,
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in 0..4 {
|
||||||
|
let f = self.filtered_energy.ref_by_index_mut(i).unwrap();
|
||||||
|
let n = scaled_energy.ref_by_index(i).unwrap();
|
||||||
|
|
||||||
|
*f = (1.0 - BRIGHTNESS_FILTER_ALPHA) * (*f) + BRIGHTNESS_FILTER_ALPHA * (*n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update all racers
|
||||||
|
let f = &self.filtered_energy;
|
||||||
|
self.racers_r.iter_mut().for_each(|x| x.update(f.r, f.w));
|
||||||
|
self.racers_g.iter_mut().for_each(|x| x.update(f.g, f.w));
|
||||||
|
self.racers_b.iter_mut().for_each(|x| x.update(f.b, f.w));
|
||||||
|
|
||||||
|
// render all racers
|
||||||
|
for racer in self.racers_r.iter() {
|
||||||
|
racer.render(&mut self.colorlists);
|
||||||
|
}
|
||||||
|
|
||||||
|
for racer in self.racers_g.iter() {
|
||||||
|
racer.render(&mut self.colorlists);
|
||||||
|
}
|
||||||
|
|
||||||
|
for racer in self.racers_b.iter() {
|
||||||
|
racer.render(&mut self.colorlists);
|
||||||
|
}
|
||||||
|
|
||||||
|
// color post-processing
|
||||||
|
for strip in 0..config::NUM_STRIPS {
|
||||||
|
for led in 0..config::NUM_LEDS_PER_STRIP {
|
||||||
|
self.colorlists[strip][led].limit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_colorlist(&self) -> &[ [Color; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS]
|
||||||
|
{
|
||||||
|
return &self.colorlists;
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,7 +44,8 @@ fn main()
|
||||||
|
|
||||||
// TODO: let the user select via the command line
|
// TODO: let the user select via the command line
|
||||||
//let mut anim: animation::particles::Particles = animation::Animation::new(sigproc.clone());
|
//let mut anim: animation::particles::Particles = animation::Animation::new(sigproc.clone());
|
||||||
let mut anim: animation::sparkles::Sparkles = animation::Animation::new(sigproc.clone());
|
//let mut anim: animation::sparkles::Sparkles = animation::Animation::new(sigproc.clone());
|
||||||
|
let mut anim: animation::racers::Racers = animation::Animation::new(sigproc.clone());
|
||||||
|
|
||||||
println!("Calling Animation::init()...");
|
println!("Calling Animation::init()...");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue