Skip to content

Commit e33299b

Browse files
authored
Merge pull request #3961 from Brandon502/0_15
Added Pinwheel Expand 1D ->2D effect mapping mode
2 parents 5f41de8 + 3cb6b17 commit e33299b

File tree

3 files changed

+118
-2
lines changed

3 files changed

+118
-2
lines changed

wled00/FX.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,8 @@ typedef enum mapping1D2D {
324324
M12_Pixels = 0,
325325
M12_pBar = 1,
326326
M12_pArc = 2,
327-
M12_pCorner = 3
327+
M12_pCorner = 3,
328+
M12_sPinwheel = 4
328329
} mapping1D2D_t;
329330

330331
// segment, 80 bytes

wled00/FX_fcn.cpp

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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
641677
uint16_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

wled00/data/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,7 @@ function populateSegments(s)
801801
`<option value="1" ${inst.m12==1?' selected':''}>Bar</option>`+
802802
`<option value="2" ${inst.m12==2?' selected':''}>Arc</option>`+
803803
`<option value="3" ${inst.m12==3?' selected':''}>Corner</option>`+
804+
`<option value="4" ${inst.m12==4?' selected':''}>Pinwheel</option>`+
804805
`</select></div>`+
805806
`</div>`;
806807
let sndSim = `<div data-snd="si" class="lbl-s hide">Sound sim<br>`+

0 commit comments

Comments
 (0)