use super::Animation;
use crate::render::Canvas;
use rand::RngExt;
pub struct Fire {
width: usize,
height: usize,
buffer: Vec<f64>,
heat_rate: f64,
rng: rand::rngs::ThreadRng,
}
impl Fire {
#[allow(unused_variables)]
pub fn new(width: usize, height: usize, _scale: f64) -> Self {
let mut buffer = vec![0.0; width * height];
for x in 0..width {
for y in height.saturating_sub(2)..height {
buffer[y * width + x] = 1.0;
}
}
Fire {
width,
height,
buffer,
heat_rate: 0.8,
rng: rand::rng(),
}
}
}
impl Animation for Fire {
fn name(&self) -> &str {
"fire"
}
fn set_params(&mut self, params: &crate::external::ExternalParams) {
if let Some(intensity) = params.intensity {
self.heat_rate = intensity.clamp(0.0, 2.0);
}
}
fn supported_params(&self) -> &'static [(&'static str, f64, f64)] {
&[("intensity", 0.0, 2.0)]
}
fn on_resize(&mut self, width: usize, height: usize) {
self.width = width;
self.height = height;
self.buffer = vec![0.0; width * height];
for x in 0..width {
for y in height.saturating_sub(2)..height {
self.buffer[y * width + x] = 1.0;
}
}
}
fn update(&mut self, canvas: &mut Canvas, _dt: f64, _time: f64) {
let w = self.width;
let h = self.height;
let intensity_scale = 1.0 / self.heat_rate.clamp(0.1, 2.0);
for x in 0..w {
for y in 0..h.saturating_sub(1) {
let wind: i32 = self.rng.random_range(-1i32..=1);
let src_x = (x as i32 + wind).clamp(0, w as i32 - 1) as usize;
let src_y = y + 1;
let src_val = self.buffer[src_y * w + src_x];
let max_decay = (3.0 / h as f64) * intensity_scale;
let decay = self.rng.random_range(0.0..max_decay.max(f64::EPSILON));
self.buffer[y * w + x] = (src_val - decay).max(0.0);
}
}
let heat = self.heat_rate.min(1.0);
let heat_min = (heat * 0.9).max(0.0);
for x in 0..w {
self.buffer[(h - 1) * w + x] = if heat_min < heat {
self.rng.random_range(heat_min..heat)
} else {
heat
};
}
canvas.clear();
for y in 0..h {
for x in 0..w {
let v = self.buffer[y * w + x];
if v > 0.01 {
let (r, g, b) = fire_color(v);
canvas.set_colored(x, y, v, r, g, b);
}
}
}
}
}
fn fire_color(v: f64) -> (u8, u8, u8) {
if v > 0.85 {
let t = (v - 0.85) / 0.15;
(255, (200.0 + 55.0 * t) as u8, (t * 200.0) as u8)
} else if v > 0.6 {
let t = (v - 0.6) / 0.25;
(255, (t * 200.0) as u8, 0)
} else if v > 0.3 {
let t = (v - 0.3) / 0.3;
((100.0 + 155.0 * t) as u8, 0, 0)
} else {
let t = v / 0.3;
((t * 100.0) as u8, 0, 0)
}
}