From 30ee87a7e24ceecbabf8cc3d018f8ac7bd312f61 Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Wed, 8 Oct 2025 22:15:18 -0400 Subject: [PATCH 1/2] Arc Expand Optmizations Faster algorithm without floats, multiplication or division. No more missing pixels. More accurate gPC. --- wled00/FX_fcn.cpp | 52 ++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 2d98dc0447..9a5dd21e91 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -663,7 +663,7 @@ uint16_t Segment::virtualLength() const { vLen = max(vW,vH); // get the longest dimension break; case M12_pArc: - vLen = sqrt32_bw(vH*vH + vW*vW); // use diagonal + vLen = (sqrt32_bw((vH*vH + vW*vW) << 2) + 1) >> 1; // diagonal rounded break; case M12_sPinwheel: vLen = getPinwheelLength(vW, vH); @@ -742,36 +742,25 @@ void WLED_O2_ATTR Segment::setPixelColor(int i, uint32_t col) const if (vStrip > 0) setPixelColorRaw(XY(vStrip - 1, vH - i - 1), col); else for (int x = 0; x < vW; x++) setPixelColorRaw(XY(x, vH - i - 1), col); break; - case M12_pArc: + case M12_pArc: { // expand in circular fashion from center - if (i == 0) - setPixelColorRaw(XY(0, 0), col); - else { - float r = i; - float step = HALF_PI / (2.8284f * r + 4); // we only need (PI/4)/(r/sqrt(2)+1) steps - for (float rad = 0.0f; rad <= (HALF_PI/2)+step/2; rad += step) { - int x = roundf(sin_t(rad) * r); - int y = roundf(cos_t(rad) * r); - // exploit symmetry + if (i == 0) setPixelColorXY(0, 0, col); + else if (i == 2) setPixelColorXY(1, 1, col); + + int x = 0, y = i; // i is the radius + int d = -(i >> 1); // Initial decision parameter + + // Barrera's circle algorithm + while (x <= y) { + if (!(i == x && i == y)) { // prevent early square setPixelColorXY(x, y, col); setPixelColorXY(y, x, col); } - // Bresenham’s Algorithm (may not fill every pixel) - //int d = 3 - (2*i); - //int y = i, x = 0; - //while (y >= x) { - // setPixelColorXY(x, y, col); - // setPixelColorXY(y, x, col); - // x++; - // if (d > 0) { - // y--; - // d += 4 * (x - y) + 10; - // } else { - // d += 4 * x + 6; - // } - //} + if (d <= 0) d += ++x; + else d -= --y; } break; + } case M12_pCorner: for (int x = 0; x <= i; x++) setPixelColorXY(x, i, col); // note: <= to include i=0. Relies on overflow check in sPC() for (int y = 0; y < i; y++) setPixelColorXY(i, y, col); @@ -947,9 +936,16 @@ uint32_t WLED_O2_ATTR Segment::getPixelColor(int i) const else { y = vH - i - 1; }; break; case M12_pArc: - if (i > vW && i > vH) { - x = y = sqrt32_bw(i*i/2); - break; // use diagonal + if (i >= vW && i >= vH) { // Barrera's circle algorithm + int x2 = 0, y2 = i, d = -(i >> 1); + int validCount = 0; // return 2nd non mirrored pixel if available + while (x2 <= y2 && validCount < 2) { + if (y2 < vH && x2 < vW) {x = x2, y = y2; validCount++;} + else if (y2 < vW && x2 < vH) {x = y2, y = x2; validCount++;} + + if (d <= 0) d += ++x2; else d -= --y2; + } + break; } // otherwise fallthrough case M12_pCorner: From 8c087b9fcba962b9d9bb3df55d1fa58d011f30a2 Mon Sep 17 00:00:00 2001 From: Brandon502 <105077712+Brandon502@users.noreply.github.com> Date: Fri, 10 Oct 2025 00:41:04 -0400 Subject: [PATCH 2/2] Arc Optimizations 2 Use sPCXYRaw(). Small logic changes. --- wled00/FX_fcn.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 9a5dd21e91..85e0f3e68d 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -743,18 +743,21 @@ void WLED_O2_ATTR Segment::setPixelColor(int i, uint32_t col) const else for (int x = 0; x < vW; x++) setPixelColorRaw(XY(x, vH - i - 1), col); break; case M12_pArc: { - // expand in circular fashion from center - if (i == 0) setPixelColorXY(0, 0, col); - else if (i == 2) setPixelColorXY(1, 1, col); + // expand in circular fashion from top left corner + // Tony Barrera's circle algorithm + // https://softwareengineering.stackexchange.com/questions/287478/drawing-concentric-circles-without-gaps/357445#357445 + + if (i == 0) setPixelColorXYRaw(0, 0, col); // 0 and 2 special cases to prevent square + else if (i == 2) setPixelColorXYRaw(1, 1, col); + else if (i == vLength()-1) setPixelColorXYRaw(vW-1, vH-1, col); // last i always draws bottom right corner int x = 0, y = i; // i is the radius int d = -(i >> 1); // Initial decision parameter - // Barrera's circle algorithm while (x <= y) { - if (!(i == x && i == y)) { // prevent early square - setPixelColorXY(x, y, col); - setPixelColorXY(y, x, col); + if (i != x || i != y) { // prevent early square + if (y < vH && x < vW) setPixelColorXYRaw(x, y, col); + if (y < vW && x < vH) setPixelColorXYRaw(y, x, col); } if (d <= 0) d += ++x; else d -= --y; @@ -937,6 +940,7 @@ uint32_t WLED_O2_ATTR Segment::getPixelColor(int i) const break; case M12_pArc: if (i >= vW && i >= vH) { // Barrera's circle algorithm + x = vW - 1; y = vH - 1; // default to bottom right if not found int x2 = 0, y2 = i, d = -(i >> 1); int validCount = 0; // return 2nd non mirrored pixel if available while (x2 <= y2 && validCount < 2) {