FastLED 3.9.15
Loading...
Searching...
No Matches
noise.cpp.hpp
Go to the documentation of this file.
1
3
4#include "platforms/is_platform.h"
5
6// IWYU pragma: begin_keep
7#include <string.h>
8// IWYU pragma: end_keep // ok include
9#include "fl/stl/array.h"
10
11
13#define FASTLED_INTERNAL
14#include "fl/system/fastled.h"
15
16
18#include "fl/stl/cstring.h"
19#include "fl/math/math.h"
20// Compiler throws a warning about stack usage possibly being unbounded even
21// though bounds are checked, silence that so users don't see it
23#if defined(FL_IS_GCC)
24 FL_DISABLE_WARNING(stack-usage=)
25#elif defined(FL_IS_CLANG)
26 FL_DISABLE_WARNING(unknown-warning-option)
27#endif
28
29
30
32#define NOISE_P(x) FL_PGM_READ_BYTE_NEAR(noise_detail::p + x)
33
34
35namespace noise_detail {
36
37FL_PROGMEM static fl::u8 const p[] = {
38 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225,
39 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148,
40 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32,
41 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175,
42 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122,
43 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54,
44 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169,
45 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64,
46 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212,
47 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213,
48 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
49 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104,
50 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241,
51 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157,
52 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93,
53 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180,
54 151};
55
56
57// Start Doxygen define hiding
59
60#if FASTLED_NOISE_ALLOW_AVERAGE_TO_OVERFLOW == 1
61#define AVG15(U,V) (((U)+(V)) >> 1)
62#else
63// See if we should use the inlined avg15 for AVR with MUL instruction
64#if defined(FL_IS_AVR) && !defined(FL_IS_AVR_ATTINY)
65#define AVG15(U,V) (noise_detail::avg15_inline_avr_mul((U),(V)))
66// inlined copy of avg15 for AVR with MUL instruction; cloned from math8.h
67// Forcing this inline in the 3-D 16bit noise produces a 12% speedup overall,
68// at a cost of just +8 bytes of net code size.
69static fl::i16 inline __attribute__((always_inline)) avg15_inline_avr_mul( fl::i16 i, fl::i16 j)
70{
71 asm volatile(
72 /* first divide j by 2, throwing away lowest bit */
73 "asr %B[j] \n\t"
74 "ror %A[j] \n\t"
75 /* now divide i by 2, with lowest bit going into C */
76 "asr %B[i] \n\t"
77 "ror %A[i] \n\t"
78 /* add j + C to i */
79 "adc %A[i], %A[j] \n\t"
80 "adc %B[i], %B[j] \n\t"
81 : [i] "+r" (i)
82 : [j] "r" (j) );
83 return i;
84}
85#else
86#define AVG15(U,V) (fl::avg15((U),(V)))
87#endif
88#endif
89
90// See fastled_config.h for notes on this;
91// "#define FASTLED_NOISE_FIXED 1" is the correct value
92#if FASTLED_NOISE_FIXED == 0
93#define EASE8(x) (FADE(x) )
94#define EASE16(x) (FADE(x) )
95#else
96#define EASE8(x) (ease8InOutQuad(x) )
97#define EASE16(x) (ease16InOutQuad(x))
98#endif
99//
100// #define FADE_12
101#define FADE_16
102
103#ifdef FADE_12
104#define FADE logfade12
105#define LERP(a,b,u) lerp15by12(a,b,u)
106#else
107#define FADE(x) scale16(x,x)
108#define LERP(a,b,u) lerp15by16(a,b,u)
109#endif
110
111// end Doxygen define hiding
113
114} // namespace noise_detail
115
116static fl::i16 inline __attribute__((always_inline)) grad16(fl::u8 hash, fl::i16 x, fl::i16 y, fl::i16 z) {
117#if 0
118 switch(hash & 0xF) {
119 case 0: return (( x) + ( y))>>1;
120 case 1: return ((-x) + ( y))>>1;
121 case 2: return (( x) + (-y))>>1;
122 case 3: return ((-x) + (-y))>>1;
123 case 4: return (( x) + ( z))>>1;
124 case 5: return ((-x) + ( z))>>1;
125 case 6: return (( x) + (-z))>>1;
126 case 7: return ((-x) + (-z))>>1;
127 case 8: return (( y) + ( z))>>1;
128 case 9: return ((-y) + ( z))>>1;
129 case 10: return (( y) + (-z))>>1;
130 case 11: return ((-y) + (-z))>>1;
131 case 12: return (( y) + ( x))>>1;
132 case 13: return ((-y) + ( z))>>1;
133 case 14: return (( y) + (-x))>>1;
134 case 15: return ((-y) + (-z))>>1;
135 }
136#else
137 hash = hash&15;
138 fl::i16 u = hash<8?x:y;
139 fl::i16 v = hash<4?y:hash==12||hash==14?x:z;
140 if(hash&1) { u = -u; }
141 if(hash&2) { v = -v; }
142
143 return AVG15(u,v);
144#endif
145}
146
147static fl::i16 inline __attribute__((always_inline)) grad16(fl::u8 hash, fl::i16 x, fl::i16 y) {
148 hash = hash & 7;
149 fl::i16 u,v;
150 if(hash < 4) { u = x; v = y; } else { u = y; v = x; }
151 if(hash&1) { u = -u; }
152 if(hash&2) { v = -v; }
153
154 return AVG15(u,v);
155}
156
157static fl::i16 inline __attribute__((always_inline)) grad16(fl::u8 hash, fl::i16 x) {
158 hash = hash & 15;
159 fl::i16 u,v;
160 if(hash > 8) { u=x;v=x; }
161 else if(hash < 4) { u=x;v=1; }
162 else { u=1;v=x; }
163 if(hash&1) { u = -u; }
164 if(hash&2) { v = -v; }
165
166 return AVG15(u,v);
167}
168
169// selectBasedOnHashBit performs this:
170// result = (hash & (1<<bitnumber)) ? a : b
171// but with an AVR asm version that's smaller and quicker than C
172// (and probably not worth including in lib8tion)
173static fl::i8 inline __attribute__((always_inline)) __attribute__((unused)) selectBasedOnHashBit(fl::u8 hash, fl::u8 bitnumber, fl::i8 a, fl::i8 b) {
174 fl::i8 result;
175#if !defined(FL_IS_AVR)
176 result = (hash & (1<<bitnumber)) ? a : b;
177#else
178 asm volatile(
179 "mov %[result],%[a] \n\t"
180 "sbrs %[hash],%[bitnumber] \n\t"
181 "mov %[result],%[b] \n\t"
182 : [result] "=r" (result)
183 : [hash] "r" (hash),
184 [bitnumber] "M" (bitnumber),
185 [a] "r" (a),
186 [b] "r" (b)
187 );
188#endif
189 return result;
190}
191
192static fl::i8 inline __attribute__((always_inline)) grad8(fl::u8 hash, fl::i8 x, fl::i8 y, fl::i8 z) {
193 // Industry-standard 3D Perlin noise gradient implementation
194 // Uses proper 12 edge vectors of a cube for maximum range coverage
195
196 switch(hash & 0xF) {
197 case 0: return fl::avg7( x, y); // (1,1,0)
198 case 1: return fl::avg7(-x, y); // (-1,1,0)
199 case 2: return fl::avg7( x, -y); // (1,-1,0)
200 case 3: return fl::avg7(-x, -y); // (-1,-1,0)
201 case 4: return fl::avg7( x, z); // (1,0,1)
202 case 5: return fl::avg7(-x, z); // (-1,0,1)
203 case 6: return fl::avg7( x, -z); // (1,0,-1)
204 case 7: return fl::avg7(-x, -z); // (-1,0,-1)
205 case 8: return fl::avg7( y, z); // (0,1,1)
206 case 9: return fl::avg7(-y, z); // (0,-1,1)
207 case 10: return fl::avg7( y, -z); // (0,1,-1)
208 case 11: return fl::avg7(-y, -z); // (0,-1,-1)
209 // Repeat first 4 for hash values 12-15 (proper wrap-around)
210 case 12: return fl::avg7( x, y); // (1,1,0)
211 case 13: return fl::avg7(-x, y); // (-1,1,0)
212 case 14: return fl::avg7( x, -y); // (1,-1,0)
213 case 15: return fl::avg7(-x, -y); // (-1,-1,0)
214 }
215 return 0; // Should never reach here
216}
217
218static fl::i8 inline __attribute__((always_inline)) grad8(fl::u8 hash, fl::i8 x, fl::i8 y)
219{
220 // since the tests below can be done bit-wise on the bottom
221 // three bits, there's no need to mask off the higher bits
222 // hash = hash & 7;
223
224 fl::i8 u,v;
225 if( hash & 4) {
226 u = y; v = x;
227 } else {
228 u = x; v = y;
229 }
230
231 if(hash&1) { u = -u; }
232 if(hash&2) { v = -v; }
233
234 return fl::avg7(u,v);
235}
236
237static fl::i8 inline __attribute__((always_inline)) grad8(fl::u8 hash, fl::i8 x)
238{
239 // since the tests below can be done bit-wise on the bottom
240 // four bits, there's no need to mask off the higher bits
241 // hash = hash & 15;
242
243 fl::i8 u,v;
244 if(hash & 8) {
245 u=x; v=x;
246 } else {
247 if(hash & 4) {
248 u=1; v=x;
249 } else {
250 u=x; v=1;
251 }
252 }
253
254 if(hash&1) { u = -u; }
255 if(hash&2) { v = -v; }
256
257 return fl::avg7(u,v);
258}
259
260
261#ifdef FADE_12
262fl::u16 logfade12(fl::u16 val) {
263 return scale16(val,val)>>4;
264}
265
266static fl::i16 inline __attribute__((always_inline)) lerp15by12( fl::i16 a, fl::i16 b, fract16 frac)
267{
268 //if(1) return (lerp(frac,a,b));
269 fl::i16 result;
270 if( b > a) {
271 fl::u16 delta = b - a;
272 fl::u16 scaled = scale16(delta,frac<<4);
273 result = a + scaled;
274 } else {
275 fl::u16 delta = a - b;
276 fl::u16 scaled = scale16(delta,frac<<4);
277 result = a - scaled;
278 }
279 return result;
280}
281#endif
282
283static fl::i8 inline __attribute__((always_inline)) lerp7by8( fl::i8 a, fl::i8 b, fract8 frac)
284{
285 // int8_t delta = b - a;
286 // int16_t prod = (uint16_t)delta * (uint16_t)frac;
287 // int8_t scaled = prod >> 8;
288 // int8_t result = a + scaled;
289 // return result;
290 fl::i8 result;
291 if( b > a) {
292 fl::u8 delta = b - a;
293 fl::u8 scaled = scale8( delta, frac);
294 result = a + scaled;
295 } else {
296 fl::u8 delta = a - b;
297 fl::u8 scaled = scale8( delta, frac);
298 result = a - scaled;
299 }
300 return result;
301}
302
303fl::i16 inoise16_raw(fl::u32 x, fl::u32 y, fl::u32 z)
304{
305 // Find the unit cube containing the point
306 fl::u8 X = (x>>16)&0xFF;
307 fl::u8 Y = (y>>16)&0xFF;
308 fl::u8 Z = (z>>16)&0xFF;
309
310 // Hash cube corner coordinates
311 fl::u8 A = NOISE_P(X)+Y;
312 fl::u8 AA = NOISE_P(A)+Z;
313 fl::u8 AB = NOISE_P(A+1)+Z;
314 fl::u8 B = NOISE_P(X+1)+Y;
315 fl::u8 BA = NOISE_P(B) + Z;
316 fl::u8 BB = NOISE_P(B+1)+Z;
317
318 // Get the relative position of the point in the cube
319 fl::u16 u = x & 0xFFFF;
320 fl::u16 v = y & 0xFFFF;
321 fl::u16 w = z & 0xFFFF;
322
323 // Get a signed version of the above for the grad function
324 fl::i16 xx = (u >> 1) & 0x7FFF;
325 fl::i16 yy = (v >> 1) & 0x7FFF;
326 fl::i16 zz = (w >> 1) & 0x7FFF;
327 fl::u16 N = 0x8000L;
328
329 u = EASE16(u); v = EASE16(v); w = EASE16(w);
330
331 // skip the log fade adjustment for the moment, otherwise here we would
332 // adjust fade values for u,v,w
333 fl::i16 X1 = LERP(grad16(NOISE_P(AA), xx, yy, zz), grad16(NOISE_P(BA), xx - N, yy, zz), u);
334 fl::i16 X2 = LERP(grad16(NOISE_P(AB), xx, yy-N, zz), grad16(NOISE_P(BB), xx - N, yy - N, zz), u);
335 fl::i16 X3 = LERP(grad16(NOISE_P(AA+1), xx, yy, zz-N), grad16(NOISE_P(BA+1), xx - N, yy, zz-N), u);
336 fl::i16 X4 = LERP(grad16(NOISE_P(AB+1), xx, yy-N, zz-N), grad16(NOISE_P(BB+1), xx - N, yy - N, zz - N), u);
337
338 fl::i16 Y1 = LERP(X1,X2,v);
339 fl::i16 Y2 = LERP(X3,X4,v);
340
341 fl::i16 ans = LERP(Y1,Y2,w);
342
343 return ans;
344}
345
346fl::i16 inoise16_raw(fl::u32 x, fl::u32 y, fl::u32 z, fl::u32 t) {
347 // 1. Extract the integer (grid) parts.
348 fl::u8 X = (x >> 16) & 0xFF;
349 fl::u8 Y = (y >> 16) & 0xFF;
350 fl::u8 Z = (z >> 16) & 0xFF;
351 fl::u8 T = (t >> 16) & 0xFF;
352
353 // 2. Extract the fractional parts.
354 fl::u16 u = x & 0xFFFF;
355 fl::u16 v = y & 0xFFFF;
356 fl::u16 w = z & 0xFFFF;
357 fl::u16 s = t & 0xFFFF;
358
359 // 3. Easing of the fractional parts.
360 u = EASE16(u);
361 v = EASE16(v);
362 w = EASE16(w);
363 s = EASE16(s);
364
365 fl::u16 N = 0x8000L; // fixed-point half-scale
366
367 // 4. Precompute fixed-point versions for the gradient evaluations.
368 fl::i16 xx = (u >> 1) & 0x7FFF;
369 fl::i16 yy = (v >> 1) & 0x7FFF;
370 fl::i16 zz = (w >> 1) & 0x7FFF;
371
372 // 5. Hash the 3D cube corners (the “base” for both t slices).
373 fl::u8 A = NOISE_P(X) + Y;
374 fl::u8 AA = NOISE_P(A) + Z;
375 fl::u8 AB = NOISE_P(A + 1) + Z;
376 fl::u8 B = NOISE_P(X + 1) + Y;
377 fl::u8 BA = NOISE_P(B) + Z;
378 fl::u8 BB = NOISE_P(B + 1) + Z;
379
380 // 6. --- Lower t Slice (using T) ---
381 fl::u8 AAA = NOISE_P(AA) + T;
382 fl::u8 AAB = NOISE_P(AA + 1) + T;
383 fl::u8 ABA = NOISE_P(AB) + T;
384 fl::u8 ABB = NOISE_P(AB + 1) + T;
385 fl::u8 BAA = NOISE_P(BA) + T;
386 fl::u8 BAB = NOISE_P(BA + 1) + T;
387 fl::u8 BBA = NOISE_P(BB) + T;
388 fl::u8 BBB = NOISE_P(BB + 1) + T;
389
390 fl::i16 L1 = LERP(grad16(AAA, xx, yy, zz), grad16(BAA, xx - N, yy, zz), u);
391 fl::i16 L2 = LERP(grad16(ABA, xx, yy - N, zz), grad16(BBA, xx - N, yy - N, zz), u);
392 fl::i16 L3 = LERP(grad16(AAB, xx, yy, zz - N), grad16(BAB, xx - N, yy, zz - N), u);
393 fl::i16 L4 = LERP(grad16(ABB, xx, yy - N, zz - N), grad16(BBB, xx - N, yy - N, zz - N), u);
394
395 fl::i16 Y1 = LERP(L1, L2, v);
396 fl::i16 Y2 = LERP(L3, L4, v);
397 fl::i16 noise_lower = LERP(Y1, Y2, w);
398
399 // 7. --- Upper t Slice (using T+1) ---
400 fl::u8 Tupper = T + 1;
401 fl::u8 AAA_u = NOISE_P(AA) + Tupper;
402 fl::u8 AAB_u = NOISE_P(AA + 1) + Tupper;
403 fl::u8 ABA_u = NOISE_P(AB) + Tupper;
404 fl::u8 ABB_u = NOISE_P(AB + 1) + Tupper;
405 fl::u8 BAA_u = NOISE_P(BA) + Tupper;
406 fl::u8 BAB_u = NOISE_P(BA + 1) + Tupper;
407 fl::u8 BBA_u = NOISE_P(BB) + Tupper;
408 fl::u8 BBB_u = NOISE_P(BB + 1) + Tupper;
409
410 fl::i16 U1 = LERP(grad16(AAA_u, xx, yy, zz), grad16(BAA_u, xx - N, yy, zz), u);
411 fl::i16 U2 = LERP(grad16(ABA_u, xx, yy - N, zz), grad16(BBA_u, xx - N, yy - N, zz), u);
412 fl::i16 U3 = LERP(grad16(AAB_u, xx, yy, zz - N), grad16(BAB_u, xx - N, yy, zz - N), u);
413 fl::i16 U4 = LERP(grad16(ABB_u, xx, yy - N, zz - N), grad16(BBB_u, xx - N, yy - N, zz - N), u);
414
415 fl::i16 V1 = LERP(U1, U2, v);
416 fl::i16 V2 = LERP(U3, U4, v);
417 fl::i16 noise_upper = LERP(V1, V2, w);
418
419 // 8. Final interpolation in the t dimension.
420 fl::i16 noise4d = LERP(noise_lower, noise_upper, s);
421
422 return noise4d;
423}
424
425fl::u16 inoise16(fl::u32 x, fl::u32 y, fl::u32 z, fl::u32 t) {
426 fl::i32 ans = inoise16_raw(x,y,z,t);
427 ans = ans + 19052L;
428 fl::u32 pan = ans;
429 // pan = (ans * 220L) >> 7. That's the same as:
430 // pan = (ans * 440L) >> 8. And this way avoids a 7X four-byte shift-loop on AVR.
431 // Identical math, except for the highest bit, which we don't care about anyway,
432 // since we're returning the 'middle' 16 out of a 32-bit value anyway.
433 pan *= 440L;
434 return (pan>>8);
435
436 // return scale16by8(pan,220)<<1;
437 // return ((inoise16_raw(x,y,z)+19052)*220)>>7;
438 // return scale16by8(inoise16_raw(x,y,z)+19052,220)<<1;
439}
440
441fl::u16 inoise16(fl::u32 x, fl::u32 y, fl::u32 z) {
442 fl::i32 ans = inoise16_raw(x,y,z);
443 ans = ans + 19052L;
444 fl::u32 pan = ans;
445 // pan = (ans * 220L) >> 7. That's the same as:
446 // pan = (ans * 440L) >> 8. And this way avoids a 7X four-byte shift-loop on AVR.
447 // Identical math, except for the highest bit, which we don't care about anyway,
448 // since we're returning the 'middle' 16 out of a 32-bit value anyway.
449 pan *= 440L;
450 return (pan>>8);
451
452 // // return scale16by8(pan,220)<<1;
453 // return ((inoise16_raw(x,y,z)+19052)*220)>>7;
454 // return scale16by8(inoise16_raw(x,y,z)+19052,220)<<1;
455}
456
457fl::i16 inoise16_raw(fl::u32 x, fl::u32 y)
458{
459 // Find the unit cube containing the point
460 fl::u8 X = x>>16;
461 fl::u8 Y = y>>16;
462
463 // Hash cube corner coordinates
464 fl::u8 A = NOISE_P(X)+Y;
465 fl::u8 AA = NOISE_P(A);
466 fl::u8 AB = NOISE_P(A+1);
467 fl::u8 B = NOISE_P(X+1)+Y;
468 fl::u8 BA = NOISE_P(B);
469 fl::u8 BB = NOISE_P(B+1);
470
471 // Get the relative position of the point in the cube
472 fl::u16 u = x & 0xFFFF;
473 fl::u16 v = y & 0xFFFF;
474
475 // Get a signed version of the above for the grad function
476 fl::i16 xx = (u >> 1) & 0x7FFF;
477 fl::i16 yy = (v >> 1) & 0x7FFF;
478 fl::u16 N = 0x8000L;
479
480 u = EASE16(u); v = EASE16(v);
481
482 fl::i16 X1 = LERP(grad16(NOISE_P(AA), xx, yy), grad16(NOISE_P(BA), xx - N, yy), u);
483 fl::i16 X2 = LERP(grad16(NOISE_P(AB), xx, yy-N), grad16(NOISE_P(BB), xx - N, yy - N), u);
484
485 fl::i16 ans = LERP(X1,X2,v);
486
487 return ans;
488}
489
490fl::u16 inoise16(fl::u32 x, fl::u32 y) {
491 fl::i32 ans = inoise16_raw(x,y);
492 ans = ans + 17308L;
493 fl::u32 pan = ans;
494 // pan = (ans * 242L) >> 7. That's the same as:
495 // pan = (ans * 484L) >> 8. And this way avoids a 7X four-byte shift-loop on AVR.
496 // Identical math, except for the highest bit, which we don't care about anyway,
497 // since we're returning the 'middle' 16 out of a 32-bit value anyway.
498 pan *= 484L;
499 return (pan>>8);
500
501 // return (uint32_t)(((int32_t)inoise16_raw(x,y)+(uint32_t)17308)*242)>>7;
502 // return scale16by8(inoise16_raw(x,y)+17308,242)<<1;
503}
504
505fl::i16 inoise16_raw(fl::u32 x)
506{
507 // Find the unit cube containing the point
508 fl::u8 X = x>>16;
509
510 // Hash cube corner coordinates
511 fl::u8 A = NOISE_P(X);
512 fl::u8 AA = NOISE_P(A);
513 fl::u8 B = NOISE_P(X+1);
514 fl::u8 BA = NOISE_P(B);
515
516 // Get the relative position of the point in the cube
517 fl::u16 u = x & 0xFFFF;
518
519 // Get a signed version of the above for the grad function
520 fl::i16 xx = (u >> 1) & 0x7FFF;
521 fl::u16 N = 0x8000L;
522
523 u = EASE16(u);
524
525 fl::i16 ans = LERP(grad16(NOISE_P(AA), xx), grad16(NOISE_P(BA), xx - N), u);
526
527 return ans;
528}
529
530fl::u16 inoise16(fl::u32 x) {
531 return ((fl::u32)((fl::i32)inoise16_raw(x) + 17308L)) << 1;
532}
533
534fl::i8 inoise8_raw(fl::u16 x, fl::u16 y, fl::u16 z)
535{
536 // Find the unit cube containing the point
537 fl::u8 X = x>>8;
538 fl::u8 Y = y>>8;
539 fl::u8 Z = z>>8;
540
541 // Hash cube corner coordinates
542 fl::u8 A = NOISE_P(X)+Y;
543 fl::u8 AA = NOISE_P(A)+Z;
544 fl::u8 AB = NOISE_P(A+1)+Z;
545 fl::u8 B = NOISE_P(X+1)+Y;
546 fl::u8 BA = NOISE_P(B) + Z;
547 fl::u8 BB = NOISE_P(B+1)+Z;
548
549 // Get the relative position of the point in the cube
550 fl::u8 u = x;
551 fl::u8 v = y;
552 fl::u8 w = z;
553
554 // Get a signed version of the above for the grad function
555 fl::i8 xx = ((fl::u8)(x)>>1) & 0x7F;
556 fl::i8 yy = ((fl::u8)(y)>>1) & 0x7F;
557 fl::i8 zz = ((fl::u8)(z)>>1) & 0x7F;
558 fl::u8 N = 0x80;
559
560 u = EASE8(u); v = EASE8(v); w = EASE8(w);
561
562 fl::i8 X1 = lerp7by8(grad8(NOISE_P(AA), xx, yy, zz), grad8(NOISE_P(BA), xx - N, yy, zz), u);
563 fl::i8 X2 = lerp7by8(grad8(NOISE_P(AB), xx, yy-N, zz), grad8(NOISE_P(BB), xx - N, yy - N, zz), u);
564 fl::i8 X3 = lerp7by8(grad8(NOISE_P(AA+1), xx, yy, zz-N), grad8(NOISE_P(BA+1), xx - N, yy, zz-N), u);
565 fl::i8 X4 = lerp7by8(grad8(NOISE_P(AB+1), xx, yy-N, zz-N), grad8(NOISE_P(BB+1), xx - N, yy - N, zz - N), u);
566
567 fl::i8 Y1 = lerp7by8(X1,X2,v);
568 fl::i8 Y2 = lerp7by8(X3,X4,v);
569
570 fl::i8 ans = lerp7by8(Y1,Y2,w);
571
572 return ans;
573}
574
575fl::u8 inoise8(fl::u16 x, fl::u16 y, fl::u16 z) {
576 //return scale8(76+(inoise8_raw(x,y,z)),215)<<1;
577 fl::i8 n = inoise8_raw( x, y, z); // -64..+64
578 n+= 64; // 0..128
579 fl::u8 ans = qadd8( n, n); // 0..255
580 return ans;
581}
582
583fl::i8 inoise8_raw(fl::u16 x, fl::u16 y)
584{
585 // Find the unit cube containing the point
586 fl::u8 X = x>>8;
587 fl::u8 Y = y>>8;
588
589 // Hash cube corner coordinates
590 fl::u8 A = NOISE_P(X)+Y;
591 fl::u8 AA = NOISE_P(A);
592 fl::u8 AB = NOISE_P(A+1);
593 fl::u8 B = NOISE_P(X+1)+Y;
594 fl::u8 BA = NOISE_P(B);
595 fl::u8 BB = NOISE_P(B+1);
596
597 // Get the relative position of the point in the cube
598 fl::u8 u = x;
599 fl::u8 v = y;
600
601 // Get a signed version of the above for the grad function
602 fl::i8 xx = ((fl::u8)(x)>>1) & 0x7F;
603 fl::i8 yy = ((fl::u8)(y)>>1) & 0x7F;
604 fl::u8 N = 0x80;
605
606 u = EASE8(u); v = EASE8(v);
607
608 fl::i8 X1 = lerp7by8(grad8(NOISE_P(AA), xx, yy), grad8(NOISE_P(BA), xx - N, yy), u);
609 fl::i8 X2 = lerp7by8(grad8(NOISE_P(AB), xx, yy-N), grad8(NOISE_P(BB), xx - N, yy - N), u);
610
611 fl::i8 ans = lerp7by8(X1,X2,v);
612
613 return ans;
614 // return scale8((70+(ans)),234)<<1;
615}
616
617
618
619fl::u8 inoise8(fl::u16 x, fl::u16 y) {
620 //return scale8(69+inoise8_raw(x,y),237)<<1;
621 fl::i8 n = inoise8_raw( x, y); // -64..+64
622 n+= 64; // 0..128
623 fl::u8 ans = qadd8( n, n); // 0..255
624 return ans;
625}
626
627// output range = -64 .. +64
629{
630 // Find the unit cube containing the point
631 fl::u8 X = x>>8;
632
633 // Hash cube corner coordinates
634 fl::u8 A = NOISE_P(X);
635 fl::u8 AA = NOISE_P(A);
636 fl::u8 B = NOISE_P(X+1);
637 fl::u8 BA = NOISE_P(B);
638
639 // Get the relative position of the point in the cube
640 fl::u8 u = x;
641
642 // Get a signed version of the above for the grad function
643 fl::i8 xx = ((fl::u8)(x)>>1) & 0x7F;
644 fl::u8 N = 0x80;
645
646 u = EASE8( u);
647
648 fl::i8 ans = lerp7by8(grad8(NOISE_P(AA), xx), grad8(NOISE_P(BA), xx - N), u);
649
650 return ans;
651}
652
653fl::u8 inoise8(fl::u16 x) {
654 fl::i8 n = inoise8_raw(x); //-64..+64
655 n += 64; // 0..128
656 fl::u8 ans = qadd8(n,n); // 0..255
657 return ans;
658}
659
660
661// struct q44 {
662// uint8_t i:4;
663// uint8_t f:4;
664// fl::q44(uint8_t _i, uint8_t _f) {i=_i; f=_f; }
665// };
666
667// uint32_t mul44(uint32_t v, fl::q44 mulby44) {
668// return (v *mulby44.i) + ((v * mulby44.f) >> 4);
669// }
670//
671// uint16_t mul44_16(uint16_t v, fl::q44 mulby44) {
672// return (v *mulby44.i) + ((v * mulby44.f) >> 4);
673// }
674
675void fill_raw_noise8(fl::u8 *pData, fl::u8 num_points, fl::u8 octaves, fl::u16 x, int scale, fl::u16 time) {
676 fl::u32 _xx = x;
677 fl::u32 scx = scale;
678 for(int o = 0; o < octaves; ++o) {
679 for(int i = 0,xx=_xx; i < num_points; ++i, xx+=scx) {
680 pData[i] = qadd8(pData[i],inoise8(xx,time)>>o);
681 }
682
683 _xx <<= 1;
684 scx <<= 1;
685 }
686}
687
688void fill_raw_noise16into8(fl::u8 *pData, fl::u8 num_points, fl::u8 octaves, fl::u32 x, int scale, fl::u32 time) {
689 fl::u32 _xx = x;
690 fl::u32 scx = scale;
691 for(int o = 0; o < octaves; ++o) {
692 for(int i = 0,xx=_xx; i < num_points; ++i, xx+=scx) {
693 fl::u32 accum = (inoise16(xx,time))>>o;
694 accum += (pData[i]<<8);
695 if(accum > 65535) { accum = 65535; }
696 pData[i] = accum>>8;
697 }
698
699 _xx <<= 1;
700 scx <<= 1;
701 }
702}
703
718void fill_raw_2dnoise8(fl::u8 *pData, int width, int height, fl::u8 octaves, fl::q44 freq44, fract8 amplitude, int skip, fl::u16 x, fl::i16 scalex, fl::u16 y, fl::i16 scaley, fl::u16 time) {
719 if(octaves > 1) {
720 fill_raw_2dnoise8(pData, width, height, octaves-1, freq44, amplitude, skip+1, x*freq44, freq44 * scalex, y*freq44, freq44 * scaley, time);
721 } else {
722 // amplitude is always 255 on the lowest level
723 amplitude=255;
724 }
725
726 scalex *= skip;
727 scaley *= skip;
728
729 fract8 invamp = 255-amplitude;
730 fl::u16 xx = x;
731 for(int i = 0; i < height; ++i, y+=scaley) {
732 fl::u8 *pRow = pData + (i*width);
733 xx = x;
734 for(int j = 0; j < width; ++j, xx+=scalex) {
735 fl::u8 noise_base = inoise8(xx,y,time);
736 noise_base = (0x80 & noise_base) ? (noise_base - 127) : (127 - noise_base);
737 noise_base = scale8(noise_base<<1,amplitude);
738 if(skip == 1) {
739 pRow[j] = scale8(pRow[j],invamp) + noise_base;
740 } else {
741 for(int ii = i; ii<(i+skip) && ii<height; ++ii) {
742 fl::u8 *pRow = pData + (ii*width);
743 for(int jj=j; jj<(j+skip) && jj<width; ++jj) {
744 pRow[jj] = scale8(pRow[jj],invamp) + noise_base;
745 }
746 }
747 }
748 }
749 }
750}
751
752void fill_raw_2dnoise8(fl::u8 *pData, int width, int height, fl::u8 octaves, fl::u16 x, int scalex, fl::u16 y, int scaley, fl::u16 time) {
753 fill_raw_2dnoise8(pData, width, height, octaves, fl::q44(2,0), 128, 1, x, scalex, y, scaley, time);
754}
755
756void fill_raw_2dnoise16(fl::u16 *pData, int width, int height, fl::u8 octaves, fl::q88 freq88, fract16 amplitude, int skip, fl::u32 x, fl::i32 scalex, fl::u32 y, fl::i32 scaley, fl::u32 time) {
757 if(octaves > 1) {
758 fill_raw_2dnoise16(pData, width, height, octaves-1, freq88, amplitude, skip, x *freq88 , scalex *freq88, y * freq88, scaley * freq88, time);
759 } else {
760 // amplitude is always 255 on the lowest level
761 amplitude=65535;
762 }
763
764 scalex *= skip;
765 scaley *= skip;
766 fract16 invamp = 65535-amplitude;
767 for(int i = 0; i < height; i+=skip, y+=scaley) {
768 fl::u16 *pRow = pData + (i*width);
769 for(int j = 0,xx=x; j < width; j+=skip, xx+=scalex) {
770 fl::u16 noise_base = inoise16(xx,y,time);
771 noise_base = (0x8000 & noise_base) ? noise_base - (32767) : 32767 - noise_base;
772 noise_base = scale16(noise_base<<1, amplitude);
773 if(skip==1) {
774 pRow[j] = scale16(pRow[j],invamp) + noise_base;
775 } else {
776 for(int ii = i; ii<(i+skip) && ii<height; ++ii) {
777 fl::u16 *pRow = pData + (ii*width);
778 for(int jj=j; jj<(j+skip) && jj<width; ++jj) {
779 pRow[jj] = scale16(pRow[jj],invamp) + noise_base;
780 }
781 }
782 }
783 }
784 }
785}
786
789fl::i32 nmin=11111110;
792fl::i32 nmax=0;
793
794void fill_raw_2dnoise16into8(fl::u8 *pData, int width, int height, fl::u8 octaves, fl::q44 freq44, fract8 amplitude, int skip, fl::u32 x, fl::i32 scalex, fl::u32 y, fl::i32 scaley, fl::u32 time) {
795 if(octaves > 1) {
796 fill_raw_2dnoise16into8(pData, width, height, octaves-1, freq44, amplitude, skip+1, x*freq44, scalex *freq44, y*freq44, scaley * freq44, time);
797 } else {
798 // amplitude is always 255 on the lowest level
799 amplitude=255;
800 }
801
802 scalex *= skip;
803 scaley *= skip;
804 fl::u32 xx;
805 fract8 invamp = 255-amplitude;
806 for(int i = 0; i < height; i+=skip, y+=scaley) {
807 fl::u8 *pRow = pData + (i*width);
808 xx = x;
809 for(int j = 0; j < width; j+=skip, xx+=scalex) {
810 fl::u16 noise_base = inoise16(xx,y,time);
811 noise_base = (0x8000 & noise_base) ? noise_base - (32767) : 32767 - noise_base;
812 noise_base = scale8(noise_base>>7,amplitude);
813 if(skip==1) {
814 pRow[j] = qadd8(scale8(pRow[j],invamp),noise_base);
815 } else {
816 for(int ii = i; ii<(i+skip) && ii<height; ++ii) {
817 fl::u8 *pRow = pData + (ii*width);
818 for(int jj=j; jj<(j+skip) && jj<width; ++jj) {
819 pRow[jj] = scale8(pRow[jj],invamp) + noise_base;
820 }
821 }
822 }
823 }
824 }
825}
826
827void fill_raw_2dnoise16into8(fl::u8 *pData, int width, int height, fl::u8 octaves, fl::u32 x, int scalex, fl::u32 y, int scaley, fl::u32 time) {
828 fill_raw_2dnoise16into8(pData, width, height, octaves, fl::q44(2,0), 171, 1, x, scalex, y, scaley, time);
829}
830
831void fill_noise8(CRGB *leds, int num_leds,
832 fl::u8 octaves, fl::u16 x, int scale,
833 fl::u8 hue_octaves, fl::u16 hue_x, int hue_scale,
834 fl::u16 time) {
835
836 if (num_leds <= 0) return;
837
838 for (int j = 0; j < num_leds; j += 255) {
839 const int LedsRemaining = num_leds - j;
840 const int LedsPer = LedsRemaining > 255 ? 255 : LedsRemaining; // limit to 255 max
841
842 if (LedsPer <= 0) continue;
843 FASTLED_STACK_ARRAY(fl::u8, V, LedsPer);
844 FASTLED_STACK_ARRAY(fl::u8, H, LedsPer);
845
846 fl::memset(V, 0, LedsPer);
847 fl::memset(H, 0, LedsPer);
848
849 fill_raw_noise8(V, LedsPer, octaves, x, scale, time);
850 fill_raw_noise8(H, LedsPer, hue_octaves, hue_x, hue_scale, time);
851
852 for (int i = 0; i < LedsPer; ++i) {
853 leds[i + j] = CHSV(H[i], 255, V[i]);
854 }
855 }
856}
857
858void fill_noise16(CRGB *leds, int num_leds,
859 fl::u8 octaves, fl::u16 x, int scale,
860 fl::u8 hue_octaves, fl::u16 hue_x, int hue_scale,
861 fl::u16 time, fl::u8 hue_shift) {
862
863 if (num_leds <= 0) return;
864
865 for (int j = 0; j < num_leds; j += 255) {
866 const int LedsRemaining = num_leds - j;
867 const int LedsPer = LedsRemaining > 255 ? 255 : LedsRemaining; // limit to 255 max
868 if (LedsPer <= 0) continue;
869 FASTLED_STACK_ARRAY(fl::u8, V, LedsPer);
870 FASTLED_STACK_ARRAY(fl::u8, H, LedsPer);
871
872 fl::memset(V, 0, LedsPer);
873 fl::memset(H, 0, LedsPer);
874
875 fill_raw_noise16into8(V, LedsPer, octaves, x, scale, time);
876 fill_raw_noise8(H, LedsPer, hue_octaves, hue_x, hue_scale, time);
877
878 for (int i = 0; i < LedsPer; ++i) {
879 leds[i + j] = CHSV(H[i] + hue_shift, 255, V[i]);
880 }
881 }
882}
883
884void fill_2dnoise8(CRGB *leds, int width, int height, bool serpentine,
885 fl::u8 octaves, fl::u16 x, int xscale, fl::u16 y, int yscale, fl::u16 time,
886 fl::u8 hue_octaves, fl::u16 hue_x, int hue_xscale, fl::u16 hue_y, fl::u16 hue_yscale,fl::u16 hue_time,bool blend) {
887 const size_t array_size = (size_t)height * width;
888 if (array_size <= 0) return;
889 FASTLED_STACK_ARRAY(fl::u8, V, array_size);
890 FASTLED_STACK_ARRAY(fl::u8, H, array_size);
891
892 fl::memset(V,0,height*width);
893 fl::memset(H,0,height*width);
894
895 fill_raw_2dnoise8((fl::u8*)V,width,height,octaves,x,xscale,y,yscale,time);
896 fill_raw_2dnoise8((fl::u8*)H,width,height,hue_octaves,hue_x,hue_xscale,hue_y,hue_yscale,hue_time);
897
898 int w1 = width-1;
899 int h1 = height-1;
900 for(int i = 0; i < height; ++i) {
901 int wb = i*width;
902 for(int j = 0; j < width; ++j) {
903 CRGB led(CHSV(H[(h1-i)*width + (w1-j)], 255, V[i*width + j]));
904
905 int pos = j;
906 if(serpentine && (i & 0x1)) {
907 pos = w1-j;
908 }
909
910 if(blend) {
911 // Safer blending using shift-and-add to avoid ambiguous operator+
912 leds[wb+pos] >>= 1;
913 leds[wb+pos] += (led >>= 1);
914 } else {
915 leds[wb+pos] = led;
916 }
917 }
918 }
919}
920
921
922void fill_2dnoise16(CRGB *leds, int width, int height, bool serpentine,
923 fl::u8 octaves, fl::u32 x, int xscale, fl::u32 y, int yscale, fl::u32 time,
924 fl::u8 hue_octaves, fl::u16 hue_x, int hue_xscale, fl::u16 hue_y, fl::u16 hue_yscale,fl::u16 hue_time, bool blend, fl::u16 hue_shift) {
925
926 FASTLED_STACK_ARRAY(fl::u8, V, height*width);
927 FASTLED_STACK_ARRAY(fl::u8, H, height*width);
928
929 fl::memset(V,0,height*width);
930 fl::memset(H,0,height*width);
931
932 fill_raw_2dnoise16into8((fl::u8*)V,width,height,octaves,fl::q44(2,0),171,1,x,xscale,y,yscale,time);
933 // fill_raw_2dnoise16into8((uint8_t*)V,width,height,octaves,x,xscale,y,yscale,time);
934 // fill_raw_2dnoise8((uint8_t*)V,width,height,hue_octaves,x,xscale,y,yscale,time);
935 fill_raw_2dnoise8((fl::u8*)H,width,height,hue_octaves,hue_x,hue_xscale,hue_y,hue_yscale,hue_time);
936
937
938 int w1 = width-1;
939 int h1 = height-1;
940 hue_shift >>= 8;
941
942 for(int i = 0; i < height; ++i) {
943 int wb = i*width;
944 for(int j = 0; j < width; ++j) {
945 CRGB led(CHSV(hue_shift + (H[(h1-i)*width + (w1-j)]), 196, V[i*width + j]));
946
947 int pos = j;
948 if(serpentine && (i & 0x1)) {
949 pos = w1-j;
950 }
951
952 if(blend) {
953 leds[wb+pos] >>= 1; leds[wb+pos] += (led>>=1);
954 } else {
955 leds[wb+pos] = led;
956 }
957 }
958 }
959}
960
fl::CRGB leds[NUM_LEDS]
int y
Definition simple.h:93
int x
Definition simple.h:92
uint8_t pos
Definition Blur.ino:11
uint32_t z[NUM_LAYERS]
Definition Fire2023.h:93
fl::UISlider scale("Scale", 4,.1, 4,.1)
uint8_t hue_octaves
int yscale
uint8_t octaves
int xscale
int hue_scale
uint32_t hue_time
static const int H
Definition PerfDisc.ino:20
#define FASTLED_STACK_ARRAY(TYPE, NAME, SIZE)
Stack-allocated array with automatic zero-initialization.
Definition alloca.h:32
CRGB blend(const CRGB &p1, const CRGB &p2, fract8 amountOfP2)
fl::size_t size_t
Definition cstddef.h:61
static uint32_t t
Definition Luminova.h:55
#define FL_PROGMEM
PROGMEM keyword for storage.
Internal FastLED header for implementation files.
qfx< u16, 8, 8 > q88
A 8.8 integer (8 bits integer, 8 bits fraction)
Definition qfx.h:49
qfx< u8, 4, 4 > q44
A 4.4 integer (4 bits integer, 4 bits fraction)
Definition qfx.h:45
fl::hsv8 CHSV
Definition chsv.h:11
fl::CRGB CRGB
Definition crgb.h:25
void fill_2dnoise8(CRGB *leds, int width, int height, bool serpentine, fl::u8 octaves, fl::u16 x, int xscale, fl::u16 y, int yscale, fl::u16 time, fl::u8 hue_octaves, fl::u16 hue_x, int hue_xscale, fl::u16 hue_y, fl::u16 hue_yscale, fl::u16 hue_time, bool blend)
Fill an LED matrix with random colors, using 8-bit noise.
void fill_noise8(CRGB *leds, int num_leds, fl::u8 octaves, fl::u16 x, int scale, fl::u8 hue_octaves, fl::u16 hue_x, int hue_scale, fl::u16 time)
Fill an LED array with random colors, using 8-bit noise.
void fill_raw_2dnoise16into8(fl::u8 *pData, int width, int height, fl::u8 octaves, fl::q44 freq44, fract8 amplitude, int skip, fl::u32 x, fl::i32 scalex, fl::u32 y, fl::i32 scaley, fl::u32 time)
Fill a 2D 8-bit buffer with noise, using inoise16()
void fill_raw_2dnoise16(fl::u16 *pData, int width, int height, fl::u8 octaves, fl::q88 freq88, fract16 amplitude, int skip, fl::u32 x, fl::i32 scalex, fl::u32 y, fl::i32 scaley, fl::u32 time)
Fill a 2D 16-bit buffer with noise, using inoise16()
void fill_raw_noise8(fl::u8 *pData, fl::u8 num_points, fl::u8 octaves, fl::u16 x, int scale, fl::u16 time)
Fill a 1D 8-bit buffer with noise, using inoise8()
void fill_noise16(CRGB *leds, int num_leds, fl::u8 octaves, fl::u16 x, int scale, fl::u8 hue_octaves, fl::u16 hue_x, int hue_scale, fl::u16 time, fl::u8 hue_shift)
Fill an LED array with random colors, using 16-bit noise.
void fill_raw_2dnoise8(fl::u8 *pData, int width, int height, fl::u8 octaves, fl::q44 freq44, fract8 amplitude, int skip, fl::u16 x, fl::i16 scalex, fl::u16 y, fl::i16 scaley, fl::u16 time)
Fill a 2D 8-bit buffer with noise, using inoise8()
void fill_raw_noise16into8(fl::u8 *pData, fl::u8 num_points, fl::u8 octaves, fl::u32 x, int scale, fl::u32 time)
Fill a 1D 8-bit buffer with noise, using inoise16()
void fill_2dnoise16(CRGB *leds, int width, int height, bool serpentine, fl::u8 octaves, fl::u32 x, int xscale, fl::u32 y, int yscale, fl::u32 time, fl::u8 hue_octaves, fl::u16 hue_x, int hue_xscale, fl::u16 hue_y, fl::u16 hue_yscale, fl::u16 hue_time, bool blend, fl::u16 hue_shift)
Fill an LED matrix with random colors, using 16-bit noise.
fl::i8 inoise8_raw(fl::u16 x, fl::u16 y, fl::u16 z)
fl::u16 inoise16(fl::u32 x, fl::u32 y, fl::u32 z, fl::u32 t)
fl::i16 inoise16_raw(fl::u32 x, fl::u32 y, fl::u32 z)
fl::u8 inoise8(fl::u16 x, fl::u16 y, fl::u16 z)
u16 fract16
ANSI: unsigned _Fract.
Definition s16x16x4.h:171
u8 fract8
Fixed-Point Fractional Types.
Definition s16x16x4.h:161
unsigned char u8
Definition stdint.h:131
void * memset(void *s, int c, size_t n) FL_NOEXCEPT
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
signed char i8
Definition stdint.h:130
static fl::u8 const p[]
Definition noise.cpp.hpp:37
static fl::i8 selectBasedOnHashBit(fl::u8 hash, fl::u8 bitnumber, fl::i8 a, fl::i8 b)
fl::i32 nmax
Unused.
fl::i32 nmin
Unused.
static fl::i16 grad16(fl::u8 hash, fl::i16 x, fl::i16 y, fl::i16 z)
static fl::i8 grad8(fl::u8 hash, fl::i8 x, fl::i8 y, fl::i8 z)
static fl::i8 lerp7by8(fl::i8 a, fl::i8 b, fract8 frac)
#define NOISE_P(x)
Reads a single byte from the p array.
Definition noise.cpp.hpp:32
#define FL_DISABLE_WARNING(warning)
#define FL_DISABLE_WARNING_PUSH
#define FL_DISABLE_WARNING_POP