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 "fl/stdint.h"
4
5#include "fl/clamp.h"
6#include "fl/namespace.h"
8#include "fl/int.h"
9
10namespace {
11
13 x = MIN(x, 32767); // Q15
14 const int Q = 15;
15 fl::u32 X = (fl::u32)x << Q; // promote to Q30
16 fl::u32 y = (1u << Q); // start at “1.0” in Q15
17
18 // 3–4 iterations is plenty for 15‑bit precision:
19 for (int i = 0; i < 4; i++) {
20 y = (y + (X / y)) >> 1;
21 }
22 return static_cast<fl::i16>(y) >> 8;
23}
24
26 x = MIN(x, 32767); // Q15
27 x *= 2;
28 return x >> 8;
29}
30
31} // namespace
32
33namespace fl {
34
35void WaveSimulation2D::setSpeed(float speed) { mSim->setSpeed(speed); }
36
38 float speed, float dampening) {
39 init(W, H, factor, speed, dampening);
40}
41
42void WaveSimulation2D::init(u32 width, u32 height, SuperSample factor,
43 float speed, int dampening) {
44 mOuterWidth = width;
45 mOuterHeight = height;
46 mMultiplier = static_cast<u32>(factor);
47 mSim.reset(); // clear out memory first.
48 u32 w = width * mMultiplier;
49 u32 h = height * mMultiplier;
50 mSim.reset(new WaveSimulation2D_Real(w, h, speed, dampening));
51 mChangeGrid.reset(w, h);
52 // Extra frames are needed because the simulation slows down in
53 // proportion to the supersampling factor.
54 mExtraFrames = u8(factor) - 1;
55}
56
57void WaveSimulation2D::setDampening(int damp) { mSim->setDampening(damp); }
58
59int WaveSimulation2D::getDampenening() const { return mSim->getDampenening(); }
60
61float WaveSimulation2D::getSpeed() const { return mSim->getSpeed(); }
62
63float WaveSimulation2D::getf(fl::size x, fl::size y) const {
64 if (!has(x, y))
65 return 0.0f;
66 float sum = 0.0f;
67 for (u32 j = 0; j < mMultiplier; ++j) {
68 for (u32 i = 0; i < mMultiplier; ++i) {
69 sum += mSim->getf(x * mMultiplier + i, y * mMultiplier + j);
70 }
71 }
72 return sum / static_cast<float>(mMultiplier * mMultiplier);
73}
74
75i16 WaveSimulation2D::geti16(fl::size x, fl::size y) const {
76 if (!has(x, y))
77 return 0;
78 i32 sum = 0;
79 u8 mult = MAX(1, mMultiplier);
80 for (u32 j = 0; j < mult; ++j) {
81 for (u32 i = 0; i < mult; ++i) {
82 u32 xx = x * mult + i;
83 u32 yy = y * mult + j;
84 i32 pt = mSim->geti16(xx, yy);
85 // i32 ch_pt = mChangeGrid[(yy * mMultiplier) + xx];
86 i32 ch_pt = mChangeGrid(xx, yy);
87 if (ch_pt != 0) { // we got a hit.
88 sum += ch_pt;
89 } else {
90 sum += pt;
91 }
92 }
93 }
94 i16 out = static_cast<i16>(sum / (mult * mult));
95 return out;
96}
97
98i16 WaveSimulation2D::geti16Previous(fl::size x, fl::size y) const {
99 if (!has(x, y))
100 return 0;
101 i32 sum = 0;
102 u8 mult = MAX(1, mMultiplier);
103 for (u32 j = 0; j < mult; ++j) {
104 for (u32 i = 0; i < mult; ++i) {
105 sum +=
106 mSim->geti16Previous(x * mult + i, y * mult + j);
107 }
108 }
109 i16 out = static_cast<i16>(sum / (mult * mult));
110 return out;
111}
112
113bool WaveSimulation2D::geti16All(fl::size x, fl::size y, i16 *curr,
114 i16 *prev, i16 *diff) const {
115 if (!has(x, y))
116 return false;
117 *curr = geti16(x, y);
118 *prev = geti16Previous(x, y);
119 *diff = *curr - *prev;
120 return true;
121}
122
123i8 WaveSimulation2D::geti8(fl::size x, fl::size y) const {
124 return static_cast<i8>(geti16(x, y) >> 8);
125}
126
127u8 WaveSimulation2D::getu8(fl::size x, fl::size y) const {
128 i16 value = geti16(x, y);
129 if (mSim->getHalfDuplex()) {
130 u16 v2 = static_cast<u16>(value);
131 switch (mU8Mode) {
133 return half_duplex_blend_linear(v2);
135 return half_duplex_blend_sqrt_q15(v2);
136 }
137 }
138 return static_cast<u8>(((static_cast<u16>(value) + 32768)) >> 8);
139}
140
141bool WaveSimulation2D::has(fl::size x, fl::size y) const {
142 return (x < mOuterWidth) && (y < mOuterHeight);
143}
144
145void WaveSimulation2D::seti16(fl::size x, fl::size y, i16 v16) {
146 if (!has(x, y))
147 return;
148
149 u8 mult = MAX(1, mMultiplier);
150
151 // radius in pixels of your diamond
152 int rad = static_cast<int>(mult) / 2;
153
154 for (fl::size j = 0; j < mult; ++j) {
155 for (fl::size i = 0; i < mult; ++i) {
156 // compute offset from the center of this block
157 int dx = static_cast<int>(i) - rad;
158 int dy = static_cast<int>(j) - rad;
159 // keep only those points whose Manhattan distance ≤ rad
160 if (ABS(dx) + ABS(dy) > rad) {
161 continue;
162 }
163 fl::size xx = x * mult + i;
164 fl::size yy = y * mult + j;
165 if (mSim->has(xx, yy)) {
166 i16 &pt = mChangeGrid.at(xx, yy);
167 if (pt == 0) {
168 // not set yet so set unconditionally.
169 pt = v16;
170 } else {
171 const bool sign_matches = (pt >= 0) == (v16 >= 0);
172 if (!sign_matches) {
173 // opposite signs, so overwrite
174 pt = v16;
175 } else {
176 // if the magnitude of the new pt is greater than what
177 // was already there, then overwrite.
178 u16 abs_pt = static_cast<u16>(ABS(pt));
179 u16 abs_v16 = static_cast<u16>(ABS(v16));
180 if (abs_v16 > abs_pt) {
181 pt = v16;
182 }
183 }
184 }
185 }
186 }
187 }
188}
189
190void WaveSimulation2D::setf(fl::size x, fl::size y, float value) {
191 if (!has(x, y))
192 return;
193
194 value = fl::clamp(value, 0.0f, 1.0f);
195 i16 v16 = wave_detail::float_to_fixed(value);
196 seti16(x, y, v16);
197}
198
200 const vec2<i16> min_max = mChangeGrid.minMax();
201 const bool has_updates = min_max != vec2<i16>(0, 0);
202 for (u8 i = 0; i < mExtraFrames + 1; ++i) {
203 if (has_updates) {
204 // apply them
205 const u32 w = mChangeGrid.width();
206 const u32 h = mChangeGrid.height();
207 for (u32 x = 0; x < w; ++x) {
208 for (u32 y = 0; y < h; ++y) {
209 i16 v16 = mChangeGrid(x, y);
210 if (v16 != 0) {
211 mSim->seti16(x, y, v16);
212 }
213 }
214 }
215 }
216 mSim->update();
217 }
218 // zero out mChangeGrid
219 mChangeGrid.clear();
220}
221
224
226
231
233 int dampening) {
235 mMultiplier = static_cast<u32>(factor);
236 mSim.reset(); // clear out memory first.
237 mSim.reset(
239 // Extra updates (frames) are applied because the simulation slows down in
240 // proportion to the supersampling factor.
241 mExtraFrames = static_cast<u8>(factor) - 1;
242}
243
244void WaveSimulation1D::setSpeed(float speed) { mSim->setSpeed(speed); }
245
246void WaveSimulation1D::setDampening(int damp) { mSim->setDampening(damp); }
247
248int WaveSimulation1D::getDampenening() const { return mSim->getDampenening(); }
249
251
252float WaveSimulation1D::getSpeed() const { return mSim->getSpeed(); }
253
254float WaveSimulation1D::getf(fl::size x) const {
255 if (!has(x))
256 return 0.0f;
257 float sum = 0.0f;
258 u8 mult = MAX(1, mMultiplier);
259 for (u32 i = 0; i < mult; ++i) {
260 sum += mSim->getf(x * mult + i);
261 }
262 return sum / static_cast<float>(mult);
263}
264
265i16 WaveSimulation1D::geti16(fl::size x) const {
266 if (!has(x))
267 return 0;
268 u8 mult = MAX(1, mMultiplier);
269 i32 sum = 0;
270 for (u32 i = 0; i < mult; ++i) {
271 sum += mSim->geti16(x * mult + i);
272 }
273 return static_cast<i16>(sum / mult);
274}
275
277 if (!has(x))
278 return 0;
279 u8 mult = MAX(1, mMultiplier);
280 i32 sum = 0;
281 for (u32 i = 0; i < mult; ++i) {
282 sum += mSim->geti16Previous(x * mult + i);
283 }
284 return static_cast<i16>(sum / mult);
285}
286
287bool WaveSimulation1D::geti16All(fl::size x, i16 *curr, i16 *prev,
288 i16 *diff) const {
289 if (!has(x))
290 return false;
291 *curr = geti16(x);
292 *prev = geti16Previous(x);
293 *diff = *curr - *prev;
294 return true;
295}
296
297i8 WaveSimulation1D::geti8(fl::size x) const {
298 return static_cast<i8>(geti16(x) >> 8);
299}
300
301// u8 WaveSimulation2D::getu8(fl::size x, fl::size y) const {
302// i16 value = geti16(x, y);
303// if (mSim->getHalfDuplex()) {
304// u16 v2 = static_cast<u16>(value);
305// switch (mU8Mode) {
306// case WAVE_U8_MODE_LINEAR:
307// return half_duplex_blend_linear(v2);
308// case WAVE_U8_MODE_SQRT:
309// return half_duplex_blend_sqrt_q15(v2);
310// }
311// }
312// return static_cast<u8>(((static_cast<u16>(value) + 32768)) >>
313// 8);
314// }
315
316u8 WaveSimulation1D::getu8(fl::size x) const {
317 i16 value = geti16(x);
318 if (mSim->getHalfDuplex()) {
319 u16 v2 = static_cast<u16>(value);
320 switch (mU8Mode) {
322 return half_duplex_blend_linear(v2);
324 return half_duplex_blend_sqrt_q15(v2);
325 }
326 }
327 return static_cast<u8>(((static_cast<u16>(value) + 32768)) >> 8);
328}
329
330bool WaveSimulation1D::has(fl::size x) const { return (x < mOuterLength); }
331
332void WaveSimulation1D::setf(fl::size x, float value) {
333 if (!has(x))
334 return;
335 value = fl::clamp(value, -1.0f, 1.0f);
336 u8 mult = MAX(1, mMultiplier);
337 for (u32 i = 0; i < mult; ++i) {
338 mSim->set(x * mult + i, value);
339 }
340}
341
343 mSim->update();
344 for (u8 i = 0; i < mExtraFrames; ++i) {
345 mSim->update();
346 }
347}
348
350
351} // namespace fl
int y
Definition simple.h:93
int x
Definition simple.h:92
uint16_t speed
Definition Noise.ino:63
float getf(fl::size x) const
fl::unique_ptr< WaveSimulation1D_Real > mSim
void setExtraFrames(u8 extra)
i16 geti16Previous(fl::size x) const
void setSpeed(float speed)
U8EasingFunction mU8Mode
void setDampening(int damp)
bool geti16All(fl::size x, i16 *curr, i16 *prev, i16 *diff) const
u8 getu8(fl::size x) const
void init(u32 length, SuperSample factor, float speed, int dampening)
WaveSimulation1D(u32 length, SuperSample factor=SuperSample::SUPER_SAMPLE_NONE, float speed=0.16f, int dampening=6)
i8 geti8(fl::size x) const
void setf(fl::size x, float value)
bool has(fl::size x) const
i16 geti16(fl::size x) const
i8 geti8(fl::size x, fl::size y) const
WaveSimulation2D(u32 W, u32 H, SuperSample factor=SuperSample::SUPER_SAMPLE_NONE, float speed=0.16f, float dampening=6.0f)
void seti16(fl::size x, fl::size y, i16 value)
i16 geti16(fl::size x, fl::size y) const
fl::Grid< i16 > mChangeGrid
void setExtraFrames(u8 extra)
float getf(fl::size x, fl::size y) const
i16 geti16Previous(fl::size x, fl::size y) const
void setSpeed(float speed)
bool geti16All(fl::size x, fl::size y, i16 *curr, i16 *prev, i16 *diff) const
void setf(fl::size x, fl::size y, float value)
bool has(fl::size x, fl::size y) const
U8EasingFunction mU8Mode
void setDampening(int damp)
u8 getu8(fl::size x, fl::size y) const
void init(u32 width, u32 height, SuperSample factor, float speed, int dampening)
fl::unique_ptr< WaveSimulation2D_Real > mSim
UISlider length("Length", 1.0f, 0.0f, 1.0f, 0.01f)
UISlider dampening("Dampening", 6.0f, 0.0f, 10.0f, 0.1f)
#define MIN(a, b)
Definition math_macros.h:41
#define ABS(x)
Definition math_macros.h:45
#define MAX(a, b)
Definition math_macros.h:37
Implements the FastLED namespace macros.
unsigned char u8
Definition int.h:17
@ WAVE_U8_MODE_LINEAR
@ WAVE_U8_MODE_SQRT
FASTLED_FORCE_INLINE T clamp(T value, T min, T max)
Definition clamp.h:10
signed char i8
Definition int.h:16
SuperSample
Definition supersample.h:4
IMPORTANT!
Definition crgb.h:20