Files
portal/leds/text.py
Jimmy d5cab2efdf Add multi-panel Pico UDP firmware and Python LED control.
Pico panels get static IPs on 10.1.1.10–14 with per-panel LED counts, Makefile deploy targets, and Python examples for animations, sync tests, and direct Pi SPI control.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 22:46:08 +12:00

125 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""5×7 bitmap text for LED matrices."""
from __future__ import annotations
from typing import Tuple
from leds.array_config import MATRIX_HEIGHT, MATRIX_WIDTH, matrix_pixel_index
RGB = Tuple[int, int, int]
# Each letter: 7 rows, 5 LSBs = columns (bit 4 = left).
_FONT: dict[str, tuple[int, ...]] = {
" ": (0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
"A": (0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11),
"B": (0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E),
"C": (0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E),
"D": (0x1E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E),
"E": (0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F),
"F": (0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10),
"G": (0x0E, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0E),
"H": (0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11),
"I": (0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E),
"J": (0x07, 0x02, 0x02, 0x02, 0x02, 0x12, 0x0C),
"K": (0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11),
"L": (0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F),
"M": (0x11, 0x1B, 0x15, 0x11, 0x11, 0x11, 0x11),
"N": (0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11),
"O": (0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E),
"P": (0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10),
"Q": (0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D),
"R": (0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11),
"S": (0x0E, 0x11, 0x10, 0x0E, 0x01, 0x11, 0x0E),
"T": (0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04),
"U": (0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E),
"V": (0x11, 0x11, 0x11, 0x11, 0x0A, 0x0A, 0x04),
"W": (0x11, 0x11, 0x11, 0x15, 0x15, 0x1B, 0x11),
"X": (0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11),
"Y": (0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04),
"Z": (0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F),
"0": (0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E),
"1": (0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E),
"2": (0x0E, 0x11, 0x01, 0x06, 0x08, 0x10, 0x1F),
"3": (0x1F, 0x02, 0x04, 0x06, 0x01, 0x11, 0x0E),
"4": (0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02),
"5": (0x1F, 0x10, 0x1E, 0x01, 0x01, 0x11, 0x0E),
"6": (0x06, 0x08, 0x10, 0x1E, 0x11, 0x11, 0x0E),
"7": (0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08),
"8": (0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E),
"9": (0x0E, 0x11, 0x11, 0x0F, 0x01, 0x02, 0x0C),
}
CHAR_WIDTH = 5
CHAR_HEIGHT = 7
CHAR_SPACING = 1
def text_width(text: str, spacing: int = CHAR_SPACING) -> int:
text = text.upper()
if not text:
return 0
return len(text) * CHAR_WIDTH + (len(text) - 1) * spacing
def _set(surface, x: int, y: int, color: RGB) -> None:
if hasattr(surface, "width") and hasattr(surface, "height"):
if 0 <= x < surface.width and 0 <= y < surface.height:
surface[x, y] = color
else:
w = getattr(surface, "width", MATRIX_WIDTH)
idx = matrix_pixel_index(x, y, w)
if 0 <= idx < len(surface):
surface[idx] = color
def draw_char(
surface,
ch: str,
x: int,
y: int,
color: RGB,
*,
scale: int = 1,
) -> None:
glyph = _FONT.get(ch.upper(), _FONT[" "])
for row, bits in enumerate(glyph):
for col in range(CHAR_WIDTH):
if bits & (1 << (CHAR_WIDTH - 1 - col)):
for sy in range(scale):
for sx in range(scale):
_set(surface, x + col * scale + sx, y + row * scale + sy, color)
def draw_text(
surface,
text: str,
x: int,
y: int,
color: RGB,
*,
scale: int = 1,
spacing: int = CHAR_SPACING,
) -> None:
cursor = x
step = (CHAR_WIDTH + spacing) * scale
for ch in text.upper():
draw_char(surface, ch, cursor, y, color, scale=scale)
cursor += step
def draw_text_centered(
surface,
text: str,
color: RGB,
*,
scale: int = 1,
spacing: int = CHAR_SPACING,
) -> None:
w = getattr(surface, "width", MATRIX_WIDTH)
h = getattr(surface, "height", MATRIX_HEIGHT)
tw = text_width(text, spacing) * scale
th = CHAR_HEIGHT * scale
x = max(0, (w - tw) // 2)
y = max(0, (h - th) // 2)
draw_text(surface, text, x, y, color, scale=scale, spacing=spacing)