FastLED 3.9.15
Loading...
Searching...
No Matches
FxWater.h
Go to the documentation of this file.
1
10
11// Author: sutaburosu
12
13// based on https://web.archive.org/web/20160418004149/http://freespace.virgin.net/hugo.elias/graphics/x_water.htm
14
15#include <FastLED.h>
16#include "Arduino.h"
17#include "fl/math/xymap.h"
18
19
20#define WIDTH 32
21#define HEIGHT 32
22#define NUM_LEDS ((WIDTH) * (HEIGHT))
24
25// the water needs 2 arrays each slightly bigger than the screen
26#define WATERWIDTH (WIDTH + 2)
27#define WATERHEIGHT (HEIGHT + 2)
29
30void wu_water(uint8_t * const buf, uint16_t x, uint16_t y, uint8_t bright);
31void process_water(uint8_t * src, uint8_t * dst) ;
32
33void setup() {
34 Serial.begin(115200);
35 FastLED.addLeds<NEOPIXEL, 2>(leds, NUM_LEDS).setScreenMap(WIDTH, HEIGHT);
36}
37
38// from: https://github.com/FastLED/FastLED/pull/202
39fl::CRGB MyColorFromPaletteExtended(const fl::CRGBPalette16& pal, uint16_t index, uint8_t brightness, TBlendType blendType) {
40 // Extract the four most significant bits of the index as a palette index.
41 uint8_t index_4bit = (index >> 12);
42 // Calculate the 8-bit offset from the palette index.
43 uint8_t offset = (uint8_t)(index >> 4);
44 // Get the palette entry from the 4-bit index
45 const fl::CRGB* entry = &(pal[0]) + index_4bit;
46 uint8_t red1 = entry->red;
47 uint8_t green1 = entry->green;
48 uint8_t blue1 = entry->blue;
49
50 uint8_t blend = offset && (blendType != NOBLEND);
51 if (blend) {
52 if (index_4bit == 15) {
53 entry = &(pal[0]);
54 } else {
55 entry++;
56 }
57
58 // Calculate the scaling factor and scaled values for the lower palette value.
59 uint8_t f1 = 255 - offset;
60 red1 = scale8_LEAVING_R1_DIRTY(red1, f1);
61 green1 = scale8_LEAVING_R1_DIRTY(green1, f1);
62 blue1 = scale8_LEAVING_R1_DIRTY(blue1, f1);
63
64 // Calculate the scaled values for the neighbouring palette value.
65 uint8_t red2 = entry->red;
66 uint8_t green2 = entry->green;
67 uint8_t blue2 = entry->blue;
68 red2 = scale8_LEAVING_R1_DIRTY(red2, offset);
69 green2 = scale8_LEAVING_R1_DIRTY(green2, offset);
70 blue2 = scale8_LEAVING_R1_DIRTY(blue2, offset);
71 cleanup_R1();
72
73 // These sums can't overflow, so no qadd8 needed.
74 red1 += red2;
75 green1 += green2;
76 blue1 += blue2;
77 }
78 if (brightness != 255) {
79 // nscale8x3_video(red1, green1, blue1, brightness);
80 nscale8x3(red1, green1, blue1, brightness);
81 }
82 return fl::CRGB(red1, green1, blue1);
83}
84
85// Rectangular grid
87
88// map X & Y coordinates onto a horizontal serpentine matrix layout
89uint16_t XY(uint8_t x, uint8_t y) {
90 return xyMap.mapToIndex(x, y);
91}
92
93void loop() {
94 // swap the src/dest buffers on each frame
95 static uint8_t buffer = 0; // okay static in header
96 uint8_t * const bufA = &water[buffer][0];
97 buffer = (buffer + 1) % 2;
98 uint8_t * const bufB = &water[buffer][0];
99
100 // add a moving stimulus
101 wu_water(bufA, beatsin16(13, 256, HEIGHT * 256), beatsin16(7, 256, WIDTH * 256), beatsin8(160, 64, 255));
102
103 // animate the water
104 process_water(bufA, bufB);
105
106
107 // display the water effect on the LEDs
108 uint8_t * input = bufB + WATERWIDTH - 1;
109 static uint16_t pal_offset = 0; // okay static in header
110 pal_offset += 256;
111 for (uint8_t y = 0; y < HEIGHT; y++) {
112 input += 2;
113 for (uint8_t x = 0; x < WIDTH; x++) {
114 leds[XY(x, y)] = MyColorFromPaletteExtended(RainbowColors_p, pal_offset + (*input++ << 8), 255, LINEARBLEND);
115 }
116 }
117 FastLED.show();
118}
119
120void process_water(uint8_t * src, uint8_t * dst) {
121 src += WATERWIDTH - 1;
122 dst += WATERWIDTH - 1;
123 for (uint8_t y = 1; y < WATERHEIGHT - 1; y++) {
124 src += 2; dst += 2;
125 for (uint8_t x = 1; x < WATERWIDTH - 1; x++) {
126 uint16_t t = src[-1] + src[1] + src[-WATERWIDTH] + src[WATERWIDTH];
127 t >>= 1;
128 if (dst[0] < t)
129 dst[0] = t - dst[0];
130 else
131 dst[0] = 0;
132
133 dst[0] -= dst[0] >> 6;
134 src++; dst++;
135 }
136 }
137}
138
139// draw a blob of 4 pixels with their relative brightnesses conveying sub-pixel positioning
140void wu_water(uint8_t * const buf, uint16_t x, uint16_t y, uint8_t bright) {
141 // extract the fractional parts and derive their inverses
142 uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
143 // calculate the intensities for each affected pixel
144 #define WU_WEIGHT(a, b) ((uint8_t)(((a) * (b) + (a) + (b)) >> 8))
145 uint8_t wu[4] = {WU_WEIGHT(ix, iy), WU_WEIGHT(xx, iy),
146 WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)
147 };
148 #undef WU_WEIGHT
149 // multiply the intensities by the colour, and saturating-add them to the pixels
150 for (uint8_t i = 0; i < 4; i++) {
151 uint8_t local_x = (x >> 8) + (i & 1);
152 uint8_t local_y = (y >> 8) + ((i >> 1) & 1);
153 uint16_t xy = WATERWIDTH * local_y + local_x;
154 if (xy >= WATERWIDTH * WATERHEIGHT) continue;
155 uint16_t this_bright = bright * wu[i];
156 buf[xy] = qadd8(buf[xy], this_bright >> 8);
157 }
158}
fl::XYMap xyMap
#define NUM_LEDS
fl::CRGB leds[NUM_LEDS]
fl::UISlider brightness("Brightness", BRIGHTNESS, 0, 255)
int y
Definition simple.h:93
int x
Definition simple.h:92
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
void process_water(uint8_t *src, uint8_t *dst)
Definition FxWater.h:120
#define WATERHEIGHT
Definition FxWater.h:27
void setup()
Definition FxWater.h:33
fl::CRGB MyColorFromPaletteExtended(const fl::CRGBPalette16 &pal, uint16_t index, uint8_t brightness, TBlendType blendType)
Definition FxWater.h:39
uint16_t XY(uint8_t x, uint8_t y)
Definition FxWater.h:89
void wu_water(uint8_t *const buf, uint16_t x, uint16_t y, uint8_t bright)
Definition FxWater.h:140
#define WU_WEIGHT(a, b)
uint8_t water[2][WATERWIDTH *WATERHEIGHT]
Definition FxWater.h:28
#define WATERWIDTH
Definition FxWater.h:26
void loop()
Definition FxWater.h:93
unsigned int xy(unsigned int x, unsigned int y)
#define WIDTH
#define HEIGHT
CRGB blend(const CRGB &p1, const CRGB &p2, fract8 amountOfP2)
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
static uint32_t t
Definition Luminova.h:55
const TProgmemRGBPalette16 RainbowColors_p
HSV Rainbow.
LIB8STATIC u8 beatsin8(accum88 beats_per_minute, u8 lowest=0, u8 highest=255, u32 timebase=0, u8 phase_offset=0) FL_NOEXCEPT
Generates an 8-bit sine wave at a given BPM that oscillates within a given range.
Definition beat.h:105
LIB8STATIC u16 beatsin16(accum88 beats_per_minute, u16 lowest=0, u16 highest=65535, u32 timebase=0, u16 phase_offset=0) FL_NOEXCEPT
Generates a 16-bit sine wave at a given BPM that oscillates within a given range.
Definition beat.h:84
fl::CRGB CRGB
Definition video.h:15
Representation of an 8-bit RGB pixel (Red, Green, Blue)
Definition crgb.h:38
#define Serial
Definition serial.h:304