esp32-sk6812/src/Animation/FireAnimation.cpp

191 lines
6.3 KiB
C++

#include "Animation/FireAnimation.h"
FireAnimation::FireAnimation(Fader *fader, bool cold)
: Animation(fader),
m_energy(fader->modules_per_strip() * fader->strips()),
m_energySmooth(fader->modules_per_strip() * fader->strips()),
m_stopping(false)
{
m_colorMapIndices.assign({0, 100, 160, 200, 225, 255});
if(cold) {
m_colorMapColors.emplace_back(Fader::Color{ 0, 0, 0, 0});
m_colorMapColors.emplace_back(Fader::Color{ 0, 0, 128, 0});
m_colorMapColors.emplace_back(Fader::Color{ 0, 64, 192, 0});
m_colorMapColors.emplace_back(Fader::Color{ 0, 128, 255, 0});
m_colorMapColors.emplace_back(Fader::Color{ 0, 128, 255, 20});
m_colorMapColors.emplace_back(Fader::Color{ 0, 128, 255, 40});
} else {
m_colorMapColors.emplace_back(Fader::Color{ 0, 0, 0, 0});
m_colorMapColors.emplace_back(Fader::Color{128, 0, 0, 0});
m_colorMapColors.emplace_back(Fader::Color{192, 64, 0, 0});
m_colorMapColors.emplace_back(Fader::Color{255, 128, 0, 0});
m_colorMapColors.emplace_back(Fader::Color{255, 128, 0, 20});
m_colorMapColors.emplace_back(Fader::Color{255, 128, 0, 40});
}
reset();
}
uint8_t FireAnimation::interpolate(uint32_t energy, std::size_t start_x, std::size_t end_x, uint8_t start_c, uint8_t end_c)
{
int ien = static_cast<int>(energy);
int isx = static_cast<int>(start_x);
int iex = static_cast<int>(end_x);
int isc = static_cast<int>(start_c);
int iec = static_cast<int>(end_c);
int result = (ien - isx) * 128 / (iex - isx) * (iec - isc) / 128 + isc;
return static_cast<uint8_t>(result);
}
void FireAnimation::colormap(uint32_t energy, Fader::Color *color)
{
std::size_t i = 0;
// find the index on the left side of the interpolation range
while((i < m_colorMapIndices.size()-1) && (m_colorMapIndices[i+1] < energy)) {
i++;
}
// if the index found is the last index of the list, no interpolation is
// possible -> return the last color
if(i >= m_colorMapIndices.size()-1) {
*color = *(m_colorMapColors.end()-1);
return;
}
// interpolate the color map
std::size_t start_idx = m_colorMapIndices[i];
std::size_t end_idx = m_colorMapIndices[i+1];
Fader::Color &start_color = m_colorMapColors[i];
Fader::Color &end_color = m_colorMapColors[i+1];
color->r = interpolate(energy, start_idx, end_idx, start_color.r, end_color.r);
color->g = interpolate(energy, start_idx, end_idx, start_color.g, end_color.g);
color->b = interpolate(energy, start_idx, end_idx, start_color.b, end_color.b);
color->w = interpolate(energy, start_idx, end_idx, start_color.w, end_color.w);
color->gamma_correct();
}
void FireAnimation::loop(uint64_t frame)
{
std::size_t nled = m_fader->modules_per_strip();
std::size_t nstrip = m_fader->strips();
if(!m_stopping && (frame % 10 == 0)) {
std::uniform_int_distribution<uint32_t> m_intensityRNG(MAX_NEW_ENERGY*3/4, MAX_NEW_ENERGY*5/4);
m_intensity = m_intensityRNG(m_gen);
} else if(m_stopping) {
m_intensity = 0;
}
if((frame % 2) == 0) {
// inject random amount of energy in bottom row
std::uniform_int_distribution<uint32_t> m_newEnergyRNG(0, m_intensity);
for(std::size_t strip = 0; strip < nstrip; strip++) {
m_energy[idx(strip, 0)] += m_newEnergyRNG(m_gen);
}
// pull energy from cell below
std::uniform_int_distribution<uint32_t> m_pullEnergyPctRNG(0, MAX_PULL_ENERGY_PCT);
// special energy removal handling of topmost row
for(std::size_t strip = 0; strip < nstrip; strip++) {
uint32_t &energy = m_energy[idx(strip,nled-1)];
uint32_t pulled_energy = m_pullEnergyPctRNG(m_gen) * energy / 100;
energy -= pulled_energy;
}
// normal rows
for(std::size_t strip = 0; strip < nstrip; strip++) {
for(std::size_t led = nled-1; led > 0; led--) {
uint32_t &energy = m_energy[idx(strip,led)];
uint32_t &energy_below = m_energy[idx(strip,led-1)];
uint32_t pulled_energy = m_pullEnergyPctRNG(m_gen) * energy_below / 100;
energy += pulled_energy;
energy_below -= pulled_energy;
}
}
// decrease global amount of energy
for(auto &energy: m_energy) {
if(energy >= RM_ENERGY) {
energy -= RM_ENERGY;
} else {
energy = 0;
}
}
// smooth the energy distribution and update the colors
for(std::size_t strip = 0; strip < nstrip; strip++) {
for(std::size_t led = 0; led < nled; led++) {
uint32_t strip_left = (strip + nstrip - 1) % nstrip;
uint32_t strip_right = (strip + nstrip + 1) % nstrip;
int32_t led_above = led + 1;
int32_t led_below = led - 1;
uint32_t &energy_smooth = m_energySmooth[idx(strip, led)];
energy_smooth = 100 * m_energy[idx(strip, led)];
energy_smooth += 30 * m_energy[idx(strip_left, led)];
energy_smooth += 30 * m_energy[idx(strip_right, led)];
uint32_t gain = 160;
if(led_above < nled) {
energy_smooth += 10 * m_energy[idx(strip, led_above)];
gain += 10;
}
if(led_below >= 0) {
energy_smooth += 10 * m_energy[idx(strip, led_below)];
gain += 10;
}
energy_smooth /= gain;
}
}
// update colors
Fader::Color color;
for(std::size_t strip = 0; strip < nstrip; strip++) {
for(std::size_t led = 0; led < nled; led+=4) {
colormap(m_energySmooth[idx(strip, led/4)], &color);
for(int i = 0; i < 4; i++) {
m_fader->fade_color(strip, led+i, color);
}
}
}
}
m_fader->update();
if(m_stopping && !m_fader->something_changed()) {
m_running = false;
}
}
void FireAnimation::reset(void)
{
m_stopping = false;
m_running = true;
m_fader->set_fadestep(10);
for(auto &energy: m_energy) {
energy = 0;
}
}