-
-
Notifications
You must be signed in to change notification settings - Fork 9
Add palettes and effects #110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
frontend ======= - Field renderer: add genPalPrev, add type palette (palette preview) backend ======= - Effects: add ColorTwinkle, Plasma, Julia, Police - WIP - Lights control: add WLED palettes and show previews
|
Note
|
| Cohort / File(s) | Summary |
|---|---|
Palette Infrastructure src/MoonLight/Modules/palettes.h |
New file: defines 60+ gradient color palettes as constant byte arrays (ib_jul01_gp, lava_gp, temperature_gp, etc.) plus public aggregations gGradientPalettes and palette_names for programmatic and UI-facing access. |
Lights Control Palette Expansion src/MoonLight/Modules/ModuleLightsControl.h |
Switches palette control from simple select to new palette type with built-in palettes (Cloud, Lava, Ocean, etc.) and palettes.h-derived entries; generates hex-encoded color data per palette; replaces static palette assignment with dynamic gradient palette loading via temporary buffer and loadDynamicGradientPalette(). |
Effect Registration & New Effects src/MoonLight/Modules/ModuleEffects.h, src/MoonLight/Nodes/Effects/E_WLED.h |
Reorders StarSkyEffect registration; adds four new public effect classes (ColorTwinkleEffect, PlasmaEffect, JuliaEffect, PoliceEffect) with setup, update, and loop lifecycle methods; each class defines bpm, intensity, speed, blur, and other configuration parameters. |
UI Palette Preview Rendering interface/src/lib/components/moonbase/FieldRenderer.svelte |
Adds genPalPrev() helper to generate CSS linear-gradient previews from hex-encoded palette data; adds UI branch for palette type with select and gradient-preview div; simplifies IP/mDNS anchor rendering by unifying to plain http://{value} links. |
Driver Palette Index Update src/MoonLight/Nodes/Drivers/D_Infrared.h |
Updates max palette index calculation in PaletteInc code path from 11 to 8 + 3 + 61 to accommodate expanded palette set. |
Device Documentation docs/moonbase/devices.md |
Removes "(controls module)" specificity from first bullet; changes second bullet from "new window" to "via IP address" phrasing for device access. |
Sequence Diagram
sequenceDiagram
actor User
participant UI as FieldRenderer.svelte
participant Control as ModuleLightsControl
participant PaletteData as palettes.h
participant Effect as Effect (LED)
User->>UI: Select palette from dropdown
UI->>UI: Render palette preview via genPalPrev()
UI->>Control: Send palette selection (name)
Control->>PaletteData: Look up palette by name
PaletteData-->>Control: Return gradient palette data
Control->>Control: Load dynamic gradient palette<br/>via loadDynamicGradientPalette()
Control->>Effect: Apply palette to effect
Effect->>Effect: Render with new palette colors
Effect-->>User: Display updated LED effect
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
- Lots of effects #77 — Both modify effect registration in ModuleEffects.h and add new effect classes to E_WLED.h.
- Board presets p4 #74 — Both expand ModuleLightsControl.h's palette handling to support dynamic palette loading and selection.
- Add node mutex #103 — Both update FieldRenderer.svelte for UI control enhancements (palette preview alongside existing throttling/slider changes).
Poem
🐰 Palettes bloom in hex and byte,
Sixty gradients shining bright!
From Fire to Plasma, stars align,
Effects dance in colors fine,
A colorful feast for LED dreams,
More vibrant than it seems! ✨
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title 'Add palettes and effects' directly and accurately summarizes the main changes: introducing a new palettes.h file with 60+ color palettes and four new effect classes (ColorTwinkleEffect, PlasmaEffect, JuliaEffect, PoliceEffect) across multiple files. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing touches
- 📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
🤖 Fix all issues with AI agents
In `@docs/moonbase/devices.md`:
- Around line 9-10: The possessive "it's" is incorrect in the device
instructions; update the text "Click on IP to go to the device via it's IP
address" to use the possessive form "its" instead. Locate the sentence in
docs/moonbase/devices.md (the line containing "Click on IP to go to the device
via it's IP address") and replace "it's" with "its" so it reads "Click on IP to
go to the device via its IP address".
In `@src/MoonLight/Modules/ModuleLightsControl.h`:
- Around line 281-355: The loop that builds hexString for gGradientPalettes uses
a hard cap while (j < 100) which can truncate palettes—replace this fixed limit
with a safe bounds check tied to the actual palette byte length (or a defined
MAX_PALETTE_BYTES) and iterate in 4-byte steps until you encounter an index==255
termination; specifically update the loop that reads from gGradientPalettes[i]
(the variables palette and j) so it ensures (j + 4) <= palette_length (or <=
MAX_PALETTE_BYTES) before reading the next 4 bytes, append the 4 bytes per
entry, and break when palette[j] (the entry index) == 255 to avoid truncation.
- Around line 425-427: The fixed-size memcpy truncates palettes and drops the
0xFF terminator; change to copying from PROGMEM byte-by-byte using
pgm_read_byte() from gGradientPalettes[index] (index computed as
updatedItem.value.as<uint8_t>() - 11) into tcp until you encounter 0xFF or reach
the tcp buffer limit, then ensure tcp contains a valid 0xFF terminator before
calling layerP.palette.loadDynamicGradientPalette(tcp); also ensure tcp’s size
is sufficient for the largest expected palette and set the final byte to 0xFF if
the loop exits due to the size limit.
In `@src/MoonLight/Modules/palettes.h`:
- Around line 28-31: The header uses the Arduino type `byte` for `ib_jul01_gp`
but doesn't include Arduino.h, making compilation order-dependent; update the
header to be self-contained by adding a guarded include or fallback typedef:
check for a prior definition of `byte` (or for ARDUINO macro) and if missing
either include <Arduino.h> or typedef unsigned char byte so `const byte
ib_jul01_gp[]` has a defined type within this header.
In `@src/MoonLight/Nodes/Drivers/D_Infrared.h`:
- Around line 367-369: The clamp for palette increase in the combined_code ==
codePaletteInc branch currently uses MIN(state.data["palette"].as<uint8_t>() +
1, 8 + 3 + 61) which allows an out‑of‑range index (72); change the ceiling to
the last valid index by subtracting 1 (i.e. use (8 + 3 + 61) - 1 or, better, a
shared MAX_PALETTE_INDEX constant) so newState["palette"] is clamped to
0..(count-1); update the same logic location (the combined_code ==
codePaletteInc branch that sets newState["palette"]) and ensure any other uses
of the literal 8+3+61 are replaced with the shared constant or consistently
subtract 1.
In `@src/MoonLight/Nodes/Effects/E_WLED.h`:
- Around line 2143-2254: The Julia and Police effect loop() implementations are
empty causing no output; fix by implementing a minimal placeholder render in
each effect's loop (e.g., in the loop() method in E_WLED.h for Julia and
similarly for Police around the region noted at 2304-2308): call
SEGMENT.setUpLeds(), clear the frame with SEGMENT.fill(BLACK) (or BLACK with
desired brightness), then draw a simple deterministic indicator (for example a
single moving pixel or a low-cost color stripe using SEGMENT.setPixelColorXY or
SEGMENT.setPixelColor) based on strip.now or SEGMENT.custom1 so the effect
visibly updates, and return; alternatively, if you prefer not to ship a
placeholder, unregister/remove these effects from the registry until full ports
are implemented. Ensure you reference and modify the loop() methods for the
Julia and Police effect classes and use SEGMENT APIs (setUpLeds, fill,
setPixelColorXY) to avoid leaving previous frames visible.
- Around line 2010-2063: The loop() for this effect (the overridden loop() in
E_WLED.h / ColorTwinkle) is currently empty and leaves stale LEDs; add a minimal
placeholder: on first call (check SEGENV.call == 0) call SEGMENT.setUpLeds()
then clear the strip by setting every pixel to BLACK (use SEGLEN and
SEGMENT.setPixelColor), or alternatively call SEGMENT.fill(BLACK) to ensure no
stale output, and ensure any required SEGENV.allocateData(...) is invoked if the
effect expects per-pixel state (use SEGENV.allocateData with dataSize computed
from SEGLEN) so the effect doesn’t crash; keep the implementation tiny and
deterministic until the full twinkle logic is ported.
- Around line 2068-2109: PlasmaEffect currently advertises 3D via dim() but only
writes a single column in loop() (layer->setRGB(Coord3D(0, i), ...)), leaving
other LEDs untouched; either change dim() to return _1D to match current
rendering, or modify loop() to iterate X and Z (and optionally clear the layer
first) and call layer->setRGB(Coord3D(x, i, z), ColorFromPalette(...)) for each
coordinate so the effect covers the full 2D/3D plane; update the dim()
implementation or the loop() iteration accordingly in the PlasmaEffect class.
🧹 Nitpick comments (2)
src/MoonLight/Modules/ModuleEffects.h (1)
138-141: Keep WLED effects list alphabetical.The section is labeled “alphabetically” but the new effects are out of order. Reordering keeps UI and allocation lists consistent.
♻️ Suggested reorder
- addControlValue(control, getNameAndTags<ColorTwinkleEffect>()); - addControlValue(control, getNameAndTags<PlasmaEffect>()); - addControlValue(control, getNameAndTags<JuliaEffect>()); - addControlValue(control, getNameAndTags<PoliceEffect>()); + addControlValue(control, getNameAndTags<ColorTwinkleEffect>()); + addControlValue(control, getNameAndTags<JuliaEffect>()); + addControlValue(control, getNameAndTags<PlasmaEffect>()); + addControlValue(control, getNameAndTags<PoliceEffect>());- if (!node) node = checkAndAlloc<ColorTwinkleEffect>(name); - if (!node) node = checkAndAlloc<PlasmaEffect>(name); - if (!node) node = checkAndAlloc<JuliaEffect>(name); - if (!node) node = checkAndAlloc<PoliceEffect>(name); + if (!node) node = checkAndAlloc<ColorTwinkleEffect>(name); + if (!node) node = checkAndAlloc<JuliaEffect>(name); + if (!node) node = checkAndAlloc<PlasmaEffect>(name); + if (!node) node = checkAndAlloc<PoliceEffect>(name);Also applies to: 235-238
interface/src/lib/components/moonbase/FieldRenderer.svelte (1)
189-207: Move the inline<style>block to component scope.Embedding
<style>inside the template inserts a<style>element per row and bypasses Svelte’s scoped styling. Prefer a top-level<style>section.♻️ Suggested change (remove inline style)
- <style> - .palette-preview { - width: 250px; - height: 40px; - border: 1px solid `#ccc`; - border-radius: 3px; - } - </style>Add this at the bottom of the component instead:
<style> .palette-preview { width: 250px; height: 40px; border: 1px solid `#ccc`; border-radius: 3px; } </style>
| * Click on the name to go to the device via mDNS | ||
| * Click on IP to go to the device via it's IP address |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix possessive “its”.
“it’s” → “its”.
✏️ Proposed fix
- * Click on IP to go to the device via it's IP address
+ * Click on IP to go to the device via its IP address📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * Click on the name to go to the device via mDNS | |
| * Click on IP to go to the device via it's IP address | |
| * Click on the name to go to the device via mDNS | |
| * Click on IP to go to the device via its IP address |
🤖 Prompt for AI Agents
In `@docs/moonbase/devices.md` around lines 9 - 10, The possessive "it's" is
incorrect in the device instructions; update the text "Click on IP to go to the
device via it's IP address" to use the possessive form "its" instead. Locate the
sentence in docs/moonbase/devices.md (the line containing "Click on IP to go to
the device via it's IP address") and replace "it's" with "its" so it reads
"Click on IP to go to the device via its IP address".
| control = addControl(controls, "palette", "palette"); // palette type | ||
| control["default"] = 9; | ||
| addControlValue(control, "Cloud"); | ||
| addControlValue(control, "Lava"); | ||
| addControlValue(control, "Ocean"); | ||
| addControlValue(control, "Forest"); | ||
| addControlValue(control, "Rainbow"); | ||
| addControlValue(control, "RainbowStripe"); | ||
| addControlValue(control, "Party"); | ||
| addControlValue(control, "Heat"); | ||
| addControlValue(control, "Random"); | ||
| addControlValue(control, "MoonModules"); | ||
| addControlValue(control, "Orange"); | ||
|
|
||
| control["values"].to<JsonArray>(); | ||
|
|
||
| const char* const builtInPaletteNames[] = {"Cloud", "Lava", "Ocean", "Forest", "Rainbow", "RainbowStripe", "Party", "Heat"}; | ||
| const CRGBPalette16 builtInPalettes[] = {CloudColors_p, LavaColors_p, OceanColors_p, ForestColors_p, RainbowColors_p, RainbowStripeColors_p, PartyColors_p, HeatColors_p}; | ||
| for (int i = 0; i < sizeof(builtInPaletteNames) / sizeof(char*); i++) { | ||
| JsonArray values = control["values"]; | ||
| JsonObject object = values.add<JsonObject>(); | ||
| object["name"] = builtInPaletteNames[i]; | ||
|
|
||
| // Convert CRGBPalette16 to hex string | ||
| String hexString = ""; | ||
| const CRGBPalette16 pal = builtInPalettes[i]; | ||
|
|
||
| char buf[3]; | ||
| for (int j = 0; j < 16; j++) { | ||
| // Add index (0, 16, 32, ... 240) | ||
| sprintf(buf, "%02x", j * 16); | ||
| hexString += buf; | ||
|
|
||
| // Add R, G, B | ||
| sprintf(buf, "%02x", pal[j].r); | ||
| hexString += buf; | ||
| sprintf(buf, "%02x", pal[j].g); | ||
| hexString += buf; | ||
| sprintf(buf, "%02x", pal[j].b); | ||
| hexString += buf; | ||
| } | ||
|
|
||
| // Add final entry at index 255 | ||
| sprintf(buf, "%02x", 255); | ||
| hexString += buf; | ||
| sprintf(buf, "%02x", pal[15].r); | ||
| hexString += buf; | ||
| sprintf(buf, "%02x", pal[15].g); | ||
| hexString += buf; | ||
| sprintf(buf, "%02x", pal[15].b); | ||
| hexString += buf; | ||
|
|
||
| object["colors"] = hexString; | ||
| } | ||
|
|
||
| const char* const customPaletteNames[] = {"Random", "MoonModules", "Orange"}; | ||
| for (int i = 0; i < sizeof(customPaletteNames) / sizeof(char*); i++) { | ||
| JsonArray values = control["values"]; | ||
| JsonObject object = values.add<JsonObject>(); | ||
| object["name"] = customPaletteNames[i]; | ||
| object["colors"] = ""; | ||
| } | ||
|
|
||
| // add palettes from palettes.h | ||
| for (int i = 0; i < sizeof(palette_names) / sizeof(char*); i++) { | ||
| JsonArray values = control["values"]; | ||
| JsonObject object = values.add<JsonObject>(); | ||
| object["name"] = palette_names[i]; | ||
|
|
||
| String hexString = ""; | ||
| const byte* palette = gGradientPalettes[i]; | ||
| int j = 0; | ||
|
|
||
| // Read 4-byte entries (index, r, g, b) until index == 255 | ||
| while (j < 100) { // Safety limit | ||
| for (int k = 0; k < 4; k++) { | ||
| char buf[3]; | ||
| sprintf(buf, "%02x", palette[j++]); | ||
| hexString += buf; | ||
| } | ||
| // Check if we just wrote the final entry (index was 255) | ||
| if (palette[j - 4] == 255) break; | ||
| } | ||
|
|
||
| object["colors"] = hexString; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Palette preview generation can truncate larger gradients.
The while (j < 100) cap will cut off palettes longer than 100 bytes (some in palettes.h exceed that), producing incomplete previews. Consider raising the cap and guarding by 4‑byte entries.
🛠️ Suggested tweak
- while (j < 100) { // Safety limit
+ const size_t kMaxPaletteBytes = 256; // allow larger gradients
+ while (j + 3 < kMaxPaletteBytes) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| control = addControl(controls, "palette", "palette"); // palette type | |
| control["default"] = 9; | |
| addControlValue(control, "Cloud"); | |
| addControlValue(control, "Lava"); | |
| addControlValue(control, "Ocean"); | |
| addControlValue(control, "Forest"); | |
| addControlValue(control, "Rainbow"); | |
| addControlValue(control, "RainbowStripe"); | |
| addControlValue(control, "Party"); | |
| addControlValue(control, "Heat"); | |
| addControlValue(control, "Random"); | |
| addControlValue(control, "MoonModules"); | |
| addControlValue(control, "Orange"); | |
| control["values"].to<JsonArray>(); | |
| const char* const builtInPaletteNames[] = {"Cloud", "Lava", "Ocean", "Forest", "Rainbow", "RainbowStripe", "Party", "Heat"}; | |
| const CRGBPalette16 builtInPalettes[] = {CloudColors_p, LavaColors_p, OceanColors_p, ForestColors_p, RainbowColors_p, RainbowStripeColors_p, PartyColors_p, HeatColors_p}; | |
| for (int i = 0; i < sizeof(builtInPaletteNames) / sizeof(char*); i++) { | |
| JsonArray values = control["values"]; | |
| JsonObject object = values.add<JsonObject>(); | |
| object["name"] = builtInPaletteNames[i]; | |
| // Convert CRGBPalette16 to hex string | |
| String hexString = ""; | |
| const CRGBPalette16 pal = builtInPalettes[i]; | |
| char buf[3]; | |
| for (int j = 0; j < 16; j++) { | |
| // Add index (0, 16, 32, ... 240) | |
| sprintf(buf, "%02x", j * 16); | |
| hexString += buf; | |
| // Add R, G, B | |
| sprintf(buf, "%02x", pal[j].r); | |
| hexString += buf; | |
| sprintf(buf, "%02x", pal[j].g); | |
| hexString += buf; | |
| sprintf(buf, "%02x", pal[j].b); | |
| hexString += buf; | |
| } | |
| // Add final entry at index 255 | |
| sprintf(buf, "%02x", 255); | |
| hexString += buf; | |
| sprintf(buf, "%02x", pal[15].r); | |
| hexString += buf; | |
| sprintf(buf, "%02x", pal[15].g); | |
| hexString += buf; | |
| sprintf(buf, "%02x", pal[15].b); | |
| hexString += buf; | |
| object["colors"] = hexString; | |
| } | |
| const char* const customPaletteNames[] = {"Random", "MoonModules", "Orange"}; | |
| for (int i = 0; i < sizeof(customPaletteNames) / sizeof(char*); i++) { | |
| JsonArray values = control["values"]; | |
| JsonObject object = values.add<JsonObject>(); | |
| object["name"] = customPaletteNames[i]; | |
| object["colors"] = ""; | |
| } | |
| // add palettes from palettes.h | |
| for (int i = 0; i < sizeof(palette_names) / sizeof(char*); i++) { | |
| JsonArray values = control["values"]; | |
| JsonObject object = values.add<JsonObject>(); | |
| object["name"] = palette_names[i]; | |
| String hexString = ""; | |
| const byte* palette = gGradientPalettes[i]; | |
| int j = 0; | |
| // Read 4-byte entries (index, r, g, b) until index == 255 | |
| while (j < 100) { // Safety limit | |
| for (int k = 0; k < 4; k++) { | |
| char buf[3]; | |
| sprintf(buf, "%02x", palette[j++]); | |
| hexString += buf; | |
| } | |
| // Check if we just wrote the final entry (index was 255) | |
| if (palette[j - 4] == 255) break; | |
| } | |
| object["colors"] = hexString; | |
| } | |
| control = addControl(controls, "palette", "palette"); // palette type | |
| control["default"] = 9; | |
| control["values"].to<JsonArray>(); | |
| const char* const builtInPaletteNames[] = {"Cloud", "Lava", "Ocean", "Forest", "Rainbow", "RainbowStripe", "Party", "Heat"}; | |
| const CRGBPalette16 builtInPalettes[] = {CloudColors_p, LavaColors_p, OceanColors_p, ForestColors_p, RainbowColors_p, RainbowStripeColors_p, PartyColors_p, HeatColors_p}; | |
| for (int i = 0; i < sizeof(builtInPaletteNames) / sizeof(char*); i++) { | |
| JsonArray values = control["values"]; | |
| JsonObject object = values.add<JsonObject>(); | |
| object["name"] = builtInPaletteNames[i]; | |
| // Convert CRGBPalette16 to hex string | |
| String hexString = ""; | |
| const CRGBPalette16 pal = builtInPalettes[i]; | |
| char buf[3]; | |
| for (int j = 0; j < 16; j++) { | |
| // Add index (0, 16, 32, ... 240) | |
| sprintf(buf, "%02x", j * 16); | |
| hexString += buf; | |
| // Add R, G, B | |
| sprintf(buf, "%02x", pal[j].r); | |
| hexString += buf; | |
| sprintf(buf, "%02x", pal[j].g); | |
| hexString += buf; | |
| sprintf(buf, "%02x", pal[j].b); | |
| hexString += buf; | |
| } | |
| // Add final entry at index 255 | |
| sprintf(buf, "%02x", 255); | |
| hexString += buf; | |
| sprintf(buf, "%02x", pal[15].r); | |
| hexString += buf; | |
| sprintf(buf, "%02x", pal[15].g); | |
| hexString += buf; | |
| sprintf(buf, "%02x", pal[15].b); | |
| hexString += buf; | |
| object["colors"] = hexString; | |
| } | |
| const char* const customPaletteNames[] = {"Random", "MoonModules", "Orange"}; | |
| for (int i = 0; i < sizeof(customPaletteNames) / sizeof(char*); i++) { | |
| JsonArray values = control["values"]; | |
| JsonObject object = values.add<JsonObject>(); | |
| object["name"] = customPaletteNames[i]; | |
| object["colors"] = ""; | |
| } | |
| // add palettes from palettes.h | |
| for (int i = 0; i < sizeof(palette_names) / sizeof(char*); i++) { | |
| JsonArray values = control["values"]; | |
| JsonObject object = values.add<JsonObject>(); | |
| object["name"] = palette_names[i]; | |
| String hexString = ""; | |
| const byte* palette = gGradientPalettes[i]; | |
| int j = 0; | |
| // Read 4-byte entries (index, r, g, b) until index == 255 | |
| const size_t kMaxPaletteBytes = 256; // allow larger gradients | |
| while (j + 3 < kMaxPaletteBytes) { | |
| for (int k = 0; k < 4; k++) { | |
| char buf[3]; | |
| sprintf(buf, "%02x", palette[j++]); | |
| hexString += buf; | |
| } | |
| // Check if we just wrote the final entry (index was 255) | |
| if (palette[j - 4] == 255) break; | |
| } | |
| object["colors"] = hexString; | |
| } |
🤖 Prompt for AI Agents
In `@src/MoonLight/Modules/ModuleLightsControl.h` around lines 281 - 355, The loop
that builds hexString for gGradientPalettes uses a hard cap while (j < 100)
which can truncate palettes—replace this fixed limit with a safe bounds check
tied to the actual palette byte length (or a defined MAX_PALETTE_BYTES) and
iterate in 4-byte steps until you encounter an index==255 termination;
specifically update the loop that reads from gGradientPalettes[i] (the variables
palette and j) so it ensures (j + 4) <= palette_length (or <= MAX_PALETTE_BYTES)
before reading the next 4 bytes, append the 4 bytes per entry, and break when
palette[j] (the entry index) == 255 to avoid truncation.
| byte tcp[76] = {255}; // WLEDMM: prevent out-of-range access in loadDynamicGradientPalette() | ||
| memcpy(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[updatedItem.value.as<uint8_t>() - 11])), 72); | ||
| layerP.palette.loadDynamicGradientPalette(tcp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Find files related to palette handling
fd -t f "\.h$|\.cpp$" | xargs rg -l "loadDynamicGradientPalette|gGradientPalettes" | head -20Repository: MoonModules/MoonLight
Length of output: 141
🏁 Script executed:
# Search for loadDynamicGradientPalette implementation details
rg -A 10 "loadDynamicGradientPalette" --type cpp --type hRepository: MoonModules/MoonLight
Length of output: 1361
🏁 Script executed:
# Check gradient palette definitions and sizes
rg -B 2 -A 5 "gGradientPalettes" --type cpp --type h | head -50Repository: MoonModules/MoonLight
Length of output: 2130
🏁 Script executed:
# Look for FastLED palette structure in the codebase
rg "CRGBPalette" --type cpp --type h | head -20Repository: MoonModules/MoonLight
Length of output: 546
🏁 Script executed:
# Read the palettes.h file to see palette definitions
cat src/MoonLight/Modules/palettes.h | head -100Repository: MoonModules/MoonLight
Length of output: 4786
🏁 Script executed:
# Search for the actual gradient palette definitions
rg -A 2 "gp =" src/MoonLight/Modules/palettes.h | head -40Repository: MoonModules/MoonLight
Length of output: 47
🏁 Script executed:
# Check what size gradients typically are by looking at their PROGMEM definitions
rg "PROGMEM|gp\[" src/MoonLight/Modules/palettes.h | head -20Repository: MoonModules/MoonLight
Length of output: 2895
🏁 Script executed:
# Count the actual byte size of all palette definitions
python3 << 'EOF'
import re
with open('src/MoonLight/Modules/palettes.h', 'r') as f:
content = f.read()
# Find all palette definitions
pattern = r'const byte (\w+)\[\] = \{([^}]+)\}'
matches = re.findall(pattern, content)
sizes = []
for name, data in matches:
# Count commas to get byte count
byte_count = len(data.split(','))
sizes.append((name, byte_count))
# Sort by size
sizes.sort(key=lambda x: x[1], reverse=True)
print("Top 15 largest palettes:")
for name, size in sizes[:15]:
print(f" {name}: {size} bytes")
print(f"\nTotal palettes: {len(sizes)}")
print(f"Max size: {max(s[1] for s in sizes)} bytes")
print(f"Min size: {min(s[1] for s in sizes)} bytes")
EOFRepository: MoonModules/MoonLight
Length of output: 151
🏁 Script executed:
# Check if there are any palettes >= 144 bytes
rg "const byte.*\[\] = \{" src/MoonLight/Modules/palettes.h | wc -lRepository: MoonModules/MoonLight
Length of output: 67
🏁 Script executed:
# Look for the loadDynamicGradientPalette implementation in FastLED
rg -B 5 -A 15 "loadDynamicGradientPalette" --type h | head -60Repository: MoonModules/MoonLight
Length of output: 2338
🏁 Script executed:
# Find the largest palette definitions
rg "const byte.*\[\] = \{" src/MoonLight/Modules/palettes.h -A 0 | while read line; do
name=$(echo "$line" | sed 's/.*const byte \([a-zA-Z_]*\).*/\1/')
# Extract just the declaration and count braces
size=$(rg "const byte $name\[\] = \{[^}]+\}" src/MoonLight/Modules/palettes.h | grep -o "{[^}]*}" | head -1 | tr ',' '\n' | wc -l)
echo "$size $name"
done | sort -rn | head -20Repository: MoonModules/MoonLight
Length of output: 592
🏁 Script executed:
# Search for documented sizes in the file
rg "Size: [0-9]+ bytes" src/MoonLight/Modules/palettes.h | sed 's/.*Size: \([0-9]*\).*/\1/' | sort -rn | uniqRepository: MoonModules/MoonLight
Length of output: 106
🏁 Script executed:
# Get specific info about the largest palettes mentioned
rg -B 3 "Size: [0-9]+ bytes" src/MoonLight/Modules/palettes.h | grep -E "(Size:|Gradient palette)" | paste - - | sort -t: -k2 -rn | head -20Repository: MoonModules/MoonLight
Length of output: 108
🏁 Script executed:
# Find which palette is 144 bytes
rg -B 5 "Size: 144 bytes" src/MoonLight/Modules/palettes.hRepository: MoonModules/MoonLight
Length of output: 351
🏁 Script executed:
# Get all palettes sorted by size to understand the distribution
rg -B 3 "Size: [0-9]+ bytes" src/MoonLight/Modules/palettes.h | rg "Size: [0-9]+" | sed 's/.*Size: \([0-9]*\).*/\1/' | sort -n | tail -20Repository: MoonModules/MoonLight
Length of output: 125
🏁 Script executed:
# Search for FastLED palette loading code in the repository
rg -i "loadDynamicGradientPalette|gradient.*palette" --type h --type cpp | grep -i "load\|terminator\|255" | head -20Repository: MoonModules/MoonLight
Length of output: 310
🏁 Script executed:
# Check the exact temperature_gp definition to understand its structure
rg -A 40 "Gradient palette.*temperature_gp" src/MoonLight/Modules/palettes.hRepository: MoonModules/MoonLight
Length of output: 1995
Copy gradient palettes completely until terminator, not with fixed byte limit.
The current code copies only 72 bytes, which truncates the temperature_gp palette (144 bytes) and removes its terminator (0xFF entry), causing loadDynamicGradientPalette() to read out-of-bounds. The buffer initialization byte tcp[76] = {255} only protects position 0; the rest is zero-initialized, not a valid terminator. Repack with a copy-until-terminator loop using pgm_read_byte() to safely handle palettes of any size up to the buffer limit.
🤖 Prompt for AI Agents
In `@src/MoonLight/Modules/ModuleLightsControl.h` around lines 425 - 427, The
fixed-size memcpy truncates palettes and drops the 0xFF terminator; change to
copying from PROGMEM byte-by-byte using pgm_read_byte() from
gGradientPalettes[index] (index computed as updatedItem.value.as<uint8_t>() -
11) into tcp until you encounter 0xFF or reach the tcp buffer limit, then ensure
tcp contains a valid 0xFF terminator before calling
layerP.palette.loadDynamicGradientPalette(tcp); also ensure tcp’s size is
sufficient for the largest expected palette and set the final byte to 0xFF if
the loop exits due to the size limit.
| // #include <Arduino.h> | ||
|
|
||
| const byte ib_jul01_gp[] = {0, 194, 1, 1, 94, 1, 29, 18, 132, 57, 131, 28, 255, 113, 1, 1}; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat -n src/MoonLight/Modules/palettes.h | head -40Repository: MoonModules/MoonLight
Length of output: 1977
🏁 Script executed:
rg "typedef.*byte|using.*byte|#define.*byte" --type cpp --type h -B2 -A2Repository: MoonModules/MoonLight
Length of output: 47
🏁 Script executed:
rg "^#include" src/MoonLight/Modules/palettes.hRepository: MoonModules/MoonLight
Length of output: 47
🏁 Script executed:
fd "Arduino.h" src/Repository: MoonModules/MoonLight
Length of output: 47
🏁 Script executed:
rg "Arduino\.h" src/ --type cpp --type hRepository: MoonModules/MoonLight
Length of output: 204
🏁 Script executed:
rg "\bbyte\b" src/MoonLight/Modules/palettes.hRepository: MoonModules/MoonLight
Length of output: 8405
🏁 Script executed:
git ls-files src/MoonLight/ | head -20Repository: MoonModules/MoonLight
Length of output: 866
🏁 Script executed:
cat -n src/MoonLight/Modules/palettes.h | head -50Repository: MoonModules/MoonLight
Length of output: 2535
🏁 Script executed:
rg "^\s*#include" src/MoonLight/Modules/palettes.h -A0Repository: MoonModules/MoonLight
Length of output: 47
🏁 Script executed:
rg "typedef.*uint8_t|using.*uint8_t" --type cpp --type hRepository: MoonModules/MoonLight
Length of output: 488
🏁 Script executed:
cat src/MoonBase/pal.h | head -50Repository: MoonModules/MoonLight
Length of output: 1474
🏁 Script executed:
wc -l src/MoonLight/Modules/palettes.hRepository: MoonModules/MoonLight
Length of output: 101
🏁 Script executed:
tail -20 src/MoonLight/Modules/palettes.hRepository: MoonModules/MoonLight
Length of output: 1759
🏁 Script executed:
grep -n "#include\|#define\|typedef\|using" src/MoonLight/Modules/palettes.h | head -20Repository: MoonModules/MoonLight
Length of output: 113
🏁 Script executed:
rg "type.*byte|byte.*type" src/ --type cpp --type hRepository: MoonModules/MoonLight
Length of output: 47
🏁 Script executed:
cat src/main.cpp | head -30Repository: MoonModules/MoonLight
Length of output: 1305
🏁 Script executed:
rg "palettes\.h" src/ --type cpp --type hRepository: MoonModules/MoonLight
Length of output: 212
🏁 Script executed:
grep -r "byte" src/MoonLight/Modules/palettes.h | head -5Repository: MoonModules/MoonLight
Length of output: 405
🏁 Script executed:
cat -n src/MoonLight/Modules/ModuleLightsControl.h | head -40Repository: MoonModules/MoonLight
Length of output: 1811
🏁 Script executed:
grep -n "#include" src/MoonLight/Modules/ModuleLightsControl.hRepository: MoonModules/MoonLight
Length of output: 284
Make byte definition self-contained.
This header relies on byte from Arduino, but Arduino.h isn't included. That makes compilation depend on include order and will fail outside Arduino builds. Add a guarded include or fallback typedef.
🛠️ Proposed fix
-// `#include` <Arduino.h>
+#if __has_include(<Arduino.h>)
+#include <Arduino.h>
+#else
+#include <cstdint>
+using byte = uint8_t;
+#endif📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // #include <Arduino.h> | |
| const byte ib_jul01_gp[] = {0, 194, 1, 1, 94, 1, 29, 18, 132, 57, 131, 28, 255, 113, 1, 1}; | |
| `#if` __has_include(<Arduino.h>) | |
| `#include` <Arduino.h> | |
| `#else` | |
| `#include` <cstdint> | |
| using byte = uint8_t; | |
| `#endif` | |
| const byte ib_jul01_gp[] = {0, 194, 1, 1, 94, 1, 29, 18, 132, 57, 131, 28, 255, 113, 1, 1}; |
🧰 Tools
🪛 Clang (14.0.6)
[error] 30-30: unknown type name 'byte'
(clang-diagnostic-error)
🤖 Prompt for AI Agents
In `@src/MoonLight/Modules/palettes.h` around lines 28 - 31, The header uses the
Arduino type `byte` for `ib_jul01_gp` but doesn't include Arduino.h, making
compilation order-dependent; update the header to be self-contained by adding a
guarded include or fallback typedef: check for a prior definition of `byte` (or
for ARDUINO macro) and if missing either include <Arduino.h> or typedef unsigned
char byte so `const byte ib_jul01_gp[]` has a defined type within this header.
| } else if (combined_code == codePaletteInc) { // palette increase | ||
| newState["palette"] = MIN(state.data["palette"].as<uint8_t>() + 1, 8 + 3 + 61); // 8 FastLED + 3 custom + 61 WLED palettes. to do: replace nr with max palette count | ||
| } else if (combined_code == codePaletteDec) { // palette decrease |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clamp max palette index to the last valid value.
Line 368 clamps to 72 (the palette count), but valid indices are 0–71. This allows palette=72, which will index past gGradientPalettes in ModuleLightsControl. Clamp to count − 1 (or a shared max constant).
🛠️ Proposed fix
- newState["palette"] = MIN(state.data["palette"].as<uint8_t>() + 1, 8 + 3 + 61); // 8 FastLED + 3 custom + 61 WLED palettes. to do: replace nr with max palette count
+ newState["palette"] = MIN(state.data["palette"].as<uint8_t>() + 1, (uint8_t)(8 + 3 + 61 - 1)); // max index = count-1📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else if (combined_code == codePaletteInc) { // palette increase | |
| newState["palette"] = MIN(state.data["palette"].as<uint8_t>() + 1, 8 + 3 + 61); // 8 FastLED + 3 custom + 61 WLED palettes. to do: replace nr with max palette count | |
| } else if (combined_code == codePaletteDec) { // palette decrease | |
| } else if (combined_code == codePaletteInc) { // palette increase | |
| newState["palette"] = MIN(state.data["palette"].as<uint8_t>() + 1, (uint8_t)(8 + 3 + 61 - 1)); // max index = count-1 | |
| } else if (combined_code == codePaletteDec) { // palette decrease |
🤖 Prompt for AI Agents
In `@src/MoonLight/Nodes/Drivers/D_Infrared.h` around lines 367 - 369, The clamp
for palette increase in the combined_code == codePaletteInc branch currently
uses MIN(state.data["palette"].as<uint8_t>() + 1, 8 + 3 + 61) which allows an
out‑of‑range index (72); change the ceiling to the last valid index by
subtracting 1 (i.e. use (8 + 3 + 61) - 1 or, better, a shared MAX_PALETTE_INDEX
constant) so newState["palette"] is clamped to 0..(count-1); update the same
logic location (the combined_code == codePaletteInc branch that sets
newState["palette"]) and ensure any other uses of the literal 8+3+61 are
replaced with the shared constant or consistently subtract 1.
| void loop() override { | ||
| // uint16_t dataSize = (SEGLEN + 7) >> 3; // 1 bit per LED | ||
| // if (!SEGENV.allocateData(dataSize)) return mode_oops(); // allocation failed | ||
| // if (SEGENV.call == 0) { | ||
| // SEGMENT.setUpLeds(); // WLEDMM use lossless getPixelColor() | ||
| // SEGMENT.fill(BLACK); | ||
| // } | ||
|
|
||
| // CRGB fastled_col, prev; | ||
| // fract8 fadeUpAmount = strip.getBrightness() > 28 ? 8 + (SEGMENT.speed >> 2) : 68 - strip.getBrightness(); | ||
| // fract8 fadeDownAmount = strip.getBrightness() > 28 ? 8 + (SEGMENT.speed >> 3) : 68 - strip.getBrightness(); | ||
| // for (uint16_t i = 0; i < SEGLEN; i++) { | ||
| // fastled_col = SEGMENT.getPixelColor(i); | ||
| // prev = fastled_col; | ||
| // uint16_t index = i >> 3; | ||
| // uint8_t bitNum = i & 0x07; | ||
| // bool fadeUp = bitRead(SEGENV.data[index], bitNum); | ||
|
|
||
| // if (fadeUp) { | ||
| // CRGB incrementalColor = fastled_col; | ||
| // incrementalColor.nscale8_video(fadeUpAmount); | ||
| // fastled_col += incrementalColor; | ||
|
|
||
| // if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) { | ||
| // bitWrite(SEGENV.data[index], bitNum, false); | ||
| // } | ||
| // SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); | ||
|
|
||
| // if (SEGMENT.getPixelColor(i) == RGBW32(prev.r, prev.g, prev.b, 0)) { // fix "stuck" pixels | ||
| // fastled_col += fastled_col; | ||
| // SEGMENT.setPixelColor(i, fastled_col); | ||
| // } | ||
| // } else { | ||
| // fastled_col.nscale8(255 - fadeDownAmount); | ||
| // SEGMENT.setPixelColor(i, fastled_col); | ||
| // } | ||
| // } | ||
|
|
||
| // for (uint16_t j = 0; j <= SEGLEN / 50; j++) { | ||
| // if (random8() <= SEGMENT.intensity) { | ||
| // for (uint8_t times = 0; times < 5; times++) { // attempt to spawn a new pixel 5 times | ||
| // int i = random16(SEGLEN); | ||
| // if (SEGMENT.getPixelColor(i) == 0) { | ||
| // fastled_col = ColorFromPalette(SEGPALETTE, random8(), 64, NOBLEND); | ||
| // uint16_t index = i >> 3; | ||
| // uint8_t bitNum = i & 0x07; | ||
| // bitWrite(SEGENV.data[index], bitNum, true); | ||
| // SEGMENT.setPixelColor(i, fastled_col); | ||
| // break; // only spawn 1 new pixel per frame per 50 LEDs | ||
| // } | ||
| // } | ||
| // } | ||
| // } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ColorTwinkle renders nothing right now.
loop() is empty, so the effect produces no output and can leave stale pixels. Either implement a minimal twinkle or keep it unregistered until the port is complete.
🛠️ Minimal placeholder to avoid a no-op
void loop() override {
- // ... commented WLED port ...
+ layer->fadeToBlackBy(32);
+ const uint16_t spawn = max<uint16_t>(1, layer->nrOfLights / 50);
+ for (uint16_t j = 0; j < spawn; ++j) {
+ if (random8() < intensity) {
+ nrOfLights_t idx = random16(layer->nrOfLights);
+ layer->setRGB(idx, ColorFromPalette(layerP.palette, random8()));
+ }
+ }
}🤖 Prompt for AI Agents
In `@src/MoonLight/Nodes/Effects/E_WLED.h` around lines 2010 - 2063, The loop()
for this effect (the overridden loop() in E_WLED.h / ColorTwinkle) is currently
empty and leaves stale LEDs; add a minimal placeholder: on first call (check
SEGENV.call == 0) call SEGMENT.setUpLeds() then clear the strip by setting every
pixel to BLACK (use SEGLEN and SEGMENT.setPixelColor), or alternatively call
SEGMENT.fill(BLACK) to ensure no stale output, and ensure any required
SEGENV.allocateData(...) is invoked if the effect expects per-pixel state (use
SEGENV.allocateData with dataSize computed from SEGLEN) so the effect doesn’t
crash; keep the implementation tiny and deterministic until the full twinkle
logic is ported.
| class PlasmaEffect : public Node { | ||
| public: | ||
| static const char* name() { return "Plasma"; } | ||
| static uint8_t dim() { return _3D; } // Dimensions supported _3D prefered, _2D or _1D can be used for first phase | ||
| static const char* tags() { return "🔥🎨⏳"; } // use emojis see https://moonmodules.org/MoonLight/moonlight/overview/#emoji-coding, 🔥 for effect, 🎨 if palette used (recommended) | ||
|
|
||
| uint8_t speed = 60; // 1 beat per second | ||
| uint8_t intensity = 128; | ||
|
|
||
| void setup() override { | ||
| // controls will show in the UI | ||
| // for different type of controls see other Nodes | ||
| addControl(speed, "speed", "slider"); | ||
| addControl(intensity, "intensity", "slider"); | ||
| // initialize phases on start | ||
| aux0 = random8(0, 2); // add a bit of randomness | ||
| } | ||
|
|
||
| void onSizeChanged(const Coord3D& prevSize) override {} // e.g. realloc variables | ||
|
|
||
| void onUpdate(const Char<20>& oldValue, const JsonObject& control) { | ||
| // add your custom onUpdate code here | ||
| if (control["name"] == "bpm") { | ||
| if (control["value"] == 0) { | ||
| } | ||
| } | ||
| } | ||
|
|
||
| uint8_t aux0; | ||
|
|
||
| // each frame of effect | ||
| // use of bpm functions like beatsin8 is recommended so independent from LEDs framerate (33000/nrOfLights) | ||
| void loop() override { | ||
| uint8_t thisPhase = beatsin8(6 + aux0, -64, 64); | ||
| uint8_t thatPhase = beatsin8(7 + aux0, -64, 64); | ||
|
|
||
| for (int i = 0; i < layer->size.y; i++) { // For each of the LED's in the strand, set color & brightness based on a wave as follows: | ||
| uint8_t colorIndex = cubicwave8((i * (2 + 3 * (speed >> 5)) + thisPhase) & 0xFF) / 2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change. | ||
| + cos8((i * (1 + 2 * (speed >> 5)) + thatPhase) & 0xFF) / 2; // factor=15 // Hey, you can even change the frequencies if you wish. | ||
| uint8_t thisBright = qsub8(colorIndex, beatsin8(7, 0, (128 - (intensity >> 1)))); | ||
| layer->setRGB(Coord3D(0, i), ColorFromPalette(layerP.palette, colorIndex, thisBright)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plasma advertises _3D but only updates a single column.
With dim() == _3D, writing only Coord3D(0, i) leaves most of the matrix untouched (and not cleared), which causes artifacts. Either downgrade to _1D or render across all axes.
🛠️ Option A: mark as 1D (matches current rendering)
- static uint8_t dim() { return _3D; }
+ static uint8_t dim() { return _1D; }🛠️ Option B: render across X/Z for 2D/3D
- for (int i = 0; i < layer->size.y; i++) {
+ for (int z = 0; z < layer->size.z; z++) {
+ for (int x = 0; x < layer->size.x; x++) {
+ for (int i = 0; i < layer->size.y; i++) {
uint8_t colorIndex = cubicwave8((i * (2 + 3 * (speed >> 5)) + thisPhase) & 0xFF) / 2
+ cos8((i * (1 + 2 * (speed >> 5)) + thatPhase) & 0xFF) / 2;
uint8_t thisBright = qsub8(colorIndex, beatsin8(7, 0, (128 - (intensity >> 1))));
- layer->setRGB(Coord3D(0, i), ColorFromPalette(layerP.palette, colorIndex, thisBright));
- }
+ layer->setRGB(Coord3D(x, i, z), ColorFromPalette(layerP.palette, colorIndex, thisBright));
+ }
+ }
+ }🤖 Prompt for AI Agents
In `@src/MoonLight/Nodes/Effects/E_WLED.h` around lines 2068 - 2109, PlasmaEffect
currently advertises 3D via dim() but only writes a single column in loop()
(layer->setRGB(Coord3D(0, i), ...)), leaving other LEDs untouched; either change
dim() to return _1D to match current rendering, or modify loop() to iterate X
and Z (and optionally clear the layer first) and call layer->setRGB(Coord3D(x,
i, z), ColorFromPalette(...)) for each coordinate so the effect covers the full
2D/3D plane; update the dim() implementation or the loop() iteration accordingly
in the PlasmaEffect class.
| void loop() override { | ||
| // if (!strip.isMatrix) return mode_oops(); // not a 2D set-up | ||
|
|
||
| // const uint16_t cols = SEGMENT.virtualWidth(); | ||
| // const uint16_t rows = SEGMENT.virtualHeight(); | ||
|
|
||
| // if (!SEGENV.allocateData(sizeof(julia))) return mode_oops(); | ||
| // Julia* julias = reinterpret_cast<Julia*>(SEGENV.data); | ||
|
|
||
| // float reAl; | ||
| // float imAg; | ||
|
|
||
| // if (SEGENV.call == 0) { // Reset the center if we've just re-started this animation. | ||
| // SEGMENT.setUpLeds(); | ||
| // SEGMENT.fill(BLACK); // WLEDMM avoids dimming when blur option is selected | ||
| // julias->xcen = 0.; | ||
| // julias->ycen = 0.; | ||
| // julias->xymag = 1.0; | ||
|
|
||
| // SEGMENT.custom1 = 128; // Make sure the location widgets are centered to start. | ||
| // SEGMENT.custom2 = 128; | ||
| // SEGMENT.custom3 = 16; | ||
| // SEGMENT.intensity = 24; | ||
| // } | ||
|
|
||
| // // WLEDMM limit drift, so we don't move away into nothing | ||
| // constexpr float maxCenter = 2.5f; // just an educated guess | ||
| // if (fabsf(julias->xcen) < maxCenter) julias->xcen = julias->xcen + (float)(SEGMENT.custom1 - 128) / 100000.f; | ||
| // if (fabsf(julias->ycen) < maxCenter) julias->ycen = julias->ycen + (float)(SEGMENT.custom2 - 128) / 100000.f; | ||
|
|
||
| // julias->xymag = julias->xymag + (float)((SEGMENT.custom3 - 16) << 3) / 100000.f; // reduced resolution slider | ||
| // if (julias->xymag < 0.01f) julias->xymag = 0.01f; | ||
| // if (julias->xymag > 1.0f) julias->xymag = 1.0f; | ||
|
|
||
| // float xmin = julias->xcen - julias->xymag; | ||
| // float xmax = julias->xcen + julias->xymag; | ||
| // float ymin = julias->ycen - julias->xymag; | ||
| // float ymax = julias->ycen + julias->xymag; | ||
|
|
||
| // // Whole set should be within -1.2,1.2 to -.8 to 1. | ||
| // xmin = constrain(xmin, -1.2f, 1.2f); | ||
| // xmax = constrain(xmax, -1.2f, 1.2f); | ||
| // ymin = constrain(ymin, -0.8f, 1.0f); | ||
| // ymax = constrain(ymax, -0.8f, 1.0f); | ||
|
|
||
| // float dx; // Delta x is mapped to the matrix size. | ||
| // float dy; // Delta y is mapped to the matrix size. | ||
|
|
||
| // int maxIterations = 15; // How many iterations per pixel before we give up. Make it 8 bits to match our range of colours. | ||
| // float maxCalc = 16.0; // How big is each calculation allowed to be before we give up. | ||
|
|
||
| // maxIterations = SEGMENT.intensity / 2; | ||
|
|
||
| // // Resize section on the fly for some animation. | ||
| // reAl = -0.94299f; // PixelBlaze example | ||
| // imAg = 0.3162f; | ||
|
|
||
| // // reAl += sinf((float)strip.now/305.f)/20.f; | ||
| // // imAg += sinf((float)strip.now/405.f)/20.f; | ||
| // reAl += (float)sin16_t(strip.now * 34) / 655340.f; | ||
| // imAg += (float)sin16_t(strip.now * 26) / 655340.f; | ||
|
|
||
| // dx = (xmax - xmin) / cols; // Scale the delta x and y values to our matrix size. | ||
| // dy = (ymax - ymin) / rows; | ||
|
|
||
| // // Start y | ||
| // float y = ymin; | ||
| // for (int j = 0; j < rows; j++) { | ||
| // // Start x | ||
| // float x = xmin; | ||
| // for (int i = 0; i < cols; i++) { | ||
| // // Now we test, as we iterate z = z^2 + c does z tend towards infinity? | ||
| // float a = x; | ||
| // float b = y; | ||
| // int iter = 0; | ||
|
|
||
| // while (iter < maxIterations) { // Here we determine whether or not we're out of bounds. | ||
| // float aa = a * a; | ||
| // float bb = b * b; | ||
| // float len = aa + bb; | ||
| // if (len > maxCalc) { // |z| = sqrt(a^2+b^2) OR z^2 = a^2+b^2 to save on having to perform a square root. | ||
| // break; // Bail | ||
| // } | ||
|
|
||
| // // This operation corresponds to z -> z^2+c where z=a+ib c=(x,y). Remember to use 'foil'. | ||
| // b = 2 * a * b + imAg; | ||
| // a = aa - bb + reAl; | ||
| // iter++; | ||
| // } // while | ||
|
|
||
| // // We color each pixel based on how long it takes to get to infinity, or black if it never gets there. | ||
| // if (iter == maxIterations) { | ||
| // SEGMENT.setPixelColorXY(i, j, 0); | ||
| // } else { | ||
| // SEGMENT.setPixelColorXY(i, j, SEGMENT.color_from_palette(iter * 255 / maxIterations, false, PALETTE_SOLID_WRAP, 0)); | ||
| // } | ||
| // x += dx; | ||
| // } | ||
| // y += dy; | ||
| // } | ||
|
|
||
| // // WLEDMM | ||
| // if (SEGMENT.check1) SEGMENT.blurRows(48, false); // slight blurr | ||
| // if (SEGMENT.check2) SEGMENT.blur(64, true); // strong blurr | ||
| // if (SEGMENT.check3) { // draw crosshair | ||
| // int screenX = lroundf((0.5f / maxCenter) * (julias->xcen + maxCenter) * float(cols)); | ||
| // int screenY = lroundf((0.5f / maxCenter) * (julias->ycen + maxCenter) * float(rows)); | ||
| // int hair = min(min(cols - 1, rows - 1) / 2, 3); | ||
| // SEGMENT.drawLine(screenX, screenY - hair, screenX, screenY + hair, GREEN, true); | ||
| // SEGMENT.drawLine(screenX - hair, screenY, screenX + hair, screenY, GREEN, true); | ||
| // } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Julia and Police are no-op effects.
Both loop() bodies are empty, so selecting these effects yields no output (and may retain previous frame data). Please implement at least a minimal render or keep them unregistered until the ports are complete.
If helpful, I can assist with a minimal placeholder or a direct WLED port.
Also applies to: 2304-2308
🤖 Prompt for AI Agents
In `@src/MoonLight/Nodes/Effects/E_WLED.h` around lines 2143 - 2254, The Julia and
Police effect loop() implementations are empty causing no output; fix by
implementing a minimal placeholder render in each effect's loop (e.g., in the
loop() method in E_WLED.h for Julia and similarly for Police around the region
noted at 2304-2308): call SEGMENT.setUpLeds(), clear the frame with
SEGMENT.fill(BLACK) (or BLACK with desired brightness), then draw a simple
deterministic indicator (for example a single moving pixel or a low-cost color
stripe using SEGMENT.setPixelColorXY or SEGMENT.setPixelColor) based on
strip.now or SEGMENT.custom1 so the effect visibly updates, and return;
alternatively, if you prefer not to ship a placeholder, unregister/remove these
effects from the registry until full ports are implemented. Ensure you reference
and modify the loop() methods for the Julia and Police effect classes and use
SEGMENT APIs (setUpLeds, fill, setPixelColorXY) to avoid leaving previous frames
visible.
Summary by CodeRabbit
Release Notes
New Features
Documentation
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.