segovia 0.3.0

A fast, chunked, memory-bounded Rust engine for electrophysiology (Neuropixels) signal processing, with Python bindings.
Documentation
from PIL import Image, ImageDraw, ImageFont

BG_TOP = (11, 16, 32)
BG_BOT = (22, 27, 46)
RUST_L = (222, 165, 132)
RUST_R = (206, 66, 43)
NODE = (90, 107, 140)
GLOW = (255, 217, 160)
CORE = (255, 231, 194)
T_WHITE = (245, 247, 250)
T_RUST = (222, 165, 132)
T_GREY = (139, 151, 176)
T_DIM = (90, 107, 140)

FONT_DIR = "C:/Windows/Fonts/"


def lerp(a, b, t):
    return tuple(round(a[i] + (b[i] - a[i]) * t) for i in range(3))


def font(bold, size):
    name = "segoeuib.ttf" if bold else "segoeui.ttf"
    return ImageFont.truetype(FONT_DIR + name, size)


def vgrad(w, h):
    img = Image.new("RGB", (w, h))
    px = img.load()
    for y in range(h):
        c = lerp(BG_TOP, BG_BOT, y / (h - 1))
        for x in range(w):
            px[x, y] = c
    return img


def span(d, y, segs, r, x0, x1):
    for sx, ex in segs:
        x = sx
        while x <= ex:
            c = lerp(RUST_L, RUST_R, (x - x0) / (x1 - x0))
            d.ellipse([x - r, y - r, x + r, y + r], fill=c)
            x += 2


def gaps(d, y, xs):
    for x in xs:
        d.ellipse([x - 7, y - 7, x + 7, y + 7], fill=NODE)


def pulse(d, x, y, rad, core):
    steps = 60
    for i in range(steps, 0, -1):
        rr = rad * i / steps
        t = i / steps
        c = lerp(GLOW, BG_BOT, t)
        d.ellipse([x - rr, y - rr, x + rr, y + rr], fill=c)
    d.ellipse([x - core, y - core, x + core, y + core], fill=CORE)


def render(path, w, h, ty, segs, r, nx, py, prad, pcore, title_sz, t1, t2, t3, lines):
    img = vgrad(w, h)
    d = ImageDraw.Draw(img)
    x0, x1 = segs[0][0], segs[-1][1]
    span(d, ty, segs, r, x0, x1)
    gaps(d, ty, nx)
    pulse(d, py[0], ty, prad, pcore)
    d.text((lines[0][0], t1), "Segovia", font=font(True, title_sz), fill=T_WHITE, anchor="ls")
    d.text((lines[0][0] + 4, t2[0]), lines[1], font=font(False, t2[1]), fill=T_RUST, anchor="ls")
    d.text((lines[0][0] + 4, t3[0]), lines[2], font=font(False, t3[1]), fill=T_GREY, anchor="ls")
    d.text((lines[0][0] + 4, lines[3][0]), lines[4], font=font(False, lines[3][1]), fill=T_DIM, anchor="ls")
    img.save(path)
    print("wrote", path, img.size)


render(
    "C:/Projects/cyber/assets/segovia-cover.png", 1200, 630, 300,
    [(120, 300), (345, 525), (570, 750), (795, 975), (1020, 1090)], 11,
    [322, 547, 772, 997], (547,), 60, 13, 120,
    240, (440, 34), (486, 26),
    [(120,), "A Rust engine for neural data", "Chunked - bounded memory - open source",
     (556, 20), "ELECTROPHYSIOLOGY  -  PyO3  -  SpikeInterface"],
)

render(
    "C:/Projects/cyber/assets/segovia-feed.png", 1080, 1080, 540,
    [(90, 290), (335, 535), (580, 780), (825, 990)], 13,
    [312, 557, 802], (557,), 75, 15, 150,
    380, (730, 42), (790, 32),
    [(90,), "A Rust engine for neural data", "Chunked - bounded memory - open source",
     (980, 24), "ELECTROPHYSIOLOGY  -  PyO3  -  SpikeInterface"],
)

render(
    "C:/Projects/cyber/assets/segovia-social.png", 1280, 640, 305,
    [(128, 320), (368, 560), (608, 800), (848, 1040), (1088, 1163)], 12,
    [344, 583, 824, 1063], (583,), 64, 14, 128,
    244, (447, 36), (494, 28),
    [(128,), "A Rust engine for neural data", "Chunked - bounded memory - open source",
     (565, 21), "ELECTROPHYSIOLOGY  -  PyO3  -  SpikeInterface"],
)