imported from "final" folder
This commit is contained in:
682
.pio/libdeps/esp01_1m/FastLED/examples/FxNoiseRing/FxNoiseRing.h
Normal file
682
.pio/libdeps/esp01_1m/FastLED/examples/FxNoiseRing/FxNoiseRing.h
Normal file
@@ -0,0 +1,682 @@
|
||||
/// @file FxNoiseRing.ino
|
||||
/// @brief Noise effect on circular ring with ScreenMap
|
||||
/// @example FxNoiseRing.ino
|
||||
///
|
||||
/// This sketch is fully compatible with the FastLED web compiler. To use it do the following:
|
||||
/// 1. Install Fastled: `pip install fastled`
|
||||
/// 2. cd into this examples page.
|
||||
/// 3. Run the FastLED web compiler at root: `fastled`
|
||||
/// 4. When the compiler is done a web page will open.
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
|
||||
#include "fl/json.h"
|
||||
#include "fl/math_macros.h"
|
||||
#include "fl/warn.h"
|
||||
#include "noisegen.h"
|
||||
#include "fl/screenmap.h"
|
||||
#include "fl/slice.h"
|
||||
#include "fl/ui.h"
|
||||
|
||||
#include "sensors/pir.h"
|
||||
#include "./simple_timer.h"
|
||||
#include "fl/sstream.h"
|
||||
#include "fl/assert.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define LED_PIN 2
|
||||
#define COLOR_ORDER GRB // Color order matters for a real device, web-compiler will ignore this.
|
||||
#define NUM_LEDS 250
|
||||
#define PIN_PIR 0
|
||||
|
||||
#define PIR_LATCH_MS 60000 // how long to keep the PIR sensor active after a trigger
|
||||
#define PIR_RISING_TIME 1000 // how long to fade in the PIR sensor
|
||||
#define PIR_FALLING_TIME 1000 // how long to fade out the PIR sensor
|
||||
|
||||
using namespace fl;
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
// Enhanced coordinate system for ring-based effects
|
||||
struct RingCoord {
|
||||
float angle; // Position on ring (0 to 2π)
|
||||
float radius; // Distance from center (normalized 0-1)
|
||||
float x, y; // Cartesian coordinates
|
||||
int led_index; // LED position on strip
|
||||
};
|
||||
|
||||
// Convert LED index to ring coordinates
|
||||
RingCoord calculateRingCoord(int led_index, int num_leds, float time_offset = 0.0f) {
|
||||
RingCoord coord;
|
||||
coord.led_index = led_index;
|
||||
coord.angle = (led_index * 2.0f * M_PI / num_leds) + time_offset;
|
||||
coord.radius = 1.0f; // Fixed radius for ring
|
||||
coord.x = cos(coord.angle);
|
||||
coord.y = sin(coord.angle);
|
||||
return coord;
|
||||
}
|
||||
|
||||
// Performance optimization with lookup tables
|
||||
class RingLUT {
|
||||
private:
|
||||
float cos_table[NUM_LEDS];
|
||||
float sin_table[NUM_LEDS];
|
||||
|
||||
public:
|
||||
void initialize() {
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
float angle = i * 2.0f * M_PI / NUM_LEDS;
|
||||
cos_table[i] = cos(angle);
|
||||
sin_table[i] = sin(angle);
|
||||
}
|
||||
}
|
||||
|
||||
RingCoord fastRingCoord(int led_index, float time_offset = 0.0f) {
|
||||
RingCoord coord;
|
||||
coord.led_index = led_index;
|
||||
coord.angle = (led_index * 2.0f * M_PI / NUM_LEDS) + time_offset;
|
||||
coord.x = cos_table[led_index];
|
||||
coord.y = sin_table[led_index];
|
||||
coord.radius = 1.0f;
|
||||
return coord;
|
||||
}
|
||||
};
|
||||
|
||||
// Plasma wave parameters
|
||||
struct PlasmaParams {
|
||||
float time_scale = 1.0f;
|
||||
float noise_intensity = 0.5f;
|
||||
float noise_amplitude = 0.8f;
|
||||
uint8_t time_bitshift = 5;
|
||||
uint8_t hue_offset = 0;
|
||||
float brightness = 1.0f;
|
||||
};
|
||||
|
||||
// Plasma wave generator - Featured Implementation
|
||||
class PlasmaWaveGenerator {
|
||||
private:
|
||||
struct WaveSource {
|
||||
float x, y; // Source position
|
||||
float frequency; // Wave frequency
|
||||
float amplitude; // Wave strength
|
||||
float phase_speed; // Phase evolution rate
|
||||
};
|
||||
|
||||
WaveSource sources[4] = {
|
||||
{0.5f, 0.5f, 1.0f, 1.0f, 0.8f}, // Center source
|
||||
{0.0f, 0.0f, 1.5f, 0.8f, 1.2f}, // Corner source
|
||||
{1.0f, 1.0f, 0.8f, 1.2f, 0.6f}, // Opposite corner
|
||||
{0.5f, 0.0f, 1.2f, 0.9f, 1.0f} // Edge source
|
||||
};
|
||||
|
||||
public:
|
||||
CRGB calculatePlasmaPixel(const RingCoord& coord, uint32_t time_ms, const PlasmaParams& params) {
|
||||
float time_scaled = time_ms * params.time_scale * 0.001f;
|
||||
|
||||
// Calculate wave interference
|
||||
float wave_sum = 0.0f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float dx = coord.x - sources[i].x;
|
||||
float dy = coord.y - sources[i].y;
|
||||
float distance = sqrt(dx*dx + dy*dy);
|
||||
|
||||
float wave_phase = distance * sources[i].frequency + time_scaled * sources[i].phase_speed;
|
||||
wave_sum += sin(wave_phase) * sources[i].amplitude;
|
||||
}
|
||||
|
||||
// Add noise modulation for organic feel
|
||||
float noise_scale = params.noise_intensity;
|
||||
float noise_x = coord.x * 0xffff * noise_scale;
|
||||
float noise_y = coord.y * 0xffff * noise_scale;
|
||||
uint32_t noise_time = time_ms << params.time_bitshift;
|
||||
|
||||
float noise_mod = (inoise16(noise_x, noise_y, noise_time) - 32768) / 65536.0f;
|
||||
wave_sum += noise_mod * params.noise_amplitude;
|
||||
|
||||
// Map to color space
|
||||
return mapWaveToColor(wave_sum, params);
|
||||
}
|
||||
|
||||
private:
|
||||
CRGB mapWaveToColor(float wave_value, const PlasmaParams& params) {
|
||||
// Normalize wave to 0-1 range
|
||||
float normalized = (wave_value + 4.0f) / 8.0f; // Assuming max amplitude ~4
|
||||
normalized = constrain(normalized, 0.0f, 1.0f);
|
||||
|
||||
// Create flowing hue based on wave phase
|
||||
uint8_t hue = (uint8_t)(normalized * 255.0f + params.hue_offset) % 256;
|
||||
|
||||
// Dynamic saturation based on wave intensity
|
||||
float intensity = abs(wave_value);
|
||||
uint8_t sat = (uint8_t)(192 + intensity * 63); // High saturation with variation
|
||||
|
||||
// Brightness modulation
|
||||
uint8_t val = (uint8_t)(normalized * 255.0f * params.brightness);
|
||||
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
};
|
||||
|
||||
// Advanced Color Palette System
|
||||
class ColorPaletteManager {
|
||||
private:
|
||||
uint8_t current_palette = 0;
|
||||
uint32_t last_palette_change = 0;
|
||||
static const uint32_t PALETTE_CHANGE_INTERVAL = 5000; // 5 seconds
|
||||
|
||||
public:
|
||||
void update(uint32_t now, bool auto_cycle_enabled, uint8_t manual_palette) {
|
||||
if (auto_cycle_enabled) {
|
||||
if (now - last_palette_change > PALETTE_CHANGE_INTERVAL) {
|
||||
current_palette = (current_palette + 1) % 5;
|
||||
last_palette_change = now;
|
||||
}
|
||||
} else {
|
||||
current_palette = manual_palette;
|
||||
}
|
||||
}
|
||||
|
||||
CRGB mapColor(float hue_norm, float intensity, float special_param = 0.0f) {
|
||||
switch(current_palette) {
|
||||
case 0: return mapSunsetBoulevard(hue_norm, intensity, special_param);
|
||||
case 1: return mapOceanBreeze(hue_norm, intensity, special_param);
|
||||
case 2: return mapNeonNights(hue_norm, intensity, special_param);
|
||||
case 3: return mapForestWhisper(hue_norm, intensity, special_param);
|
||||
case 4: return mapGalaxyExpress(hue_norm, intensity, special_param);
|
||||
default: return mapSunsetBoulevard(hue_norm, intensity, special_param);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CRGB mapSunsetBoulevard(float hue_norm, float intensity, float special_param) {
|
||||
// Warm oranges, deep reds, golden yellows (Hue 0-45)
|
||||
uint8_t hue = (uint8_t)(hue_norm * 45);
|
||||
uint8_t sat = 200 + (uint8_t)(intensity * 55);
|
||||
uint8_t val = 150 + (uint8_t)(intensity * 105);
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
|
||||
CRGB mapOceanBreeze(float hue_norm, float intensity, float special_param) {
|
||||
// Deep blues, aqua, seafoam green (Hue 120-210)
|
||||
uint8_t hue = 120 + (uint8_t)(hue_norm * 90);
|
||||
uint8_t sat = 180 + (uint8_t)(intensity * 75);
|
||||
uint8_t val = 120 + (uint8_t)(intensity * 135);
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
|
||||
CRGB mapNeonNights(float hue_norm, float intensity, float special_param) {
|
||||
// Electric pink, cyan, purple, lime green - high contrast
|
||||
uint8_t base_hues[] = {0, 85, 128, 192}; // Red, Cyan, Pink, Purple
|
||||
uint8_t selected_hue = base_hues[(int)(hue_norm * 4) % 4];
|
||||
uint8_t sat = 255; // Maximum saturation for neon effect
|
||||
uint8_t val = 100 + (uint8_t)(intensity * 155);
|
||||
return CHSV(selected_hue, sat, val);
|
||||
}
|
||||
|
||||
CRGB mapForestWhisper(float hue_norm, float intensity, float special_param) {
|
||||
// Deep greens, earth browns, golden highlights (Hue 60-150)
|
||||
uint8_t hue = 60 + (uint8_t)(hue_norm * 90);
|
||||
uint8_t sat = 150 + (uint8_t)(intensity * 105);
|
||||
uint8_t val = 100 + (uint8_t)(intensity * 155);
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
|
||||
CRGB mapGalaxyExpress(float hue_norm, float intensity, float special_param) {
|
||||
// Deep purples, cosmic blues, silver stars (Hue 200-300)
|
||||
if (special_param > 0.8f) {
|
||||
// Silver/white stars
|
||||
uint8_t brightness = 200 + (uint8_t)(intensity * 55);
|
||||
return CRGB(brightness, brightness, brightness);
|
||||
} else {
|
||||
uint8_t hue = 200 + (uint8_t)(hue_norm * 100);
|
||||
uint8_t sat = 180 + (uint8_t)(intensity * 75);
|
||||
uint8_t val = 80 + (uint8_t)(intensity * 175);
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ALL 10 ALGORITHM IMPLEMENTATIONS
|
||||
CRGB drawCosmicSwirl(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float time_factor = time_ms * 0.0008f;
|
||||
|
||||
// Multi-octave noise for organic complexity
|
||||
float noise1 = inoise16(coord.x * 2000, coord.y * 2000, time_factor * 1000) / 65536.0f;
|
||||
float noise2 = inoise16(coord.x * 1000, coord.y * 1000, time_factor * 2000) / 65536.0f * 0.5f;
|
||||
float noise3 = inoise16(coord.x * 4000, coord.y * 4000, time_factor * 500) / 65536.0f * 0.25f;
|
||||
|
||||
float combined_noise = noise1 + noise2 + noise3;
|
||||
float hue_norm = (combined_noise + coord.angle / (2*M_PI) + 1.0f) * 0.5f;
|
||||
float intensity = (combined_noise + 1.0f) * 0.5f;
|
||||
|
||||
return palette.mapColor(hue_norm, intensity);
|
||||
}
|
||||
|
||||
CRGB drawElectricStorm(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
uint32_t fast_time = time_ms << 3; // 8x time acceleration
|
||||
|
||||
float x_noise = coord.x * 8000;
|
||||
float y_noise = coord.y * 8000;
|
||||
|
||||
uint16_t noise1 = inoise16(x_noise, y_noise, fast_time);
|
||||
uint16_t noise2 = inoise16(x_noise + 10000, y_noise + 10000, fast_time + 5000);
|
||||
|
||||
uint8_t threshold = 200;
|
||||
bool lightning = (noise1 >> 8) > threshold || (noise2 >> 8) > threshold;
|
||||
|
||||
if (lightning) {
|
||||
float lightning_intensity = max((noise1 >> 8) - threshold, (noise2 >> 8) - threshold) / 55.0f;
|
||||
return palette.mapColor(0.7f, lightning_intensity, 1.0f); // Special lightning effect
|
||||
} else {
|
||||
float storm_intensity = (noise1 >> 8) / 1020.0f; // Very low intensity for background
|
||||
return palette.mapColor(0.6f, storm_intensity);
|
||||
}
|
||||
}
|
||||
|
||||
CRGB drawLavaLamp(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float slow_time = time_ms * 0.0002f;
|
||||
|
||||
float blob_scale = 800;
|
||||
uint16_t primary_noise = inoise16(coord.x * blob_scale, coord.y * blob_scale, slow_time * 1000);
|
||||
uint16_t secondary_noise = inoise16(coord.x * blob_scale * 0.5f, coord.y * blob_scale * 0.5f, slow_time * 1500);
|
||||
|
||||
float blob_value = (primary_noise + secondary_noise * 0.3f) / 65536.0f;
|
||||
|
||||
if (blob_value > 0.6f) {
|
||||
// Hot blob center
|
||||
float intensity = (blob_value - 0.6f) / 0.4f;
|
||||
return palette.mapColor(0.1f, intensity); // Warm colors
|
||||
} else if (blob_value > 0.3f) {
|
||||
// Blob edge gradient
|
||||
float edge_factor = (blob_value - 0.3f) / 0.3f;
|
||||
return palette.mapColor(0.2f, edge_factor);
|
||||
} else {
|
||||
// Background
|
||||
return palette.mapColor(0.8f, 0.2f); // Cool background
|
||||
}
|
||||
}
|
||||
|
||||
CRGB drawDigitalRain(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float vertical_pos = sin(coord.angle) * 0.5f + 0.5f;
|
||||
float cascade_speed = 0.002f;
|
||||
float time_offset = time_ms * cascade_speed;
|
||||
|
||||
int stream_id = (int)(coord.angle * 10) % 8;
|
||||
float stream_phase = fmod(vertical_pos + time_offset + stream_id * 0.125f, 1.0f);
|
||||
|
||||
uint16_t noise = inoise16(stream_id * 1000, stream_phase * 10000, time_ms / 4);
|
||||
uint8_t digital_value = (noise >> 8) > 128 ? 255 : 0;
|
||||
|
||||
if (digital_value > 0) {
|
||||
float intensity = 1.0f - stream_phase * 0.8f; // Fade trailing
|
||||
return palette.mapColor(0.4f, intensity); // Matrix green area
|
||||
} else {
|
||||
return CRGB::Black;
|
||||
}
|
||||
}
|
||||
|
||||
CRGB drawGlitchCity(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
uint32_t glitch_time = (time_ms / 100) * 100; // Quantize time
|
||||
|
||||
uint16_t noise1 = inoise16(coord.x * 3000, coord.y * 3000, glitch_time);
|
||||
uint16_t noise2 = inoise16(coord.x * 5000, coord.y * 5000, glitch_time + 1000);
|
||||
|
||||
uint16_t glitch_value = noise1 ^ noise2; // XOR for harsh digital effects
|
||||
|
||||
if ((glitch_value & 0xF000) == 0xF000) {
|
||||
return CRGB(255, 255, 255); // Full-bright glitch flash
|
||||
}
|
||||
|
||||
float intensity = (glitch_value & 0xFF) / 255.0f;
|
||||
float hue_chaos = ((glitch_value >> 8) & 0xFF) / 255.0f;
|
||||
|
||||
return palette.mapColor(hue_chaos, intensity, 0.5f);
|
||||
}
|
||||
|
||||
CRGB drawOceanDepths(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float ocean_time = time_ms * 0.0005f;
|
||||
|
||||
float current1 = inoise16(coord.x * 1200, coord.y * 1200, ocean_time * 800) / 65536.0f;
|
||||
float current2 = inoise16(coord.x * 2400, coord.y * 2400, ocean_time * 600) / 65536.0f * 0.5f;
|
||||
float current3 = inoise16(coord.x * 600, coord.y * 600, ocean_time * 1000) / 65536.0f * 0.3f;
|
||||
|
||||
float depth_factor = (current1 + current2 + current3 + 1.5f) / 3.0f;
|
||||
float hue_variation = (current2 + 0.5f);
|
||||
|
||||
return palette.mapColor(hue_variation, depth_factor);
|
||||
}
|
||||
|
||||
CRGB drawFireDance(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float vertical_component = sin(coord.angle) * 0.5f + 0.5f;
|
||||
|
||||
float flame_x = coord.x * 1500;
|
||||
float flame_y = coord.y * 1500 + time_ms * 0.003f;
|
||||
|
||||
uint16_t turbulence = inoise16(flame_x, flame_y, time_ms);
|
||||
float flame_intensity = (turbulence / 65536.0f) * (1.0f - vertical_component * 0.3f);
|
||||
|
||||
float fire_hue = flame_intensity * 0.15f; // Red to orange range
|
||||
return palette.mapColor(fire_hue, flame_intensity);
|
||||
}
|
||||
|
||||
CRGB drawNebulaDrift(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float nebula_time = time_ms * 0.0003f;
|
||||
|
||||
float cloud1 = inoise16(coord.x * 800, coord.y * 800, nebula_time * 1000) / 65536.0f;
|
||||
float cloud2 = inoise16(coord.x * 1600, coord.y * 1600, nebula_time * 700) / 65536.0f * 0.5f;
|
||||
float cloud3 = inoise16(coord.x * 400, coord.y * 400, nebula_time * 1200) / 65536.0f * 0.25f;
|
||||
|
||||
float nebula_density = cloud1 + cloud2 + cloud3;
|
||||
|
||||
uint16_t star_noise = inoise16(coord.x * 4000, coord.y * 4000, nebula_time * 200);
|
||||
bool is_star = (star_noise > 60000);
|
||||
|
||||
if (is_star) {
|
||||
float star_intensity = (star_noise - 60000) / 5536.0f;
|
||||
return palette.mapColor(0.0f, star_intensity, 1.0f); // Stars
|
||||
} else {
|
||||
float hue_drift = (nebula_density + 1.0f) * 0.5f;
|
||||
float intensity = (nebula_density + 1.0f) * 0.4f;
|
||||
return palette.mapColor(hue_drift, intensity);
|
||||
}
|
||||
}
|
||||
|
||||
CRGB drawBinaryPulse(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
float pulse_period = 2000.0f;
|
||||
float pulse_phase = fmod(time_ms, pulse_period) / pulse_period;
|
||||
|
||||
float distance_from_center = sqrt(coord.x * coord.x + coord.y * coord.y);
|
||||
|
||||
float ring_frequency = 5.0f;
|
||||
float pulse_offset = pulse_phase * 2.0f;
|
||||
float ring_value = sin((distance_from_center * ring_frequency - pulse_offset) * 2 * M_PI);
|
||||
|
||||
uint16_t noise = inoise16(coord.x * 2000, coord.y * 2000, time_ms / 8);
|
||||
float digital_mod = ((noise >> 8) > 128) ? 1.0f : -0.5f;
|
||||
|
||||
float final_value = ring_value * digital_mod;
|
||||
|
||||
if (final_value > 0.3f) {
|
||||
return palette.mapColor(0.8f, final_value, 0.8f); // Active pulse
|
||||
} else if (final_value > -0.2f) {
|
||||
float transition_intensity = (final_value + 0.2f) * 2.0f;
|
||||
return palette.mapColor(0.3f, transition_intensity); // Transition zones
|
||||
} else {
|
||||
return palette.mapColor(0.7f, 0.1f); // Background
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced variant manager with ALL 10 ALGORITHMS and smooth transitions
|
||||
class NoiseVariantManager {
|
||||
private:
|
||||
uint8_t current_variant = 0;
|
||||
uint8_t target_variant = 0;
|
||||
float transition_progress = 1.0f; // 0.0 = old, 1.0 = new
|
||||
uint32_t transition_start = 0;
|
||||
static const uint32_t TRANSITION_DURATION = 1500; // 1.5 second fade for smoother transitions
|
||||
|
||||
// Algorithm instances
|
||||
PlasmaWaveGenerator plasma_gen;
|
||||
PlasmaParams plasma_params;
|
||||
ColorPaletteManager& palette_manager;
|
||||
|
||||
public:
|
||||
NoiseVariantManager(ColorPaletteManager& palette_mgr) : palette_manager(palette_mgr) {}
|
||||
|
||||
void update(uint32_t now, bool auto_cycle_enabled, uint8_t manual_variant, const PlasmaParams& params) {
|
||||
plasma_params = params;
|
||||
|
||||
// Handle automatic cycling vs manual override
|
||||
if (auto_cycle_enabled) {
|
||||
EVERY_N_MILLISECONDS(12000) { // Slightly longer for each variant
|
||||
startTransition((current_variant + 1) % 10, now); // ALL 10 variants
|
||||
}
|
||||
} else if (manual_variant != target_variant && transition_progress >= 1.0f) {
|
||||
// Manual override
|
||||
startTransition(manual_variant, now);
|
||||
}
|
||||
|
||||
// Update transition progress
|
||||
if (transition_progress < 1.0f) {
|
||||
uint32_t elapsed = now - transition_start;
|
||||
transition_progress = min(1.0f, elapsed / (float)TRANSITION_DURATION);
|
||||
|
||||
if (transition_progress >= 1.0f) {
|
||||
current_variant = target_variant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CRGB renderPixel(const RingCoord& coord, uint32_t time_ms) {
|
||||
if (transition_progress >= 1.0f) {
|
||||
// No transition, render current variant
|
||||
return renderVariant(current_variant, coord, time_ms);
|
||||
} else {
|
||||
// Advanced cross-fade with brightness preservation
|
||||
CRGB old_color = renderVariant(current_variant, coord, time_ms);
|
||||
CRGB new_color = renderVariant(target_variant, coord, time_ms);
|
||||
return smoothLerpCRGB(old_color, new_color, transition_progress);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t getCurrentVariant() const { return current_variant; }
|
||||
const char* getCurrentVariantName() const {
|
||||
const char* names[] = {
|
||||
"Cosmic Swirl", "Electric Storm", "Lava Lamp", "Digital Rain", "Plasma Waves",
|
||||
"Glitch City", "Ocean Depths", "Fire Dance", "Nebula Drift", "Binary Pulse"
|
||||
};
|
||||
return names[current_variant % 10];
|
||||
}
|
||||
|
||||
private:
|
||||
void startTransition(uint8_t new_variant, uint32_t now) {
|
||||
target_variant = new_variant % 10; // Ensure valid range
|
||||
transition_start = now;
|
||||
transition_progress = 0.0f;
|
||||
}
|
||||
|
||||
CRGB renderVariant(uint8_t variant, const RingCoord& coord, uint32_t time_ms) {
|
||||
switch(variant % 10) {
|
||||
case 0: return drawCosmicSwirl(coord, time_ms, palette_manager);
|
||||
case 1: return drawElectricStorm(coord, time_ms, palette_manager);
|
||||
case 2: return drawLavaLamp(coord, time_ms, palette_manager);
|
||||
case 3: return drawDigitalRain(coord, time_ms, palette_manager);
|
||||
case 4: return drawPlasmaWithPalette(coord, time_ms, palette_manager);
|
||||
case 5: return drawGlitchCity(coord, time_ms, palette_manager);
|
||||
case 6: return drawOceanDepths(coord, time_ms, palette_manager);
|
||||
case 7: return drawFireDance(coord, time_ms, palette_manager);
|
||||
case 8: return drawNebulaDrift(coord, time_ms, palette_manager);
|
||||
case 9: return drawBinaryPulse(coord, time_ms, palette_manager);
|
||||
default: return drawCosmicSwirl(coord, time_ms, palette_manager);
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced Plasma Waves with palette integration
|
||||
CRGB drawPlasmaWithPalette(const RingCoord& coord, uint32_t time_ms, ColorPaletteManager& palette) {
|
||||
// Generate base plasma waves
|
||||
CRGB plasma_color = plasma_gen.calculatePlasmaPixel(coord, time_ms, plasma_params);
|
||||
|
||||
// Extract intensity and hue information from plasma
|
||||
float intensity = (plasma_color.r + plasma_color.g + plasma_color.b) / 765.0f;
|
||||
|
||||
// Calculate wave interference for hue mapping
|
||||
float time_scaled = time_ms * plasma_params.time_scale * 0.001f;
|
||||
float wave_sum = 0.0f;
|
||||
|
||||
// Simplified wave calculation for hue determination
|
||||
float dx = coord.x - 0.5f;
|
||||
float dy = coord.y - 0.5f;
|
||||
float distance = sqrt(dx*dx + dy*dy);
|
||||
float wave_phase = distance * 2.0f + time_scaled * 1.5f;
|
||||
wave_sum = sin(wave_phase);
|
||||
|
||||
float hue_norm = (wave_sum + 1.0f) * 0.5f; // Normalize to 0-1
|
||||
|
||||
// Use palette system for consistent color theming
|
||||
return palette.mapColor(hue_norm, intensity, intensity > 0.8f ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
// Enhanced interpolation with brightness preservation and smooth curves
|
||||
CRGB smoothLerpCRGB(const CRGB& a, const CRGB& b, float t) {
|
||||
// Apply smooth curve to transition
|
||||
float smooth_t = t * t * (3.0f - 2.0f * t); // Smoothstep function
|
||||
|
||||
// Preserve brightness during transition to avoid flickering
|
||||
float brightness_a = (a.r + a.g + a.b) / 765.0f;
|
||||
float brightness_b = (b.r + b.g + b.b) / 765.0f;
|
||||
float target_brightness = brightness_a + (brightness_b - brightness_a) * smooth_t;
|
||||
|
||||
CRGB result = CRGB(
|
||||
a.r + (int)((b.r - a.r) * smooth_t),
|
||||
a.g + (int)((b.g - a.g) * smooth_t),
|
||||
a.b + (int)((b.b - a.b) * smooth_t)
|
||||
);
|
||||
|
||||
// Brightness compensation
|
||||
float current_brightness = (result.r + result.g + result.b) / 765.0f;
|
||||
if (current_brightness > 0.01f) {
|
||||
float compensation = target_brightness / current_brightness;
|
||||
compensation = min(compensation, 2.0f); // Limit boost
|
||||
result.r = min(255, (int)(result.r * compensation));
|
||||
result.g = min(255, (int)(result.g * compensation));
|
||||
result.b = min(255, (int)(result.b * compensation));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// ALL 10 Variant names for UI
|
||||
fl::string variant_names[10] = {
|
||||
"Cosmic Swirl", "Electric Storm", "Lava Lamp", "Digital Rain", "Plasma Waves",
|
||||
"Glitch City", "Ocean Depths", "Fire Dance", "Nebula Drift", "Binary Pulse"
|
||||
};
|
||||
|
||||
// 5 Color Palette names for UI
|
||||
fl::string palette_names[5] = {
|
||||
"Sunset Boulevard", "Ocean Breeze", "Neon Nights", "Forest Whisper", "Galaxy Express"
|
||||
};
|
||||
|
||||
// Helper functions to get indices from names
|
||||
uint8_t getVariantIndex(const fl::string& name) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (variant_names[i] == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0; // Default to first variant
|
||||
}
|
||||
|
||||
uint8_t getPaletteIndex(const fl::string& name) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (palette_names[i] == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0; // Default to first palette
|
||||
}
|
||||
|
||||
// Global instances - order matters for initialization
|
||||
ColorPaletteManager palette_manager;
|
||||
NoiseVariantManager variant_manager(palette_manager);
|
||||
RingLUT ring_lut;
|
||||
|
||||
// KICKASS UI controls - comprehensive control suite
|
||||
UISlider brightness("Brightness", 1, 0, 1);
|
||||
UISlider scale("Scale", 4, .1, 4, .1);
|
||||
UISlider timeBitshift("Time Bitshift", 5, 0, 16, 1);
|
||||
UISlider timescale("Time Scale", 1, .1, 10, .1);
|
||||
|
||||
// Advanced variant and palette controls
|
||||
UIDropdown variants("Noise Variants", variant_names);
|
||||
UIDropdown palettes("Color Palettes", palette_names);
|
||||
UICheckbox autoCycle("Auto Cycle Effects", true);
|
||||
UICheckbox autoPalette("Auto Cycle Palettes", true);
|
||||
// This PIR type is special because it will bind to a pin for a real device,
|
||||
// but also provides a UIButton when run in the simulator.
|
||||
Pir pir(PIN_PIR, PIR_LATCH_MS, PIR_RISING_TIME, PIR_FALLING_TIME);
|
||||
UICheckbox useDither("Use Binary Dither", true);
|
||||
|
||||
Timer timer;
|
||||
float current_brightness = 0;
|
||||
|
||||
// Save a pointer to the controller so that we can modify the dither in real time.
|
||||
CLEDController* controller = nullptr;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
// ScreenMap is purely something that is needed for the sketch to correctly
|
||||
// show on the web display. For deployements to real devices, this essentially
|
||||
// becomes a no-op.
|
||||
ScreenMap xyMap = ScreenMap::Circle(NUM_LEDS, 2.0, 2.0);
|
||||
controller = &FastLED.addLeds<WS2811, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setDither(DISABLE_DITHER)
|
||||
.setScreenMap(xyMap);
|
||||
FastLED.setBrightness(brightness);
|
||||
pir.activate(millis()); // Activate the PIR sensor on startup.
|
||||
|
||||
// Initialize performance optimizations
|
||||
ring_lut.initialize();
|
||||
}
|
||||
|
||||
void draw(uint32_t now) {
|
||||
// Configure plasma parameters from UI controls with enhanced scaling
|
||||
PlasmaParams plasma_params;
|
||||
plasma_params.time_scale = timescale.as<float>();
|
||||
plasma_params.noise_intensity = scale.as<float>() * 0.8f; // Slightly reduce for better visual balance
|
||||
plasma_params.brightness = brightness.as<float>();
|
||||
plasma_params.time_bitshift = timeBitshift.as<int>();
|
||||
plasma_params.hue_offset = (now / 100) % 256; // Slow hue rotation for extra dynamism
|
||||
plasma_params.noise_amplitude = 0.6f + 0.4f * sin(now * 0.001f); // Breathing noise effect
|
||||
|
||||
// Update palette manager with auto-cycling and manual control
|
||||
palette_manager.update(now, autoPalette.value(), getPaletteIndex(palettes.value()));
|
||||
|
||||
// Update variant manager with enhanced parameters
|
||||
variant_manager.update(now, autoCycle.value(), getVariantIndex(variants.value()), plasma_params);
|
||||
|
||||
// KICKASS rendering with performance optimizations
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
RingCoord coord = ring_lut.fastRingCoord(i);
|
||||
CRGB pixel_color = variant_manager.renderPixel(coord, now);
|
||||
|
||||
// Apply global brightness and gamma correction for better visual quality
|
||||
float global_brightness = brightness.as<float>();
|
||||
pixel_color.r = (uint8_t)(pixel_color.r * global_brightness);
|
||||
pixel_color.g = (uint8_t)(pixel_color.g * global_brightness);
|
||||
pixel_color.b = (uint8_t)(pixel_color.b * global_brightness);
|
||||
|
||||
leds[i] = pixel_color;
|
||||
}
|
||||
|
||||
// Optional: Add subtle sparkle overlay for extra visual interest
|
||||
EVERY_N_MILLISECONDS(50) {
|
||||
// Add random sparkles to 1% of LEDs
|
||||
int sparkle_count = NUM_LEDS / 100 + 1;
|
||||
for (int s = 0; s < sparkle_count; s++) {
|
||||
int sparkle_pos = random16() % NUM_LEDS;
|
||||
if (random8() > 250) { // Very rare sparkles
|
||||
leds[sparkle_pos] = blend(leds[sparkle_pos], CRGB::White, 128);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Allow the dither to be enabled and disabled.
|
||||
controller->setDither(useDither ? BINARY_DITHER : DISABLE_DITHER);
|
||||
uint32_t now = millis();
|
||||
uint8_t bri = pir.transition(now);
|
||||
FastLED.setBrightness(bri * brightness.as<float>());
|
||||
// Apply leds generation to the leds.
|
||||
draw(now);
|
||||
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "fl/sketch_macros.h"
|
||||
|
||||
#if SKETCH_HAS_LOTS_OF_MEMORY
|
||||
|
||||
#include "FxNoiseRing.h"
|
||||
|
||||
#else
|
||||
void setup() {}
|
||||
void loop() {}
|
||||
|
||||
#endif // SKETCH_HAS_LOTS_OF_MEMORY
|
||||
@@ -0,0 +1,760 @@
|
||||
# NoiseRing Enhanced Design Document
|
||||
|
||||
## Overview
|
||||
Enhanced version of the FxNoiseRing example that automatically cycles through different noise effects and color palettes, providing dynamic visual variety with user controls for manual selection.
|
||||
|
||||
**Featured Implementation**: **Plasma Waves** - Advanced graphics technique showcasing sine wave interference with noise modulation, demonstrating sophisticated mathematical visualization on circular LED arrays.
|
||||
|
||||
## Core Features
|
||||
|
||||
### Automatic Cycling
|
||||
- **Palette Rotation**: Every 5 seconds using `EVERY_N_MILLISECONDS(5000)`
|
||||
- **Noise Effect Rotation**: Every 10 seconds using `EVERY_N_MILLISECONDS(10000)`
|
||||
- **User Override**: Dropdown controls allow manual selection to override automatic cycling
|
||||
|
||||
### User Interface Controls
|
||||
- **Variants Dropdown**: "Noise Variants" - Manual selection of noise effects (0-9)
|
||||
- **Palettes Dropdown**: "Color Palettes" - Manual selection of color schemes (0-4)
|
||||
- **Auto Cycle Checkbox**: "Auto Cycle" - Enable/disable automatic rotation
|
||||
- **Existing Controls**: Retain all current sliders (Brightness, Scale, Time Bitshift, Time Scale, PIR, Dither)
|
||||
|
||||
## 10 Noise Variations - Detailed Algorithmic Implementation
|
||||
|
||||
### 1. "Cosmic Swirl" - Enhanced Perlin Flow
|
||||
- **Description**: Classic perlin noise with slow, flowing movements using multi-octave complexity
|
||||
- **Parameters**: Base noise with moderate scale, gentle time progression
|
||||
- **Characteristics**: Smooth gradients, organic flow patterns
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawCosmicSwirl(const RingCoord& coord, uint32_t time_ms) {
|
||||
float time_factor = time_ms * 0.0008f;
|
||||
|
||||
// Multi-octave noise for organic complexity
|
||||
float noise1 = inoise16(coord.x * 2000, coord.y * 2000, time_factor * 1000) / 65536.0f;
|
||||
float noise2 = inoise16(coord.x * 1000, coord.y * 1000, time_factor * 2000) / 65536.0f * 0.5f;
|
||||
float noise3 = inoise16(coord.x * 4000, coord.y * 4000, time_factor * 500) / 65536.0f * 0.25f;
|
||||
|
||||
float combined_noise = noise1 + noise2 + noise3;
|
||||
|
||||
// Flowing hue with gentle progression
|
||||
uint8_t hue = (uint8_t)((combined_noise + coord.angle / (2*M_PI)) * 255) % 256;
|
||||
uint8_t sat = 220 + (uint8_t)(abs(noise2) * 35);
|
||||
uint8_t val = 180 + (uint8_t)(combined_noise * 75);
|
||||
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. "Electric Storm" - High-Frequency Chaos
|
||||
- **Description**: High-frequency noise with rapid temporal changes creating lightning effects
|
||||
- **Parameters**: 8x time acceleration, high spatial frequency, quantized thresholds
|
||||
- **Characteristics**: Crackling, energetic, lightning-like patterns
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawElectricStorm(const RingCoord& coord, uint32_t time_ms) {
|
||||
// Rapid temporal changes with quantized effects
|
||||
uint32_t fast_time = time_ms << 3; // 8x time acceleration
|
||||
|
||||
// High-frequency spatial noise
|
||||
float x_noise = coord.x * 8000;
|
||||
float y_noise = coord.y * 8000;
|
||||
|
||||
uint16_t noise1 = inoise16(x_noise, y_noise, fast_time);
|
||||
uint16_t noise2 = inoise16(x_noise + 10000, y_noise + 10000, fast_time + 5000);
|
||||
|
||||
// Create lightning-like quantization
|
||||
uint8_t threshold = 200;
|
||||
bool lightning = (noise1 >> 8) > threshold || (noise2 >> 8) > threshold;
|
||||
|
||||
if (lightning) {
|
||||
// Bright electric flash
|
||||
uint8_t intensity = max((noise1 >> 8) - threshold, (noise2 >> 8) - threshold) * 4;
|
||||
return CRGB(intensity, intensity, 255); // Electric blue-white
|
||||
} else {
|
||||
// Dark storm background
|
||||
uint8_t hue = 160 + ((noise1 >> 10) % 32); // Blue-purple range
|
||||
return CHSV(hue, 255, (noise1 >> 8) / 4); // Low brightness
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. "Lava Lamp" - Slow Blobby Movement
|
||||
- **Description**: Slow, blobby movements with high contrast using low-frequency modulation
|
||||
- **Parameters**: Ultra-low frequency, high amplitude, threshold-based blob creation
|
||||
- **Characteristics**: Large, slow-moving color blobs with organic boundaries
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawLavaLamp(const RingCoord& coord, uint32_t time_ms) {
|
||||
float slow_time = time_ms * 0.0002f; // Very slow movement
|
||||
|
||||
// Large-scale blob generation
|
||||
float blob_scale = 800; // Large spatial scale for big blobs
|
||||
uint16_t primary_noise = inoise16(coord.x * blob_scale, coord.y * blob_scale, slow_time * 1000);
|
||||
uint16_t secondary_noise = inoise16(coord.x * blob_scale * 0.5f, coord.y * blob_scale * 0.5f, slow_time * 1500);
|
||||
|
||||
// Create blob boundaries with thresholding
|
||||
float blob_value = (primary_noise + secondary_noise * 0.3f) / 65536.0f;
|
||||
|
||||
// High contrast blob regions
|
||||
if (blob_value > 0.6f) {
|
||||
// Hot blob center
|
||||
uint8_t hue = 0 + (uint8_t)((blob_value - 0.6f) * 400); // Red to orange
|
||||
return CHSV(hue, 255, 255);
|
||||
} else if (blob_value > 0.3f) {
|
||||
// Blob edge gradient
|
||||
float edge_factor = (blob_value - 0.3f) / 0.3f;
|
||||
uint8_t brightness = (uint8_t)(edge_factor * 255);
|
||||
return CHSV(20, 200, brightness); // Orange edge
|
||||
} else {
|
||||
// Background
|
||||
return CHSV(240, 100, 30); // Dark blue background
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. "Digital Rain" - Matrix Cascade
|
||||
- **Description**: Matrix-style cascading effect using vertical noise mapping
|
||||
- **Parameters**: Angle-to-vertical conversion, time-based cascade, stream segregation
|
||||
- **Characteristics**: Vertical streams, binary-like transitions, matrix green
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawDigitalRain(const RingCoord& coord, uint32_t time_ms) {
|
||||
// Convert angle to vertical position for cascade effect
|
||||
float vertical_pos = sin(coord.angle) * 0.5f + 0.5f; // 0-1 range
|
||||
|
||||
// Time-based cascade with varying speeds
|
||||
float cascade_speed = 0.002f;
|
||||
float time_offset = time_ms * cascade_speed;
|
||||
|
||||
// Create vertical streams
|
||||
int stream_id = (int)(coord.angle * 10) % 8; // 8 distinct streams
|
||||
float stream_phase = fmod(vertical_pos + time_offset + stream_id * 0.125f, 1.0f);
|
||||
|
||||
// Binary-like transitions
|
||||
uint16_t noise = inoise16(stream_id * 1000, stream_phase * 10000, time_ms / 4);
|
||||
uint8_t digital_value = (noise >> 8) > 128 ? 255 : 0;
|
||||
|
||||
// Matrix green with digital artifacts
|
||||
uint8_t green_intensity = digital_value;
|
||||
uint8_t trailing = max(0, green_intensity - (int)(stream_phase * 200));
|
||||
|
||||
return CRGB(0, green_intensity, trailing / 2);
|
||||
}
|
||||
```
|
||||
|
||||
### 5. "Plasma Waves" - **FEATURED IMPLEMENTATION**
|
||||
- **Description**: Multiple overlapping sine waves with noise modulation creating electromagnetic plasma effects
|
||||
- **Parameters**: 4-source wave interference, noise modulation, dynamic color mapping
|
||||
- **Characteristics**: Smooth wave interference patterns, flowing electromagnetic appearance
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
class PlasmaWaveGenerator {
|
||||
private:
|
||||
struct WaveSource {
|
||||
float x, y; // Source position
|
||||
float frequency; // Wave frequency
|
||||
float amplitude; // Wave strength
|
||||
float phase_speed; // Phase evolution rate
|
||||
};
|
||||
|
||||
WaveSource sources[4] = {
|
||||
{0.5f, 0.5f, 1.0f, 1.0f, 0.8f}, // Center source
|
||||
{0.0f, 0.0f, 1.5f, 0.8f, 1.2f}, // Corner source
|
||||
{1.0f, 1.0f, 0.8f, 1.2f, 0.6f}, // Opposite corner
|
||||
{0.5f, 0.0f, 1.2f, 0.9f, 1.0f} // Edge source
|
||||
};
|
||||
|
||||
public:
|
||||
CRGB calculatePlasmaPixel(const RingCoord& coord, uint32_t time_ms, const PlasmaParams& params) {
|
||||
float time_scaled = time_ms * params.time_scale * 0.001f;
|
||||
|
||||
// Calculate wave interference
|
||||
float wave_sum = 0.0f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float dx = coord.x - sources[i].x;
|
||||
float dy = coord.y - sources[i].y;
|
||||
float distance = sqrt(dx*dx + dy*dy);
|
||||
|
||||
float wave_phase = distance * sources[i].frequency + time_scaled * sources[i].phase_speed;
|
||||
wave_sum += sin(wave_phase) * sources[i].amplitude;
|
||||
}
|
||||
|
||||
// Add noise modulation for organic feel
|
||||
float noise_scale = params.noise_intensity;
|
||||
float noise_x = coord.x * 0xffff * noise_scale;
|
||||
float noise_y = coord.y * 0xffff * noise_scale;
|
||||
uint32_t noise_time = time_ms << params.time_bitshift;
|
||||
|
||||
float noise_mod = (inoise16(noise_x, noise_y, noise_time) - 32768) / 65536.0f;
|
||||
wave_sum += noise_mod * params.noise_amplitude;
|
||||
|
||||
// Map to color space
|
||||
return mapWaveToColor(wave_sum, params);
|
||||
}
|
||||
|
||||
private:
|
||||
CRGB mapWaveToColor(float wave_value, const PlasmaParams& params) {
|
||||
// Normalize wave to 0-1 range
|
||||
float normalized = (wave_value + 4.0f) / 8.0f; // Assuming max amplitude ~4
|
||||
normalized = constrain(normalized, 0.0f, 1.0f);
|
||||
|
||||
// Create flowing hue based on wave phase
|
||||
uint8_t hue = (uint8_t)(normalized * 255.0f + params.hue_offset) % 256;
|
||||
|
||||
// Dynamic saturation based on wave intensity
|
||||
float intensity = abs(wave_value);
|
||||
uint8_t sat = (uint8_t)(192 + intensity * 63); // High saturation with variation
|
||||
|
||||
// Brightness modulation
|
||||
uint8_t val = (uint8_t)(normalized * 255.0f * params.brightness);
|
||||
|
||||
return CHSV(hue, sat, val);
|
||||
}
|
||||
};
|
||||
|
||||
struct PlasmaParams {
|
||||
float time_scale = 1.0f;
|
||||
float noise_intensity = 0.5f;
|
||||
float noise_amplitude = 0.8f;
|
||||
uint8_t time_bitshift = 5;
|
||||
uint8_t hue_offset = 0;
|
||||
float brightness = 1.0f;
|
||||
};
|
||||
```
|
||||
|
||||
### 6. "Glitch City" - Chaotic Digital Artifacts
|
||||
- **Description**: Chaotic, stuttering effects with quantized noise and bit manipulation
|
||||
- **Parameters**: Time quantization, XOR operations, random bit shifts
|
||||
- **Characteristics**: Harsh transitions, digital artifacts, strobe-like effects
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawGlitchCity(const RingCoord& coord, uint32_t time_ms) {
|
||||
// Stuttering time progression
|
||||
uint32_t glitch_time = (time_ms / 100) * 100; // Quantize time to create stutters
|
||||
|
||||
// Bit manipulation for digital artifacts
|
||||
uint16_t noise1 = inoise16(coord.x * 3000, coord.y * 3000, glitch_time);
|
||||
uint16_t noise2 = inoise16(coord.x * 5000, coord.y * 5000, glitch_time + 1000);
|
||||
|
||||
// XOR operation for harsh digital effects
|
||||
uint16_t glitch_value = noise1 ^ noise2;
|
||||
|
||||
// Random bit shifts for channel corruption
|
||||
uint8_t r = (glitch_value >> (time_ms % 8)) & 0xFF;
|
||||
uint8_t g = (glitch_value << (time_ms % 5)) & 0xFF;
|
||||
uint8_t b = ((noise1 | noise2) >> 4) & 0xFF;
|
||||
|
||||
// Occasional full-bright flashes
|
||||
if ((glitch_value & 0xF000) == 0xF000) {
|
||||
return CRGB(255, 255, 255);
|
||||
}
|
||||
|
||||
return CRGB(r, g, b);
|
||||
}
|
||||
```
|
||||
|
||||
### 7. "Ocean Depths" - Underwater Currents
|
||||
- **Description**: Slow, deep undulations mimicking underwater currents with blue-green bias
|
||||
- **Parameters**: Ultra-low frequency, blue-green color bias, depth-based brightness
|
||||
- **Characteristics**: Calm, flowing, deep water feel with gentle undulations
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawOceanDepths(const RingCoord& coord, uint32_t time_ms) {
|
||||
float ocean_time = time_ms * 0.0005f; // Very slow like deep water
|
||||
|
||||
// Multi-layer current simulation
|
||||
float current1 = inoise16(coord.x * 1200, coord.y * 1200, ocean_time * 800) / 65536.0f;
|
||||
float current2 = inoise16(coord.x * 2400, coord.y * 2400, ocean_time * 600) / 65536.0f * 0.5f;
|
||||
float current3 = inoise16(coord.x * 600, coord.y * 600, ocean_time * 1000) / 65536.0f * 0.3f;
|
||||
|
||||
float depth_factor = current1 + current2 + current3;
|
||||
|
||||
// Ocean color palette (blue-green spectrum)
|
||||
uint8_t base_hue = 140; // Cyan-blue
|
||||
uint8_t hue_variation = (uint8_t)(abs(depth_factor) * 40); // Vary within blue-green
|
||||
uint8_t final_hue = (base_hue + hue_variation) % 256;
|
||||
|
||||
// Depth-based brightness (deeper = darker)
|
||||
uint8_t depth_brightness = 120 + (uint8_t)(depth_factor * 135);
|
||||
uint8_t saturation = 200 + (uint8_t)(abs(current2) * 55);
|
||||
|
||||
return CHSV(final_hue, saturation, depth_brightness);
|
||||
}
|
||||
```
|
||||
|
||||
### 8. "Fire Dance" - Upward Flame Simulation
|
||||
- **Description**: Flickering, flame-like patterns with upward bias and turbulent noise
|
||||
- **Parameters**: Vertical gradient bias, turbulent noise, fire color palette
|
||||
- **Characteristics**: Orange/red dominated, upward movement, flickering
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawFireDance(const RingCoord& coord, uint32_t time_ms) {
|
||||
// Vertical bias for upward flame movement
|
||||
float vertical_component = sin(coord.angle) * 0.5f + 0.5f; // 0 at bottom, 1 at top
|
||||
|
||||
// Turbulent noise with upward bias
|
||||
float flame_x = coord.x * 1500;
|
||||
float flame_y = coord.y * 1500 + time_ms * 0.003f; // Upward drift
|
||||
|
||||
uint16_t turbulence = inoise16(flame_x, flame_y, time_ms);
|
||||
float flame_intensity = (turbulence / 65536.0f) * (1.0f - vertical_component * 0.3f);
|
||||
|
||||
// Fire color palette (red->orange->yellow)
|
||||
uint8_t base_hue = 0; // Red
|
||||
uint8_t hue_variation = (uint8_t)(flame_intensity * 45); // Up to orange/yellow
|
||||
uint8_t final_hue = (base_hue + hue_variation) % 256;
|
||||
|
||||
uint8_t saturation = 255 - (uint8_t)(vertical_component * 100); // Less saturated at top
|
||||
uint8_t brightness = (uint8_t)(flame_intensity * 255);
|
||||
|
||||
return CHSV(final_hue, saturation, brightness);
|
||||
}
|
||||
```
|
||||
|
||||
### 9. "Nebula Drift" - Cosmic Cloud Simulation
|
||||
- **Description**: Slow cosmic clouds with starfield sparkles using multi-octave noise
|
||||
- **Parameters**: Multiple noise octaves, sparse bright spots, cosmic color palette
|
||||
- **Characteristics**: Misty backgrounds with occasional bright stars
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawNebulaDrift(const RingCoord& coord, uint32_t time_ms) {
|
||||
float nebula_time = time_ms * 0.0003f; // Cosmic slow drift
|
||||
|
||||
// Multi-octave nebula clouds
|
||||
float cloud1 = inoise16(coord.x * 800, coord.y * 800, nebula_time * 1000) / 65536.0f;
|
||||
float cloud2 = inoise16(coord.x * 1600, coord.y * 1600, nebula_time * 700) / 65536.0f * 0.5f;
|
||||
float cloud3 = inoise16(coord.x * 400, coord.y * 400, nebula_time * 1200) / 65536.0f * 0.25f;
|
||||
|
||||
float nebula_density = cloud1 + cloud2 + cloud3;
|
||||
|
||||
// Sparse starfield generation
|
||||
uint16_t star_noise = inoise16(coord.x * 4000, coord.y * 4000, nebula_time * 200);
|
||||
bool is_star = (star_noise > 60000); // Very sparse stars
|
||||
|
||||
if (is_star) {
|
||||
// Bright white/blue stars
|
||||
uint8_t star_brightness = 200 + ((star_noise - 60000) / 256);
|
||||
return CRGB(star_brightness, star_brightness, 255);
|
||||
} else {
|
||||
// Nebula background
|
||||
uint8_t nebula_hue = 200 + (uint8_t)(nebula_density * 80); // Purple-pink spectrum
|
||||
uint8_t nebula_sat = 150 + (uint8_t)(abs(cloud2) * 105);
|
||||
uint8_t nebula_bright = 40 + (uint8_t)(nebula_density * 120);
|
||||
|
||||
return CHSV(nebula_hue, nebula_sat, nebula_bright);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10. "Binary Pulse" - Digital Heartbeat
|
||||
- **Description**: Digital heartbeat with expanding/contracting rings using threshold-based noise
|
||||
- **Parameters**: Concentric pattern generation, rhythmic pulsing, geometric thresholds
|
||||
- **Characteristics**: Rhythmic, geometric, tech-inspired
|
||||
|
||||
**Algorithm**:
|
||||
```cpp
|
||||
CRGB drawBinaryPulse(const RingCoord& coord, uint32_t time_ms) {
|
||||
// Create rhythmic heartbeat timing
|
||||
float pulse_period = 2000.0f; // 2-second pulse cycle
|
||||
float pulse_phase = fmod(time_ms, pulse_period) / pulse_period; // 0-1 cycle
|
||||
|
||||
// Generate expanding rings from center
|
||||
float distance_from_center = sqrt(coord.x * coord.x + coord.y * coord.y);
|
||||
|
||||
// Pulse wave propagation
|
||||
float ring_frequency = 5.0f; // Number of rings
|
||||
float pulse_offset = pulse_phase * 2.0f; // Expanding wave
|
||||
float ring_value = sin((distance_from_center * ring_frequency - pulse_offset) * 2 * M_PI);
|
||||
|
||||
// Digital quantization
|
||||
uint16_t noise = inoise16(coord.x * 2000, coord.y * 2000, time_ms / 8);
|
||||
float digital_mod = ((noise >> 8) > 128) ? 1.0f : -0.5f;
|
||||
|
||||
float final_value = ring_value * digital_mod;
|
||||
|
||||
// Binary color mapping
|
||||
if (final_value > 0.3f) {
|
||||
// Active pulse regions
|
||||
uint8_t intensity = (uint8_t)(final_value * 255);
|
||||
return CRGB(intensity, 0, intensity); // Magenta pulse
|
||||
} else if (final_value > -0.2f) {
|
||||
// Transition zones
|
||||
uint8_t dim_intensity = (uint8_t)((final_value + 0.2f) * 500);
|
||||
return CRGB(0, dim_intensity, 0); // Green transitions
|
||||
} else {
|
||||
// Background
|
||||
return CRGB(10, 0, 20); // Dark purple background
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5 Color Palettes
|
||||
|
||||
### 1. "Sunset Boulevard"
|
||||
- **Colors**: Warm oranges, deep reds, golden yellows
|
||||
- **Description**: Classic sunset gradient perfect for relaxing ambiance
|
||||
- **HSV Range**: Hue 0-45, high saturation, varying brightness
|
||||
|
||||
### 2. "Ocean Breeze"
|
||||
- **Colors**: Deep blues, aqua, seafoam green, white caps
|
||||
- **Description**: Cool ocean palette for refreshing visual effects
|
||||
- **HSV Range**: Hue 120-210, medium-high saturation
|
||||
|
||||
### 3. "Neon Nights"
|
||||
- **Colors**: Electric pink, cyan, purple, lime green
|
||||
- **Description**: Cyberpunk-inspired high-contrast palette
|
||||
- **HSV Range**: Saturated primaries, high brightness contrasts
|
||||
|
||||
### 4. "Forest Whisper"
|
||||
- **Colors**: Deep greens, earth browns, golden highlights
|
||||
- **Description**: Natural woodland palette for organic feels
|
||||
- **HSV Range**: Hue 60-150, natural saturation levels
|
||||
|
||||
### 5. "Galaxy Express"
|
||||
- **Colors**: Deep purples, cosmic blues, silver stars, pink nebula
|
||||
- **Description**: Space-themed palette for cosmic adventures
|
||||
- **HSV Range**: Hue 200-300, with bright white accents
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### Core Mathematical Framework
|
||||
|
||||
```cpp
|
||||
// Enhanced coordinate system for ring-based effects
|
||||
struct RingCoord {
|
||||
float angle; // Position on ring (0 to 2π)
|
||||
float radius; // Distance from center (normalized 0-1)
|
||||
float x, y; // Cartesian coordinates
|
||||
int led_index; // LED position on strip
|
||||
};
|
||||
|
||||
// Convert LED index to ring coordinates
|
||||
RingCoord calculateRingCoord(int led_index, int num_leds, float time_offset = 0.0f) {
|
||||
RingCoord coord;
|
||||
coord.led_index = led_index;
|
||||
coord.angle = (led_index * 2.0f * M_PI / num_leds) + time_offset;
|
||||
coord.radius = 1.0f; // Fixed radius for ring
|
||||
coord.x = cos(coord.angle);
|
||||
coord.y = sin(coord.angle);
|
||||
return coord;
|
||||
}
|
||||
|
||||
// Performance optimization with lookup tables
|
||||
class RingLUT {
|
||||
private:
|
||||
float cos_table[NUM_LEDS];
|
||||
float sin_table[NUM_LEDS];
|
||||
|
||||
public:
|
||||
void initialize() {
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
float angle = i * 2.0f * M_PI / NUM_LEDS;
|
||||
cos_table[i] = cos(angle);
|
||||
sin_table[i] = sin(angle);
|
||||
}
|
||||
}
|
||||
|
||||
RingCoord fastRingCoord(int led_index, float time_offset = 0.0f) {
|
||||
RingCoord coord;
|
||||
coord.led_index = led_index;
|
||||
coord.angle = (led_index * 2.0f * M_PI / NUM_LEDS) + time_offset;
|
||||
coord.x = cos_table[led_index];
|
||||
coord.y = sin_table[led_index];
|
||||
coord.radius = 1.0f;
|
||||
return coord;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Enhanced Control System with Smooth Transitions
|
||||
|
||||
```cpp
|
||||
class NoiseVariantManager {
|
||||
private:
|
||||
uint8_t current_variant = 0;
|
||||
uint8_t target_variant = 0;
|
||||
float transition_progress = 1.0f; // 0.0 = old, 1.0 = new
|
||||
uint32_t transition_start = 0;
|
||||
static const uint32_t TRANSITION_DURATION = 1000; // 1 second fade
|
||||
|
||||
// Algorithm instances
|
||||
PlasmaWaveGenerator plasma_gen;
|
||||
PlasmaParams plasma_params;
|
||||
|
||||
public:
|
||||
void update(uint32_t now, bool auto_cycle_enabled, uint8_t manual_variant) {
|
||||
// Handle automatic cycling vs manual override
|
||||
if (auto_cycle_enabled) {
|
||||
EVERY_N_MILLISECONDS(10000) {
|
||||
startTransition((current_variant + 1) % 10, now);
|
||||
}
|
||||
} else if (manual_variant != target_variant && transition_progress >= 1.0f) {
|
||||
// Manual override
|
||||
startTransition(manual_variant, now);
|
||||
}
|
||||
|
||||
// Update transition progress
|
||||
if (transition_progress < 1.0f) {
|
||||
uint32_t elapsed = now - transition_start;
|
||||
transition_progress = min(1.0f, elapsed / (float)TRANSITION_DURATION);
|
||||
|
||||
if (transition_progress >= 1.0f) {
|
||||
current_variant = target_variant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CRGB renderPixel(const RingCoord& coord, uint32_t time_ms) {
|
||||
if (transition_progress >= 1.0f) {
|
||||
// No transition, render current variant
|
||||
return renderVariant(current_variant, coord, time_ms);
|
||||
} else {
|
||||
// Blend between variants
|
||||
CRGB old_color = renderVariant(current_variant, coord, time_ms);
|
||||
CRGB new_color = renderVariant(target_variant, coord, time_ms);
|
||||
return lerpCRGB(old_color, new_color, transition_progress);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void startTransition(uint8_t new_variant, uint32_t now) {
|
||||
target_variant = new_variant;
|
||||
transition_start = now;
|
||||
transition_progress = 0.0f;
|
||||
}
|
||||
|
||||
CRGB renderVariant(uint8_t variant, const RingCoord& coord, uint32_t time_ms) {
|
||||
switch(variant) {
|
||||
case 0: return drawCosmicSwirl(coord, time_ms);
|
||||
case 1: return drawElectricStorm(coord, time_ms);
|
||||
case 2: return drawLavaLamp(coord, time_ms);
|
||||
case 3: return drawDigitalRain(coord, time_ms);
|
||||
case 4: return plasma_gen.calculatePlasmaPixel(coord, time_ms, plasma_params);
|
||||
case 5: return drawGlitchCity(coord, time_ms);
|
||||
case 6: return drawOceanDepths(coord, time_ms);
|
||||
case 7: return drawFireDance(coord, time_ms);
|
||||
case 8: return drawNebulaDrift(coord, time_ms);
|
||||
case 9: return drawBinaryPulse(coord, time_ms);
|
||||
default: return CRGB::Black;
|
||||
}
|
||||
}
|
||||
|
||||
CRGB lerpCRGB(const CRGB& a, const CRGB& b, float t) {
|
||||
return CRGB(
|
||||
a.r + (int)((b.r - a.r) * t),
|
||||
a.g + (int)((b.g - a.g) * t),
|
||||
a.b + (int)((b.b - a.b) * t)
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Data Structures and UI Integration
|
||||
|
||||
```cpp
|
||||
// Enhanced data structures
|
||||
String variant_names[10] = {
|
||||
"Cosmic Swirl", "Electric Storm", "Lava Lamp", "Digital Rain", "Plasma Waves",
|
||||
"Glitch City", "Ocean Depths", "Fire Dance", "Nebula Drift", "Binary Pulse"
|
||||
};
|
||||
|
||||
String palette_names[5] = {
|
||||
"Sunset Boulevard", "Ocean Breeze", "Neon Nights", "Forest Whisper", "Galaxy Express"
|
||||
};
|
||||
|
||||
// Global instances
|
||||
NoiseVariantManager variant_manager;
|
||||
RingLUT ring_lut;
|
||||
|
||||
// Enhanced UI controls
|
||||
UIDropdown variants("Noise Variants", variant_names, 10);
|
||||
UIDropdown palettes("Color Palettes", palette_names, 5);
|
||||
UICheckbox autoCycle("Auto Cycle", true);
|
||||
```
|
||||
|
||||
### Integration with Existing Framework
|
||||
|
||||
```cpp
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
ScreenMap xyMap = ScreenMap::Circle(NUM_LEDS, 2.0, 2.0);
|
||||
controller = &FastLED.addLeds<WS2811, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
|
||||
.setCorrection(TypicalLEDStrip)
|
||||
.setDither(DISABLE_DITHER)
|
||||
.setScreenMap(xyMap);
|
||||
FastLED.setBrightness(brightness);
|
||||
pir.activate(millis());
|
||||
|
||||
// Initialize performance optimizations
|
||||
ring_lut.initialize();
|
||||
}
|
||||
|
||||
void draw(uint32_t now) {
|
||||
// Update variant manager
|
||||
variant_manager.update(now, autoCycle.value(), variants.value());
|
||||
|
||||
// Render each LED with current variant
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
RingCoord coord = ring_lut.fastRingCoord(i);
|
||||
leds[i] = variant_manager.renderPixel(coord, now);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
controller->setDither(useDither ? BINARY_DITHER : DISABLE_DITHER);
|
||||
uint32_t now = millis();
|
||||
uint8_t bri = pir.transition(now);
|
||||
FastLED.setBrightness(bri * brightness.as<float>());
|
||||
|
||||
draw(now);
|
||||
FastLED.show();
|
||||
}
|
||||
```
|
||||
|
||||
## Technical Considerations
|
||||
|
||||
### Performance Optimization
|
||||
- **Lookup Table Pre-computation**: Pre-calculate trigonometric values for ring positions
|
||||
- **Fixed-Point Arithmetic**: Use integer math where possible for embedded systems
|
||||
- **Noise Caching**: Cache noise parameters between frames for consistent animation
|
||||
- **Memory-Efficient Algorithms**: Optimize noise calculations for real-time performance
|
||||
- **Parallel Processing**: Structure algorithms for potential multi-core optimization
|
||||
|
||||
### Memory Management
|
||||
- **PROGMEM Storage**: Store palettes and static data in program memory for Arduino compatibility
|
||||
- **Dynamic Allocation Avoidance**: Minimize heap usage during effect transitions
|
||||
- **Stack Optimization**: Use local variables efficiently in nested algorithm calls
|
||||
- **Buffer Management**: Reuse coordinate calculation buffers where possible
|
||||
|
||||
### Mathematical Precision
|
||||
- **16-bit Noise Space**: Maintain precision in noise calculations before 8-bit mapping
|
||||
- **Floating Point Efficiency**: Balance precision vs. performance based on target platform
|
||||
- **Color Space Optimization**: Use HSV for smooth transitions, RGB for final output
|
||||
- **Numerical Stability**: Prevent overflow/underflow in wave interference calculations
|
||||
|
||||
### User Experience
|
||||
- **Smooth Transitions**: 1-second cross-fade between effects using linear interpolation
|
||||
- **Responsive Controls**: Immediate override of automatic cycling via manual selection
|
||||
- **Visual Feedback**: Clear indication of current variant and palette selection
|
||||
- **Performance Consistency**: Maintain stable frame rate across all effect variants
|
||||
|
||||
## First Pass Implementation: Plasma Waves
|
||||
|
||||
### Why Start with Plasma Waves?
|
||||
1. **Visual Impact**: Most impressive demonstration of advanced graphics programming
|
||||
2. **Mathematical Showcase**: Demonstrates sine wave interference and noise modulation
|
||||
3. **Building Foundation**: Establishes the RingCoord system used by all other variants
|
||||
4. **Performance Baseline**: Tests the most computationally intensive algorithm first
|
||||
|
||||
### Development Strategy
|
||||
```cpp
|
||||
// Phase 1: Core Infrastructure
|
||||
void setupPlasmaDemo() {
|
||||
// Initialize basic ring coordinate system
|
||||
ring_lut.initialize();
|
||||
|
||||
// Configure plasma parameters
|
||||
plasma_params.time_scale = timescale.as<float>();
|
||||
plasma_params.noise_intensity = scale.as<float>();
|
||||
plasma_params.brightness = brightness.as<float>();
|
||||
}
|
||||
|
||||
// Phase 2: Plasma-Only Implementation
|
||||
void drawPlasmaOnly(uint32_t now) {
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
RingCoord coord = ring_lut.fastRingCoord(i);
|
||||
leds[i] = plasma_gen.calculatePlasmaPixel(coord, now, plasma_params);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Add Manual Variants (No Auto-Cycling Yet)
|
||||
void drawWithManualSelection(uint32_t now) {
|
||||
uint8_t selected_variant = variants.value();
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
RingCoord coord = ring_lut.fastRingCoord(i);
|
||||
|
||||
switch(selected_variant) {
|
||||
case 0: leds[i] = drawCosmicSwirl(coord, now); break;
|
||||
case 1: leds[i] = plasma_gen.calculatePlasmaPixel(coord, now, plasma_params); break;
|
||||
// Add variants incrementally
|
||||
default: leds[i] = plasma_gen.calculatePlasmaPixel(coord, now, plasma_params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 4: Full System with Auto-Cycling and Transitions
|
||||
void drawFullSystem(uint32_t now) {
|
||||
variant_manager.update(now, autoCycle.value(), variants.value());
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; i++) {
|
||||
RingCoord coord = ring_lut.fastRingCoord(i);
|
||||
leds[i] = variant_manager.renderPixel(coord, now);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Testing and Validation
|
||||
1. **Plasma Waves Only**: Verify smooth wave interference and noise modulation
|
||||
2. **Parameter Responsiveness**: Test all UI sliders affect plasma generation correctly
|
||||
3. **Performance Metrics**: Measure frame rate with plasma algorithm on target hardware
|
||||
4. **Visual Quality**: Confirm smooth color transitions and no artifacts
|
||||
5. **Memory Usage**: Monitor RAM consumption during plasma calculations
|
||||
|
||||
### Incremental Development Plan
|
||||
1. **Week 1**: Implement Plasma Waves algorithm and RingCoord system
|
||||
2. **Week 2**: Add 2-3 simpler variants (Cosmic Swirl, Electric Storm, Fire Dance)
|
||||
3. **Week 3**: Implement transition system and automatic cycling
|
||||
4. **Week 4**: Add remaining variants and color palette system
|
||||
5. **Week 5**: Optimization, polish, and platform-specific tuning
|
||||
|
||||
## Advanced Graphics Techniques Demonstrated
|
||||
|
||||
### Wave Interference Mathematics
|
||||
The plasma algorithm showcases classical physics simulation:
|
||||
- **Superposition Principle**: Multiple wave sources combine linearly
|
||||
- **Phase Relationships**: Time-varying phase creates animation
|
||||
- **Distance-Based Attenuation**: Realistic wave propagation modeling
|
||||
- **Noise Modulation**: Organic variation through Perlin noise overlay
|
||||
|
||||
### Color Theory Implementation
|
||||
- **HSV Color Space**: Smooth hue transitions for natural color flow
|
||||
- **Saturation Modulation**: Dynamic saturation based on wave intensity
|
||||
- **Brightness Mapping**: Normalized wave values to brightness curves
|
||||
- **Gamma Correction**: Perceptually linear brightness progression
|
||||
|
||||
### Performance Optimization Strategies
|
||||
- **Trigonometric Lookup**: Pre-computed sine/cosine tables
|
||||
- **Fixed-Point Math**: Integer approximations for embedded platforms
|
||||
- **Loop Unrolling**: Minimize function call overhead in tight loops
|
||||
- **Memory Access Patterns**: Cache-friendly coordinate calculations
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Advanced Features
|
||||
- **Save/Load Configurations**: User-defined effect combinations and parameters
|
||||
- **BPM Synchronization**: Music-reactive timing for effect transitions
|
||||
- **Custom Palette Editor**: User-defined color schemes with preview
|
||||
- **Effect Intensity Controls**: Per-variant amplitude and speed modulation
|
||||
- **Multi-Ring Support**: Expand to multiple concentric LED rings
|
||||
|
||||
### Platform Extensions
|
||||
- **Multi-Core Optimization**: Parallel processing for complex calculations
|
||||
- **GPU Acceleration**: WebGL compute shaders for web platform
|
||||
- **Hardware Acceleration**: Platform-specific optimizations (ESP32, Teensy)
|
||||
- **Memory Mapping**: Direct hardware buffer access for maximum performance
|
||||
|
||||
### Algorithm Enhancements
|
||||
- **Physically-Based Rendering**: More realistic light simulation
|
||||
- **Particle Systems**: Dynamic particle-based effects
|
||||
- **Fractal Algorithms**: Mandelbrot and Julia set visualizations
|
||||
- **Audio Visualization**: Spectrum analysis and reactive algorithms
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
/**
|
||||
* @brief A simple timer utility class for tracking timed events
|
||||
*
|
||||
* This class provides basic timer functionality for animations and effects.
|
||||
* It can be used to track whether a specific duration has elapsed since
|
||||
* the timer was started.
|
||||
*/
|
||||
class Timer {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Timer object
|
||||
*
|
||||
* Creates a timer in the stopped state with zero duration.
|
||||
*/
|
||||
Timer() : start_time(0), duration(0), running(false) {}
|
||||
|
||||
/**
|
||||
* @brief Start the timer with a specific duration
|
||||
*
|
||||
* @param now Current time in milliseconds (typically from millis())
|
||||
* @param duration How long the timer should run in milliseconds
|
||||
*/
|
||||
void start(uint32_t now, uint32_t duration) {
|
||||
start_time = now;
|
||||
this->duration = duration;
|
||||
running = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the timer state based on current time
|
||||
*
|
||||
* Checks if the timer is still running based on the current time.
|
||||
* If the specified duration has elapsed, the timer will stop.
|
||||
*
|
||||
* @param now Current time in milliseconds (typically from millis())
|
||||
* @return true if the timer is still running, false if stopped or elapsed
|
||||
*/
|
||||
bool update(uint32_t now) {
|
||||
if (!running) {
|
||||
return false;
|
||||
}
|
||||
uint32_t elapsed = now - start_time;
|
||||
if (elapsed > duration) {
|
||||
running = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t start_time; // When the timer was started (in milliseconds)
|
||||
uint32_t duration; // How long the timer should run (in milliseconds)
|
||||
bool running; // Whether the timer is currently active
|
||||
};
|
||||
Reference in New Issue
Block a user