Added fire animation; better transitions
This commit is contained in:
parent
1db57ca62d
commit
368c5bec72
|
@ -2,3 +2,4 @@
|
|||
|
||||
#include "ConnectingAnimation.h"
|
||||
#include "ConnectionEstablishedAnimation.h"
|
||||
#include "FireAnimation.h"
|
||||
|
|
|
@ -14,6 +14,11 @@ class AnimationController
|
|||
|
||||
void restart(void);
|
||||
|
||||
bool isIdle(void)
|
||||
{
|
||||
return m_animation->finished() && !m_nextAnimation;
|
||||
}
|
||||
|
||||
private:
|
||||
Fader *m_fader;
|
||||
std::unique_ptr<Animation> m_animation;
|
||||
|
|
49
include/Animation/FireAnimation.h
Normal file
49
include/Animation/FireAnimation.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#include "Animation.h"
|
||||
|
||||
class FireAnimation : public Animation
|
||||
{
|
||||
public:
|
||||
FireAnimation(Fader *fader, bool cold);
|
||||
|
||||
void loop(uint64_t frame) override;
|
||||
|
||||
void stop(void) override
|
||||
{
|
||||
m_stopping = true;
|
||||
}
|
||||
|
||||
void reset(void) override
|
||||
{
|
||||
m_stopping = false;
|
||||
m_running = true;
|
||||
}
|
||||
|
||||
private:
|
||||
static const constexpr unsigned MAX_NEW_ENERGY = 160;
|
||||
static const constexpr unsigned RM_ENERGY = 7;
|
||||
static const constexpr unsigned MAX_PULL_ENERGY_PCT = 100;
|
||||
|
||||
std::vector<uint32_t> m_energy;
|
||||
std::vector<uint32_t> m_energySmooth;
|
||||
|
||||
std::vector<int> m_colorMapIndices;
|
||||
std::vector<Fader::Color> m_colorMapColors;
|
||||
|
||||
std::default_random_engine m_gen;
|
||||
|
||||
bool m_stopping;
|
||||
uint32_t m_intensity;
|
||||
|
||||
uint8_t interpolate(uint32_t energy, std::size_t start_x, std::size_t end_x, uint8_t start_c, uint8_t end_c);
|
||||
void colormap(uint32_t energy, Fader::Color *color);
|
||||
|
||||
uint32_t idx(uint32_t strip, uint32_t module)
|
||||
{
|
||||
return strip * m_fader->modules_per_strip() + module;
|
||||
}
|
||||
};
|
|
@ -25,6 +25,14 @@ class Fader
|
|||
if(b > 255) { b = 255; } else if (b < 0) { b = 0; };
|
||||
if(w > 255) { w = 255; } else if (w < 0) { w = 0; };
|
||||
}
|
||||
|
||||
void gamma_correct(void)
|
||||
{
|
||||
r = r * r / 255;
|
||||
g = g * g / 255;
|
||||
b = b * b / 255;
|
||||
w = w * w / 255;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -37,7 +37,11 @@ void ConnectionEstablishedAnimation::loop(uint64_t frame)
|
|||
m_fader->fade_color(strip, led, Fader::Color{0,0,0,0});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
m_fader->update();
|
||||
|
||||
if(led >= nled) {
|
||||
// stop the animation if everything is dark
|
||||
m_running = m_fader->something_changed();
|
||||
}
|
||||
|
|
165
src/Animation/FireAnimation.cpp
Normal file
165
src/Animation/FireAnimation.cpp
Normal file
|
@ -0,0 +1,165 @@
|
|||
#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, 70, 120, 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});
|
||||
}
|
||||
|
||||
m_fader->set_fadestep(10);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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++) {
|
||||
colormap(m_energySmooth[idx(strip, led)], &color);
|
||||
m_fader->fade_color(strip, led, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_fader->update();
|
||||
|
||||
if(m_stopping && !m_fader->something_changed()) {
|
||||
m_running = false;
|
||||
}
|
||||
}
|
|
@ -96,6 +96,10 @@ static void ledTask( void * parameter )
|
|||
|
||||
animController.loop();
|
||||
|
||||
if(animController.isIdle()) {
|
||||
animController.changeAnimation(std::unique_ptr<Animation>(new FireAnimation(&ledFader, false)));
|
||||
}
|
||||
|
||||
if((WiFi.status() == WL_CONNECTED) || (WiFi.getMode() == WIFI_MODE_AP)) {
|
||||
udpProto.loop();
|
||||
}
|
||||
|
@ -111,7 +115,9 @@ static void ledTask( void * parameter )
|
|||
strands[i] = &STRANDS[i];
|
||||
}
|
||||
|
||||
if(animController.isIdle()) {
|
||||
ledFader.update();
|
||||
}
|
||||
|
||||
if(ledFader.something_changed()) {
|
||||
const std::vector<Fader::Color> &colors = ledFader.get_color_values();
|
||||
|
|
Loading…
Reference in a new issue