@@ -637,6 +637,42 @@ uint16_t IRAM_ATTR Segment::nrOfVStrips() const {
637637 return vLen;
638638}
639639
640+ // Constants for mapping mode "Pinwheel"
641+ #ifndef WLED_DISABLE_2D
642+ constexpr int Pinwheel_Steps_Small = 72 ; // no holes up to 16x16
643+ constexpr int Pinwheel_Size_Small = 16 ; // larger than this -> use "Medium"
644+ constexpr int Pinwheel_Steps_Medium = 192 ; // no holes up to 32x32
645+ constexpr int Pinwheel_Size_Medium = 32 ; // larger than this -> use "Big"
646+ constexpr int Pinwheel_Steps_Big = 304 ; // no holes up to 50x50
647+ constexpr int Pinwheel_Size_Big = 50 ; // larger than this -> use "XL"
648+ constexpr int Pinwheel_Steps_XL = 368 ;
649+ constexpr float Int_to_Rad_Small = (DEG_TO_RAD * 360 ) / Pinwheel_Steps_Small; // conversion: from 0...72 to Radians
650+ constexpr float Int_to_Rad_Med = (DEG_TO_RAD * 360 ) / Pinwheel_Steps_Medium; // conversion: from 0...192 to Radians
651+ constexpr float Int_to_Rad_Big = (DEG_TO_RAD * 360 ) / Pinwheel_Steps_Big; // conversion: from 0...304 to Radians
652+ constexpr float Int_to_Rad_XL = (DEG_TO_RAD * 360 ) / Pinwheel_Steps_XL; // conversion: from 0...368 to Radians
653+
654+ constexpr int Fixed_Scale = 512 ; // fixpoint scaling factor (9bit for fraction)
655+
656+ // Pinwheel helper function: pixel index to radians
657+ static float getPinwheelAngle (int i, int vW, int vH) {
658+ int maxXY = max (vW, vH);
659+ if (maxXY <= Pinwheel_Size_Small) return float (i) * Int_to_Rad_Small;
660+ if (maxXY <= Pinwheel_Size_Medium) return float (i) * Int_to_Rad_Med;
661+ if (maxXY <= Pinwheel_Size_Big) return float (i) * Int_to_Rad_Big;
662+ // else
663+ return float (i) * Int_to_Rad_XL;
664+ }
665+ // Pinwheel helper function: matrix dimensions to number of rays
666+ static int getPinwheelLength (int vW, int vH) {
667+ int maxXY = max (vW, vH);
668+ if (maxXY <= Pinwheel_Size_Small) return Pinwheel_Steps_Small;
669+ if (maxXY <= Pinwheel_Size_Medium) return Pinwheel_Steps_Medium;
670+ if (maxXY <= Pinwheel_Size_Big) return Pinwheel_Steps_Big;
671+ // else
672+ return Pinwheel_Steps_XL;
673+ }
674+ #endif
675+
640676// 1D strip
641677uint16_t IRAM_ATTR Segment::virtualLength () const {
642678#ifndef WLED_DISABLE_2D
@@ -652,6 +688,9 @@ uint16_t IRAM_ATTR Segment::virtualLength() const {
652688 case M12_pArc:
653689 vLen = max (vW,vH); // get the longest dimension
654690 break ;
691+ case M12_sPinwheel:
692+ vLen = getPinwheelLength (vW, vH);
693+ break ;
655694 }
656695 return vLen;
657696 }
@@ -718,6 +757,52 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
718757 for (int x = 0 ; x <= i; x++) setPixelColorXY (x, i, col);
719758 for (int y = 0 ; y < i; y++) setPixelColorXY (i, y, col);
720759 break ;
760+ case M12_sPinwheel: {
761+ // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small)
762+ float centerX = roundf ((vW-1 ) / 2 .0f );
763+ float centerY = roundf ((vH-1 ) / 2 .0f );
764+ float angleRad = getPinwheelAngle (i, vW, vH); // angle in radians
765+ float cosVal = cos_t (angleRad);
766+ float sinVal = sin_t (angleRad);
767+
768+ // avoid re-painting the same pixel
769+ int lastX = INT_MIN; // impossible position
770+ int lastY = INT_MIN; // impossible position
771+ // draw line at angle, starting at center and ending at the segment edge
772+ // we use fixed point math for better speed. Starting distance is 0.5 for better rounding
773+ // int_fast16_t and int_fast32_t types changed to int, minimum bits commented
774+ int posx = (centerX + 0 .5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit
775+ int posy = (centerY + 0 .5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit
776+ int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit
777+ int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit
778+
779+ int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint
780+ int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint
781+
782+ // Odd rays start further from center if prevRay started at center.
783+ static int prevRay = INT_MIN; // previous ray number
784+ if ((i % 2 == 1 ) && (i - 1 == prevRay || i + 1 == prevRay)) {
785+ int jump = min (vW/3 , vH/3 ); // can add 2 if using medium pinwheel
786+ posx += inc_x * jump;
787+ posy += inc_y * jump;
788+ }
789+ prevRay = i;
790+
791+ // draw ray until we hit any edge
792+ while ((posx >= 0 ) && (posy >= 0 ) && (posx < maxX) && (posy < maxY)) {
793+ // scale down to integer (compiler will replace division with appropriate bitshift)
794+ int x = posx / Fixed_Scale;
795+ int y = posy / Fixed_Scale;
796+ // set pixel
797+ if (x != lastX || y != lastY) setPixelColorXY (x, y, col); // only paint if pixel position is different
798+ lastX = x;
799+ lastY = y;
800+ // advance to next position
801+ posx += inc_x;
802+ posy += inc_y;
803+ }
804+ break ;
805+ }
721806 }
722807 return ;
723808 } else if (Segment::maxHeight!=1 && (width ()==1 || height ()==1 )) {
@@ -833,7 +918,36 @@ uint32_t IRAM_ATTR Segment::getPixelColor(int i)
833918 // use longest dimension
834919 return vW>vH ? getPixelColorXY (i, 0 ) : getPixelColorXY (0 , i);
835920 break ;
836- }
921+ case M12_sPinwheel:
922+ // not 100% accurate, returns pixel at outer edge
923+ // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small)
924+ float centerX = roundf ((vW-1 ) / 2 .0f );
925+ float centerY = roundf ((vH-1 ) / 2 .0f );
926+ float angleRad = getPinwheelAngle (i, vW, vH); // angle in radians
927+ float cosVal = cos_t (angleRad);
928+ float sinVal = sin_t (angleRad);
929+
930+ int posx = (centerX + 0 .5f * cosVal) * Fixed_Scale; // X starting position in fixed point 18 bit
931+ int posy = (centerY + 0 .5f * sinVal) * Fixed_Scale; // Y starting position in fixed point 18 bit
932+ int inc_x = cosVal * Fixed_Scale; // X increment per step (fixed point) 10 bit
933+ int inc_y = sinVal * Fixed_Scale; // Y increment per step (fixed point) 10 bit
934+ int32_t maxX = vW * Fixed_Scale; // X edge in fixedpoint
935+ int32_t maxY = vH * Fixed_Scale; // Y edge in fixedpoint
936+
937+ // trace ray from center until we hit any edge - to avoid rounding problems, we use the same method as in setPixelColor
938+ int x = INT_MIN;
939+ int y = INT_MIN;
940+ while ((posx >= 0 ) && (posy >= 0 ) && (posx < maxX) && (posy < maxY)) {
941+ // scale down to integer (compiler will replace division with appropriate bitshift)
942+ x = posx / Fixed_Scale;
943+ y = posy / Fixed_Scale;
944+ // advance to next position
945+ posx += inc_x;
946+ posy += inc_y;
947+ }
948+ return getPixelColorXY (x, y);
949+ break ;
950+ }
837951 return 0 ;
838952 }
839953#endif
0 commit comments