| 71 | } |
| 72 | |
| 73 | void Luminova::plotSoftDot(fl::span<CRGB> leds, float fx, float fy, float s) const { |
| 74 | // Map s (decays from ~3) to a pixel radius 1..3 |
| 75 | float r = fl::clamp<float>(s * 0.5f, 1.0f, 3.0f); |
| 76 | int R = static_cast<int>(fl::ceil(r)); |
| 77 | // Avoid roundf() to prevent AVR macro conflicts; do manual symmetric rounding |
| 78 | int cx = static_cast<int>(fx + (fx >= 0.0f ? 0.5f : -0.5f)); |
| 79 | int cy = static_cast<int>(fy + (fy >= 0.0f ? 0.5f : -0.5f)); |
| 80 | float r2 = r * r; |
| 81 | for (int dy = -R; dy <= R; ++dy) { |
| 82 | for (int dx = -R; dx <= R; ++dx) { |
| 83 | float d2 = static_cast<float>(dx * dx + dy * dy); |
| 84 | if (d2 <= r2) { |
| 85 | float fall = 1.0f - (d2 / (r2 + 0.0001f)); |
| 86 | float vf = 255.0f * fall; |
| 87 | if (vf < 0.0f) vf = 0.0f; |
| 88 | if (vf > 255.0f) vf = 255.0f; |
| 89 | u8 v = static_cast<u8>(vf); |
| 90 | plotDot(leds, cx + dx, cy + dy, v); |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | void Luminova::draw(DrawContext context) { |
| 97 | // Fade + blur trails each frame |