use smart_leds_trait::RGB8;
use crate::segment::{EffectConfig, EffectState};
use crate::utils::{BLACK, color_blend, color_wheel, fade_out, next_rand, sine8};
pub fn twinkle_fox(pixels: &mut [RGB8], state: &mut EffectState, config: &EffectConfig) {
let c0 = config.colors[0];
let c1 = config.colors[1];
let c2 = config.colors[2];
let call = state.counter as u16;
let mut seed: u16 = 0;
for pixel in pixels.iter_mut() {
seed = seed.wrapping_mul(2053).wrapping_add(13849);
let init = ((seed.wrapping_add(seed >> 8)) & 0xFF) as u8;
seed = seed.wrapping_mul(2053).wrapping_add(13849);
let incr = (((seed.wrapping_add(seed >> 8)) & 0x07) as u8 + 1) * 2;
let blend_index = init.wrapping_add((call.wrapping_mul(incr as u16) & 0xFF) as u8);
let blend_amt = sine8(blend_index);
let is_black = |c: RGB8| c.r == 0 && c.g == 0 && c.b == 0;
*pixel = if is_black(c0) {
color_blend(color_wheel(init), c1, blend_amt)
} else if !is_black(c2) && init >= 128 {
color_blend(c2, c1, blend_amt)
} else {
color_blend(c0, c1, blend_amt)
};
}
state.counter = state.counter.wrapping_add(1);
}
pub fn rain(pixels: &mut [RGB8], state: &mut EffectState, config: &EffectConfig) {
let len = pixels.len();
let rng = next_rand(state.aux);
state.aux = rng;
let rain_color = if rng & 1 == 0 {
config.colors[0]
} else {
config.colors[2]
};
fade_out(pixels, BLACK, 128);
if rng % 8 == 0 && len > 2 {
let rng2 = next_rand(rng);
state.aux = rng2;
let idx = (rng2 % (len as u32 - 2) + 1) as usize;
pixels[idx] = rain_color;
}
if len > 2 {
pixels.copy_within(0..len - 2, 2);
pixels[0] = BLACK;
pixels[1] = BLACK;
}
}
pub fn icu(pixels: &mut [RGB8], state: &mut EffectState, config: &EffectConfig) {
let len = pixels.len();
let half = (len / 2).max(1);
let pos = state.counter as usize;
let dest = (state.aux & 0xFFFF) as usize;
let settled = (state.aux >> 16) & 1 == 1;
for p in pixels.iter_mut() {
*p = BLACK;
}
if settled {
if pos < len && pos + half < len {
pixels[pos] = config.colors[0];
pixels[pos + half] = config.colors[0];
}
let wait = (state.aux >> 17) as u32;
if wait >= 8 {
let rng = next_rand(state.aux as u32);
let new_dest = (rng % half as u32) as usize;
state.aux = (new_dest as u32) & 0xFFFF;
} else {
state.aux = (state.aux & !(0x7F << 17)) | ((wait + 1) << 17);
}
return;
}
let new_pos = if dest > pos {
pos + 1
} else if dest < pos {
pos - 1
} else {
pos
};
state.counter = new_pos as u32;
if new_pos < len && new_pos + half < len {
pixels[new_pos] = config.colors[0];
pixels[new_pos + half] = config.colors[0];
}
if new_pos == dest {
state.aux = (dest as u32 & 0xFFFF) | (1 << 16); }
}
pub fn filler_up(pixels: &mut [RGB8], state: &mut EffectState, config: &EffectConfig) {
let len = pixels.len();
let drop_pos = state.counter as usize;
let fill_level = (state.aux & 0xFFFF) as usize;
let swapped = (state.aux >> 16) & 1 == 1;
let fg = if swapped {
config.colors[1]
} else {
config.colors[0]
};
let bg = if swapped {
config.colors[0]
} else {
config.colors[1]
};
for p in pixels.iter_mut() {
*p = bg;
}
if fill_level > 0 && fill_level <= len {
for p in pixels[len - fill_level..len].iter_mut() {
*p = fg;
}
}
let dp = drop_pos.min(len.saturating_sub(fill_level + 1));
if dp < len {
pixels[dp] = fg;
}
let next_drop = drop_pos + 1;
if next_drop >= len.saturating_sub(fill_level) {
let new_fill = fill_level + 1;
if new_fill >= len {
state.aux = (swapped as u32 ^ 1) << 16;
} else {
state.aux = (new_fill as u32) | ((swapped as u32) << 16);
}
state.counter = 0;
} else {
state.counter = next_drop as u32;
}
}
pub fn trifade(pixels: &mut [RGB8], state: &mut EffectState, config: &EffectConfig) {
let is_black = |c: RGB8| c.r == 0 && c.g == 0 && c.b == 0;
let use_black = is_black(config.colors[2]);
let colors_main: [RGB8; 3] = config.colors;
let colors_alt: [RGB8; 6] = [
config.colors[0],
BLACK,
config.colors[1],
BLACK,
config.colors[2],
BLACK,
];
let num = if use_black { 6usize } else { 3usize };
let idx = (state.aux as usize) % num;
let next_idx = (idx + 1) % num;
let (c1, c2) = if use_black {
(colors_alt[idx], colors_alt[next_idx])
} else {
(colors_main[idx], colors_main[next_idx])
};
let blend = (state.counter & 0xFF) as u8;
let color = color_blend(c1, c2, blend);
for pixel in pixels.iter_mut() {
*pixel = color;
}
state.counter = state.counter.wrapping_add(4);
if state.counter & 0xFF < 4 {
state.aux = (state.aux + 1) % num as u32;
}
}
pub fn heartbeat(pixels: &mut [RGB8], state: &mut EffectState, config: &EffectConfig) {
let len = pixels.len();
let half = len / 2;
if half > 1 {
pixels.copy_within(1..half, 0); pixels.copy_within(half..len - 1, half + 1); }
fade_out(pixels, BLACK, 32);
let step = state.counter;
if step == 0 || step == 8 {
let size = 2.min(half);
for p in pixels[half - size..half + size].iter_mut() {
*p = config.colors[0];
}
}
state.counter = (state.counter + 1) % 60;
}
pub fn rainbow_fireworks(pixels: &mut [RGB8], state: &mut EffectState, _config: &EffectConfig) {
let len = pixels.len();
for i in 0..len {
let c = pixels[i];
let faded = RGB8 {
r: c.r / 2,
g: c.g / 2,
b: c.b / 2,
};
pixels[i] = faded;
macro_rules! spawn_neighbor {
($threshold_r:expr, $color:expr) => {
if faded.r == $threshold_r && faded.g == 0 && faded.b == 0 {
let neighbor = ($color) as RGB8;
let dist = match $threshold_r {
0x7F => 1usize,
0x3F => 2,
0x1F => 3,
0x0F => 4,
0x07 => 5,
0x03 => 6,
_ => 0,
};
if i >= dist {
pixels[i - dist] = neighbor;
}
if i + dist < len {
pixels[i + dist] = neighbor;
}
}
};
}
spawn_neighbor!(
0x7F,
RGB8 {
r: 255,
g: 127,
b: 0
}
); spawn_neighbor!(
0x3F,
RGB8 {
r: 255,
g: 255,
b: 0
}
); spawn_neighbor!(0x1F, RGB8 { r: 0, g: 255, b: 0 }); spawn_neighbor!(0x0F, RGB8 { r: 0, g: 0, b: 255 }); spawn_neighbor!(
0x07,
RGB8 {
r: 75,
g: 0,
b: 130
}
); spawn_neighbor!(
0x03,
RGB8 {
r: 148,
g: 0,
b: 211
}
); }
let rng = next_rand(state.aux);
state.aux = rng;
if rng % 4 == 0 && len > 12 {
let idx = (next_rand(rng) % (len - 12) as u32 + 6) as usize;
pixels[idx] = RGB8 { r: 255, g: 0, b: 0 };
}
}