imported from "final" folder
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
#include "FastLED.h"
|
||||
|
||||
|
||||
#include "fl/sketch_macros.h"
|
||||
|
||||
#if !SKETCH_HAS_LOTS_OF_MEMORY
|
||||
// Platform does not have enough memory
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
#else
|
||||
|
||||
#include "curr.h"
|
||||
|
||||
#endif
|
||||
811
.pio/libdeps/esp01_1m/FastLED/examples/FestivalStick/curr.h
Normal file
811
.pio/libdeps/esp01_1m/FastLED/examples/FestivalStick/curr.h
Normal file
@@ -0,0 +1,811 @@
|
||||
/*
|
||||
Festival Stick - Corkscrew LED Mapping Demo
|
||||
|
||||
This example demonstrates proper corkscrew LED mapping for a festival stick
|
||||
(19+ turns, 288 LEDs) using the new Corkscrew ScreenMap functionality.
|
||||
|
||||
Key Features:
|
||||
- Uses Corkscrew.toScreenMap() for accurate web interface visualization
|
||||
- Draws patterns into a rectangular grid (frameBuffer)
|
||||
- Maps the rectangular grid to the corkscrew LED positions using readFrom()
|
||||
- Supports both noise patterns and manual LED positioning
|
||||
- Proper color boost and brightness controls
|
||||
|
||||
Workflow:
|
||||
1. Draw patterns into frameBuffer (rectangular grid for easy 2D drawing)
|
||||
2. Use corkscrew.readFrom(frameBuffer) to map grid to corkscrew LED positions
|
||||
3. Display the corkscrew buffer directly via FastLED
|
||||
4. Web interface shows actual corkscrew spiral shape via ScreenMap
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "FastLED.h"
|
||||
#include "fl/compiler_control.h"
|
||||
|
||||
|
||||
#include "fl/assert.h"
|
||||
#include "fl/corkscrew.h"
|
||||
#include "fl/grid.h"
|
||||
#include "fl/leds.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/sstream.h"
|
||||
#include "fl/warn.h"
|
||||
#include "noise.h"
|
||||
#include "fl/array.h"
|
||||
#include "fx/2d/wave.h"
|
||||
#include "fx/2d/blend.h"
|
||||
#include "fx/fx_engine.h"
|
||||
#include "fx/2d/animartrix.hpp"
|
||||
|
||||
// #include "vec3.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
|
||||
|
||||
#ifndef PIN_DATA
|
||||
#define PIN_DATA 1 // Universally available pin
|
||||
#endif
|
||||
|
||||
#ifndef PIN_CLOCK
|
||||
#define PIN_CLOCK 2 // Universally available pin
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef TEST
|
||||
#define NUM_LEDS 4
|
||||
#define CORKSCREW_TURNS 2 // Default to 19 turns
|
||||
#else
|
||||
#define NUM_LEDS 288
|
||||
#define CORKSCREW_TURNS 19.25 // Default to 19 turns
|
||||
#endif
|
||||
|
||||
// #define CM_BETWEEN_LEDS 1.0 // 1cm between LEDs
|
||||
// #define CM_LED_DIAMETER 0.5 // 0.5cm LED diameter
|
||||
|
||||
UITitle festivalStickTitle("Festival Stick - Advanced Version");
|
||||
UIDescription festivalStickDescription(
|
||||
"# Festival Stick Demo\n\n"
|
||||
"This example demonstrates **proper corkscrew LED mapping** for a festival stick using FastLED's advanced mapping capabilities.\n\n"
|
||||
"## Key Features\n"
|
||||
"- **19+ turns** with 288 LEDs total\n"
|
||||
"- Uses `Corkscrew.toScreenMap()` for accurate web interface visualization\n"
|
||||
"- Multiple render modes: **Noise**, **Position**, **Fire**, **Wave**, and **Animartrix** effects\n"
|
||||
"- Real-time cylindrical surface mapping\n"
|
||||
"- **Wave mode**: Cylindrical 2D wave simulation with ripple effects and configurable blur\n"
|
||||
"- **Animartrix mode**: Advanced 2D animation effects with polar coordinate patterns\n\n"
|
||||
"## How It Works\n"
|
||||
"1. Draws patterns into a rectangular grid (`frameBuffer`)\n"
|
||||
"2. Maps the grid to corkscrew LED positions using `readFrom()`\n"
|
||||
"3. Web interface shows the actual spiral shape via ScreenMap\n\n"
|
||||
"*Select different render modes and adjust parameters to see various effects!*");
|
||||
|
||||
// UIHelp festivalStickHelp("Festival Stick - Advanced Guide");
|
||||
|
||||
// UIHelp corkscrewMappingHelp("Understanding Corkscrew Mapping");
|
||||
|
||||
// UIHelp uiControlsHelp("UI Controls Guide");
|
||||
|
||||
|
||||
|
||||
UISlider speed("Speed", 0.1f, 0.01f, 1.0f, 0.01f);
|
||||
UISlider positionCoarse("Position Coarse (10x)", 0.0f, 0.0f, 1.0f, 0.01f);
|
||||
UISlider positionFine("Position Fine (1x)", 0.0f, 0.0f, 0.1f, 0.001f);
|
||||
UISlider positionExtraFine("Position Extra Fine (0.1x)", 0.0f, 0.0f, 0.01f, 0.0001f);
|
||||
UISlider brightness("Brightness", 255, 0, 255, 1);
|
||||
|
||||
UICheckbox autoAdvance("Auto Advance", true);
|
||||
UICheckbox allWhite("All White", false);
|
||||
UICheckbox splatRendering("Splat Rendering", true);
|
||||
|
||||
// Noise controls (grouped under noiseGroup)
|
||||
|
||||
UISlider noiseScale("Noise Scale", 100, 10, 200, 5);
|
||||
UISlider noiseSpeed("Noise Speed", 4, 1, 100, 1);
|
||||
|
||||
// UIDropdown examples - noise-related color palette
|
||||
string paletteOptions[] = {"Party", "Heat", "Ocean", "Forest", "Rainbow"};
|
||||
string renderModeOptions[] = { "Wave", "Animartrix", "Noise", "Position", "Fire" };
|
||||
|
||||
|
||||
UIDropdown paletteDropdown("Color Palette", paletteOptions);
|
||||
UIDropdown renderModeDropdown("Render Mode", renderModeOptions);
|
||||
|
||||
|
||||
// fl::array<fl::pair<int, fl::string>> easeInfo = {
|
||||
// pair(EASE_IN_QUAD, "EASE_IN_QUAD"),
|
||||
// pair(EASE_OUT_QUAD, "EASE_OUT_QUAD"),
|
||||
// pair(EASE_IN_OUT_QUAD, "EASE_IN_OUT_QUAD"),
|
||||
// pair(EASE_IN_CUBIC, "EASE_IN_CUBIC"),
|
||||
// pair(EASE_OUT_CUBIC, "EASE_OUT_CUBIC"),
|
||||
// pair(EASE_IN_OUT_CUBIC, "EASE_IN_OUT_CUBIC"),
|
||||
// pair(EASE_IN_SINE, "EASE_IN_SINE"),
|
||||
// pair(EASE_OUT_SINE, "EASE_OUT_SINE"),
|
||||
// pair(EASE_IN_OUT_SINE, "EASE_IN_OUT_SINE")
|
||||
// };
|
||||
|
||||
|
||||
fl::vector<fl::string> easeInfo = {
|
||||
"EASE_NONE",
|
||||
"EASE_IN_QUAD",
|
||||
"EASE_OUT_QUAD",
|
||||
"EASE_IN_OUT_QUAD",
|
||||
"EASE_IN_CUBIC",
|
||||
"EASE_OUT_CUBIC",
|
||||
"EASE_IN_OUT_CUBIC",
|
||||
"EASE_IN_SINE",
|
||||
"EASE_OUT_SINE",
|
||||
"EASE_IN_OUT_SINE"
|
||||
};
|
||||
|
||||
EaseType getEaseType(fl::string value) {
|
||||
if (value == "EASE_NONE") {
|
||||
return EASE_NONE;
|
||||
} else if (value == "EASE_IN_QUAD") {
|
||||
return EASE_IN_QUAD;
|
||||
} else if (value == "EASE_OUT_QUAD") {
|
||||
return EASE_OUT_QUAD;
|
||||
} else if (value == "EASE_IN_OUT_QUAD") {
|
||||
return EASE_IN_OUT_QUAD;
|
||||
} else if (value == "EASE_IN_CUBIC") {
|
||||
return EASE_IN_CUBIC;
|
||||
} else if (value == "EASE_OUT_CUBIC") {
|
||||
return EASE_OUT_CUBIC;
|
||||
} else if (value == "EASE_IN_OUT_CUBIC") {
|
||||
return EASE_IN_OUT_CUBIC;
|
||||
} else if (value == "EASE_IN_SINE") {
|
||||
return EASE_IN_SINE;
|
||||
} else if (value == "EASE_OUT_SINE") {
|
||||
return EASE_OUT_SINE;
|
||||
} else if (value == "EASE_IN_OUT_SINE") {
|
||||
return EASE_IN_OUT_SINE;
|
||||
} else {
|
||||
return EASE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Color boost controls
|
||||
UIDropdown saturationFunction("Saturation Function", easeInfo);
|
||||
UIDropdown luminanceFunction("Luminance Function", easeInfo);
|
||||
|
||||
// Fire-related UI controls (added for cylindrical fire effect)
|
||||
UISlider fireScaleXY("Fire Scale", 8, 1, 100, 1);
|
||||
UISlider fireSpeedY("Fire SpeedY", 1.3, 1, 6, .1);
|
||||
UISlider fireScaleX("Fire ScaleX", .3, 0.1, 3, .01);
|
||||
UISlider fireInvSpeedZ("Fire Inverse SpeedZ", 20, 1, 100, 1);
|
||||
UINumberField firePalette("Fire Palette", 0, 0, 2);
|
||||
|
||||
// Wave-related UI controls (cylindrical wave effects)
|
||||
UISlider waveSpeed("Wave Speed", 0.03f, 0.0f, 1.0f, 0.01f);
|
||||
UISlider waveDampening("Wave Dampening", 9.1f, 0.0f, 20.0f, 0.1f);
|
||||
UICheckbox waveHalfDuplex("Wave Half Duplex", true);
|
||||
UICheckbox waveAutoTrigger("Wave Auto Trigger", true);
|
||||
UISlider waveTriggerSpeed("Wave Trigger Speed", 0.5f, 0.0f, 1.0f, 0.01f);
|
||||
UIButton waveTriggerButton("Trigger Wave");
|
||||
UINumberField wavePalette("Wave Palette", 0, 0, 2);
|
||||
|
||||
// Wave blur controls (added for smoother wave effects)
|
||||
UISlider waveBlurAmount("Wave Blur Amount", 50, 0, 172, 1);
|
||||
UISlider waveBlurPasses("Wave Blur Passes", 1, 1, 10, 1);
|
||||
|
||||
// Fire color palettes (from FireCylinder)
|
||||
DEFINE_GRADIENT_PALETTE(firepal){
|
||||
0, 0, 0, 0,
|
||||
32, 255, 0, 0,
|
||||
190, 255, 255, 0,
|
||||
255, 255, 255, 255
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricGreenFirePal){
|
||||
0, 0, 0, 0,
|
||||
32, 0, 70, 0,
|
||||
190, 57, 255, 20,
|
||||
255, 255, 255, 255
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(electricBlueFirePal){
|
||||
0, 0, 0, 0,
|
||||
32, 0, 0, 70,
|
||||
128, 20, 57, 255,
|
||||
255, 255, 255, 255
|
||||
};
|
||||
|
||||
// Wave color palettes (for cylindrical wave effects)
|
||||
DEFINE_GRADIENT_PALETTE(waveBluepal){
|
||||
0, 0, 0, 0, // Black (no wave)
|
||||
32, 0, 0, 70, // Dark blue (low wave)
|
||||
128, 20, 57, 255, // Electric blue (medium wave)
|
||||
255, 255, 255, 255 // White (high wave)
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(waveGreenpal){
|
||||
0, 0, 0, 0, // Black (no wave)
|
||||
8, 128, 64, 64, // Green with red tint (very low wave)
|
||||
16, 255, 222, 222, // Pinkish red (low wave)
|
||||
64, 255, 255, 255, // White (medium wave)
|
||||
255, 255, 255, 255 // White (high wave)
|
||||
};
|
||||
|
||||
DEFINE_GRADIENT_PALETTE(waveRainbowpal){
|
||||
0, 255, 0, 0, // Red (no wave)
|
||||
64, 255, 127, 0, // Orange (low wave)
|
||||
128, 255, 255, 0, // Yellow (medium wave)
|
||||
192, 0, 255, 0, // Green (high wave)
|
||||
255, 0, 0, 255 // Blue (maximum wave)
|
||||
};
|
||||
|
||||
// Create UIGroup for noise controls using variadic constructor
|
||||
// This automatically assigns all specified controls to the "Noise Controls" group
|
||||
UIGroup noiseGroup("Noise Controls", noiseScale, noiseSpeed, paletteDropdown);
|
||||
UIGroup fireGroup("Fire Controls", fireScaleXY, fireSpeedY, fireScaleX, fireInvSpeedZ, firePalette);
|
||||
UIGroup waveGroup("Wave Controls", waveSpeed, waveDampening, waveHalfDuplex, waveAutoTrigger, waveTriggerSpeed, waveTriggerButton, wavePalette, waveBlurAmount, waveBlurPasses);
|
||||
UIGroup renderGroup("Render Options", renderModeDropdown, splatRendering, allWhite, brightness);
|
||||
UIGroup colorBoostGroup("Color Boost", saturationFunction, luminanceFunction);
|
||||
UIGroup pointGraphicsGroup("Point Graphics Mode", speed, positionCoarse, positionFine, positionExtraFine, autoAdvance);
|
||||
|
||||
// Animartrix-related UI controls
|
||||
UINumberField animartrixIndex("Animartrix Animation", 5, 0, NUM_ANIMATIONS - 1);
|
||||
UINumberField animartrixColorOrder("Animartrix Color Order", 0, 0, 5);
|
||||
UISlider animartrixTimeSpeed("Animartrix Time Speed", 1, -10, 10, .1);
|
||||
|
||||
UIGroup animartrixGroup("Animartrix Controls", animartrixIndex, animartrixTimeSpeed, animartrixColorOrder);
|
||||
|
||||
// Color palette for noise
|
||||
CRGBPalette16 noisePalette = PartyColors_p;
|
||||
uint8_t colorLoop = 1;
|
||||
|
||||
// Option 1: Runtime Corkscrew (flexible, configurable at runtime)
|
||||
Corkscrew corkscrew(CORKSCREW_TURNS, NUM_LEDS);
|
||||
|
||||
// Simple position tracking - one variable for both modes
|
||||
static float currentPosition = 0.0f;
|
||||
static uint32_t lastUpdateTime = 0;
|
||||
|
||||
// Wave effect globals
|
||||
static uint32_t nextWaveTrigger = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
// Option 2: Constexpr dimensions for compile-time array sizing
|
||||
constexpr uint16_t CORKSCREW_WIDTH =
|
||||
calculateCorkscrewWidth(CORKSCREW_TURNS, NUM_LEDS);
|
||||
constexpr uint16_t CORKSCREW_HEIGHT =
|
||||
calculateCorkscrewHeight(CORKSCREW_TURNS, NUM_LEDS);
|
||||
|
||||
// Now you can use these for array initialization:
|
||||
// CRGB frameBuffer[CORKSCREW_WIDTH * CORKSCREW_HEIGHT]; // Compile-time sized
|
||||
// array
|
||||
|
||||
// Create a corkscrew with:
|
||||
// - 30cm total length (300mm)
|
||||
// - 5cm width (50mm)
|
||||
// - 2mm LED inner diameter
|
||||
// - 24 LEDs per turn
|
||||
// ScreenMap screenMap = makeCorkScrew(NUM_LEDS,
|
||||
// 300.0f, 50.0f, 2.0f, 24.0f);
|
||||
|
||||
// vector<vec3f> mapCorkScrew = makeCorkScrew(args);
|
||||
ScreenMap screenMap;
|
||||
fl::shared_ptr<Grid<CRGB>> frameBufferPtr;
|
||||
|
||||
// Wave effect objects - declared here but initialized in setup()
|
||||
WaveFxPtr waveFx;
|
||||
Blend2dPtr waveBlend;
|
||||
|
||||
// Animartrix effect objects - declared here but initialized in setup()
|
||||
fl::unique_ptr<Animartrix> animartrix;
|
||||
fl::unique_ptr<FxEngine> fxEngine;
|
||||
WaveCrgbGradientMapPtr crgMap = fl::make_shared<WaveCrgbGradientMap>();
|
||||
|
||||
void setup() {
|
||||
// Use constexpr dimensions (computed at compile time)
|
||||
constexpr int width = CORKSCREW_WIDTH; // = 16
|
||||
constexpr int height = CORKSCREW_HEIGHT; // = 18
|
||||
|
||||
|
||||
// Noise controls are now automatically grouped by the UIGroup constructor
|
||||
// The noiseGroup variadic constructor automatically called setGroup() on all controls
|
||||
|
||||
|
||||
// Or use runtime corkscrew for dynamic sizing
|
||||
// int width = corkscrew.cylinder_width();
|
||||
// int height = corkscrew.cylinder_height();
|
||||
|
||||
XYMap xyMap = XYMap::constructRectangularGrid(width, height, 0);
|
||||
|
||||
// Use the corkscrew's internal buffer for the LED strip
|
||||
CLEDController *controller =
|
||||
&FastLED.addLeds<APA102HD, PIN_DATA, PIN_CLOCK, BGR>(corkscrew.rawData(), NUM_LEDS);
|
||||
|
||||
// CLEDController *controller =
|
||||
// &FastLED.addLeds<WS2812, 3, BGR>(stripLeds, NUM_LEDS);
|
||||
|
||||
// NEW: Create ScreenMap directly from Corkscrew using toScreenMap()
|
||||
// This maps each LED index to its exact position on the corkscrew spiral
|
||||
// instead of using a rectangular grid mapping
|
||||
ScreenMap corkscrewScreenMap = corkscrew.toScreenMap(0.2f);
|
||||
|
||||
// OLD WAY (rectangular grid - not accurate for corkscrew visualization):
|
||||
// ScreenMap screenMap = xyMap.toScreenMap();
|
||||
// screenMap.setDiameter(.2f);
|
||||
|
||||
// Set the corkscrew screen map for the controller
|
||||
// This allows the web interface to display the actual corkscrew spiral shape
|
||||
controller->setScreenMap(corkscrewScreenMap);
|
||||
|
||||
// Initialize wave effects for cylindrical surface
|
||||
XYMap xyRect(width, height, false); // Rectangular grid for wave simulation
|
||||
WaveFx::Args waveArgs;
|
||||
waveArgs.factor = SuperSample::SUPER_SAMPLE_2X; // 2x supersampling for smoother waves
|
||||
waveArgs.half_duplex = true; // Only positive waves
|
||||
waveArgs.auto_updates = true; // Auto-update simulation
|
||||
waveArgs.speed = 0.16f; // Wave propagation speed
|
||||
waveArgs.dampening = 6.0f; // Wave energy loss
|
||||
waveArgs.x_cyclical = true; // Enable cylindrical wrapping!
|
||||
waveArgs.crgbMap = fl::make_shared<WaveCrgbGradientMap>(waveBluepal); // Default color palette
|
||||
|
||||
// Create wave effect with cylindrical mapping
|
||||
waveFx = fl::make_shared<WaveFx>(xyRect, waveArgs);
|
||||
|
||||
// Create blender for wave effects (allows multiple wave layers in future)
|
||||
waveBlend = fl::make_shared<Blend2d>(xyRect);
|
||||
waveBlend->add(waveFx);
|
||||
|
||||
// Initialize Animartrix effect
|
||||
XYMap animartrixXyMap = XYMap::constructRectangularGrid(width, height, 0);
|
||||
animartrix.reset(new Animartrix(animartrixXyMap, POLAR_WAVES));
|
||||
fxEngine.reset(new FxEngine(width * height));
|
||||
fxEngine->addFx(*animartrix);
|
||||
|
||||
// Demonstrate UIGroup functionality for noise controls
|
||||
FL_WARN("Noise UI Group initialized: " << noiseGroup.name());
|
||||
FL_WARN(" This group contains noise pattern controls:");
|
||||
FL_WARN(" - Use Noise Pattern toggle");
|
||||
FL_WARN(" - Noise Scale and Speed sliders");
|
||||
FL_WARN(" - Color Palette selection for noise");
|
||||
FL_WARN(" UIGroup automatically applied group membership via variadic constructor");
|
||||
|
||||
// Set initial dropdown selections
|
||||
paletteDropdown.setSelectedIndex(0); // Party
|
||||
renderModeDropdown.setSelectedIndex(0); // Fire (new default)
|
||||
|
||||
// Add onChange callbacks for dropdowns
|
||||
paletteDropdown.onChanged([](UIDropdown &dropdown) {
|
||||
string selectedPalette = dropdown.value();
|
||||
FL_WARN("Noise palette changed to: " << selectedPalette);
|
||||
if (selectedPalette == "Party") {
|
||||
noisePalette = PartyColors_p;
|
||||
} else if (selectedPalette == "Heat") {
|
||||
noisePalette = HeatColors_p;
|
||||
} else if (selectedPalette == "Ocean") {
|
||||
noisePalette = OceanColors_p;
|
||||
} else if (selectedPalette == "Forest") {
|
||||
noisePalette = ForestColors_p;
|
||||
} else if (selectedPalette == "Rainbow") {
|
||||
noisePalette = RainbowColors_p;
|
||||
}
|
||||
});
|
||||
|
||||
renderModeDropdown.onChanged([](UIDropdown &dropdown) {
|
||||
string mode = dropdown.value();
|
||||
// Simple example of using getOption()
|
||||
for(size_t i = 0; i < dropdown.getOptionCount(); i++) {
|
||||
if(dropdown.getOption(i) == mode) {
|
||||
FL_WARN("Render mode changed to: " << mode);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add onChange callback for animartrix color order
|
||||
animartrixColorOrder.onChanged([](int value) {
|
||||
EOrder order = RGB;
|
||||
switch(value) {
|
||||
case 0: order = RGB; break;
|
||||
case 1: order = RBG; break;
|
||||
case 2: order = GRB; break;
|
||||
case 3: order = GBR; break;
|
||||
case 4: order = BRG; break;
|
||||
case 5: order = BGR; break;
|
||||
}
|
||||
if (animartrix.get()) {
|
||||
animartrix->setColorOrder(order);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
waveFx->setCrgbMap(crgMap);
|
||||
|
||||
frameBufferPtr = corkscrew.getOrCreateInputSurface();
|
||||
}
|
||||
|
||||
|
||||
FL_OPTIMIZATION_LEVEL_O0_BEGIN // Works around a compile bug in clang 19
|
||||
float get_position(uint32_t now) {
|
||||
if (autoAdvance.value()) {
|
||||
// Check if auto-advance was just enabled
|
||||
// Auto-advance mode: increment smoothly from current position
|
||||
float elapsedSeconds = float(now - lastUpdateTime) / 1000.0f;
|
||||
float increment = elapsedSeconds * speed.value() *
|
||||
0.3f; // Make it 1/20th the original speed
|
||||
currentPosition = fmodf(currentPosition + increment, 1.0f);
|
||||
lastUpdateTime = now;
|
||||
return currentPosition;
|
||||
} else {
|
||||
// Manual mode: use the dual slider control
|
||||
float combinedPosition = positionCoarse.value() + positionFine.value() + positionExtraFine.value();
|
||||
// Clamp to ensure we don't exceed 1.0
|
||||
if (combinedPosition > 1.0f)
|
||||
combinedPosition = 1.0f;
|
||||
return combinedPosition;
|
||||
}
|
||||
}
|
||||
FL_OPTIMIZATION_LEVEL_O0_END
|
||||
|
||||
void fillFrameBufferNoise() {
|
||||
// Get current UI values
|
||||
uint8_t noise_scale = noiseScale.value();
|
||||
uint8_t noise_speed = noiseSpeed.value();
|
||||
|
||||
// Derive noise coordinates from current time instead of forward iteration
|
||||
uint32_t now = millis();
|
||||
uint16_t noise_z = now * noise_speed / 10; // Primary time dimension
|
||||
uint16_t noise_x = now * noise_speed / 80; // Slow drift in x
|
||||
uint16_t noise_y = now * noise_speed / 160; // Even slower drift in y (opposite direction)
|
||||
|
||||
int width = frameBufferPtr->width();
|
||||
int height = frameBufferPtr->height();
|
||||
|
||||
// Data smoothing for low speeds (from NoisePlusPalette example)
|
||||
uint8_t dataSmoothing = 0;
|
||||
if(noise_speed < 50) {
|
||||
dataSmoothing = 200 - (noise_speed * 4);
|
||||
}
|
||||
|
||||
// Generate noise for each pixel in the frame buffer using cylindrical mapping
|
||||
for(int x = 0; x < width; x++) {
|
||||
for(int y = 0; y < height; y++) {
|
||||
// Convert rectangular coordinates to cylindrical coordinates
|
||||
// Map x to angle (0 to 2*PI), y remains as height
|
||||
float angle = (float(x) / float(width)) * 2.0f * PI;
|
||||
|
||||
// Convert cylindrical coordinates to cartesian for noise sampling
|
||||
// Use the noise_scale to control the cylinder size in noise space
|
||||
float cylinder_radius = noise_scale; // Use the existing noise_scale parameter
|
||||
|
||||
// Calculate cartesian coordinates on the cylinder surface
|
||||
float noise_x_cyl = cos(angle) * cylinder_radius;
|
||||
float noise_y_cyl = sin(angle) * cylinder_radius;
|
||||
float noise_z_height = float(y) * noise_scale; // Height component
|
||||
|
||||
// Apply time-based offsets
|
||||
int xoffset = int(noise_x_cyl) + noise_x;
|
||||
int yoffset = int(noise_y_cyl) + noise_y;
|
||||
int zoffset = int(noise_z_height) + noise_z;
|
||||
|
||||
// Generate 8-bit noise value using 3D Perlin noise with cylindrical coordinates
|
||||
uint8_t data = inoise8(xoffset, yoffset, zoffset);
|
||||
|
||||
// Expand the range from ~16-238 to 0-255 (from NoisePlusPalette)
|
||||
data = qsub8(data, 16);
|
||||
data = qadd8(data, scale8(data, 39));
|
||||
|
||||
// Apply data smoothing if enabled
|
||||
if(dataSmoothing) {
|
||||
CRGB oldColor = frameBufferPtr->at(x, y);
|
||||
uint8_t olddata = (oldColor.r + oldColor.g + oldColor.b) / 3; // Simple brightness extraction
|
||||
uint8_t newdata = scale8(olddata, dataSmoothing) + scale8(data, 256 - dataSmoothing);
|
||||
data = newdata;
|
||||
}
|
||||
|
||||
// Map noise to color using palette (adapted from NoisePlusPalette)
|
||||
uint8_t index = data;
|
||||
uint8_t bri = data;
|
||||
|
||||
// Add color cycling if enabled - also derive from time
|
||||
uint8_t ihue = 0;
|
||||
if(colorLoop) {
|
||||
ihue = (now / 100) % 256; // Derive hue from time instead of incrementing
|
||||
index += ihue;
|
||||
}
|
||||
|
||||
// Enhance brightness (from NoisePlusPalette example)
|
||||
// if(bri > 127) {
|
||||
// //bri = 255;
|
||||
// } else {
|
||||
// //bri = dim8_raw(bri * 2);
|
||||
// }
|
||||
|
||||
// Get color from palette and set pixel
|
||||
CRGB color = ColorFromPalette(noisePalette, index, bri);
|
||||
|
||||
// Apply color boost using ease functions
|
||||
EaseType sat_ease = getEaseType(saturationFunction.value());
|
||||
EaseType lum_ease = getEaseType(luminanceFunction.value());
|
||||
color = color.colorBoost(sat_ease, lum_ease);
|
||||
|
||||
frameBufferPtr->at(x, y) = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawNoise(uint32_t now) {
|
||||
FL_UNUSED(now);
|
||||
fillFrameBufferNoise();
|
||||
}
|
||||
|
||||
void draw(float pos) {
|
||||
if (splatRendering) {
|
||||
Tile2x2_u8_wrap pos_tile = corkscrew.at_wrap(pos);
|
||||
//FL_WARN("pos_tile: " << pos_tile);
|
||||
CRGB color = CRGB::Blue;
|
||||
// Apply color boost using ease functions
|
||||
EaseType sat_ease = getEaseType(saturationFunction.value());
|
||||
EaseType lum_ease = getEaseType(luminanceFunction.value());
|
||||
color = color.colorBoost(sat_ease, lum_ease);
|
||||
// Draw each pixel in the 2x2 tile using the new wrapping API
|
||||
for (int dx = 0; dx < 2; ++dx) {
|
||||
for (int dy = 0; dy < 2; ++dy) {
|
||||
Tile2x2_u8_wrap::Entry data = pos_tile.at(dx, dy);
|
||||
vec2<u16> wrapped_pos = data.first; // Already wrapped position
|
||||
uint8_t alpha = data.second; // Alpha value
|
||||
|
||||
if (alpha > 0) { // Only draw if there's some alpha
|
||||
CRGB c = color;
|
||||
c.nscale8(alpha); // Scale the color by the alpha value
|
||||
frameBufferPtr->at(wrapped_pos.x, wrapped_pos.y) = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// None splat rendering, looks aweful.
|
||||
vec2f pos_vec2f = corkscrew.at_no_wrap(pos);
|
||||
vec2<u16> pos_i16 = vec2<u16>(pos_vec2f.x, pos_vec2f.y);
|
||||
|
||||
CRGB color = CRGB::Blue;
|
||||
// Apply color boost using ease functions
|
||||
EaseType sat_ease = getEaseType(saturationFunction.value());
|
||||
EaseType lum_ease = getEaseType(luminanceFunction.value());
|
||||
color = color.colorBoost(sat_ease, lum_ease);
|
||||
|
||||
// Now map the cork screw position to the cylindrical buffer that we
|
||||
// will draw.
|
||||
frameBufferPtr->at(pos_i16.x, pos_i16.y) = color; // Draw a blue pixel at (w, h)
|
||||
}
|
||||
}
|
||||
|
||||
CRGBPalette16 getFirePalette() {
|
||||
int paletteIndex = (int)firePalette.value();
|
||||
switch (paletteIndex) {
|
||||
case 0:
|
||||
return firepal;
|
||||
case 1:
|
||||
return electricGreenFirePal;
|
||||
case 2:
|
||||
return electricBlueFirePal;
|
||||
default:
|
||||
return firepal;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t getFirePaletteIndex(uint32_t millis32, int width, int max_width, int height, int max_height,
|
||||
uint32_t y_speed) {
|
||||
uint16_t scale = fireScaleXY.as<uint16_t>();
|
||||
|
||||
float xf = (float)width / (float)max_width;
|
||||
uint8_t x = (uint8_t)(xf * 255);
|
||||
|
||||
uint32_t cosx = cos8(x);
|
||||
uint32_t sinx = sin8(x);
|
||||
|
||||
float trig_scale = scale * fireScaleX.value();
|
||||
cosx *= trig_scale;
|
||||
sinx *= trig_scale;
|
||||
|
||||
uint32_t y = height * scale + y_speed;
|
||||
|
||||
uint16_t z = millis32 / fireInvSpeedZ.as<uint16_t>();
|
||||
|
||||
uint16_t noise16 = inoise16(cosx << 8, sinx << 8, y << 8, z << 8);
|
||||
|
||||
uint8_t noise_val = noise16 >> 8;
|
||||
|
||||
int8_t subtraction_factor = abs8(height - (max_height - 1)) * 255 /
|
||||
(max_height - 1);
|
||||
|
||||
return qsub8(noise_val, subtraction_factor);
|
||||
}
|
||||
|
||||
void fillFrameBufferFire(uint32_t now) {
|
||||
CRGBPalette16 myPal = getFirePalette();
|
||||
|
||||
// Calculate the current y-offset for animation (makes the fire move)
|
||||
uint32_t y_speed = now * fireSpeedY.value();
|
||||
|
||||
int width = frameBufferPtr->width();
|
||||
int height = frameBufferPtr->height();
|
||||
|
||||
// Loop through every pixel in our cylindrical matrix
|
||||
for (int w = 0; w < width; w++) {
|
||||
for (int h = 0; h < height; h++) {
|
||||
// Calculate which color to use from our palette for this pixel
|
||||
uint8_t palette_index =
|
||||
getFirePaletteIndex(now, w, width, h, height, y_speed);
|
||||
|
||||
// Get the actual RGB color from the palette
|
||||
CRGB color = ColorFromPalette(myPal, palette_index, 255);
|
||||
|
||||
// Apply color boost using ease functions
|
||||
EaseType sat_ease = getEaseType(saturationFunction.value());
|
||||
EaseType lum_ease = getEaseType(luminanceFunction.value());
|
||||
color = color.colorBoost(sat_ease, lum_ease);
|
||||
|
||||
// Set the pixel in the frame buffer
|
||||
// Flip coordinates to make fire rise from bottom
|
||||
frameBufferPtr->at((width - 1) - w, (height - 1) - h) = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawFire(uint32_t now) {
|
||||
fillFrameBufferFire(now);
|
||||
}
|
||||
|
||||
// Wave effect helper functions
|
||||
CRGBPalette16 getWavePalette() {
|
||||
int paletteIndex = (int)wavePalette.value();
|
||||
switch (paletteIndex) {
|
||||
case 0:
|
||||
return waveBluepal; // Electric blue waves
|
||||
case 1:
|
||||
return waveGreenpal; // Green/red waves
|
||||
case 2:
|
||||
return waveRainbowpal; // Rainbow waves
|
||||
default:
|
||||
return waveBluepal; // Default to blue
|
||||
}
|
||||
}
|
||||
|
||||
void triggerWaveRipple() {
|
||||
// Create a ripple at a random position within the central area
|
||||
float perc = 0.15f; // 15% margin from edges
|
||||
int width = corkscrew.cylinderWidth();
|
||||
int height = corkscrew.cylinderHeight();
|
||||
|
||||
int min_x = perc * width;
|
||||
int max_x = (1 - perc) * width;
|
||||
int min_y = perc * height;
|
||||
int max_y = (1 - perc) * height;
|
||||
|
||||
int x = random8(min_x, max_x);
|
||||
int y = random8(min_y, max_y);
|
||||
|
||||
// Trigger a 2x2 wave ripple for more punch (compensates for blur reduction)
|
||||
float ripple_strength = 1.5f; // Higher value for more impact
|
||||
waveFx->setf(x, y, ripple_strength);
|
||||
waveFx->setf(x + 1, y, ripple_strength);
|
||||
waveFx->setf(x, y + 1, ripple_strength);
|
||||
waveFx->setf(x + 1, y + 1, ripple_strength);
|
||||
|
||||
FL_WARN("Wave ripple triggered at (" << x << ", " << y << ") with 2x2 pattern");
|
||||
}
|
||||
|
||||
void processWaveAutoTrigger(uint32_t now) {
|
||||
// Handle automatic wave triggering
|
||||
if (waveAutoTrigger.value()) {
|
||||
if (now >= nextWaveTrigger) {
|
||||
triggerWaveRipple();
|
||||
|
||||
// Calculate next trigger time based on speed
|
||||
float speed = 1.0f - waveTriggerSpeed.value();
|
||||
uint32_t min_interval = (uint32_t)(500 * speed); // Minimum 500ms * speed
|
||||
uint32_t max_interval = (uint32_t)(3000 * speed); // Maximum 3000ms * speed
|
||||
|
||||
// Ensure valid range
|
||||
uint32_t min = MIN(min_interval, max_interval);
|
||||
uint32_t max = MAX(min_interval, max_interval);
|
||||
if (min >= max) max = min + 1;
|
||||
|
||||
nextWaveTrigger = now + random16(min, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void drawWave(uint32_t now) {
|
||||
// Update wave parameters from UI
|
||||
waveFx->setSpeed(waveSpeed.value());
|
||||
waveFx->setDampening(waveDampening.value());
|
||||
waveFx->setHalfDuplex(waveHalfDuplex.value());
|
||||
waveFx->setXCylindrical(true); // Always keep cylindrical for corkscrew
|
||||
|
||||
// Update wave color palette
|
||||
CRGBPalette16 currentPalette = getWavePalette();
|
||||
crgMap->setGradient(currentPalette);
|
||||
|
||||
|
||||
|
||||
// Apply blur settings to the wave blend (for smoother wave effects)
|
||||
waveBlend->setGlobalBlurAmount(waveBlurAmount.value());
|
||||
waveBlend->setGlobalBlurPasses(waveBlurPasses.value());
|
||||
|
||||
// Check if manual trigger button was pressed
|
||||
if (waveTriggerButton.value()) {
|
||||
triggerWaveRipple();
|
||||
}
|
||||
|
||||
// Handle auto-triggering
|
||||
processWaveAutoTrigger(now);
|
||||
|
||||
// Draw the wave effect directly to the frame buffer
|
||||
// Create a DrawContext for the wave renderer
|
||||
Fx::DrawContext waveContext(now, frameBufferPtr->data());
|
||||
waveBlend->draw(waveContext);
|
||||
}
|
||||
|
||||
void drawAnimartrix(uint32_t now) {
|
||||
// Update animartrix parameters from UI
|
||||
fxEngine->setSpeed(animartrixTimeSpeed.value());
|
||||
|
||||
// Handle animation index changes
|
||||
static int lastAnimartrixIndex = -1;
|
||||
if (animartrixIndex.value() != lastAnimartrixIndex) {
|
||||
lastAnimartrixIndex = animartrixIndex.value();
|
||||
animartrix->fxSet(animartrixIndex.value());
|
||||
}
|
||||
|
||||
// Draw the animartrix effect directly to the frame buffer
|
||||
CRGB* dst = corkscrew.rawData();
|
||||
fxEngine->draw(now, dst);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
delay(4);
|
||||
uint32_t now = millis();
|
||||
frameBufferPtr->clear();
|
||||
|
||||
if (allWhite) {
|
||||
CRGB whiteColor = CRGB(8, 8, 8);
|
||||
for (u32 x = 0; x < frameBufferPtr->width(); x++) {
|
||||
for (u32 y = 0; y < frameBufferPtr->height(); y++) {
|
||||
frameBufferPtr->at(x, y) = whiteColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update the corkscrew mapping with auto-advance or manual position control
|
||||
float combinedPosition = get_position(now);
|
||||
float pos = combinedPosition * (corkscrew.size() - 1);
|
||||
|
||||
|
||||
if (renderModeDropdown.value() == "Noise") {
|
||||
drawNoise(now);
|
||||
} else if (renderModeDropdown.value() == "Fire") {
|
||||
drawFire(now);
|
||||
} else if (renderModeDropdown.value() == "Wave") {
|
||||
|
||||
drawWave(now);
|
||||
} else if (renderModeDropdown.value() == "Animartrix") {
|
||||
drawAnimartrix(now);
|
||||
} else {
|
||||
draw(pos);
|
||||
}
|
||||
|
||||
|
||||
// Use the new readFrom workflow:
|
||||
// 1. Read directly from the frameBuffer Grid into the corkscrew's internal buffer
|
||||
// use_multi_sampling = true will use multi-sampling to sample from the source grid,
|
||||
// this will give a little bit better accuracy and the screenmap will be more accurate.
|
||||
const bool use_multi_sampling = splatRendering;
|
||||
// corkscrew.readFrom(frameBuffer, use_multi_sampling);
|
||||
corkscrew.draw(use_multi_sampling);
|
||||
|
||||
// The corkscrew's buffer is now populated and FastLED will display it directly
|
||||
|
||||
FastLED.setBrightness(brightness.value());
|
||||
FastLED.show();
|
||||
}
|
||||
220
.pio/libdeps/esp01_1m/FastLED/examples/FestivalStick/old.h
Normal file
220
.pio/libdeps/esp01_1m/FastLED/examples/FestivalStick/old.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
Festival Stick is a dense corkscrew of LEDs that is wrapped around one end of
|
||||
a wooden walking stick commonly found on amazon.A0
|
||||
|
||||
The UI screenmap projects this cork screw into polar coordinates, so that the LEDs are
|
||||
mapped to a sprial, with the inner portion of the spiral being the top, the outer
|
||||
most portion being the bottom.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "fl/assert.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/warn.h"
|
||||
#include "noise.h"
|
||||
#include <FastLED.h>
|
||||
// #include "vec3.h"
|
||||
|
||||
using namespace fl;
|
||||
|
||||
// Power management settings
|
||||
#define VOLTS 5
|
||||
#define MAX_AMPS 1
|
||||
|
||||
|
||||
#define PIN_DATA 9
|
||||
#define PIN_CLOCK 7
|
||||
|
||||
// Pin could have been tied to ground, instead it's tied to another pin.
|
||||
#define PIN_BUTTON 1
|
||||
#define PIN_GRND 2
|
||||
|
||||
#define NUM_LEDS 288
|
||||
// #define CM_BETWEEN_LEDS 1.0 // 1cm between LEDs
|
||||
// #define CM_LED_DIAMETER 0.5 // 0.5cm LED diameter
|
||||
|
||||
UITitle festivalStickTitle("Festival Stick - Classic Version");
|
||||
UIDescription festivalStickDescription(
|
||||
"Take a wooden walking stick, wrap dense LEDs around it like a corkscrew. Super simple but very awesome looking. "
|
||||
"This classic version uses 3D Perlin noise to create organic, flowing patterns around the cylindrical surface. "
|
||||
"Assumes dense 144 LEDs/meter (288 total LEDs).");
|
||||
|
||||
// UIHelp festivalStickHelp("Festival Stick - Classic Guide");
|
||||
|
||||
// UIHelp technicalHelp("Technical Details - Classic Festival Stick");
|
||||
// UIHelp usageHelp("Usage Guide - Classic Festival Stick");
|
||||
// UIHelp physicalBuildHelp("Building Your Festival Stick");
|
||||
|
||||
|
||||
UISlider ledsScale("Leds scale", 0.1f, 0.1f, 1.0f, 0.01f);
|
||||
UIButton button("Button");
|
||||
|
||||
// Adding a brightness slider
|
||||
UISlider brightness("Brightness", 16, 0, 255, 1); // Brightness from 0 to 255
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
|
||||
// fl::vector<vec3f>
|
||||
struct corkscrew_args {
|
||||
int num_leds = NUM_LEDS;
|
||||
float leds_per_turn = 15.5;
|
||||
float width_cm = 1.0;
|
||||
};
|
||||
|
||||
fl::vector<vec3f> makeCorkScrew(corkscrew_args args = corkscrew_args()) {
|
||||
// int num_leds, float leds_per_turn, float width_cm
|
||||
int num_leds = args.num_leds;
|
||||
float leds_per_turn = args.leds_per_turn;
|
||||
float width_cm = args.width_cm;
|
||||
|
||||
const float circumference = leds_per_turn;
|
||||
const float radius = circumference / (2.0 * PI); // radius in mm
|
||||
const float angle_per_led = 2.0 * PI / leds_per_turn; // degrees per LED
|
||||
const float total_angle_radians = angle_per_led * num_leds;
|
||||
const float total_turns = total_angle_radians / (2.0 * PI); // total turns
|
||||
const float height_per_turn_cm = width_cm; // 10cm height per turn
|
||||
const float height_per_led =
|
||||
height_per_turn_cm /
|
||||
leds_per_turn; // this is the changing height per led.
|
||||
const float total_height =
|
||||
height_per_turn_cm * total_turns; // total height of the corkscrew
|
||||
fl::vector<vec3f> out;
|
||||
for (int i = 0; i < num_leds; i++) {
|
||||
float angle = i * angle_per_led; // angle in radians
|
||||
float height = (i / leds_per_turn) * height_per_turn_cm; // height in cm
|
||||
|
||||
// Calculate the x, y, z coordinates for the corkscrew
|
||||
float x = radius * cos(angle); // x coordinate
|
||||
float z = radius * sin(angle); // y coordinate
|
||||
float y = height; // z coordinate
|
||||
|
||||
// Store the 3D coordinates in the vector
|
||||
vec3f led_position(x, y, z);
|
||||
// screenMap.set(i, led_position);
|
||||
out.push_back(led_position);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
fl::ScreenMap makeScreenMap(corkscrew_args args = corkscrew_args()) {
|
||||
// Create a ScreenMap for the corkscrew
|
||||
fl::vector<vec2f> points(args.num_leds);
|
||||
|
||||
int num_leds = args.num_leds;
|
||||
float leds_per_turn = args.leds_per_turn;
|
||||
float width_cm = args.width_cm;
|
||||
|
||||
|
||||
const float circumference = leds_per_turn;
|
||||
const float radius = circumference / (2.0 * PI); // radius in mm
|
||||
const float angle_per_led = 2.0 * PI / leds_per_turn; // degrees per LED
|
||||
const float height_per_turn_cm = width_cm; // 10cm height per turn
|
||||
const float height_per_led =
|
||||
height_per_turn_cm /
|
||||
leds_per_turn * 1.3; // this is the changing height per led.
|
||||
|
||||
|
||||
|
||||
for (int i = 0; i < num_leds; i++) {
|
||||
float angle = i * angle_per_led; // angle in radians
|
||||
float r = radius + 10 + i * height_per_led; // height in cm
|
||||
|
||||
// Calculate the x, y coordinates for the corkscrew
|
||||
float x = r * cos(angle); // x coordinate
|
||||
float y = r * sin(angle); // y coordinate
|
||||
|
||||
// Store the 2D coordinates in the vector
|
||||
points[i] = vec2f(x, y);
|
||||
}
|
||||
|
||||
FASTLED_WARN("Creating ScreenMap with:\n" << points);
|
||||
|
||||
// Create a ScreenMap from the points
|
||||
fl::ScreenMap screenMap(points.data(), num_leds, .5);
|
||||
return screenMap;
|
||||
}
|
||||
|
||||
|
||||
// Create a corkscrew with:
|
||||
// - 30cm total length (300mm)
|
||||
// - 5cm width (50mm)
|
||||
// - 2mm LED inner diameter
|
||||
// - 24 LEDs per turn
|
||||
// fl::ScreenMap screenMap = makeCorkScrew(NUM_LEDS,
|
||||
// 300.0f, 50.0f, 2.0f, 24.0f);
|
||||
|
||||
corkscrew_args args = corkscrew_args();
|
||||
fl::vector<vec3f> mapCorkScrew = makeCorkScrew(args);
|
||||
fl::ScreenMap screenMap;
|
||||
|
||||
|
||||
CLEDController* addController() {
|
||||
CLEDController* controller = &FastLED.addLeds<APA102HD, PIN_DATA, PIN_CLOCK, BGR>(leds, NUM_LEDS);
|
||||
return controller;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
pinMode(PIN_GRND, OUTPUT);
|
||||
digitalWrite(PIN_GRND, LOW); // Set ground pin to low
|
||||
button.addRealButton(Button(PIN_BUTTON));
|
||||
screenMap = makeScreenMap(args);
|
||||
//screenMap = ScreenMap::Circle(NUM_LEDS, 1.5f, 0.5f, 1.0f);
|
||||
auto controller = addController();
|
||||
// Set the screen map for the controller
|
||||
controller->setScreenMap(screenMap);
|
||||
|
||||
// Set power management. This allows this festival stick to conformatable
|
||||
// run on any USB battery that can output at least 1A at 5V.
|
||||
// Keep in mind that this sketch is designed to use APA102HD mode, which will
|
||||
// result in even lowwer run power consumption, since the power mode does not take
|
||||
// into account the APA102HD gamma correction. However it is still a correct upper bound
|
||||
// that will match the ledset exactly when the display tries to go full white.
|
||||
FastLED.setMaxPowerInVoltsAndMilliamps(VOLTS, MAX_AMPS * 1000);
|
||||
// set brightness 8
|
||||
FastLED.setBrightness(brightness.as_int());
|
||||
button.onChanged([](UIButton& but) {
|
||||
// This function is called when the button is pressed
|
||||
// If the button is pressed, show the generative pattern
|
||||
if (but.isPressed()) {
|
||||
FASTLED_WARN("Button pressed");
|
||||
} else {
|
||||
FASTLED_WARN("NOT Button pressed");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
void showGenerative(uint32_t now) {
|
||||
// This function is called to show the generative pattern
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
// Get the 2D position of this LED from the screen map
|
||||
fl::vec3f pos = mapCorkScrew[i];
|
||||
float x = pos.x;
|
||||
float y = pos.y;
|
||||
float z = pos.z;
|
||||
|
||||
x*= 20.0f * ledsScale.value();
|
||||
y*= 20.0f * ledsScale.value();
|
||||
z*= 20.0f * ledsScale.value();
|
||||
|
||||
uint16_t noise_value = inoise16(x,y,z, now / 100);
|
||||
// Normalize the noise value to 0-255
|
||||
uint8_t brightness = map(noise_value, 0, 65535, 0, 255);
|
||||
// Create a hue that changes with position and time
|
||||
uint8_t sat = int32_t((x * 10 + y * 5 + now / 5)) % 256;
|
||||
// Set the color
|
||||
leds[i] = CHSV(170, sat, fl::clamp(255- sat, 64, 255));
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
uint32_t now = millis();
|
||||
fl::clear(leds);
|
||||
showGenerative(now);
|
||||
FastLED.show();
|
||||
}
|
||||
Reference in New Issue
Block a user