import random import utime from patterns.pattern_modes import style_mode _LEGACY = {"sparkle_trail": 0, "ice_sparkle": 1, "fireflies": 2} class Sparkle: def __init__(self, driver): self.driver = driver def _run_trail(self, preset, colors, last): density = max(1, int(preset.n1) if int(preset.n1) > 0 else 24) decay = max(1, min(255, int(preset.n2) if int(preset.n2) > 0 else 210)) d = max(1, int(preset.d)) now = utime.ticks_ms() if utime.ticks_diff(now, last) < d: return last, False for i in range(self.driver.num_leds): r, g, b = self.driver.n[i] self.driver.n[i] = ((r * decay) // 255, (g * decay) // 255, (b * decay) // 255) sparks = max(1, self.driver.num_leds * density // 255) for _ in range(sparks): idx = random.randint(0, max(0, self.driver.num_leds - 1)) c = self.driver.apply_brightness( colors[random.randint(0, len(colors) - 1)], preset.b ) self.driver.n[idx] = c self.driver.n.write() return utime.ticks_add(last, d), True def _run_ice(self, preset, colors, sparks, last): rate = max(1, min(255, int(preset.n1) if int(preset.n1) > 0 else 55)) decay = max(1, min(255, int(preset.n2) if int(preset.n2) > 0 else 140)) halo = max(0, min(3, int(preset.n3))) cap = 28 d = max(1, int(preset.d)) now = utime.ticks_ms() if utime.ticks_diff(now, last) < d: return sparks, last, False bg = self.driver.apply_brightness(preset.background_or(colors), preset.b) for i in range(self.driver.num_leds): self.driver.n[i] = bg ns = [] for s in sparks: lv = s["lv"] - decay if lv > 0: s["lv"] = lv ns.append(s) sparks = ns if len(sparks) < cap and random.randint(0, 255) < rate: sparks.append( { "p": random.randint(0, max(0, self.driver.num_leds - 1)), "lv": 255, "ci": random.randint(0, len(colors) - 1), } ) for s in sparks: p = s["p"] lv = s["lv"] ci = s["ci"] base = colors[ci] for off in range(-halo, halo + 1): idx = p + off if 0 <= idx < self.driver.num_leds: dist = abs(off) fac = lv if dist == 0 else (lv * (halo - dist + 1)) // (halo + 1) lit = self.driver.apply_brightness( ( (base[0] * fac) // 255, (base[1] * fac) // 255, (base[2] * fac) // 255, ), preset.b, ) o = self.driver.n[idx] self.driver.n[idx] = ( min(255, o[0] + lit[0]), min(255, o[1] + lit[1]), min(255, o[2] + lit[2]), ) self.driver.n.write() return sparks, utime.ticks_add(last, d), True def _run_fireflies(self, preset, colors, bugs, last): count = max(1, int(preset.n1) if int(preset.n1) > 0 else 6) speed = max(1, int(preset.n2) if int(preset.n2) > 0 else 8) d = max(1, int(preset.d)) now = utime.ticks_ms() if utime.ticks_diff(now, last) < d: return bugs, last, False bg_color = self.driver.apply_brightness(preset.background_or(colors), preset.b) for i in range(self.driver.num_leds): self.driver.n[i] = bg_color for b in bugs: idx, ph = b tri = 255 - abs(128 - ph) * 2 c = self.driver.apply_brightness(colors[idx % len(colors)], preset.b) self.driver.n[idx] = ((c[0] * tri) // 255, (c[1] * tri) // 255, (c[2] * tri) // 255) b[1] = (ph + speed) & 255 if random.randint(0, 31) == 0: b[0] = random.randint(0, max(0, self.driver.num_leds - 1)) self.driver.n.write() return bugs, utime.ticks_add(last, d), True def run(self, preset): """Sparkles: n6 0 trail decay, 1 ice burst+halo, 2 fireflies.""" mode = style_mode(preset, 0, _LEGACY) colors = preset.c if preset.c else [(120, 120, 255)] last = utime.ticks_ms() if mode == 2: colors = preset.c if preset.c else [(255, 210, 80), (120, 255, 120)] count = max(1, int(preset.n1) if int(preset.n1) > 0 else 6) bugs = [ [random.randint(0, max(0, self.driver.num_leds - 1)), random.randint(0, 255)] for _ in range(count) ] while True: bugs, last, stepped = self._run_fireflies(preset, colors, bugs, last) if stepped and not preset.a: yield return yield if mode == 1: colors = preset.c if preset.c else [ (240, 248, 255), (200, 235, 255), (255, 255, 255), ] sparks = [] while True: sparks, last, stepped = self._run_ice(preset, colors, sparks, last) if stepped and not preset.a: yield return yield while True: last, stepped = self._run_trail(preset, colors, last) if stepped and not preset.a: yield return yield