#!/usr/bin/env python3 """Temple rite: beat-locked rings with energy racing the portal perimeter.""" from __future__ import annotations import math from leds.bpm_limits import ( band, resolve_beat_index, resolve_beat_phase, resolve_pulse, ) from leds.colors import dim from leds.pattern import Color, Param, Pattern, run_cli, set_pixel, size from leds.portal_span import global_x, ring_metrics class Rite(Pattern): id = "rite" label = "Rite" fps = 32 colors = { "gold": Color("#f0c060"), "blood": Color("#6a1208"), "void": Color("#060408"), } params = { "intensity": Param(0.85, min=0.2, max=1.0, step=0.05, label="Intensity"), } def draw(self, surface, t, params): w, h = size(surface) extras = params["extras"] intensity = float(extras["intensity"]) gold = params["colors"]["gold"] blood = params["colors"]["blood"] void = params["colors"]["void"] pulse = resolve_pulse(params, t, fallback_bpm=120, attack=0.18) phase = resolve_beat_phase(params, t, fallback_bpm=120) step = resolve_beat_index(params, t, fallback_bpm=120) kick = band(params, "kick") bass = band(params, "bass") energy = band(params, "energy") x_offset, ring, panel = ring_metrics(params, w) cx = (w - 1) / 2.0 cy = (h - 1) / 2.0 radius = phase * (max(w, h) * 0.85) hit = max(pulse, kick, bass * 0.7) for y in range(h): for x in range(w): gx = global_x(x, params, w) dist = math.hypot(x - cx, y - cy) ring_glow = math.exp(-((dist - radius) ** 2) / 2.8) floor = 0.12 + 0.1 * math.sin(gx * 0.08 + t * 0.7) + 0.08 * energy base = dim(void, floor) warm = dim( blood, (0.25 + 0.55 * hit) * intensity * (1.0 - dist / (max(w, h))), ) rim = dim(gold, ring_glow * intensity * (0.4 + 0.6 * hit)) set_pixel( surface, x, y, ( min(255, base[0] + warm[0] + rim[0]), min(255, base[1] + warm[1] + rim[1]), min(255, base[2] + warm[2] + rim[2]), ), ) courier = int(((step + phase) / 8.0) * ring) % max(ring, 1) for dx in range(-2, 3): gx = (courier + dx) % max(ring, 1) local = gx - x_offset if not (0 <= local < w): continue level = (1.0 - abs(dx) / 3.0) * (0.45 + 0.55 * hit) * intensity for y in range(h): edge = 1.0 - abs(y - cy) / (cy + 0.5) set_pixel(surface, local, y, dim(gold, level * edge)) face = step % 5 if panel == face: for x in range(w): set_pixel(surface, x, 0, dim(gold, 0.2 + 0.4 * hit)) set_pixel(surface, x, h - 1, dim(gold, 0.15 + 0.35 * hit)) if __name__ == "__main__": run_cli(Rite)