"""LED matrix layout defaults.""" from __future__ import annotations import os from typing import Literal MATRIX_WIDTH = 45 MATRIX_HEIGHT = 9 MATRIX_PIXELS = MATRIX_WIDTH * MATRIX_HEIGHT # 405 # --- Hexagonal portal ------------------------------------------------------- # Bottom face = floor (no LEDs). Panels 0–4 numbered clockwise from bottom-left. # Panel 2 is the top face. All five panels are Pico UDP adapters. # # Viewed from outside: # # [2] TOP # [1] [3] # top-left top-right # # [0] [4] # bottom-left bottom-right # # Floor (no LEDs) is below. TOP_PANEL_INDEX = 2 PORTAL_PANEL_ORDER_CLOCKWISE: tuple[int, ...] = (0, 1, 2, 3, 4) SIDE_PANEL_ORDER: tuple[int, ...] = (0, 1, 3, 4) PANEL_POSITION_BY_INDEX: dict[int, str] = { 0: "bottom-left", 1: "top-left", 2: "top", 3: "top-right", 4: "bottom-right", } PortalFace = Literal["top", "side", "floor"] PANEL_FACE_BY_INDEX: dict[int, PortalFace] = { 2: "top", 0: "side", 1: "side", 3: "side", 4: "side", } # Per-panel width (height is always 9 rows). Edit to match your hardware. # Panel 4 may still be 38 or 39 — run: panel_sync_test.py --test width --panel-index 4 PANEL_HEIGHT = 9 PANEL_WIDTH_BY_INDEX: dict[int, int] = { 0: 39, 1: 45, 2: 45, 3: 45, 4: 39, } DEFAULT_PANEL_WIDTH = 45 PANEL_COUNT = 5 # Panels driven in-process by the animation script (SPI/PIO on this Pi). LOCAL_PANEL_INDICES: frozenset[int] = frozenset() # Optional: Pi UDP→SPI bridge panels (examples/panel_spi_bridge.py). Empty = all Pico. PI_BRIDGE_PANEL_INDICES: frozenset[int] = frozenset() # Optional: two panels on one Pico (GP27 + GP28). # Example — panels 0 and 1 on 10.1.1.10, strips 0 and 1: # PANEL_HOST_BY_INDEX = {0: "10.1.1.10", 1: "10.1.1.10"} # PANEL_STRIP_BY_INDEX = {0: 0, 1: 1} # PANEL_WS2812_PIN_BY_INDEX = {0: 28, 1: 27} PANEL_HOST_BY_INDEX: dict[int, str] = {} PANEL_STRIP_BY_INDEX: dict[int, int] = {} PANEL_WS2812_PIN_BY_INDEX: dict[int, int] = {} # Optional SPI bus override per panel index (Pi 5: 0 = GPIO 10, 1 = GPIO 20). PANEL_SPI_BUS_BY_INDEX: dict[int, int] = {} # SPI data line: bus 0 = GPIO 10 (MOSI), bus 1 = GPIO 20 (SPI1 MOSI) MATRIX_SPI_BUS = 0 MATRIX_SPI_DEVICE = 0 # One continuous chain on a single data pin; rows all run left → right. MATRIX_ROWS_PER_STRIP = None MATRIX_SERPENTINE = "none" # use "rows" for zigzag / serpentine wiring # Set True if DIN enters at the bottom row instead of the top. MATRIX_FLIP_Y = False MATRIX_BRIGHTNESS = 0.25 # Optional wire reorder for direct SPI tests only (set PORTAL_WIRE_ORDER env). # UDP/bridge/firmware pass logical RGB through unchanged (default rgb = no swap). WIRE_ORDER = os.environ.get("PORTAL_WIRE_ORDER", "rgb").lower() def panel_width(panel_index: int) -> int: return PANEL_WIDTH_BY_INDEX.get(panel_index, DEFAULT_PANEL_WIDTH) def panel_layout(panel_index: int) -> tuple[int, int]: return panel_width(panel_index), PANEL_HEIGHT def panel_pixel_count(panel_index: int) -> int: w, h = panel_layout(panel_index) return w * h def panel_face(panel_index: int) -> PortalFace | None: return PANEL_FACE_BY_INDEX.get(panel_index) def panel_label(panel_index: int) -> str: face = panel_face(panel_index) pos = PANEL_POSITION_BY_INDEX.get(panel_index) if face == "top": return f"panel {panel_index} (top / Pi)" if pos: return f"panel {panel_index} ({pos})" if face == "side": return f"panel {panel_index} (side wall)" return f"panel {panel_index}" def matrix_pixel_index( x: int, y: int, width: int = MATRIX_WIDTH, height: int = MATRIX_HEIGHT, *, serpentine: str = MATRIX_SERPENTINE, flip_y: bool = MATRIX_FLIP_Y, ) -> int: """Map (x, y) to strip index. Default: every row left → right.""" if flip_y: y = height - 1 - y col = x if serpentine == "rows" and y % 2 == 1: col = width - 1 - x return y * width + col