FastLED 3.9.15
Loading...
Searching...
No Matches
wave_simulation_real.h
Go to the documentation of this file.
1/*
2Wave simulation classes for 1D and 2D simulations. This is called _Real because
3there is a one to one mapping between the simulation and the LED grid. For
4flexible super sampling see wave_simluation.h.
5
6Based on works and code by Shawn Silverman.
7*/
8
9#pragma once
10
11#include "fl/stl/stdint.h"
12
13#include "fl/stl/vector.h"
14#include "fl/stl/noexcept.h"
15
16namespace fl {
17
18namespace wave_detail {
19i16 float_to_fixed(float f);
20
21// Convert fixed Q15 to float.
22float fixed_to_float(i16 f);
23
24// Compute the Q15 damping decay factor for a power-of-two damping
25// exponent. Equivalent (modulo 1-LSB rounding) to the arithmetic-shift
26// form `f -= f >> damp` used by the kernel before #3099/§6.
28} // namespace wave_detail
29
31 public:
32 // Constructor:
33 // - length: inner simulation grid length (excluding the 2 boundary cells).
34 // - speed: simulation speed (Courant squared, C^2) stored in Q15.
35 // Clamped to [0.0, 1.0] (CFL stability bound for the 1D 5-point
36 // stencil). Values outside that range produce visual instability
37 // and are silently clamped.
38 // - dampening: exponent so that the effective damping factor is
39 // 2^(dampening).
40 WaveSimulation1D_Real(u32 length, float speed = 0.16f,
41 int dampening = 6) FL_NOEXCEPT;
43
44 // Set simulation speed (Courant squared). Clamped to [0.0, 1.0] — see
45 // constructor for rationale.
46 void setSpeed(float something);
47
48 // Set the dampening exponent (effective damping factor is 2^(dampening)).
49 void setDampening(int damp) FL_NOEXCEPT;
50
51 // Get the current dampening exponent.
52 int getDampenening() const;
53
54 // Get the simulation speed as a float.
55 float getSpeed() const;
56
57 void setHalfDuplex(bool on) { mHalfDuplex = on; }
58
59 bool getHalfDuplex() const { return mHalfDuplex; }
60
61 // Get the simulation value at the inner grid cell x (converted to float in
62 // the range [-1.0, 1.0]).
63 float getf(fl::size x) const;
64
65 i16 geti16(fl::size x) const;
66 i16 geti16Previous(fl::size x) const;
67
68 i8 geti8(fl::size x) const { return static_cast<i8>(geti16(x) >> 8); }
69
70 // If mHalfDuplex is set then the the values are adjusted so that negative
71 // values will instead be represented by zero.
72 u8 getu8(fl::size x) const {
73 i16 value = geti16(x);
74 // Rebase the range from [-32768, 32767] to [0, 65535] then extract the
75 // upper 8 bits.
76 // return static_cast<u8>(((static_cast<u16>(value) + 32768))
77 // >>
78 // 8);
79 if (mHalfDuplex) {
80 u16 v2 = static_cast<u16>(value);
81 v2 *= 2;
82 return static_cast<u8>(v2 >> 8);
83 } else {
84 return static_cast<u8>(
85 ((static_cast<u16>(value) + 32768)) >> 8);
86 }
87 }
88
89 // Returns whether x is within the inner grid bounds.
90 bool has(fl::size x) const;
91
92 // Set the value at grid cell x (expected range: [-1.0, 1.0]); the value is
93 // stored in Q15.
94 void set(fl::size x, float value);
95
96 // Advance the simulation one time step.
97 void update();
98
99 private:
100 u32 length; // Length of the inner simulation grid.
101 // Two grids stored in fixed Q15 format, each with length+2 entries
102 // (including boundary cells).
105 fl::size whichGrid; // Indicates the active grid (0 or 1).
106
107 i16 mCourantSq; // Simulation speed (courant squared) stored in Q15.
108 int mDampenening; // Dampening exponent (damping factor = 2^(mDampenening)).
109 // Precomputed Q15 decay multiplier (1 - 1/2^mDampenening). The inner
110 // loop uses `f = (i64(f) * mDampDecayQ15) >> 15` instead of the shift,
111 // which generalizes cleanly to non-power-of-two damping if the public
112 // API ever needs it.
115 true; // Flag to restrict values to positive range during update.
116
117};
118
119// Discrete Laplacian stencil for the 2D wave PDE.
120// FivePoint — standard 5-point: N+S+E+W - 4*C, O(h^2) accurate
121// but anisotropic (waves visibly prefer cardinal
122// directions, producing square-ish ripples at high
123// super-sample factors).
124// NinePointIsotropic — 1/6 * sum(diagonals) + 2/3 * sum(neighbors)
125// - 10/3 * C, also O(h^2) but with isotropic
126// leading error. ~2x reads + ALU per cell; gives
127// visibly rounder ripples at SUPER_SAMPLE_2X and
128// above. The wave_simulation.h wrapper auto-
129// selects this when SuperSample >= 2X.
134
136 public:
137 // Constructor: Initializes the simulation with inner grid size (W x H).
138 // The grid dimensions include a 1-cell border on each side.
139 // - speed: Courant squared (C^2) stored in Q15. Clamped to [0.0, 0.5]
140 // (CFL stability bound for the 2D 5-point stencil — stricter than 1D).
141 // Values outside that range produce visual instability and are
142 // silently clamped.
143 // - dampening: exponent so that the damping factor is 2^dampening.
144 WaveSimulation2D_Real(u32 W, u32 H, float speed = 0.16f,
145 float dampening = 6.0f) FL_NOEXCEPT;
147
148 // Set the simulation speed (Courant squared). Clamped to [0.0, 0.5] — see
149 // constructor for rationale.
150 void setSpeed(float something);
151
152 // Set the dampening factor exponent.
153 // The dampening factor used is 2^(dampening).
154 void setDampening(int damp) FL_NOEXCEPT;
155
156 // Get the current dampening exponent.
157 int getDampenening() const;
158
159 // Get the simulation speed as a float (converted from fixed Q15).
160 float getSpeed() const;
161
162 // Return the value at an inner grid cell (x,y), converted to float.
163 // The value returned is in the range [-1.0, 1.0].
164 float getf(fl::size x, fl::size y) const;
165
166 // Return the value at an inner grid cell (x,y) as a fixed Q15 integer
167 // in the range [-32768, 32767].
168 i16 geti16(fl::size x, fl::size y) const;
169 i16 geti16Previous(fl::size x, fl::size y) const;
170
171 i8 geti8(fl::size x, fl::size y) const {
172 return static_cast<i8>(geti16(x, y) >> 8);
173 }
174
175 u8 getu8(fl::size x, fl::size y) const {
176 i16 value = geti16(x, y);
177 // Rebase the range from [-32768, 32767] to [0, 65535] then extract the
178 // upper 8 bits.
179 // return static_cast<u8>(((static_cast<u16>(value) + 32768))
180 // >>
181 // 8);
182 if (mHalfDuplex) {
183 u16 v2 = static_cast<u16>(value);
184 v2 *= 2;
185 return static_cast<u8>(v2 >> 8);
186 } else {
187 return static_cast<u8>(
188 ((static_cast<u16>(value) + 32768)) >> 8);
189 }
190 }
191
192 // Select the discrete Laplacian stencil used by update(). Default is
193 // FivePoint (backward compatible). NinePointIsotropic costs ~2x reads
194 // + ALU per cell but produces visibly rounder ripples at high super-
195 // sample factors; the wrapper class auto-selects it when appropriate.
198
199 void setXCylindrical(bool on) { mXCylindrical = on; }
200
201 // Check if (x,y) is within the inner grid.
202 bool has(fl::size x, fl::size y) const;
203
204 // Set the value at an inner grid cell using a float;
205 // the value is stored in fixed Q15 format.
206 // value shoudl be between -1.0 and 1.0.
207 void setf(fl::size x, fl::size y, float value);
208
209 void seti16(fl::size x, fl::size y, i16 value);
210
211 void setHalfDuplex(bool on) { mHalfDuplex = on; }
212
213 bool getHalfDuplex() const { return mHalfDuplex; }
214
215 // Advance the simulation one time step using fixed-point arithmetic.
216 void update();
217
218 u32 getWidth() const { return width; }
219 u32 getHeight() const { return height; }
220
221 private:
222 u32 width; // Width of the inner grid.
223 u32 height; // Height of the inner grid.
224 u32 stride; // Row length (width + 2 for the borders).
225
226 // Two separate grids stored in fixed Q15 format.
229
230 fl::size whichGrid; // Indicates the active grid (0 or 1).
231
232 i16 mCourantSq; // Fixed speed parameter in Q15.
233 int mDampening; // Dampening exponent; used as 2^(dampening).
234 // Precomputed Q15 decay multiplier (1 - 1/2^mDampening). See the 1D
235 // class for the rationale.
238 true; // Flag to restrict values to positive range during update.
239 bool mXCylindrical = false; // Default to non-cylindrical mode
241 LaplacianStencil::FivePoint; // Backward-compatible default
242};
243
244} // namespace fl
uint16_t speed
Definition Noise.ino:66
static const int H
Definition PerfDisc.ino:20
static const int W
Definition PerfDisc.ino:19
WaveSimulation1D_Real(u32 length, float speed=0.16f, int dampening=6) FL_NOEXCEPT
void set(fl::size x, float value)
void setDampening(int damp) FL_NOEXCEPT
~WaveSimulation1D_Real() FL_NOEXCEPT=default
void setf(fl::size x, fl::size y, float value)
LaplacianStencil getStencil() const FL_NOEXCEPT
i16 geti16Previous(fl::size x, fl::size y) const
~WaveSimulation2D_Real() FL_NOEXCEPT=default
u8 getu8(fl::size x, fl::size y) const
i8 geti8(fl::size x, fl::size y) const
i16 geti16(fl::size x, fl::size y) const
void setStencil(LaplacianStencil s) FL_NOEXCEPT
fl::vector_psram< i16 > grid1
WaveSimulation2D_Real(u32 W, u32 H, float speed=0.16f, float dampening=6.0f) FL_NOEXCEPT
float getf(fl::size x, fl::size y) const
void seti16(fl::size x, fl::size y, i16 value)
bool has(fl::size x, fl::size y) const
fl::vector_psram< i16 > grid2
void setDampening(int damp) FL_NOEXCEPT
fl::UISlider dampening("Dampening", 6.0f, 0.0f, 10.0f, 0.1f)
i16 compute_damp_decay_q15(int damp) FL_NOEXCEPT
unsigned char u8
Definition stdint.h:131
constexpr int type_rank< T >::value
signed char i8
Definition stdint.h:130
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT