testsAndMisc/python_pkg/wallpaper_rotator/_style_gradient.py
Krzysztof kuhy Rudnicki d502580e3f Dark wallpaper rotator: new style every 3 h
Six text-free styles (gradient, flow/Voronoi, habit heatmap, shutdown
clock, Earth day/night, plasma) replace the white i3 root. A 15-minute
systemd user timer picks a new random style per monitor every 3 hours,
unlike its previous one and the other monitor's; live styles refresh in
between. Also syncs i3/config with the live config and prunes the
oldest evidence artifact for the 60-file cap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWX51FMD58yKUC1eYWf
2026-11-11 16:31:06 +01:00

33 lines
1.2 KiB
Python

"""Style 1: a dark diagonal gradient with film grain."""
from __future__ import annotations
from typing import TYPE_CHECKING
import numpy as np
from PIL import Image
from python_pkg.wallpaper_rotator import _palette
if TYPE_CHECKING:
from python_pkg.wallpaper_rotator._context import RenderContext
_GRAIN = 3
def render(
_ctx: RenderContext, size: tuple[int, int], rng: np.random.Generator
) -> Image.Image:
"""Blend BG into DEEP along a random diagonal, then add faint noise."""
width, height = size
angle = rng.uniform(0.0, 2.0 * np.pi)
# The ramp is smooth: compute it at a quarter size, then upscale.
small_w, small_h = max(width // 4, 2), max(height // 4, 2)
ys, xs = np.indices((small_h, small_w), dtype=np.float64)
proj = xs / small_w * np.cos(angle) + ys / small_h * np.sin(angle)
t = (proj - proj.min()) / max(float(np.ptp(proj)), 1e-9)
small = Image.fromarray(_palette.ramp(t, (_palette.BG, _palette.DEEP)), "RGB")
base = np.asarray(small.resize(size, Image.Resampling.BICUBIC), dtype=np.int16)
grain = rng.integers(-_GRAIN, _GRAIN + 1, size=(height, width, 1), dtype=np.int16)
return Image.fromarray(np.clip(base + grain, 0, 255).astype(np.uint8), "RGB")