FastLED 3.9.15
Loading...
Searching...
No Matches
wave_simulation_real.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
8namespace fl {
9
10// Define Q15 conversion constants.
11#define FIXED_SCALE (1 << 15) // 32768: 1.0 in Q15
12#define FIXED_ONE (FIXED_SCALE)
13
14namespace wave_detail { // Anonymous namespace for internal linkage
15// Convert float to fixed Q15.
16int16_t float_to_fixed(float f) { return (int16_t)(f * FIXED_SCALE); }
17
18// Convert fixed Q15 to float.
19float fixed_to_float(int16_t f) { return ((float)f) / FIXED_SCALE; }
20
21// // Multiply two Q15 fixed point numbers.
22// int16_t fixed_mul(int16_t a, int16_t b) {
23// return (int16_t)(((int32_t)a * b) >> 15);
24// }
25} // namespace
26
27using namespace wave_detail;
28
30 int dampening)
31 : length(len),
32 grid1(new int16_t[length + 2]()), // Allocate and zero-initialize with
33 // length+2 elements.
34 grid2(new int16_t[length + 2]()), whichGrid(0),
36 // Additional initialization can be added here if needed.
37}
38
39void WaveSimulation1D_Real::setSpeed(float something) {
40 mCourantSq = float_to_fixed(something);
41}
42
44
46
50
51int16_t WaveSimulation1D_Real::geti16(size_t x) const {
52 if (x >= length) {
53 FASTLED_WARN("Out of range.");
54 return 0;
55 }
56 const int16_t *curr = (whichGrid == 0) ? grid1.get() : grid2.get();
57 return curr[x + 1];
58}
59
61 if (x >= length) {
62 FASTLED_WARN("Out of range.");
63 return 0;
64 }
65 const int16_t *prev = (whichGrid == 0) ? grid2.get() : grid1.get();
66 return prev[x + 1];
67}
68
69float WaveSimulation1D_Real::getf(size_t x) const {
70 if (x >= length) {
71 FASTLED_WARN("Out of range.");
72 return 0.0f;
73 }
74 // Retrieve value from the active grid (offset by 1 for boundary).
75 const int16_t *curr = (whichGrid == 0) ? grid1.get() : grid2.get();
76 return fixed_to_float(curr[x + 1]);
77}
78
79bool WaveSimulation1D_Real::has(size_t x) const { return (x < length); }
80
81void WaveSimulation1D_Real::set(size_t x, float value) {
82 if (x >= length) {
83 FASTLED_WARN("warning X value too high");
84 return;
85 }
86 int16_t *curr = (whichGrid == 0) ? grid1.get() : grid2.get();
87 curr[x + 1] = float_to_fixed(value);
88}
89
91 int16_t *curr = (whichGrid == 0) ? grid1.get() : grid2.get();
92 int16_t *next = (whichGrid == 0) ? grid2.get() : grid1.get();
93
94 // Update boundaries with a Neumann (zero-gradient) condition:
95 curr[0] = curr[1];
96 curr[length + 1] = curr[length];
97
98 // Compute dampening factor as an integer value: 2^(mDampenening)
99 int32_t dampening_factor = 1 << mDampenening;
100
101 int32_t mCourantSq32 = static_cast<int32_t>(mCourantSq);
102 // Iterate over each inner cell.
103 for (size_t i = 1; i < length + 1; i++) {
104 // Compute the 1D Laplacian:
105 // lap = curr[i+1] - 2 * curr[i] + curr[i-1]
106 int32_t lap =
107 (int32_t)curr[i + 1] - ((int32_t)curr[i] << 1) + curr[i - 1];
108
109 // Multiply the Laplacian by the simulation speed using Q15 arithmetic:
110 int32_t term = (mCourantSq32 * lap) >> 15;
111
112 // Compute the new value:
113 // f = -next[i] + 2 * curr[i] + term
114 int32_t f = -(int32_t)next[i] + ((int32_t)curr[i] << 1) + term;
115
116 // Apply damping:
117 f = f - (f / dampening_factor);
118
119 // Clamp the result to the Q15 range [-32768, 32767].
120 if (f > 32767)
121 f = 32767;
122 else if (f < -32768)
123 f = -32768;
124
125 next[i] = (int16_t)f;
126 }
127
128 if (mHalfDuplex) {
129 // Set the negative values to zero.
130 for (size_t i = 1; i < length + 1; i++) {
131 if (next[i] < 0) {
132 next[i] = 0;
133 }
134 }
135 }
136
137 // Toggle the active grid.
138 whichGrid ^= 1;
139}
140
142 float speed, float dampening)
143 : width(W), height(H), stride(W + 2),
144 grid1(new int16_t[(W + 2) * (H + 2)]()),
145 grid2(new int16_t[(W + 2) * (H + 2)]()), whichGrid(0),
146 // Initialize speed 0.16 in fixed Q15
148 // Dampening exponent; e.g., 6 means a factor of 2^6 = 64.
150
151void WaveSimulation2D_Real::setSpeed(float something) {
152 mCourantSq = float_to_fixed(something);
153}
154
156
158
162
163float WaveSimulation2D_Real::getf(size_t x, size_t y) const {
164 if (x >= width || y >= height) {
165 FASTLED_WARN("Out of range: " << x << ", " << y);
166 return 0.0f;
167 }
168 const int16_t *curr = (whichGrid == 0 ? grid1.get() : grid2.get());
169 return fixed_to_float(curr[(y + 1) * stride + (x + 1)]);
170}
171
172int16_t WaveSimulation2D_Real::geti16(size_t x, size_t y) const {
173 if (x >= width || y >= height) {
174 FASTLED_WARN("Out of range: " << x << ", " << y);
175 return 0;
176 }
177 const int16_t *curr = (whichGrid == 0 ? grid1.get() : grid2.get());
178 return curr[(y + 1) * stride + (x + 1)];
179}
180
181int16_t WaveSimulation2D_Real::geti16Previous(size_t x, size_t y) const {
182 if (x >= width || y >= height) {
183 FASTLED_WARN("Out of range: " << x << ", " << y);
184 return 0;
185 }
186 const int16_t *prev = (whichGrid == 0 ? grid2.get() : grid1.get());
187 return prev[(y + 1) * stride + (x + 1)];
188}
189
190bool WaveSimulation2D_Real::has(size_t x, size_t y) const {
191 return (x < width && y < height);
192}
193
194void WaveSimulation2D_Real::setf(size_t x, size_t y, float value) {
195 int16_t v = float_to_fixed(value);
196 return seti16(x, y, v);
197}
198
199void WaveSimulation2D_Real::seti16(size_t x, size_t y, int16_t value) {
200 if (x >= width || y >= height) {
201 FASTLED_WARN("Out of range: " << x << ", " << y);
202 return;
203 }
204 int16_t *curr = (whichGrid == 0 ? grid1.get() : grid2.get());
205 curr[(y + 1) * stride + (x + 1)] = value;
206}
207
209 int16_t *curr = (whichGrid == 0 ? grid1.get() : grid2.get());
210 int16_t *next = (whichGrid == 0 ? grid2.get() : grid1.get());
211
212 // Update horizontal boundaries.
213 for (size_t j = 0; j < height + 2; ++j) {
214 curr[j * stride + 0] = curr[j * stride + 1];
215 curr[j * stride + (width + 1)] = curr[j * stride + width];
216 }
217
218 // Update vertical boundaries.
219 for (size_t i = 0; i < width + 2; ++i) {
220 curr[0 * stride + i] = curr[1 * stride + i];
221 curr[(height + 1) * stride + i] = curr[height * stride + i];
222 }
223
224 // Compute the dampening factor as an integer: 2^(dampening).
225 int32_t dampening_factor = 1 << mDampening; // e.g., 6 -> 64
226 int32_t mCourantSq32 = static_cast<int32_t>(mCourantSq);
227
228 // Update each inner cell.
229 for (size_t j = 1; j <= height; ++j) {
230 for (size_t i = 1; i <= width; ++i) {
231 int index = j * stride + i;
232 // Laplacian: sum of four neighbors minus 4 times the center.
233 int32_t laplacian = (int32_t)curr[index + 1] + curr[index - 1] +
234 curr[index + stride] + curr[index - stride] -
235 ((int32_t)curr[index] << 2);
236 // Compute the new value:
237 // f = - next[index] + 2 * curr[index] + mCourantSq * laplacian
238 // The multiplication is in Q15, so we shift right by 15.
239 int32_t term = (mCourantSq32 * laplacian) >> 15;
240 int32_t f =
241 -(int32_t)next[index] + ((int32_t)curr[index] << 1) + term;
242
243 // Apply damping:
244 f = f - (f / dampening_factor);
245
246 // Clamp f to the Q15 range.
247 if (f > 32767)
248 f = 32767;
249 else if (f < -32768)
250 f = -32768;
251
252 next[index] = (int16_t)f;
253 }
254 }
255
256 if (mHalfDuplex) {
257 // Set negative values to zero.
258 for (size_t j = 1; j <= height; ++j) {
259 for (size_t i = 1; i <= width; ++i) {
260 int index = j * stride + i;
261 if (next[index] < 0) {
262 next[index] = 0;
263 }
264 }
265 }
266 }
267
268 // Swap the roles of the grids.
269 whichGrid ^= 1;
270}
271
272} // 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 set(size_t x, float value)
fl::scoped_array< int16_t > grid2
int16_t geti16(size_t x) const
int16_t geti16Previous(size_t x) const
fl::scoped_array< int16_t > grid1
WaveSimulation1D_Real(uint32_t length, float speed=0.16f, int dampening=6)
int16_t geti16Previous(size_t x, size_t y) const
bool has(size_t x, size_t y) const
fl::scoped_array< int16_t > grid1
void setf(size_t x, size_t y, float value)
int16_t geti16(size_t x, size_t y) const
fl::scoped_array< int16_t > grid2
void seti16(size_t x, size_t y, int16_t value)
WaveSimulation2D_Real(uint32_t W, uint32_t H, float speed=0.16f, float dampening=6.0f)
float getf(size_t x, size_t y) const
Implements the FastLED namespace macros.
float fixed_to_float(int16_t f)
int16_t float_to_fixed(float f)
Implements a simple red square effect for 2D LED grids.
Definition crgb.h:16
#define FASTLED_WARN
Definition warn.h:7
#define FIXED_SCALE