FastLED 3.9.15
Loading...
Searching...
No Matches
wave_simulation.cpp
Go to the documentation of this file.
1// Based on works and code by Shawn Silverman.
2
3#include <stdint.h>
4
5#include "fl/namespace.h"
7
8
9namespace {
10
11uint8_t half_duplex_blend_sqrt_q15(uint16_t x) {
12 x = MIN(x, 32767); // Q15
13 const int Q = 15;
14 uint32_t X = (uint32_t)x << Q; // promote to Q30
15 uint32_t y = (1u << Q); // start at “1.0” in Q15
16
17 // 3–4 iterations is plenty for 15‑bit precision:
18 for (int i = 0; i < 4; i++) {
19 y = ( y + (X / y) ) >> 1;
20 }
21 return static_cast<int16_t>(y) >> 8;
22}
23
24uint8_t half_duplex_blend_linear(uint16_t x) {
25 x = MIN(x, 32767); // Q15
26 x *= 2;
27 return x >> 8;
28}
29
30} // namespace
31
32
33namespace fl {
34
35
36void WaveSimulation2D::setSpeed(float speed) { sim->setSpeed(speed); }
37
38WaveSimulation2D::WaveSimulation2D(uint32_t W, uint32_t H, SuperSample factor,
39 float speed, float dampening) {
40 init(W, H, factor, speed, dampening);
41}
42
43void WaveSimulation2D::init(uint32_t width, uint32_t height, SuperSample factor, float speed, int dampening) {
44 outerWidth = width;
45 outerHeight = height;
46 multiplier = static_cast<uint32_t>(factor);
47 sim.reset(new WaveSimulation2D_Real(width * multiplier, height * multiplier, speed, dampening));
48 // Extra frames are needed because the simulation slows down in
49 // proportion to the supersampling factor.
50 extraFrames = uint8_t(factor) - 1;
51}
52
53void WaveSimulation2D::setDampening(int damp) { sim->setDampening(damp); }
54
55int WaveSimulation2D::getDampenening() const { return sim->getDampenening(); }
56
57float WaveSimulation2D::getSpeed() const { return sim->getSpeed(); }
58
59float WaveSimulation2D::getf(size_t x, size_t y) const {
60 if (!has(x, y))
61 return 0.0f;
62 float sum = 0.0f;
63 for (uint32_t j = 0; j < multiplier; ++j) {
64 for (uint32_t i = 0; i < multiplier; ++i) {
65 sum += sim->getf(x * multiplier + i, y * multiplier + j);
66 }
67 }
68 return sum / static_cast<float>(multiplier * multiplier);
69}
70
71int16_t WaveSimulation2D::geti16(size_t x, size_t y) const {
72 if (!has(x, y))
73 return 0;
74 int32_t sum = 0;
75 for (uint32_t j = 0; j < multiplier; ++j) {
76 for (uint32_t i = 0; i < multiplier; ++i) {
77 sum += sim->geti16(x * multiplier + i, y * multiplier + j);
78 }
79 }
80 return static_cast<int16_t>(sum / (multiplier * multiplier));
81}
82
83int16_t WaveSimulation2D::geti16Previous(size_t x, size_t y) const {
84 if (!has(x, y))
85 return 0;
86 int32_t sum = 0;
87 for (uint32_t j = 0; j < multiplier; ++j) {
88 for (uint32_t i = 0; i < multiplier; ++i) {
89 sum += sim->geti16Previous(x * multiplier + i, y * multiplier + j);
90 }
91 }
92 return static_cast<int16_t>(sum / (multiplier * multiplier));
93}
94
95bool WaveSimulation2D::geti16All(size_t x, size_t y, int16_t *curr,
96 int16_t *prev, int16_t *diff) const {
97 if (!has(x, y))
98 return false;
99 *curr = geti16(x, y);
100 *prev = geti16Previous(x, y);
101 *diff = *curr - *prev;
102 return true;
103}
104
105int8_t WaveSimulation2D::geti8(size_t x, size_t y) const {
106 return static_cast<int8_t>(geti16(x, y) >> 8);
107}
108
109uint8_t WaveSimulation2D::getu8(size_t x, size_t y) const {
110 int16_t value = geti16(x, y);
111 if (sim->getHalfDuplex()) {
112 uint16_t v2 = static_cast<uint16_t>(value);
113 switch (mU8Mode) {
115 return half_duplex_blend_linear(v2);
117 return half_duplex_blend_sqrt_q15(v2);
118 }
119 }
120 return static_cast<uint8_t>(((static_cast<uint16_t>(value) + 32768)) >> 8);
121}
122
123bool WaveSimulation2D::has(size_t x, size_t y) const {
124 return (x < outerWidth) && (y < outerHeight);
125}
126
127void WaveSimulation2D::seti16(size_t x, size_t y, int16_t v16) {
128 if (!has(x, y))
129 return;
130
131 // radius in pixels of your diamond
132 int rad = static_cast<int>(multiplier) / 2;
133
134 for (size_t j = 0; j < multiplier; ++j) {
135 for (size_t i = 0; i < multiplier; ++i) {
136 // compute offset from the center of this block
137 int dx = static_cast<int>(i) - rad;
138 int dy = static_cast<int>(j) - rad;
139
140 // keep only those points whose Manhattan distance ≤ rad
141 if (ABS(dx) + ABS(dy) > rad)
142 continue;
143
144 size_t xx = x * multiplier + i;
145 size_t yy = y * multiplier + j;
146 if (sim->has(xx, yy)) {
147 sim->seti16(xx, yy, v16);
148 }
149 }
150 }
151}
152
153void WaveSimulation2D::setf(size_t x, size_t y, float value) {
154 if (!has(x, y))
155 return;
156
157 int16_t v16 = wave_detail::float_to_fixed(value);
158 seti16(x, y, v16);
159}
160
162 sim->update();
163 for (uint8_t i = 0; i < extraFrames; ++i) {
164 sim->update();
165 }
166}
167
168uint32_t WaveSimulation2D::getWidth() const { return outerWidth; }
169uint32_t WaveSimulation2D::getHeight() const { return outerHeight; }
170
171void WaveSimulation2D::setExtraFrames(uint8_t extra) { extraFrames = extra; }
172
174 float speed, int dampening) {
175 init(length, factor, speed, dampening);
176}
177
178void WaveSimulation1D::init(uint32_t length, SuperSample factor,
179 float speed, int dampening) {
180 outerLength = length;
181 multiplier = static_cast<uint32_t>(factor);
182 sim.reset(new WaveSimulation1D_Real(length * multiplier, speed, dampening));
183 // Extra updates (frames) are applied because the simulation slows down in
184 // proportion to the supersampling factor.
185 extraFrames = static_cast<uint8_t>(factor) - 1;
186}
187
188void WaveSimulation1D::setSpeed(float speed) { sim->setSpeed(speed); }
189
190void WaveSimulation1D::setDampening(int damp) { sim->setDampening(damp); }
191
192int WaveSimulation1D::getDampenening() const { return sim->getDampenening(); }
193
194void WaveSimulation1D::setExtraFrames(uint8_t extra) { extraFrames = extra; }
195
196float WaveSimulation1D::getSpeed() const { return sim->getSpeed(); }
197
198float WaveSimulation1D::getf(size_t x) const {
199 if (!has(x))
200 return 0.0f;
201 float sum = 0.0f;
202 for (uint32_t i = 0; i < multiplier; ++i) {
203 sum += sim->getf(x * multiplier + i);
204 }
205 return sum / static_cast<float>(multiplier);
206}
207
208int16_t WaveSimulation1D::geti16(size_t x) const {
209 if (!has(x))
210 return 0;
211 int32_t sum = 0;
212 for (uint32_t i = 0; i < multiplier; ++i) {
213 sum += sim->geti16(x * multiplier + i);
214 }
215 return static_cast<int16_t>(sum / multiplier);
216}
217
218int16_t WaveSimulation1D::geti16Previous(size_t x) const {
219 if (!has(x))
220 return 0;
221 int32_t sum = 0;
222 for (uint32_t i = 0; i < multiplier; ++i) {
223 sum += sim->geti16Previous(x * multiplier + i);
224 }
225 return static_cast<int16_t>(sum / multiplier);
226}
227
228bool WaveSimulation1D::geti16All(size_t x, int16_t *curr, int16_t *prev,
229 int16_t *diff) const {
230 if (!has(x))
231 return false;
232 *curr = geti16(x);
233 *prev = geti16Previous(x);
234 *diff = *curr - *prev;
235 return true;
236}
237
238int8_t WaveSimulation1D::geti8(size_t x) const {
239 return static_cast<int8_t>(geti16(x) >> 8);
240}
241
242// uint8_t WaveSimulation2D::getu8(size_t x, size_t y) const {
243// int16_t value = geti16(x, y);
244// if (sim->getHalfDuplex()) {
245// uint16_t v2 = static_cast<uint16_t>(value);
246// switch (mU8Mode) {
247// case WAVE_U8_MODE_LINEAR:
248// return half_duplex_blend_linear(v2);
249// case WAVE_U8_MODE_SQRT:
250// return half_duplex_blend_sqrt_q15(v2);
251// }
252// }
253// return static_cast<uint8_t>(((static_cast<uint16_t>(value) + 32768)) >> 8);
254// }
255
256uint8_t WaveSimulation1D::getu8(size_t x) const {
257 int16_t value = geti16(x);
258 if (sim->getHalfDuplex()) {
259 uint16_t v2 = static_cast<uint16_t>(value);
260 switch (mU8Mode) {
262 return half_duplex_blend_linear(v2);
264 return half_duplex_blend_sqrt_q15(v2);
265 }
266 }
267 return static_cast<uint8_t>(((static_cast<uint16_t>(value) + 32768)) >> 8);
268}
269
270bool WaveSimulation1D::has(size_t x) const { return (x < outerLength); }
271
272void WaveSimulation1D::setf(size_t x, float value) {
273 if (!has(x))
274 return;
275 for (uint32_t i = 0; i < multiplier; ++i) {
276 sim->set(x * multiplier + i, value);
277 }
278}
279
281 sim->update();
282 for (uint8_t i = 0; i < extraFrames; ++i) {
283 sim->update();
284 }
285}
286
287uint32_t WaveSimulation1D::getLength() const { return outerLength; }
288
289} // namespace fl
uint32_t x[NUM_LAYERS]
Definition Fire2023.ino:80
uint32_t y[NUM_LAYERS]
Definition Fire2023.ino:81
uint16_t speed
Definition Noise.ino:63
UISlider dampening("Dampening", 6.0f, 0.0f, 10.0f, 0.1f)
void setf(size_t x, float value)
void init(uint32_t length, SuperSample factor, float speed, int dampening)
WaveSimulation1D(uint32_t length, SuperSample factor=SuperSample::SUPER_SAMPLE_NONE, float speed=0.16f, int dampening=6)
int8_t geti8(size_t x) const
int16_t geti16(size_t x) const
bool has(size_t x) const
float getf(size_t x) const
void setSpeed(float speed)
U8EasingFunction mU8Mode
void setDampening(int damp)
void setExtraFrames(uint8_t extra)
uint8_t getu8(size_t x) const
fl::scoped_ptr< WaveSimulation1D_Real > sim
int16_t geti16Previous(size_t x) const
bool geti16All(size_t x, int16_t *curr, int16_t *prev, int16_t *diff) const
uint32_t getLength() const
int16_t geti16(size_t x, size_t y) const
uint32_t getWidth() const
void init(uint32_t width, uint32_t height, SuperSample factor, float speed, int dampening)
void setExtraFrames(uint8_t extra)
uint32_t getHeight() const
int8_t geti8(size_t x, size_t y) const
void setSpeed(float speed)
bool geti16All(size_t x, size_t y, int16_t *curr, int16_t *prev, int16_t *diff) const
float getf(size_t x, size_t y) const
uint8_t getu8(size_t x, size_t y) const
void seti16(size_t x, size_t y, int16_t value)
fl::scoped_ptr< WaveSimulation2D_Real > sim
void setf(size_t x, size_t y, float value)
U8EasingFunction mU8Mode
bool has(size_t x, size_t y) const
void setDampening(int damp)
WaveSimulation2D(uint32_t W, uint32_t H, SuperSample factor=SuperSample::SUPER_SAMPLE_NONE, float speed=0.16f, float dampening=6.0f)
int16_t geti16Previous(size_t x, size_t y) const
#define MIN(a, b)
Definition math_macros.h:8
#define ABS(x)
Definition math_macros.h:12
Implements the FastLED namespace macros.
int16_t float_to_fixed(float f)
@ WAVE_U8_MODE_LINEAR
@ WAVE_U8_MODE_SQRT
SuperSample
Definition supersample.h:4
Implements a simple red square effect for 2D LED grids.
Definition crgb.h:16