Merge pull request #3501 from TripleWhy/spookierEyes
A spookier version of Halloween Eyes
This commit is contained in:
commit
552ae836e9
158
wled00/FX.cpp
158
wled00/FX.cpp
@ -2615,9 +2615,28 @@ uint16_t mode_twinklecat()
|
|||||||
static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;!,!;!";
|
static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;!,!;!";
|
||||||
|
|
||||||
|
|
||||||
//inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes
|
|
||||||
uint16_t mode_halloween_eyes()
|
uint16_t mode_halloween_eyes()
|
||||||
{
|
{
|
||||||
|
enum eyeState : uint8_t {
|
||||||
|
initializeOn = 0,
|
||||||
|
on,
|
||||||
|
blink,
|
||||||
|
initializeOff,
|
||||||
|
off,
|
||||||
|
|
||||||
|
count
|
||||||
|
};
|
||||||
|
struct EyeData {
|
||||||
|
eyeState state;
|
||||||
|
uint8_t color;
|
||||||
|
uint16_t startPos;
|
||||||
|
// duration + endTime could theoretically be replaced by a single endTime, however we would lose
|
||||||
|
// the ability to end the animation early when the user reduces the animation time.
|
||||||
|
uint16_t duration;
|
||||||
|
uint32_t startTime;
|
||||||
|
uint32_t blinkEndTime;
|
||||||
|
};
|
||||||
|
|
||||||
if (SEGLEN == 1) return mode_static();
|
if (SEGLEN == 1) return mode_static();
|
||||||
const uint16_t maxWidth = strip.isMatrix ? SEGMENT.virtualWidth() : SEGLEN;
|
const uint16_t maxWidth = strip.isMatrix ? SEGMENT.virtualWidth() : SEGLEN;
|
||||||
const uint16_t HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEGMENT.virtualWidth()>>4: SEGLEN>>5);
|
const uint16_t HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEGMENT.virtualWidth()>>4: SEGLEN>>5);
|
||||||
@ -2625,57 +2644,132 @@ uint16_t mode_halloween_eyes()
|
|||||||
uint16_t eyeLength = (2*HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE;
|
uint16_t eyeLength = (2*HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE;
|
||||||
if (eyeLength >= maxWidth) return mode_static(); //bail if segment too short
|
if (eyeLength >= maxWidth) return mode_static(); //bail if segment too short
|
||||||
|
|
||||||
|
if (!SEGENV.allocateData(sizeof(EyeData))) return mode_static(); //allocation failed
|
||||||
|
EyeData& data = *reinterpret_cast<EyeData*>(SEGENV.data);
|
||||||
|
|
||||||
if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); //fill background
|
if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); //fill background
|
||||||
|
|
||||||
uint8_t state = SEGENV.aux1 >> 8;
|
data.state = static_cast<eyeState>(data.state % eyeState::count);
|
||||||
uint16_t stateTime = SEGENV.call;
|
uint16_t duration = max(uint16_t{1u}, data.duration);
|
||||||
if (stateTime == 0) stateTime = 2000;
|
const uint32_t elapsedTime = strip.now - data.startTime;
|
||||||
|
|
||||||
if (state == 0) { //spawn eyes
|
switch (data.state) {
|
||||||
SEGENV.aux0 = random16(0, maxWidth - eyeLength - 1); //start pos
|
case eyeState::initializeOn: {
|
||||||
SEGENV.aux1 = random8(); //color
|
// initialize the eyes-on state:
|
||||||
|
// - select eye position and color
|
||||||
|
// - select a duration
|
||||||
|
// - immediately switch to eyes on state.
|
||||||
|
|
||||||
|
data.startPos = random16(0, maxWidth - eyeLength - 1);
|
||||||
|
data.color = random8();
|
||||||
if (strip.isMatrix) SEGMENT.offset = random16(SEGMENT.virtualHeight()-1); // a hack: reuse offset since it is not used in matrices
|
if (strip.isMatrix) SEGMENT.offset = random16(SEGMENT.virtualHeight()-1); // a hack: reuse offset since it is not used in matrices
|
||||||
state = 1;
|
duration = 128u + random16(SEGMENT.intensity*64u);
|
||||||
|
data.duration = duration;
|
||||||
|
data.state = eyeState::on;
|
||||||
|
[[fallthrough]];
|
||||||
|
}
|
||||||
|
case eyeState::on: {
|
||||||
|
// eyes-on steate:
|
||||||
|
// - fade eyes in for some time
|
||||||
|
// - keep eyes on until the pre-selected duration is over
|
||||||
|
// - randomly switch to the blink (sub-)state, and initialize it with a blink duration (more precisely, a blink end time stamp)
|
||||||
|
// - never switch to the blink state if the animation just started or is about to end
|
||||||
|
|
||||||
|
uint16_t start2ndEye = data.startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE;
|
||||||
|
// If the user reduces the input while in this state, limit the duration.
|
||||||
|
duration = min(duration, static_cast<uint16_t>(128u + (SEGMENT.intensity * 64u)));
|
||||||
|
|
||||||
|
constexpr uint32_t minimumOnTimeBegin = 1024u;
|
||||||
|
constexpr uint32_t minimumOnTimeEnd = 1024u;
|
||||||
|
const uint32_t fadeInAnimationState = elapsedTime * uint32_t{256u * 8u} / duration;
|
||||||
|
const uint32_t backgroundColor = SEGCOLOR(1);
|
||||||
|
const uint32_t eyeColor = SEGMENT.color_from_palette(data.color, false, false, 0);
|
||||||
|
uint32_t c = eyeColor;
|
||||||
|
if (fadeInAnimationState < 256u) {
|
||||||
|
c = color_blend(backgroundColor, eyeColor, fadeInAnimationState);
|
||||||
|
} else if (elapsedTime > minimumOnTimeBegin) {
|
||||||
|
const uint32_t remainingTime = (elapsedTime >= duration) ? 0u : (duration - elapsedTime);
|
||||||
|
if (remainingTime > minimumOnTimeEnd) {
|
||||||
|
if (random8() < 4u)
|
||||||
|
{
|
||||||
|
c = backgroundColor;
|
||||||
|
data.state = eyeState::blink;
|
||||||
|
data.blinkEndTime = strip.now + random8(8, 128);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state < 2) { //fade eyes
|
if (c != backgroundColor) {
|
||||||
uint16_t startPos = SEGENV.aux0;
|
// render eyes
|
||||||
uint16_t start2ndEye = startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE;
|
|
||||||
|
|
||||||
uint32_t fadestage = (strip.now - SEGENV.step)*255 / stateTime;
|
|
||||||
if (fadestage > 255) fadestage = 255;
|
|
||||||
uint32_t c = color_blend(SEGMENT.color_from_palette(SEGENV.aux1 & 0xFF, false, false, 0), SEGCOLOR(1), fadestage);
|
|
||||||
|
|
||||||
for (int i = 0; i < HALLOWEEN_EYE_WIDTH; i++) {
|
for (int i = 0; i < HALLOWEEN_EYE_WIDTH; i++) {
|
||||||
if (strip.isMatrix) {
|
if (strip.isMatrix) {
|
||||||
SEGMENT.setPixelColorXY(startPos + i, SEGMENT.offset, c);
|
SEGMENT.setPixelColorXY(data.startPos + i, SEGMENT.offset, c);
|
||||||
SEGMENT.setPixelColorXY(start2ndEye + i, SEGMENT.offset, c);
|
SEGMENT.setPixelColorXY(start2ndEye + i, SEGMENT.offset, c);
|
||||||
} else {
|
} else {
|
||||||
SEGMENT.setPixelColor(startPos + i, c);
|
SEGMENT.setPixelColor(data.startPos + i, c);
|
||||||
SEGMENT.setPixelColor(start2ndEye + i, c);
|
SEGMENT.setPixelColor(start2ndEye + i, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
if (strip.now - SEGENV.step > stateTime) {
|
}
|
||||||
state++;
|
case eyeState::blink: {
|
||||||
if (state > 2) state = 0;
|
// eyes-on but currently blinking state:
|
||||||
|
// - wait until the blink time is over, then switch back to eyes-on
|
||||||
if (state < 2) {
|
|
||||||
stateTime = 100 + SEGMENT.intensity*10; //eye fade time
|
if (strip.now >= data.blinkEndTime) {
|
||||||
} else {
|
data.state = eyeState::on;
|
||||||
uint16_t eyeOffTimeBase = (256 - SEGMENT.speed)*10;
|
}
|
||||||
stateTime = eyeOffTimeBase + random16(eyeOffTimeBase);
|
break;
|
||||||
|
}
|
||||||
|
case eyeState::initializeOff: {
|
||||||
|
// initialize eyes-off state:
|
||||||
|
// - select a duration
|
||||||
|
// - immediately switch to eyes-off state
|
||||||
|
|
||||||
|
const uint16_t eyeOffTimeBase = SEGMENT.speed*128u;
|
||||||
|
duration = eyeOffTimeBase + random16(eyeOffTimeBase);
|
||||||
|
data.duration = duration;
|
||||||
|
data.state = eyeState::off;
|
||||||
|
[[fallthrough]];
|
||||||
|
}
|
||||||
|
case eyeState::off: {
|
||||||
|
// eyes-off state:
|
||||||
|
// - not much to do here
|
||||||
|
|
||||||
|
// If the user reduces the input while in this state, limit the duration.
|
||||||
|
const uint16_t eyeOffTimeBase = SEGMENT.speed*128u;
|
||||||
|
duration = min(duration, static_cast<uint16_t>(2u * eyeOffTimeBase));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case eyeState::count: {
|
||||||
|
// Can't happen, not an actual state.
|
||||||
|
data.state = eyeState::initializeOn;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
SEGENV.step = strip.now;
|
|
||||||
SEGENV.call = stateTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SEGENV.aux1 = (SEGENV.aux1 & 0xFF) + (state << 8); //save state
|
if (elapsedTime > duration) {
|
||||||
|
// The current state duration is over, switch to the next state.
|
||||||
|
switch (data.state) {
|
||||||
|
case eyeState::initializeOn:
|
||||||
|
case eyeState::on:
|
||||||
|
case eyeState::blink:
|
||||||
|
data.state = eyeState::initializeOff;
|
||||||
|
break;
|
||||||
|
case eyeState::initializeOff:
|
||||||
|
case eyeState::off:
|
||||||
|
case eyeState::count:
|
||||||
|
default:
|
||||||
|
data.state = eyeState::initializeOn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data.startTime = strip.now;
|
||||||
|
}
|
||||||
|
|
||||||
return FRAMETIME;
|
return FRAMETIME;
|
||||||
}
|
}
|
||||||
static const char _data_FX_MODE_HALLOWEEN_EYES[] PROGMEM = "Halloween Eyes@Duration,Eye fade time,,,,,Overlay;!,!;!;12";
|
static const char _data_FX_MODE_HALLOWEEN_EYES[] PROGMEM = "Halloween Eyes@Eye off time,Eye on time,,,,,Overlay;!,!;!;12";
|
||||||
|
|
||||||
|
|
||||||
//Speed slider sets amount of LEDs lit, intensity sets unlit
|
//Speed slider sets amount of LEDs lit, intensity sets unlit
|
||||||
|
@ -1187,7 +1187,7 @@ void WS2812FX::service() {
|
|||||||
Segment::modeBlend(false); // unset semaphore
|
Segment::modeBlend(false); // unset semaphore
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
|
seg.call++;
|
||||||
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
|
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user