import utime from patterns.pattern_modes import style_mode _LEGACY = {"rainbow": 1, "gradient_scroll": 0} class ColourCycle: def __init__(self, driver): self.driver = driver def _wheel(self, pos): if pos < 85: return (pos * 3, 255 - pos * 3, 0) if pos < 170: pos -= 85 return (255 - pos * 3, 0, pos * 3) pos -= 170 return (0, pos * 3, 255 - pos * 3) def _render_gradient(self, preset, colors, phase, brightness): num_leds = self.driver.num_leds color_count = len(colors) if num_leds <= 0 or color_count <= 0: return if color_count == 1: self.driver.fill(self.driver.apply_brightness(colors[0], brightness)) return full_span = color_count * 256 phase_shift = (phase * full_span) // 256 for i in range(num_leds): pos = ((i * full_span) // num_leds + phase_shift) % full_span idx = pos // 256 frac = pos & 255 c1 = colors[idx] c2 = colors[(idx + 1) % color_count] blended = ( c1[0] + ((c2[0] - c1[0]) * frac) // 256, c1[1] + ((c2[1] - c1[1]) * frac) // 256, c1[2] + ((c2[2] - c1[2]) * frac) // 256, ) self.driver.n[self.driver.led_i(preset, i)] = self.driver.apply_brightness( blended, brightness ) self.driver.n.write() def _render_rainbow(self, preset, phase, brightness): num_leds = self.driver.num_leds for i in range(num_leds): rc_index = (i * 256 // max(1, num_leds)) + phase self.driver.n[self.driver.led_i(preset, i)] = self.driver.apply_brightness( self._wheel(rc_index & 255), brightness ) self.driver.n.write() def run(self, preset): """Scroll gradient (n6=0) or fixed spectrum wheel (n6=1, legacy rainbow). n1: step rate n6: 0 gradient scroll, 1 rainbow wheel """ mode = style_mode(preset, 0, _LEGACY) step_amount = max(1, int(preset.n1) if int(preset.n1) > 0 else 1) phase = self.driver.step % 256 if mode == 1: if not preset.a: self._render_rainbow(preset, phase, preset.b) self.driver.step = (phase + self.driver.signed(preset, step_amount)) % 256 yield return last_update = utime.ticks_ms() while True: delay_ms = max(1, int(preset.d)) now = utime.ticks_ms() if utime.ticks_diff(now, last_update) >= delay_ms: self._render_rainbow(preset, phase, preset.b) phase = (phase + self.driver.signed(preset, step_amount)) % 256 self.driver.step = phase last_update = utime.ticks_add(last_update, delay_ms) yield colors = preset.c if preset.c else [(255, 0, 0), (0, 0, 255)] if not preset.a: self._render_gradient(preset, colors, phase, preset.b) self.driver.step = (phase + self.driver.signed(preset, step_amount)) % 256 yield return last_update = utime.ticks_ms() while True: delay_ms = max(1, int(preset.d)) now = utime.ticks_ms() if utime.ticks_diff(now, last_update) >= delay_ms: self._render_gradient(preset, colors, phase, preset.b) phase = (phase + self.driver.signed(preset, step_amount)) % 256 self.driver.step = phase last_update = utime.ticks_add(last_update, delay_ms) yield