// 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::() * new_energy; // Remove energy at the top self.energy[strip][config::NUM_LEDS_PER_STRIP-1] *= 1.0 - rng.gen::() * 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::() * 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>, } impl Animation for Fire { fn new(sigproc: Rc>) -> 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; } }