diff --git a/wled00/FX.h b/wled00/FX.h index 037ef181d0..acd0313923 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -355,7 +355,7 @@ typedef enum mapping1D2D { M12_jMap = 4, //WLEDMM jMap M12_sCircle = 5, //WLEDMM Circle M12_sBlock = 6, //WLEDMM Block - M12_sPinWheel = 7 //WLEDMM PinWheel + M12_sPinwheel = 7 //WLEDMM Pinwheel } mapping1D2D_t; // segment, 72 bytes diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 01af8b5206..9ce628bd29 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -793,14 +793,41 @@ void Segment::deletejMap() { } -// WLEDMM constants for mapping mode "Pinwheel" -constexpr int Pinwheel_Steps_Medium = 208; // no holes up to 32x32; 60fps -constexpr int Pinwheel_Size_Medium = 32; // larger than this -> use "Big" -constexpr int Pinwheel_Steps_Big = 360; // no holes expected up to 58x58; 40fps -constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...208 to Radians -constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...360 to Radians -// WLEDMM end - +// Constants for mapping mode "Pinwheel" +#ifndef WLED_DISABLE_2D +constexpr int Pinwheel_Steps_Small = 72; // no holes up to 16x16 +constexpr int Pinwheel_Size_Small = 16; // larger than this -> use "Medium" +constexpr int Pinwheel_Steps_Medium = 192; // no holes up to 32x32 +constexpr int Pinwheel_Size_Medium = 32; // larger than this -> use "Big" +constexpr int Pinwheel_Steps_Big = 304; // no holes up to 50x50 +constexpr int Pinwheel_Size_Big = 50; // larger than this -> use "XL" +constexpr int Pinwheel_Steps_XL = 368; +constexpr float Int_to_Rad_Small = (DEG_TO_RAD * 360) / Pinwheel_Steps_Small; // conversion: from 0...72 to Radians +constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360) / Pinwheel_Steps_Medium; // conversion: from 0...192 to Radians +constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360) / Pinwheel_Steps_Big; // conversion: from 0...304 to Radians +constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360) / Pinwheel_Steps_XL; // conversion: from 0...368 to Radians + +constexpr int Fixed_Scale = 512; // fixpoint scaling factor (9bit for fraction) + +// Pinwheel helper function: pixel index to radians +static float getPinwheelAngle(int i, int vW, int vH) { + int maxXY = max(vW, vH); + if (maxXY <= Pinwheel_Size_Small) return float(i) * Int_to_Rad_Small; + if (maxXY <= Pinwheel_Size_Medium) return float(i) * Int_to_Rad_Med; + if (maxXY <= Pinwheel_Size_Big) return float(i) * Int_to_Rad_Big; + // else + return float(i) * Int_to_Rad_XL; +} +// Pinwheel helper function: matrix dimensions to number of rays +static int getPinwheelLength(int vW, int vH) { + int maxXY = max(vW, vH); + if (maxXY <= Pinwheel_Size_Small) return Pinwheel_Steps_Small; + if (maxXY <= Pinwheel_Size_Medium) return Pinwheel_Steps_Medium; + if (maxXY <= Pinwheel_Size_Big) return Pinwheel_Steps_Big; + // else + return Pinwheel_Steps_XL; +} +#endif // 1D strip uint16_t Segment::virtualLength() const { @@ -831,12 +858,8 @@ uint16_t Segment::virtualLength() const { else vLen = max(vW,vH) * 0.5; // get the longest dimension break; - case M12_sPinWheel: //WLEDMM - //vLen = full circle - if (max(vW,vH) <= Pinwheel_Size_Medium) - vLen = Pinwheel_Steps_Medium; - else - vLen = Pinwheel_Steps_Big; + case M12_sPinwheel: + vLen = getPinwheelLength(vW, vH); break; } return vLen; @@ -978,32 +1001,46 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT } } break; - case M12_sPinWheel: { // WLEDMM - // i = angle --> 0 through 359 (Big), OR 0 through 208 (Medium) + case M12_sPinwheel: { + // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small) float centerX = roundf((vW-1) / 2.0f); float centerY = roundf((vH-1) / 2.0f); - // int maxDistance = sqrt(centerX * centerX + centerY * centerY) + 1; - float angleRad = (max(vW,vH) > Pinwheel_Size_Medium) ? float(i) * Int_to_Rad_Big : float(i) * Int_to_Rad_Med; // angle in radians + float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians float cosVal = cosf(angleRad); float sinVal = sinf(angleRad); + // avoid re-painting the same pixel + int lastX = INT_MIN; // impossible position + int lastY = INT_MIN; // impossible position // draw line at angle, starting at center and ending at the segment edge // we use fixed point math for better speed. Starting distance is 0.5 for better rounding - constexpr int_fast32_t Fixed_Scale = 512; // fixpoint scaling factor - int_fast32_t posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point - int_fast32_t posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point - int_fast16_t inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) - int_fast16_t inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) + // int_fast16_t and int_fast32_t types changed to int, minimum bits commented + int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit + int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit + int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit + int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint - // draw until we hit any edge - while ((posx > 0) && (posy > 0) && (posx < maxX) && (posy < maxY)) { + + // Odd rays start further from center if prevRay started at center. + static int prevRay = INT_MIN; // previous ray number + if ((i % 2 == 1) && (i - 1 == prevRay || i + 1 == prevRay)) { + int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel + posx += inc_x * jump; + posy += inc_y * jump; + } + prevRay = i; + + // draw ray until we hit any edge + while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) { // scale down to integer (compiler will replace division with appropriate bitshift) int x = posx / Fixed_Scale; int y = posy / Fixed_Scale; // set pixel - setPixelColorXY(x, y, col); + if (x != lastX || y != lastY) setPixelColorXY(x, y, col); // only paint if pixel position is different + lastX = x; + lastY = y; // advance to next position posx += inc_x; posy += inc_y; @@ -1154,16 +1191,36 @@ uint32_t Segment::getPixelColor(int i) else return getPixelColorXY(vW / 2, vH / 2 - i - 1); break; - case M12_sPinWheel: //WLEDMM - // not 100% accurate, returns outer edge of circle - float distance = max(1.0f, min(vH-1, vW-1) / 2.0f); - float centerX = (vW - 1) / 2.0f; - float centerY = (vH - 1) / 2.0f; - float angleRad = (max(vW,vH) > Pinwheel_Size_Medium) ? float(i) * Int_to_Rad_Big : float(i) * Int_to_Rad_Med; // angle in radians - int x = roundf(centerX + distance * cosf(angleRad)); - int y = roundf(centerY + distance * sinf(angleRad)); + case M12_sPinwheel: + // not 100% accurate, returns pixel at outer edge + // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small) + float centerX = roundf((vW-1) / 2.0f); + float centerY = roundf((vH-1) / 2.0f); + float angleRad = getPinwheelAngle(i, vW, vH); // angle in radians + float cosVal = cosf(angleRad); + float sinVal = sinf(angleRad); + + int posx = (centerX + 0.5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit + int posy = (centerY + 0.5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit + int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit + int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit + int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint + int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint + + // trace ray from center until we hit any edge - to avoid rounding problems, we use the same method as in setPixelColor + int x = INT_MIN; + int y = INT_MIN; + while ((posx >= 0) && (posy >= 0) && (posx < maxX) && (posy < maxY)) { + // scale down to integer (compiler will replace division with appropriate bitshift) + x = posx / Fixed_Scale; + y = posy / Fixed_Scale; + // advance to next position + posx += inc_x; + posy += inc_y; + } return getPixelColorXY(x, y); - } + break; + } return 0; } #endif