Files
micronomicon/backend/uframe/borders.py
2026-04-01 00:53:55 +02:00

149 lines
5.4 KiB
Python

"""Border merging post-pass — fix junction characters where borders meet.
Scans the CharGrid for adjacent border cells and replaces with the
correct junction character (T-junctions, crosses, corners) from the
Unicode box-drawing set.
"""
from __future__ import annotations
from uframe.grid import CharGrid
from uframe.ir import BorderWeight
# ---------------------------------------------------------------------------
# Connection detection
# ---------------------------------------------------------------------------
# For each border cell, check which directions have adjacent borders.
# Direction flags:
UP = 1
DOWN = 2
LEFT = 4
RIGHT = 8
# Junction lookup: connections bitmask → character
# Only light weight for now (most common case)
_LIGHT_JUNCTIONS: dict[int, str] = {
UP | DOWN: "",
LEFT | RIGHT: "",
DOWN | RIGHT: "",
DOWN | LEFT: "",
UP | RIGHT: "",
UP | LEFT: "",
UP | DOWN | RIGHT: "",
UP | DOWN | LEFT: "",
LEFT | RIGHT | DOWN: "",
LEFT | RIGHT | UP: "",
UP | DOWN | LEFT | RIGHT: "",
RIGHT: "",
LEFT: "",
UP: "",
DOWN: "",
}
_HEAVY_JUNCTIONS: dict[int, str] = {
UP | DOWN: "",
LEFT | RIGHT: "",
DOWN | RIGHT: "",
DOWN | LEFT: "",
UP | RIGHT: "",
UP | LEFT: "",
UP | DOWN | RIGHT: "",
UP | DOWN | LEFT: "",
LEFT | RIGHT | DOWN: "",
LEFT | RIGHT | UP: "",
UP | DOWN | LEFT | RIGHT: "",
RIGHT: "",
LEFT: "",
UP: "",
DOWN: "",
}
_DOUBLE_JUNCTIONS: dict[int, str] = {
UP | DOWN: "",
LEFT | RIGHT: "",
DOWN | RIGHT: "",
DOWN | LEFT: "",
UP | RIGHT: "",
UP | LEFT: "",
UP | DOWN | RIGHT: "",
UP | DOWN | LEFT: "",
LEFT | RIGHT | DOWN: "",
LEFT | RIGHT | UP: "",
UP | DOWN | LEFT | RIGHT: "",
RIGHT: "",
LEFT: "",
UP: "",
DOWN: "",
}
_JUNCTION_TABLES = {
BorderWeight.LIGHT: _LIGHT_JUNCTIONS,
BorderWeight.HEAVY: _HEAVY_JUNCTIONS,
BorderWeight.DOUBLE: _DOUBLE_JUNCTIONS,
BorderWeight.ROUNDED: _LIGHT_JUNCTIONS, # rounded uses light junctions
}
# Weight priority for mixed-weight junctions
_WEIGHT_PRIORITY = {
BorderWeight.DOUBLE: 3,
BorderWeight.HEAVY: 2,
BorderWeight.LIGHT: 1,
BorderWeight.ROUNDED: 0,
}
def merge_borders(grid: CharGrid) -> None:
"""Scan the grid for adjacent border cells and fix junction characters.
This pass resolves cases where two boxes share an edge or corner,
replacing the overlapping border characters with proper junctions.
"""
for row in range(grid.height):
for col in range(grid.width):
cell = grid.cells[row][col]
if not cell.is_border:
continue
# Detect connections in 4 directions
connections = 0
max_weight = cell.border_weight or BorderWeight.LIGHT
# Check each neighbor
if row > 0 and grid.cells[row - 1][col].is_border:
connections |= UP
nw = grid.cells[row - 1][col].border_weight or BorderWeight.LIGHT
if _WEIGHT_PRIORITY.get(nw, 0) > _WEIGHT_PRIORITY.get(max_weight, 0):
max_weight = nw
if row < grid.height - 1 and grid.cells[row + 1][col].is_border:
connections |= DOWN
nw = grid.cells[row + 1][col].border_weight or BorderWeight.LIGHT
if _WEIGHT_PRIORITY.get(nw, 0) > _WEIGHT_PRIORITY.get(max_weight, 0):
max_weight = nw
if col > 0 and grid.cells[row][col - 1].is_border:
connections |= LEFT
nw = grid.cells[row][col - 1].border_weight or BorderWeight.LIGHT
if _WEIGHT_PRIORITY.get(nw, 0) > _WEIGHT_PRIORITY.get(max_weight, 0):
max_weight = nw
if col < grid.width - 1 and grid.cells[row][col + 1].is_border:
connections |= RIGHT
nw = grid.cells[row][col + 1].border_weight or BorderWeight.LIGHT
if _WEIGHT_PRIORITY.get(nw, 0) > _WEIGHT_PRIORITY.get(max_weight, 0):
max_weight = nw
# Skip rounded corners — they should preserve ╭╮╰╯
if cell.border_weight == BorderWeight.ROUNDED and connections in (
DOWN | RIGHT, DOWN | LEFT, UP | RIGHT, UP | LEFT
):
continue
# Look up the junction character
if connections:
table = _JUNCTION_TABLES.get(max_weight, _LIGHT_JUNCTIONS)
junction = table.get(connections)
if junction:
cell.char = junction