import utime class Roll: def __init__(self, driver): self.driver = driver def run(self, preset): """Roll: moving band with gradient from color1 to color2 over the strips. - n1: offset from start of strip (effective start = start + n1) - n2: offset from end of strip (effective end = end - n2, inclusive) - n3: number of full rotations before stopping (0 = infinite) - n4: direction (0 = clockwise, 1 = anti-clockwise) - c[0]: color1 at the head strip - c[1]: color2 at the tail strip """ colors = preset.c color1_raw = colors[0] if colors else (255, 255, 255) color2_raw = colors[1] if len(colors) > 1 else (0, 0, 0) color1 = self.driver.apply_brightness(color1_raw, preset.b) color2 = self.driver.apply_brightness(color2_raw, preset.b) n_segments = self.driver.n.num_strips if hasattr(self.driver.n, "num_strips") else 1 # Margins from the start and end of each strip start_margin = max(0, int(getattr(preset, "n1", 0))) end_margin = max(0, int(getattr(preset, "n2", 0))) # Debug info to see why roll might be black try: print( "ROLL preset", "p=", getattr(preset, "p", None), "b=", getattr(preset, "b", None), "colors_raw=", color1_raw, color2_raw, "colors_bright=", color1, color2, ) print( "ROLL n1..n4", getattr(preset, "n1", None), getattr(preset, "n2", None), getattr(preset, "n3", None), getattr(preset, "n4", None), "n_segments=", n_segments, "start_margin=", start_margin, "end_margin=", end_margin, ) except Exception: pass # n3: number of rotations (0 = infinite) max_rotations = int(getattr(preset, "n3", 0)) or 0 # n4: direction (0=cw, 1=ccw); default clockwise if missing clockwise = int(getattr(preset, "n4", 0)) == 0 step = self.driver.step delay_ms = max(1, int(preset.d) or 1) last_update = utime.ticks_ms() rotations_done = 0 def scale_color(c, f): return tuple(int(x * f) for x in c) def lerp_color(c1, c2, t): """Linear gradient between two colors.""" if t <= 0: return c1 if t >= 1: return c2 return ( int(c1[0] + (c2[0] - c1[0]) * t), int(c1[1] + (c2[1] - c1[1]) * t), int(c1[2] + (c2[2] - c1[2]) * t), ) def draw(head): # Remember head strip for flare try: self.driver.last_roll_head = head except AttributeError: pass strips_list = self.driver.strips for strip_idx, strip in enumerate(strips_list): if strip_idx < 0 or strip_idx >= n_segments: continue # Distance from head along direction, 0..n_segments-1 if clockwise: dist = (head - strip_idx) % n_segments else: dist = (strip_idx - head) % n_segments # Color gradient from color1 at the head strip to color2 at the tail strip if n_segments > 1: t = dist / (n_segments - 1) else: t = 0.0 c_strip = lerp_color(color1, color2, t) n = strip.num_leds # Effective segment per strip: # start = 0 + start_margin # end = (n - 1) - end_margin (inclusive) width = n - start_margin - end_margin if width <= 0: # If margins are too large, fall back to full strip seg_s = 0 seg_e = n else: seg_s = max(0, min(n, start_margin)) seg_e = min(n, n - end_margin) # Debug for first strip/head to see segment try: if strip_idx == 0 and head == 0: print("ROLL seg strip0 n=", n, "seg_s=", seg_s, "seg_e=", seg_e) except Exception: pass for i in range(n): if seg_s <= i < seg_e: strip.set(i, c_strip) else: strip.set(i, (0, 0, 0)) strip.show() if not preset.a: head = step % n_segments if n_segments > 0 else 0 draw(head) self.driver.step = step + 1 yield return while True: current_time = utime.ticks_ms() if utime.ticks_diff(current_time, last_update) >= delay_ms: head = step % n_segments if n_segments > 0 else 0 if not clockwise and n_segments > 0: head = (n_segments - 1 - head) draw(head) step += 1 if max_rotations > 0 and n_segments > 0 and (step % n_segments) == 0: rotations_done += 1 if rotations_done >= max_rotations: self.driver.step = step last_update = current_time return self.driver.step = step last_update = current_time yield