FastLED 3.9.15
Loading...
Searching...
No Matches
luminova.cpp
Go to the documentation of this file.
1#ifndef FASTLED_INTERNAL
2#define FASTLED_INTERNAL
3#endif
4
5#include "luminova.h"
6
7namespace fl {
8
9Luminova::Luminova(const XYMap &xyMap, const Params &params)
10 : Fx2d(xyMap), mParams(params) {
11 int cap = params.max_particles;
12 if (cap <= 0) {
13 cap = 1;
14 }
15 mParticles.resize(static_cast<size_t>(cap));
16 // Initialize as dead
17 for (size_t i = 0; i < mParticles.size(); ++i) {
18 mParticles[i].alive = false;
19 }
20}
21
22void Luminova::setMaxParticles(int max_particles) {
23 if (max_particles <= 0) {
24 max_particles = 1;
25 }
26 if (static_cast<int>(mParticles.size()) == max_particles) {
27 return;
28 }
29 mParticles.clear();
30 mParticles.resize(static_cast<size_t>(max_particles));
31 for (size_t i = 0; i < mParticles.size(); ++i) {
32 mParticles[i].alive = false;
33 }
34}
35
36void Luminova::resetParticle(Particle &p, fl::u32 tt) {
37 // Position at center
38 const float cx = static_cast<float>(getWidth() - 1) * 0.5f;
39 const float cy = static_cast<float>(getHeight() - 1) * 0.5f;
40 p.x = cx;
41 p.y = cy;
42
43 // Original used noise(I)*W, we approximate with 1D noise scaled to width
44 int I = static_cast<int>(tt / 50);
45 uint8_t n1 = inoise8(static_cast<uint16_t>(I * 19));
46 float noiseW = (static_cast<float>(n1) / 255.0f) * static_cast<float>(getWidth());
47
48 p.a = static_cast<float>(tt) * 1.25f + noiseW;
49 p.f = (tt & 1u) ? +1 : -1;
50 p.g = I;
51 p.s = 3.0f;
52 p.alive = true;
53}
54
55void Luminova::plotDot(CRGB *leds, int x, int y, uint8_t v) const {
56 if (!mXyMap.has(x, y)) {
57 return;
58 }
59 const uint16_t idx = mXyMap.mapToIndex(static_cast<uint16_t>(x), static_cast<uint16_t>(y));
60 leds[idx] += CHSV(0, 0, scale8(v, mParams.point_gain));
61}
62
63void Luminova::plotSoftDot(CRGB *leds, float fx, float fy, float s) const {
64 // Map s (decays from ~3) to a pixel radius 1..3
65 float r = fl::clamp<float>(s * 0.5f, 1.0f, 3.0f);
66 int R = static_cast<int>(fl::ceil(r));
67 // Avoid roundf() to prevent AVR macro conflicts; do manual symmetric rounding
68 int cx = static_cast<int>(fx + (fx >= 0.0f ? 0.5f : -0.5f));
69 int cy = static_cast<int>(fy + (fy >= 0.0f ? 0.5f : -0.5f));
70 float r2 = r * r;
71 for (int dy = -R; dy <= R; ++dy) {
72 for (int dx = -R; dx <= R; ++dx) {
73 float d2 = static_cast<float>(dx * dx + dy * dy);
74 if (d2 <= r2) {
75 float fall = 1.0f - (d2 / (r2 + 0.0001f));
76 float vf = 255.0f * fall;
77 if (vf < 0.0f) vf = 0.0f;
78 if (vf > 255.0f) vf = 255.0f;
79 uint8_t v = static_cast<uint8_t>(vf);
80 plotDot(leds, cx + dx, cy + dy, v);
81 }
82 }
83 }
84}
85
87 // Fade + blur trails each frame
88 fadeToBlackBy(context.leds, getNumLeds(), mParams.fade_amount);
89 blur2d(context.leds, static_cast<fl::u8>(getWidth()), static_cast<fl::u8>(getHeight()),
90 mParams.blur_amount, mXyMap);
91
92 // Spawn/overwrite one particle per frame, round-robin across pool
93 if (!mParticles.empty()) {
94 size_t idx = static_cast<size_t>(mTick % static_cast<fl::u32>(mParticles.size()));
96 }
97
98 // Update and draw all particles
99 for (size_t i = 0; i < mParticles.size(); ++i) {
100 Particle &p = mParticles[i];
101 if (!p.alive) {
102 continue;
103 }
104
105 // s *= 0.997
106 p.s *= 0.997f;
107 if (p.s < 0.5f) {
108 p.alive = false;
109 continue;
110 }
111
112 // angle jitter using 2D noise: (t/99, g)
113 float tOver99 = static_cast<float>(mTick) / 99.0f;
114 uint8_t n2 = inoise8(static_cast<uint16_t>(tOver99 * 4096.0f), static_cast<uint16_t>(p.g * 37));
115 float n2c = (static_cast<int>(n2) - 128) / 255.0f; // ~ -0.5 .. +0.5
116 p.a += (n2c) / 9.0f;
117
118 float aa = p.a * static_cast<float>(p.f);
119 p.x += ::cosf(aa);
120 p.y += ::sinf(aa);
121
122 plotSoftDot(context.leds, p.x, p.y, p.s);
123 }
124
125 ++mTick;
126}
127
128} // namespace fl
CRGB leds[NUM_LEDS]
int y
Definition simple.h:93
int x
Definition simple.h:92
XYMap mXyMap
Definition fx2d.h:31
uint16_t xyMap(uint16_t x, uint16_t y) const
Definition fx2d.h:21
uint16_t getHeight() const
Definition fx2d.h:24
Fx2d(const XYMap &xyMap)
Definition fx2d.h:20
uint16_t getWidth() const
Definition fx2d.h:25
_DrawContext DrawContext
Definition fx.h:21
uint16_t getNumLeds() const
Definition fx.h:49
void resetParticle(Particle &p, fl::u32 tick)
Definition luminova.cpp:36
fl::u32 mTick
Definition luminova.h:64
LuminovaParams Params
Definition luminova.h:33
Params mParams
Definition luminova.h:63
Luminova(const XYMap &xyMap, const Params &params=Params())
Definition luminova.cpp:9
void plotDot(CRGB *leds, int x, int y, uint8_t v) const
Definition luminova.cpp:55
fl::vector< Particle > mParticles
Definition luminova.h:65
void draw(DrawContext context) override
Definition luminova.cpp:86
void setMaxParticles(int max_particles)
Definition luminova.cpp:22
void plotSoftDot(CRGB *leds, float fx, float fy, float s) const
Definition luminova.cpp:63
void blur2d(CRGB *leds, fl::u8 width, fl::u8 height, fract8 blur_amount, const XYMap &xymap)
Two-dimensional blur filter.
Definition blur.cpp:72
uint8_t inoise8(uint16_t x, uint16_t y, uint16_t z)
8-Bit, fixed point implementation of Perlin's noise.
Definition noise.cpp:570
LIB8STATIC_ALWAYS_INLINE uint8_t scale8(uint8_t i, fract8 scale)
Scale one byte by a second one, which is treated as the numerator of a fraction whose denominator is ...
Definition scale8.h:44
unsigned char u8
Definition int.h:17
void fadeToBlackBy(CRGB *leds, fl::u16 num_leds, fl::u8 fadeBy)
FASTLED_FORCE_INLINE T clamp(T value, T min, T max)
Definition clamp.h:10
T ceil(T value)
Definition math.h:39
IMPORTANT!
Definition crgb.h:20
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:86
Representation of an HSV pixel (hue, saturation, value (aka brightness)).
Definition hsv.h:15