186 lines
4.6 KiB
Rust
186 lines
4.6 KiB
Rust
// 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 std::collections::VecDeque;
|
|
|
|
use rand::Rng;
|
|
|
|
const COOLDOWN_FACTOR : f32 = 0.99995;
|
|
const MAX_ENERGY_PROPAGATION : f32 = 0.4;
|
|
const RM_ENERGY_SUB : f32 = 0.011;
|
|
const RM_ENERGY_MULT : f32 = 0.995;
|
|
const EXPONENT : f32 = 1.50;
|
|
const W_EXPONENT : f32 = 2.20;
|
|
const W_SCALE : f32 = 0.3;
|
|
const OVERDRIVE : f32 = 0.3;
|
|
|
|
// A single-color flame.
|
|
// This struct contains the energy generation and propagation algorithm.
|
|
pub struct Flame
|
|
{
|
|
energy: [ [f32; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS],
|
|
}
|
|
|
|
impl Flame
|
|
{
|
|
fn new() -> Flame
|
|
{
|
|
Flame {
|
|
energy: [ [0.0; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS]
|
|
}
|
|
}
|
|
|
|
fn update(&mut self, new_energy: f32)
|
|
{
|
|
let mut rng = rand::thread_rng();
|
|
|
|
for strip in 0..config::NUM_STRIPS {
|
|
// Add new energy in the bottom row
|
|
self.energy[strip][0] += rng.gen::<f32>() * new_energy;
|
|
|
|
// Remove energy at the top
|
|
self.energy[strip][config::NUM_LEDS_PER_STRIP-1] *= 1.0 - rng.gen::<f32>() * MAX_ENERGY_PROPAGATION;
|
|
|
|
// move energy upwards
|
|
for led_out in (1..config::NUM_LEDS_PER_STRIP).rev() {
|
|
let led_in = led_out - 1;
|
|
let energy_moved = self.energy[strip][led_in] * rng.gen::<f32>() * MAX_ENERGY_PROPAGATION;
|
|
|
|
self.energy[strip][led_in] -= energy_moved;
|
|
self.energy[strip][led_out] += energy_moved;
|
|
}
|
|
|
|
// globally remove energy
|
|
for led in 0..config::NUM_LEDS_PER_STRIP {
|
|
self.energy[strip][led] *= RM_ENERGY_MULT;
|
|
if self.energy[strip][led] > RM_ENERGY_SUB {
|
|
self.energy[strip][led] -= RM_ENERGY_SUB;
|
|
} else {
|
|
self.energy[strip][led] = 0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn print(&self)
|
|
{
|
|
println!("-----");
|
|
for led in 0..config::NUM_LEDS_PER_STRIP {
|
|
for strip in 0..config::NUM_STRIPS {
|
|
print!("{:8.3} ", self.energy[strip][led]);
|
|
}
|
|
println!();
|
|
}
|
|
println!("-END-");
|
|
}
|
|
|
|
fn get_energy(&self, strip: usize, led: usize) -> f32
|
|
{
|
|
self.energy[strip][led]
|
|
}
|
|
}
|
|
|
|
|
|
// Merges multiple single-colored flames in a multi-colored one.
|
|
pub struct Fire
|
|
{
|
|
r_flame: Flame,
|
|
g_flame: Flame,
|
|
b_flame: Flame,
|
|
w_flame: Flame,
|
|
|
|
max_energy: Color,
|
|
|
|
colorlists: [ [Color; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS],
|
|
|
|
sigproc: Rc<RefCell<SignalProcessing>>,
|
|
}
|
|
|
|
impl Animation for Fire
|
|
{
|
|
fn new(sigproc: Rc<RefCell<SignalProcessing>>) -> Fire
|
|
{
|
|
Fire {
|
|
r_flame: Flame::new(),
|
|
g_flame: Flame::new(),
|
|
b_flame: Flame::new(),
|
|
w_flame: Flame::new(),
|
|
max_energy: Color{r: 1.0, g: 1.0, b: 1.0, w: 1.0},
|
|
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<()>
|
|
{
|
|
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;
|
|
}
|
|
|
|
// update the flames
|
|
self.r_flame.update((cur_energy.r / self.max_energy.r).powf(EXPONENT));
|
|
self.g_flame.update((cur_energy.g / self.max_energy.g).powf(EXPONENT));
|
|
self.b_flame.update((cur_energy.b / self.max_energy.b).powf(EXPONENT));
|
|
self.w_flame.update((cur_energy.w / self.max_energy.w).powf(W_EXPONENT));
|
|
|
|
//self.r_flame.print();
|
|
|
|
// color rendering and post-processing
|
|
for strip in 0..config::NUM_STRIPS {
|
|
for led in 0..config::NUM_LEDS_PER_STRIP {
|
|
self.colorlists[strip][led] = Color {
|
|
r: self.r_flame.get_energy(strip, led),
|
|
g: self.g_flame.get_energy(strip, led),
|
|
b: self.b_flame.get_energy(strip, led),
|
|
w: self.w_flame.get_energy(strip, led) * W_SCALE
|
|
};
|
|
|
|
self.colorlists[strip][led].limit();
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn get_colorlist(&self) -> &[ [Color; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS]
|
|
{
|
|
return &self.colorlists;
|
|
}
|
|
}
|