Spookier version of Halloween Eyes

This commit is contained in:
TripleWhy 2023-10-30 17:05:27 +01:00
parent 1dab26bcbc
commit 2c1fbe103b
2 changed files with 110 additions and 40 deletions

View File

@ -2600,9 +2600,27 @@ 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 class eyeState : uint8_t {
initializeOn = 0,
on,
blink,
initializeOff,
off,
count
};
struct EyeData {
eyeState state;
uint8 color;
uint16_t startPos;
uint16_t duration;
uint32_t startTime;
uint8_t blinkDuration;
uint32_t blinkStartTime;
};
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);
@ -2610,57 +2628,109 @@ 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>(static_cast<uint8_t>(data.state) % static_cast<uint8_t>(eyeState::count));
uint16_t stateTime = SEGENV.call; uint16_t duration = data.duration;
if (stateTime == 0) stateTime = 2000; duration = max(uint16_t{1u}, duration);
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 data.startPos = random16(0, maxWidth - eyeLength - 1);
if (strip.isMatrix) SEGMENT.offset = random16(SEGMENT.virtualHeight()-1); // a hack: reuse offset since it is not used in matrices data.color = random8();
state = 1; if (strip.isMatrix) SEGMENT.offset = random16(SEGMENT.virtualHeight()-1); // a hack: reuse offset since it is not used in matrices
} duration = 128u + random16(SEGMENT.intensity*64u);
data.duration = duration;
data.state = eyeState::on;
[[fallthrough]];
}
case eyeState::on: {
uint16_t start2ndEye = data.startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE;
duration = min(duration, static_cast<uint16_t>(128u + (SEGMENT.intensity*64u)));
if (state < 2) { //fade eyes constexpr uint32_t minimumOnTimeBegin = 1024u;
uint16_t startPos = SEGENV.aux0; constexpr uint32_t minimumOnTimeEnd = 1024u;
uint16_t start2ndEye = startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE; const uint32_t fadeInAnimationState = elapsedTime * static_cast<uint32_t>(256*8) / duration;
const uint32_t backgroundColor = SEGCOLOR(1);
uint32_t fadestage = (strip.now - SEGENV.step)*255 / stateTime; const uint32_t eyeColor = SEGMENT.color_from_palette(data.color, false, false, 0);
if (fadestage > 255) fadestage = 255; uint32_t c = eyeColor;
uint32_t c = color_blend(SEGMENT.color_from_palette(SEGENV.aux1 & 0xFF, false, false, 0), SEGCOLOR(1), fadestage); if (fadeInAnimationState < 256u) {
c = color_blend(backgroundColor, eyeColor, fadeInAnimationState);
for (int i = 0; i < HALLOWEEN_EYE_WIDTH; i++) { } else if (elapsedTime > minimumOnTimeBegin) {
if (strip.isMatrix) { const uint32_t remainingTime = (elapsedTime >= duration) ? 0u : (duration - elapsedTime);
SEGMENT.setPixelColorXY(startPos + i, SEGMENT.offset, c); if (remainingTime > minimumOnTimeEnd) {
SEGMENT.setPixelColorXY(start2ndEye + i, SEGMENT.offset, c); if (random8() < 4u)
} else { {
SEGMENT.setPixelColor(startPos + i, c); c = backgroundColor;
SEGMENT.setPixelColor(start2ndEye + i, c); data.state = eyeState::blink;
data.blinkDuration = random8(8, 128);
data.blinkStartTime = strip.now;
}
}
} }
if (c != backgroundColor) {
for (int i = 0; i < HALLOWEEN_EYE_WIDTH; i++) {
if (strip.isMatrix) {
SEGMENT.setPixelColorXY(data.startPos + i, SEGMENT.offset, c);
SEGMENT.setPixelColorXY(start2ndEye + i, SEGMENT.offset, c);
} else {
SEGMENT.setPixelColor(data.startPos + i, c);
SEGMENT.setPixelColor(start2ndEye + i, c);
}
}
}
break;
}
case eyeState::blink: {
const uint32_t elapsedBlinkTime = strip.now - data.blinkStartTime;
if (elapsedBlinkTime >= data.blinkDuration) {
data.state = eyeState::on;
}
break;
}
case eyeState::initializeOff: {
const uint16_t eyeOffTimeBase = SEGMENT.speed*128u;
duration = eyeOffTimeBase + random16(eyeOffTimeBase);
data.duration = duration;
data.state = eyeState::off;
[[fallthrough]];
}
case eyeState::off: {
const uint16_t eyeOffTimeBase = SEGMENT.speed*128u;
duration = min(duration, static_cast<uint16_t>(2u * eyeOffTimeBase));
break;
}
case eyeState::count: {
data.state = eyeState::initializeOn;
break;
} }
} }
if (strip.now - SEGENV.step > stateTime) { if (elapsedTime > duration) {
state++; switch (data.state) {
if (state > 2) state = 0; case eyeState::initializeOn:
case eyeState::on:
if (state < 2) { case eyeState::blink:
stateTime = 100 + SEGMENT.intensity*10; //eye fade time data.state = eyeState::initializeOff;
} else { break;
uint16_t eyeOffTimeBase = (256 - SEGMENT.speed)*10; case eyeState::initializeOff:
stateTime = eyeOffTimeBase + random16(eyeOffTimeBase); case eyeState::off:
case eyeState::count:
default:
data.state = eyeState::initializeOn;
break;
} }
SEGENV.step = strip.now; data.startTime = strip.now;
SEGENV.call = stateTime;
} }
SEGENV.aux1 = (SEGENV.aux1 & 0xFF) + (state << 8); //save state
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

View File

@ -1261,7 +1261,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.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
} }