FastLED 3.9.15
Loading...
Searching...
No Matches
flowfield.h
Go to the documentation of this file.
1
3
4#pragma once
5
6#include "fl/stl/stdint.h"
7
8#include "crgb.h"
10#include "fl/math/math.h"
11#include "fl/stl/shared_ptr.h"
12#include "fl/fx/fx2d.h"
13#include "fl/fx/time.h"
14#include "fl/math/xymap.h"
15#include "fl/stl/align.h"
16#include "fl/stl/vector.h"
18#include "fl/stl/noexcept.h"
19
20namespace fl {
21
23enum class BumpShape : u8 {
26};
27
34 public:
38 NoiseBias1D(u16 size, float attackTau, float decayTau)
39 : mSize(size) {
40 mFilters.reserve(size);
41 for (u16 i = 0; i < size; ++i) {
42 mFilters.push_back(
43 AttackDecayFilter<float>(attackTau, decayTau, 0.0f));
44 }
45 mPending.resize(size);
46 for (u16 i = 0; i < size; ++i) {
47 mPending[i] = 0.0f;
48 }
49 }
50
52 void trigger(float center, float width, float amplitude,
54 if (width <= 0.0f || amplitude == 0.0f || mSize == 0) {
55 return;
56 }
57 const float halfWidth = width * 0.5f;
58 int iStart = fl::max(0, static_cast<int>(fl::floorf(center - halfWidth)));
59 int iEnd = fl::min(static_cast<int>(mSize) - 1,
60 static_cast<int>(fl::ceilf(center + halfWidth)));
61 for (int i = iStart; i <= iEnd; ++i) {
62 float t = (static_cast<float>(i) - (center - halfWidth)) / width;
63 t = fl::clamp(t, 0.0f, 1.0f);
64 float shaped = 0.0f;
65 switch (shape) {
67 shaped = fl::sinf(static_cast<float>(FL_PI) * t);
68 break;
70 float u = (t - 0.5f) / 0.2f;
71 shaped = fl::expf(-0.5f * u * u);
72 break;
73 }
74 }
75 mPending[i] += amplitude * shaped;
76 }
77 }
78
80 void update(float dtSeconds) {
81 for (u16 i = 0; i < mSize; ++i) {
82 mFilters[i].update(mPending[i], dtSeconds);
83 mPending[i] = 0.0f;
84 }
85 }
86
87 float get(u16 i) const { return mFilters[i].value(); }
88 u16 size() const { return mSize; }
89
90 void reset() {
91 for (u16 i = 0; i < mSize; ++i) {
92 mFilters[i].reset(0.0f);
93 mPending[i] = 0.0f;
94 }
95 }
96
97 private:
100 u16 mSize;
101};
102
105 public:
106 NoiseBias2D(u16 width, u16 height, float attackTau, float decayTau)
107 : mXBias(width, attackTau, decayTau),
108 mYBias(height, attackTau, decayTau) {}
109
110 void triggerX(float center, float width, float amplitude,
112 mXBias.trigger(center, width, amplitude, shape);
113 }
114
115 void triggerY(float center, float width, float amplitude,
117 mYBias.trigger(center, width, amplitude, shape);
118 }
119
120 void update(float dtSeconds) {
121 mXBias.update(dtSeconds);
122 mYBias.update(dtSeconds);
123 }
124
125 float getX(u16 x) const { return mXBias.get(x); }
126 float getY(u16 y) const { return mYBias.get(y); }
127 float get(u16 x, u16 y) const { return mXBias.get(x) + mYBias.get(y); }
128
129 NoiseBias1D &x() { return mXBias; }
130 const NoiseBias1D &x() const { return mXBias; }
131 NoiseBias1D &y() { return mYBias; }
132 const NoiseBias1D &y() const { return mYBias; }
133
134 u16 width() const { return mXBias.size(); }
135 u16 height() const { return mYBias.size(); }
136
137 void reset() {
138 mXBias.reset();
139 mYBias.reset();
140 }
141
142 private:
145};
146
154 // Color grids — raw s16x16 values
155 fl::vector<fl::i32> r, g, b; // Main RGB grid
156 fl::vector<fl::i32> tr, tg, tb; // Temp grid for advection pass 1
157
158 // Flow profiles — raw s16x16 values in [-1.0, 1.0] range
159 fl::vector<fl::i32> x_prof; // Per-column vertical shift driver
160 fl::vector<fl::i32> y_prof; // Per-row horizontal shift driver
161
162 // Perlin fade LUT (257 entries, Q8.24 format)
163 FL_ALIGNAS(16) fl::i32 fade_lut[257] = {};
165
166 int count = 0; // W*H pixel count (padded to multiple of 4)
167 int width = 0;
168 int height = 0;
169
171
172 void init(int w, int h) {
173 width = w;
174 height = h;
175 count = (w * h + 3) & ~3; // Pad to multiple of 4
176
177 r.resize(count, 0);
178 g.resize(count, 0);
179 b.resize(count, 0);
180 tr.resize(count, 0);
181 tg.resize(count, 0);
182 tb.resize(count, 0);
183 x_prof.resize(w, 0);
184 y_prof.resize(h, 0);
185 }
186};
187
191
194 float persistence = 0.86f;
195 float color_shift = 0.04f;
196 float flow_amp_x = 1.0f;
197 float flow_amp_y = 1.0f;
198 float flow_shift = 1.8f;
199 float flow_speed_x = 0.10f;
200 float flow_speed_y = 0.10f;
201 float noise_freq_x = 0.33f;
202 float noise_freq_y = 0.32f;
203 int dot_count = 3;
204 int emitter_mode = 0;
205 float endpoint_speed = 0.80f;
206 bool reverse_x_profile = true;
207 bool show_flow_vectors = false;
208};
209
226class FlowField : public Fx2d {
227 public:
229
230 ~FlowField() FL_NOEXCEPT override = default;
231
234 void draw(DrawContext context) override;
235
236 // Parameter setters.
237 void setPersistence(float halfLife) { mParams.persistence = halfLife; }
238 void setColorShift(float speed) { mParams.color_shift = speed; }
239 void setFlowAmplitudeX(float amp) { mParams.flow_amp_x = amp; }
240 void setFlowAmplitudeY(float amp) { mParams.flow_amp_y = amp; }
241 void setFlowShift(float shift) { mParams.flow_shift = shift; }
242 void setFlowSpeedX(float speed) { mParams.flow_speed_x = speed; }
243 void setFlowSpeedY(float speed) { mParams.flow_speed_y = speed; }
244 void setNoiseFrequencyX(float freq) { mParams.noise_freq_x = freq; }
245 void setNoiseFrequencyY(float freq) { mParams.noise_freq_y = freq; }
246 void setNoiseFrequency(float freq) {
247 mParams.noise_freq_x = freq;
248 mParams.noise_freq_y = freq;
249 }
250 void setDotCount(int count) { mParams.dot_count = count; }
251 void setEmitterMode(int mode) { mParams.emitter_mode = mode; }
252 void setEndpointSpeed(float speed) { mParams.endpoint_speed = speed; }
253 void setReverseXProfile(bool rev) { mParams.reverse_x_profile = rev; }
254 void setShowFlowVectors(bool show) { mParams.show_flow_vectors = show; }
255
257 void setSpeed(float speed) { mTimeWarp.setSpeed(speed); }
258 float speed() const { return mTimeWarp.scale(); }
259
261 void noisePunch(float amplitude = 1.0f,
263
265 void noisePunchX(float center, float width, float amplitude = 1.0f,
267
269 void noisePunchY(float center, float width, float amplitude = 1.0f,
271
273 const NoiseBias2D &noiseBias() const { return mNoiseBias; }
274
275 Params &getParams() { return mParams; }
276 const Params &getParams() const { return mParams; }
277
278 protected:
279 explicit FlowField(const XYMap &xyMap, const Params &params = Params());
280
284 virtual void drawImpl(DrawContext context, u32 dt_ms, u32 t_ms) = 0;
285
288
289 private:
292};
293
295class FlowFieldFloat : public FlowField {
296 public:
297 explicit FlowFieldFloat(const XYMap &xyMap, const Params &params = Params());
298
299 fl::string fxName() const override { return "FlowFieldFloat"; }
300
301 protected:
302 void drawImpl(DrawContext context, u32 dt_ms, u32 t_ms) override;
303
304 private:
305 // Seeded 2D Perlin noise generator.
306 class Perlin2D {
307 public:
308 void init(u32 seed);
309 float noise(float x, float y) const;
310
311 private:
312 u8 perm[512];
313 static float fade(float t) {
314 return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
315 }
316 static float lerp(float a, float b, float t) {
317 return a + t * (b - a);
318 }
319 static float grad(int h, float x, float y);
320 };
321
322 // Grid access helpers (row-major layout).
323 int idx(int y, int x) const {
324 return y * (int)getWidth() + x;
325 }
326
327 // Helpers.
328 static float fmodPos(float x, float m);
329 static float clampf(float v, float lo, float hi);
330 static u8 f2u8(float v);
331 static CRGB rainbow(float t, float speed, float phase);
332
333 // Sub-steps called from drawImpl().
334 void flowPrepare(float t);
335 void emitOrbitalDots(float t);
336 void flowAdvect(float dt);
337 void drawDot(float cx, float cy, float diam,
338 u8 cr, u8 cg, u8 cb);
339 void drawAALine(float x0, float y0, float x1, float y1,
340 float t, float colorShift);
341 void emitLissajousLine(float t);
342
344
345 // Float-precision RGB grids (main + temp for advection).
348
349 // Noise generators and profiles.
353};
354
360class FlowFieldFP : public FlowField {
361 public:
362 explicit FlowFieldFP(const XYMap &xyMap, const Params &params = Params());
363
364 fl::string fxName() const override { return "FlowFieldFP"; }
365
366 protected:
367 void drawImpl(DrawContext context, u32 dt_ms, u32 t_ms) override;
368
369 private:
370 friend struct FlowFieldFPProfiler; // For per-phase profiling
371
372 // Grid access helpers (row-major layout).
373 int idx(int y, int x) const {
374 return y * mState.width + x;
375 }
376
377 // Helpers.
378 static i32 clamp_q16(i32 v, i32 lo, i32 hi);
379 static u8 q16_to_u8(i32 v);
380 static CRGB rainbow(s16x16 t, s16x16 speed, s16x16 phase);
381
382 // Sub-steps — all pure fixed-point.
383 void flowPrepare(s16x16 t);
385 void flowAdvect(i32 dt_raw);
386 void drawDot(s16x16 cx, s16x16 cy, s16x16 diam,
387 u8 cr, u8 cg, u8 cb);
388 void drawAALine(s16x16 x0, s16x16 y0, s16x16 x1, s16x16 y1,
391
393
394 // Convert float params to cached s16x16 values.
395 void syncParams();
396
397 // Perm table initialization (Fisher-Yates shuffle).
398 static void initPerm256(u8 *perm, u32 seed);
399
401 u8 mPermX[256] = {};
402 u8 mPermY[256] = {};
403
404 // Cached s16x16 versions of float params.
412};
413
414} // namespace fl
fl::XYMap xyMap
fl::CRGB leds[NUM_LEDS]
FastLED show()
void rainbow()
uint8_t noise[NUM_LAYERS][WIDTH][HEIGHT]
Definition Fire2023.h:97
fl::UIButton noisePunch("NoisePunch")
fl::UISlider colorShift("Color Shift", 0.04f, 0.0f, 0.5f, 0.01f)
uint16_t speed
Definition Noise.ino:66
Alignment macros and utilities for FastLED.
NoiseBias2D mNoiseBias
Definition flowfield.h:287
void setNoiseFrequency(float freq)
Definition flowfield.h:246
void setColorShift(float speed)
Definition flowfield.h:238
Params mParams
Definition flowfield.h:286
void noisePunchX(float center, float width, float amplitude=1.0f, BumpShape shape=BumpShape::HalfSine)
Trigger a noise punch on the X axis (columns).
void setFlowSpeedX(float speed)
Definition flowfield.h:242
float speed() const
Definition flowfield.h:258
void setFlowSpeedY(float speed)
Definition flowfield.h:243
void noisePunchY(float center, float width, float amplitude=1.0f, BumpShape shape=BumpShape::HalfSine)
Trigger a noise punch on the Y axis (rows).
FlowFieldParams Params
Definition flowfield.h:228
void setNoiseFrequencyX(float freq)
Definition flowfield.h:244
void setEndpointSpeed(float speed)
Definition flowfield.h:252
void setDotCount(int count)
Definition flowfield.h:250
void setReverseXProfile(bool rev)
Definition flowfield.h:253
void setPersistence(float halfLife)
Definition flowfield.h:237
void setFlowAmplitudeX(float amp)
Definition flowfield.h:239
void draw(DrawContext context) override
Handles timing, then delegates to drawImpl().
const NoiseBias2D & noiseBias() const
Definition flowfield.h:273
~FlowField() FL_NOEXCEPT override=default
void setSpeed(float speed)
Set time speed/scale (1.0 = normal, 0.5 = half speed, etc.)
Definition flowfield.h:257
virtual void drawImpl(DrawContext context, u32 dt_ms, u32 t_ms)=0
Subclasses implement rendering given the time delta and total time.
const Params & getParams() const
Definition flowfield.h:276
FlowField(const XYMap &xyMap, const Params &params=Params())
void setFlowShift(float shift)
Definition flowfield.h:241
Params & getParams()
Definition flowfield.h:275
void setNoiseFrequencyY(float freq)
Definition flowfield.h:245
void setEmitterMode(int mode)
Definition flowfield.h:251
TimeWarp mTimeWarp
Definition flowfield.h:290
NoiseBias2D & noiseBias()
Definition flowfield.h:272
void setShowFlowVectors(bool show)
Definition flowfield.h:254
void setFlowAmplitudeY(float amp)
Definition flowfield.h:240
FlowFieldFPState mState
Definition flowfield.h:400
int idx(int y, int x) const
Definition flowfield.h:373
void drawAALine(s16x16 x0, s16x16 y0, s16x16 x1, s16x16 y1, s16x16 t, s16x16 colorShift)
s16x16 mFlowAmpY_fp
Definition flowfield.h:411
s16x16 mNoiseFreqY_fp
Definition flowfield.h:409
FlowFieldFP(const XYMap &xyMap, const Params &params=Params())
s16x16 mColorShift_fp
Definition flowfield.h:405
s16x16 mFlowShift_fp
Definition flowfield.h:406
fl::string fxName() const override
Definition flowfield.h:364
s16x16 mFlowSpeedX_fp
Definition flowfield.h:410
void drawDot(s16x16 cx, s16x16 cy, s16x16 diam, u8 cr, u8 cg, u8 cb)
static i32 clamp_q16(i32 v, i32 lo, i32 hi)
friend struct FlowFieldFPProfiler
Definition flowfield.h:370
void flowAdvect(i32 dt_raw)
void flowPrepare(s16x16 t)
static u8 q16_to_u8(i32 v)
void emitLissajousLine(s16x16 t)
void drawImpl(DrawContext context, u32 dt_ms, u32 t_ms) override
Subclasses implement rendering given the time delta and total time.
s16x16 mNoiseFreqX_fp
Definition flowfield.h:409
void emitOrbitalDots(s16x16 t)
s16x16 mPersistence_fp
Definition flowfield.h:408
s16x16 mEndpointSpeed_fp
Definition flowfield.h:407
s16x16 mFlowSpeedY_fp
Definition flowfield.h:410
static void initPerm256(u8 *perm, u32 seed)
void drawFlowVectors(fl::span< CRGB > leds)
s16x16 mFlowAmpX_fp
Definition flowfield.h:411
Pure fixed-point (s16x16) flow field implementation for maximum speed.
Definition flowfield.h:360
static float fade(float t)
Definition flowfield.h:313
static float lerp(float a, float b, float t)
Definition flowfield.h:316
static float grad(int h, float x, float y)
fl::string fxName() const override
Definition flowfield.h:299
void drawAALine(float x0, float y0, float x1, float y1, float t, float colorShift)
fl::vector< float > mB
Definition flowfield.h:346
fl::vector< float > mYProf
Per-row values; drives horizontal shift.
Definition flowfield.h:352
fl::vector< float > mG
Definition flowfield.h:346
void drawDot(float cx, float cy, float diam, u8 cr, u8 cg, u8 cb)
void drawFlowVectors(fl::span< CRGB > leds)
Perlin2D mNoiseGenY
Definition flowfield.h:350
static float clampf(float v, float lo, float hi)
FlowFieldFloat(const XYMap &xyMap, const Params &params=Params())
void flowAdvect(float dt)
Perlin2D mNoiseGenX
Definition flowfield.h:350
void drawImpl(DrawContext context, u32 dt_ms, u32 t_ms) override
Subclasses implement rendering given the time delta and total time.
fl::vector< float > mXProf
Per-column values; drives vertical shift.
Definition flowfield.h:351
void emitOrbitalDots(float t)
static u8 f2u8(float v)
void flowPrepare(float t)
void emitLissajousLine(float t)
fl::vector< float > mTG
Definition flowfield.h:347
int idx(int y, int x) const
Definition flowfield.h:323
fl::vector< float > mTR
Definition flowfield.h:347
fl::vector< float > mTB
Definition flowfield.h:347
static float fmodPos(float x, float m)
fl::vector< float > mR
Definition flowfield.h:346
Float-precision flow field implementation.
Definition flowfield.h:295
Abstract base class for 2D flow field effects.
Definition flowfield.h:226
Fx2d(const XYMap &xyMap)
Definition fx2d.h:19
u16 getWidth() const
Definition fx2d.h:24
u16 xyMap(u16 x, u16 y) const
Definition fx2d.h:20
void trigger(float center, float width, float amplitude, BumpShape shape=BumpShape::HalfSine)
Inject a shaped bump. Multiple triggers per frame accumulate additively.
Definition flowfield.h:52
void update(float dtSeconds)
Advance all filters by dt seconds.
Definition flowfield.h:80
fl::vector< float > mPending
Definition flowfield.h:99
NoiseBias1D(u16 size, float attackTau, float decayTau)
Definition flowfield.h:38
u16 size() const
Definition flowfield.h:88
fl::vector< AttackDecayFilter< float > > mFilters
Definition flowfield.h:98
float get(u16 i) const
Definition flowfield.h:87
Per-position attack/decay bias for one axis.
Definition flowfield.h:33
u16 width() const
Definition flowfield.h:134
NoiseBias1D mXBias
Definition flowfield.h:143
const NoiseBias1D & y() const
Definition flowfield.h:132
float get(u16 x, u16 y) const
Definition flowfield.h:127
u16 height() const
Definition flowfield.h:135
void update(float dtSeconds)
Definition flowfield.h:120
float getY(u16 y) const
Definition flowfield.h:126
NoiseBias2D(u16 width, u16 height, float attackTau, float decayTau)
Definition flowfield.h:106
NoiseBias1D & y()
Definition flowfield.h:131
const NoiseBias1D & x() const
Definition flowfield.h:130
void triggerY(float center, float width, float amplitude, BumpShape shape=BumpShape::HalfSine)
Definition flowfield.h:115
NoiseBias1D mYBias
Definition flowfield.h:144
void triggerX(float center, float width, float amplitude, BumpShape shape=BumpShape::HalfSine)
Definition flowfield.h:110
NoiseBias1D & x()
Definition flowfield.h:129
float getX(u16 x) const
Definition flowfield.h:125
Two-axis attack/decay bias for 2D effects (per-column X + per-row Y).
Definition flowfield.h:104
#define FL_PI
Definition math.h:26
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NOEXCEPT
Definition math.h:71
unsigned char u8
Definition stdint.h:131
constexpr common_type_t< T, U > max(T a, U b) FL_NOEXCEPT
Definition math.h:75
float expf(float value) FL_NOEXCEPT
Definition math.h:398
float floorf(float value) FL_NOEXCEPT
Definition math.h:304
float sinf(float value) FL_NOEXCEPT
Definition math.h:352
float ceilf(float value) FL_NOEXCEPT
Definition math.h:310
u8 width
Definition blur.h:186
BumpShape
Shape function for NoiseBias triggers.
Definition flowfield.h:23
@ HalfSine
sin(pi * t), smooth bell, zero at edges
Definition flowfield.h:24
@ Gaussian
exp(-0.5 * ((t-0.5)/sigma)^2), tighter peak
Definition flowfield.h:25
constexpr enable_if< is_fixed_point< T >::value, T >::type clamp(T x, T lo, T hi) FL_NOEXCEPT
Base definition for an LED controller.
Definition crgb.hpp:179
float flow_amp_y
Flow amplitude Y.
Definition flowfield.h:197
float flow_amp_x
Flow amplitude X.
Definition flowfield.h:196
float flow_speed_y
Noise scroll speed Y.
Definition flowfield.h:200
float color_shift
Color shift speed.
Definition flowfield.h:195
bool show_flow_vectors
Draw flow profiles as overlay.
Definition flowfield.h:207
bool reverse_x_profile
Reverse X profile (matches Python)
Definition flowfield.h:206
float endpoint_speed
Lissajous endpoint speed.
Definition flowfield.h:205
int emitter_mode
0=Lissajous, 1=Dots, 2=Both
Definition flowfield.h:204
float flow_shift
Pixel shift amount.
Definition flowfield.h:198
float flow_speed_x
Noise scroll speed X.
Definition flowfield.h:199
float persistence
Trail half-life in seconds.
Definition flowfield.h:194
float noise_freq_y
Noise frequency Y.
Definition flowfield.h:202
float noise_freq_x
Noise frequency X.
Definition flowfield.h:201
int dot_count
Number of orbital dots.
Definition flowfield.h:203
Configuration parameters for FlowField.
Definition flowfield.h:193
#define FL_NOEXCEPT
#define FASTLED_SHARED_PTR(type)
Definition shared_ptr.h:535
Representation of an 8-bit RGB pixel (Red, Green, Blue)
Definition crgb.h:38
fl::vector< fl::i32 > tr
Definition flowfield.h:156
void init(int w, int h)
Definition flowfield.h:172
fl::vector< fl::i32 > b
Definition flowfield.h:155
fl::vector< fl::i32 > r
Definition flowfield.h:155
fl::vector< fl::i32 > y_prof
Definition flowfield.h:160
fl::vector< fl::i32 > x_prof
Definition flowfield.h:159
fl::vector< fl::i32 > tg
Definition flowfield.h:156
FlowFieldFPState() FL_NOEXCEPT
Definition flowfield.h:170
fl::vector< fl::i32 > tb
Definition flowfield.h:156
fl::vector< fl::i32 > g
Definition flowfield.h:155
SoA (Structure-of-Arrays) state for FlowFieldFP.
Definition flowfield.h:153