262 lines
7.7 KiB
C++
262 lines
7.7 KiB
C++
|
|
|
|
/*
|
|
This demo is best viewed using the FastLED compiler.
|
|
Install: pip install fastled
|
|
Run: fastled <this sketch directory>
|
|
This will compile and preview the sketch in the browser, and enable
|
|
all the UI elements you see below.
|
|
*/
|
|
|
|
#include <Arduino.h>
|
|
#include <FastLED.h>
|
|
|
|
#include "fl/math_macros.h"
|
|
#include "fl/time_alpha.h"
|
|
#include "fl/ui.h"
|
|
#include "fx/2d/blend.h"
|
|
#include "fx/2d/wave.h"
|
|
|
|
using namespace fl;
|
|
|
|
#define HEIGHT 64
|
|
#define WIDTH 64
|
|
#define NUM_LEDS ((WIDTH) * (HEIGHT))
|
|
#define IS_SERPINTINE true
|
|
|
|
CRGB leds[NUM_LEDS];
|
|
|
|
UITitle title("FxWave2D Demo");
|
|
UIDescription description("Advanced layered and blended wave effects.");
|
|
|
|
UIButton button("Trigger");
|
|
UIButton buttonFancy("Trigger Fancy");
|
|
UICheckbox autoTrigger("Auto Trigger", true);
|
|
UISlider triggerSpeed("Trigger Speed", .5f, 0.0f, 1.0f, 0.01f);
|
|
UICheckbox easeModeSqrt("Ease Mode Sqrt", false);
|
|
UISlider blurAmount("Global Blur Amount", 0, 0, 172, 1);
|
|
UISlider blurPasses("Global Blur Passes", 1, 1, 10, 1);
|
|
UISlider superSample("SuperSampleExponent", 1.f, 0.f, 3.f, 1.f);
|
|
|
|
UISlider speedUpper("Wave Upper: Speed", 0.12f, 0.0f, 1.0f);
|
|
UISlider dampeningUpper("Wave Upper: Dampening", 8.9f, 0.0f, 20.0f, 0.1f);
|
|
UICheckbox halfDuplexUpper("Wave Upper: Half Duplex", true);
|
|
UISlider blurAmountUpper("Wave Upper: Blur Amount", 95, 0, 172, 1);
|
|
UISlider blurPassesUpper("Wave Upper: Blur Passes", 1, 1, 10, 1);
|
|
|
|
UISlider speedLower("Wave Lower: Speed", 0.26f, 0.0f, 1.0f);
|
|
UISlider dampeningLower("Wave Lower: Dampening", 9.0f, 0.0f, 20.0f, 0.1f);
|
|
UICheckbox halfDuplexLower("Wave Lower: Half Duplex", true);
|
|
UISlider blurAmountLower("Wave Lower: Blur Amount", 0, 0, 172, 1);
|
|
UISlider blurPassesLower("Wave Lower: Blur Passes", 1, 1, 10, 1);
|
|
|
|
UISlider fancySpeed("Fancy Speed", 796, 0, 1000, 1);
|
|
UISlider fancyIntensity("Fancy Intensity", 32, 1, 255, 1);
|
|
UISlider fancyParticleSpan("Fancy Particle Span", 0.06f, 0.01f, 0.2f, 0.01f);
|
|
|
|
|
|
DEFINE_GRADIENT_PALETTE(electricBlueFirePal){
|
|
0, 0, 0, 0, // Black
|
|
32, 0, 0, 70, // Dark blue
|
|
128, 20, 57, 255, // Electric blue
|
|
255, 255, 255, 255 // White
|
|
};
|
|
|
|
DEFINE_GRADIENT_PALETTE(electricGreenFirePal){
|
|
0, 0, 0, 0, // black
|
|
8, 128, 64, 64, // green
|
|
16, 255, 222, 222, // red
|
|
64, 255, 255, 255, // white
|
|
255, 255, 255, 255 // white
|
|
};
|
|
|
|
XYMap xyMap(WIDTH, HEIGHT, IS_SERPINTINE);
|
|
XYMap xyRect(WIDTH, HEIGHT, false);
|
|
WaveFx
|
|
waveFxLower(xyRect,
|
|
WaveFx::Args{
|
|
.factor = SUPER_SAMPLE_4X,
|
|
.half_duplex = true,
|
|
.speed = 0.18f,
|
|
.dampening = 9.0f,
|
|
.crgbMap = WaveCrgbGradientMapPtr::New(electricBlueFirePal),
|
|
});
|
|
|
|
WaveFx waveFxUpper(
|
|
xyRect, WaveFx::Args{
|
|
.factor = SUPER_SAMPLE_4X,
|
|
.half_duplex = true,
|
|
.speed = 0.25f,
|
|
.dampening = 3.0f,
|
|
.crgbMap = WaveCrgbGradientMapPtr::New(electricGreenFirePal),
|
|
});
|
|
|
|
Blend2d fxBlend(xyMap);
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
auto screenmap = xyMap.toScreenMap();
|
|
screenmap.setDiameter(.2);
|
|
FastLED.addLeds<NEOPIXEL, 2>(leds, NUM_LEDS).setScreenMap(screenmap);
|
|
fxBlend.add(waveFxLower);
|
|
fxBlend.add(waveFxUpper);
|
|
}
|
|
|
|
SuperSample getSuperSample() {
|
|
switch (int(superSample)) {
|
|
case 0:
|
|
return SuperSample::SUPER_SAMPLE_NONE;
|
|
case 1:
|
|
return SuperSample::SUPER_SAMPLE_2X;
|
|
case 2:
|
|
return SuperSample::SUPER_SAMPLE_4X;
|
|
case 3:
|
|
return SuperSample::SUPER_SAMPLE_8X;
|
|
default:
|
|
return SuperSample::SUPER_SAMPLE_NONE;
|
|
}
|
|
}
|
|
|
|
void triggerRipple() {
|
|
float perc = .15f;
|
|
uint8_t min_x = perc * WIDTH;
|
|
uint8_t max_x = (1 - perc) * WIDTH;
|
|
uint8_t min_y = perc * HEIGHT;
|
|
uint8_t max_y = (1 - perc) * HEIGHT;
|
|
int x = random(min_x, max_x);
|
|
int y = random(min_y, max_y);
|
|
waveFxLower.setf(x, y, 1);
|
|
waveFxUpper.setf(x, y, 1);
|
|
}
|
|
|
|
void applyFancyEffect(uint32_t now, bool button_active) {
|
|
uint32_t total = map(fancySpeed.as<uint32_t>(), 0, fancySpeed.max_value(), 1000, 100);
|
|
static TimeRamp pointTransition = TimeRamp(total, 0, 0);
|
|
|
|
if (button_active) {
|
|
pointTransition.trigger(now, total, 0, 0);
|
|
}
|
|
|
|
if (!pointTransition.isActive(now)) {
|
|
// no need to draw
|
|
return;
|
|
}
|
|
int mid_x = WIDTH / 2;
|
|
int mid_y = HEIGHT / 2;
|
|
// now make a cross
|
|
int amount = WIDTH / 2;
|
|
int start_x = mid_x - amount;
|
|
int end_x = mid_x + amount;
|
|
int start_y = mid_y - amount;
|
|
int end_y = mid_y + amount;
|
|
int curr_alpha = pointTransition.update(now);
|
|
int left_x = map(curr_alpha, 0, 255, mid_x, start_x);
|
|
int down_y = map(curr_alpha, 0, 255, mid_y, start_y);
|
|
int right_x = map(curr_alpha, 0, 255, mid_x, end_x);
|
|
int up_y = map(curr_alpha, 0, 255, mid_y, end_y);
|
|
|
|
float curr_alpha_f = curr_alpha / 255.0f;
|
|
|
|
float valuef = (1.0f - curr_alpha_f) * fancyIntensity.value() / 255.0f;
|
|
|
|
int span = fancyParticleSpan.value() * WIDTH;
|
|
for (int x = left_x - span; x < left_x + span; x++) {
|
|
waveFxLower.addf(x, mid_y, valuef);
|
|
waveFxUpper.addf(x, mid_y, valuef);
|
|
}
|
|
|
|
for (int x = right_x - span; x < right_x + span; x++) {
|
|
waveFxLower.addf(x, mid_y, valuef);
|
|
waveFxUpper.addf(x, mid_y, valuef);
|
|
}
|
|
|
|
for (int y = down_y - span; y < down_y + span; y++) {
|
|
waveFxLower.addf(mid_x, y, valuef);
|
|
waveFxUpper.addf(mid_x, y, valuef);
|
|
}
|
|
|
|
for (int y = up_y - span; y < up_y + span; y++) {
|
|
waveFxLower.addf(mid_x, y, valuef);
|
|
waveFxUpper.addf(mid_x, y, valuef);
|
|
}
|
|
}
|
|
|
|
struct ui_state {
|
|
bool button = false;
|
|
bool bigButton = false;
|
|
};
|
|
|
|
ui_state ui() {
|
|
U8EasingFunction easeMode = easeModeSqrt
|
|
? U8EasingFunction::WAVE_U8_MODE_SQRT
|
|
: U8EasingFunction::WAVE_U8_MODE_LINEAR;
|
|
waveFxLower.setSpeed(speedLower);
|
|
waveFxLower.setDampening(dampeningLower);
|
|
waveFxLower.setHalfDuplex(halfDuplexLower);
|
|
waveFxLower.setSuperSample(getSuperSample());
|
|
waveFxLower.setEasingMode(easeMode);
|
|
|
|
waveFxUpper.setSpeed(speedUpper);
|
|
waveFxUpper.setDampening(dampeningUpper);
|
|
waveFxUpper.setHalfDuplex(halfDuplexUpper);
|
|
waveFxUpper.setSuperSample(getSuperSample());
|
|
waveFxUpper.setEasingMode(easeMode);
|
|
fxBlend.setGlobalBlurAmount(blurAmount);
|
|
fxBlend.setGlobalBlurPasses(blurPasses);
|
|
|
|
Blend2dParams lower_params = {
|
|
.blur_amount = blurAmountLower,
|
|
.blur_passes = blurPassesLower,
|
|
};
|
|
|
|
Blend2dParams upper_params = {
|
|
.blur_amount = blurAmountUpper,
|
|
.blur_passes = blurPassesUpper,
|
|
};
|
|
|
|
fxBlend.setParams(waveFxLower, lower_params);
|
|
fxBlend.setParams(waveFxUpper, upper_params);
|
|
ui_state state{
|
|
.button = button,
|
|
.bigButton = buttonFancy,
|
|
};
|
|
return state;
|
|
}
|
|
|
|
void processAutoTrigger(uint32_t now) {
|
|
static uint32_t nextTrigger = 0;
|
|
uint32_t trigger_delta = nextTrigger - now;
|
|
if (trigger_delta > 10000) {
|
|
// rolled over!
|
|
trigger_delta = 0;
|
|
}
|
|
if (autoTrigger) {
|
|
if (now >= nextTrigger) {
|
|
triggerRipple();
|
|
float speed = 1.0f - triggerSpeed.value();
|
|
uint32_t min_rand = 400 * speed;
|
|
uint32_t max_rand = 2000 * speed;
|
|
|
|
uint32_t min = MIN(min_rand, max_rand);
|
|
uint32_t max = MAX(min_rand, max_rand);
|
|
if (min == max) {
|
|
max += 1;
|
|
}
|
|
nextTrigger = now + random(min, max);
|
|
}
|
|
}
|
|
}
|
|
|
|
void loop() {
|
|
// Your code here
|
|
uint32_t now = millis();
|
|
ui_state state = ui();
|
|
if (state.button) {
|
|
triggerRipple();
|
|
}
|
|
applyFancyEffect(now, state.bigButton);
|
|
processAutoTrigger(now);
|
|
Fx::DrawContext ctx(now, leds);
|
|
fxBlend.draw(ctx);
|
|
FastLED.show();
|
|
} |