diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 2d98dc0447..85e0f3e68d 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,28 @@ 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: - // 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 - setPixelColorXY(x, y, col); - setPixelColorXY(y, x, col); + case M12_pArc: { + // 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 + + while (x <= y) { + 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); } - // 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 +939,17 @@ 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 + 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) { + 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: