-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Perlinscape FX (2D) in the user_fx usermod #5587
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?
Changes from 7 commits
73ca612
427e61a
c58ccb8
5c65919
e76d4af
1af6f89
8d8e107
0dcdbaa
4d1d818
b41d994
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1258,6 +1258,103 @@ static void mode_morsecode(void) { | |||||||||
| static const char _data_FX_MODE_MORSECODE[] PROGMEM = "Morse Code@Speed,,,,Color mode,Color by Word,Punctuation,EndOfMessage;;!;1;sx=192,c3=8,o1=1,o2=1"; | ||||||||||
|
|
||||||||||
|
|
||||||||||
| /* | ||||||||||
| / Perlinscape effect - a Perlin noise Landscape | ||||||||||
| * Created by stepko as part of Stepko Land on soulmatelights.com | ||||||||||
| * Adapted to WLED by Bob Loeffler with additional features (and help from Claude) | ||||||||||
| * First slider is for speed | ||||||||||
| * Second slider is for zooming in/out (Perlin scaling) | ||||||||||
| * Third slider is the X multiplier | ||||||||||
| * Fourth slider is the Y multiplier | ||||||||||
| * First checkbox will use the selected palette | ||||||||||
| * Second checkbox will rotate the image | ||||||||||
| * Third checkbox will randomize the horizonal and vertical directions | ||||||||||
| */ | ||||||||||
| static void mode_2D_perlinscape(void) { | ||||||||||
| if (!strip.isMatrix || !SEGMENT.is2D()) FX_FALLBACK_STATIC; // not a 2D set-up | ||||||||||
| const uint16_t width = SEG_W; | ||||||||||
| const uint16_t height = SEG_H; | ||||||||||
| if (!SEGENV.allocateData(5 * sizeof(float) + width * height)) FX_FALLBACK_STATIC; | ||||||||||
|
|
||||||||||
| uint32_t speedDiv = map(SEGMENT.speed, 0, 255, 20, 1); | ||||||||||
| uint32_t t = strip.now / speedDiv; | ||||||||||
| uint8_t Xmult = map(SEGMENT.custom1, 0, 255, 0, 64); | ||||||||||
| uint8_t Ymult = map(SEGMENT.custom2, 0, 255, 0, 64); | ||||||||||
|
|
||||||||||
| float *offX = reinterpret_cast<float*>(SEGENV.data) + 0; | ||||||||||
| float *offY = reinterpret_cast<float*>(SEGENV.data) + 1; | ||||||||||
| float *stepX = reinterpret_cast<float*>(SEGENV.data) + 2; | ||||||||||
| float *stepY = reinterpret_cast<float*>(SEGENV.data) + 3; | ||||||||||
| float *prevT = reinterpret_cast<float*>(SEGENV.data) + 4; | ||||||||||
|
|
||||||||||
| if (SEGENV.call == 0) { | ||||||||||
| SEGENV.aux0 = hw_random16(5000, 10000); | ||||||||||
| SEGENV.aux1 = 0b00; | ||||||||||
| *offX = 0.0f; | ||||||||||
| *offY = 0.0f; | ||||||||||
| *stepX = 1.0f; | ||||||||||
| *stepY = 1.0f; | ||||||||||
| *prevT = (float)t; | ||||||||||
| } | ||||||||||
|
Comment on lines
+1295
to
+1296
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify timestamp-delta patterns in this file (read-only audit)
rg -n -C2 'float\s+dt\s*=|strip\.now\s*-|lastBumpUpdate|prevT' usermods/user_fx/user_fx.cppRepository: wled/WLED Length of output: 3778 🏁 Script executed: #!/bin/bash
# Get detailed view of lines 1280-1320 to see full context and data allocation
sed -n '1275,1320p' usermods/user_fx/user_fx.cpp | cat -nRepository: wled/WLED Length of output: 1837 Change The current code (line 1312) subtracts two separately-cast floats: Suggested changes- if (!SEGENV.allocateData(5 * sizeof(float))) FX_FALLBACK_STATIC;
+ if (!SEGENV.allocateData(4 * sizeof(float) + sizeof(uint32_t))) FX_FALLBACK_STATIC;
...
- float *prevT = reinterpret_cast<float*>(SEGENV.data) + 4;
+ uint32_t *prevT = reinterpret_cast<uint32_t*>(reinterpret_cast<float*>(SEGENV.data) + 4);
...
- *prevT = (float)t;
+ *prevT = t;
...
- float dt = (float)t - *prevT;
+ float dt = float(int32_t(t - *prevT));
...
- *prevT = (float)t;
+ *prevT = t;🤖 Prompt for AI Agents
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this finding is right.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The correct fix (as noted in the suggestion) is to keep // allocation: 4 floats + 1 uint32_t
if (!SEGENV.allocateData(4 * sizeof(float) + sizeof(uint32_t))) FX_FALLBACK_STATIC;
uint32_t *prevT = reinterpret_cast<uint32_t*>(reinterpret_cast<float*>(SEGENV.data) + 4);
// on init:
*prevT = t;
// each frame:
float dt = float(int32_t(t - *prevT));
*offX += *stepX * dt;
*offY += *stepY * dt;
*prevT = t;This matches the pattern already used in the Ants effect for
🐇 📐 ✏️ Learnings added
🧠 Learnings used |
||||||||||
|
|
||||||||||
| if (SEGMENT.check3 && (strip.now - SEGENV.step > SEGENV.aux0)) { | ||||||||||
| SEGENV.aux0 = hw_random16(5000, 10000); | ||||||||||
| SEGENV.aux1 = hw_random8(4); | ||||||||||
| SEGENV.step = strip.now; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| bool flipX = SEGMENT.check3 ? (SEGENV.aux1 & 0x01) : false; | ||||||||||
| bool flipY = SEGMENT.check3 ? (SEGENV.aux1 & 0x02) : false; | ||||||||||
|
|
||||||||||
| float targetX = flipX ? -1.0f : 1.0f; | ||||||||||
| float targetY = flipY ? -1.0f : 1.0f; | ||||||||||
| *stepX += (targetX - *stepX) * 0.05f; | ||||||||||
| *stepY += (targetY - *stepY) * 0.05f; | ||||||||||
|
|
||||||||||
| float dt = (float)t - *prevT; | ||||||||||
| *offX += *stepX * dt; | ||||||||||
| *offY += *stepY * dt; | ||||||||||
| *prevT = (float)t; | ||||||||||
|
|
||||||||||
| int32_t tX = (int32_t)*offX; | ||||||||||
| int32_t tY = (int32_t)*offY; | ||||||||||
|
|
||||||||||
| // Rotation | ||||||||||
| float cosA = 1.0f, sinA = 0.0f; | ||||||||||
| float cx = width * 0.5f; | ||||||||||
| float cy = height * 0.5f; | ||||||||||
|
|
||||||||||
| if (SEGMENT.check2) { | ||||||||||
| float angle = strip.now / 5000.0f; | ||||||||||
| cosA = cosf(angle); | ||||||||||
| sinA = sinf(angle); | ||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||||||||||
| } | ||||||||||
|
|
||||||||||
| float scale = map(SEGMENT.intensity, 0, 255, 10, 200) / 100.0f; // range 0.1 to 2.0 | ||||||||||
|
|
||||||||||
| for (byte x = 0; x < width; x++) { | ||||||||||
| for (byte y = 0; y < height; y++) { | ||||||||||
|
Comment on lines
+1333
to
+1334
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prevent loop-counter overflow on wider matrices. Line 1333 and Line 1334 use Suggested fix- for (byte x = 0; x < width; x++) {
- for (byte y = 0; y < height; y++) {
+ for (uint16_t x = 0; x < width; x++) {
+ for (uint16_t y = 0; y < height; y++) {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @BobLoeffler68 in the main FX.cpp we usually don't use 8bit types at all. They are not faster, and just add subtle bugs like the "fails when width > 255" highlighted by the rabbit. It's true that wled currently does not work with a panel width > 255, but there are plans to change this in future. Simply changing "byte" to "unsigned" in both loops should do the trick.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||
| float rx = cosA * (x - cx) - sinA * (y - cy) + cx; | ||||||||||
| float ry = sinA * (x - cx) + cosA * (y - cy) + cy; | ||||||||||
|
|
||||||||||
| float scaled_x = rx * Xmult * scale; | ||||||||||
| float scaled_y = ry * Ymult * scale; | ||||||||||
|
|
||||||||||
| if (SEGMENT.check1) { | ||||||||||
| // Palette mode | ||||||||||
| uint8_t paletteIndex = perlin8(scaled_x, scaled_y, t); | ||||||||||
| uint8_t brightness = perlin8(scaled_x + tX, scaled_y + tY); | ||||||||||
| SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(paletteIndex, false, PALETTE_SOLID_WRAP, brightness)); | ||||||||||
| } else { | ||||||||||
| // Raw RGB mode | ||||||||||
| SEGMENT.setPixelColorXY(x, y, perlin8(scaled_x, scaled_y, t), perlin8(scaled_x, scaled_y + tY), perlin8(scaled_x + tX, scaled_y)); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
| static const char _data_FX_MODE_2D_PERLINSCAPE[] PROGMEM = "Perlinscape@!,Zoom (+/-),X multiplier,Y multiplier,,Palettes,Rotation,Random direction;;!;2;"; | ||||||||||
|
|
||||||||||
|
|
||||||||||
| ///////////////////// | ||||||||||
| // UserMod Class // | ||||||||||
| ///////////////////// | ||||||||||
|
|
@@ -1272,6 +1369,7 @@ class UserFxUsermod : public Usermod { | |||||||||
| strip.addEffect(255, &mode_2D_magma, _data_FX_MODE_2D_MAGMA); | ||||||||||
| strip.addEffect(255, &mode_ants, _data_FX_MODE_ANTS); | ||||||||||
| strip.addEffect(255, &mode_morsecode, _data_FX_MODE_MORSECODE); | ||||||||||
| strip.addEffect(255, &mode_2D_perlinscape, _data_FX_MODE_2D_PERLINSCAPE); | ||||||||||
|
|
||||||||||
| //////////////////////////////////////// | ||||||||||
| // add your effect function(s) here // | ||||||||||
|
|
||||||||||
Uh oh!
There was an error while loading. Please reload this page.