gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1182Shell 87Rust 63Markdown 541592 Sutichaykuna

⎇ 7cce81611c922bc05a8400df9c8b543127ea3cf9 scripts/logo-ascii.py Python · 128 siq'ikuna · 4.9 KB Ñawpa willay Pi imata llamkarqan ch'uya

marca: el logo de terminal pasa a medios bloques, con placa y juntas 6735a75d4 · Sergio ·

1#!/usr/bin/env python3
2"""Emite el símbolo de takana como arte de terminal, al estilo de los `*fetch`.
3
4Se DERIVA del SVG de `docs/marca/`, no se dibuja a mano: una copia dibujada
5aparte se desincroniza del símbolo el día que la marca cambie, y nadie se
6entera.
7
8Tres formas, de más a menos fiel:
9
10  --medio   (default) MEDIOS BLOQUES: `▀` con color de frente y de fondo, así
11            cada renglón de terminal lleva DOS filas de píxeles. Es lo que usan
12            los fetch modernos y lo que permite subir la escala sin que el
13            dibujo se vuelva un ladrillo. Trae la placa de fondo y la celda de
14            espacio libre que pide la hoja de marca.
15  --bloques BLOQUES ENTEROS: `██` por celda. Sin fondo. Para terminales que
16            renderizan mal los medios bloques.
17  --plano   ASCII de densidad, sin color. Para pipes, `TERM` tonto y consolas
18            que no hablan 24-bit.
19
20**No se interpola ni se suaviza el gradiente.** El símbolo ES una retícula
21tocapu de cuadrados; difuminarlo sería «recolorear y deformar», que la hoja de
22marca prohíbe explícitamente. Subir `--escala` repite píxeles (vecino más
23cercano) y mantiene el borde duro.
24"""
25import re, sys, pathlib
26
27RAIZ = pathlib.Path(__file__).resolve().parent.parent
28SVG = RAIZ / "docs/marca/simbolo-fondo-oscuro.svg"
29OBSIDIANA = "#17130E"          # el fondo de la hoja de marca
30DENSIDAD = {"#A63A25": "#", "#DF6B2A": "*", "#E3A63D": "+", "#FFF6DE": "@"}
31
32
33def rgb(h):
34    return int(h[1:3], 16), int(h[3:5], 16), int(h[5:7], 16)
35
36
37def grilla():
38    """La retícula {(fila, col): color} leída del SVG."""
39    s = SVG.read_text()
40    # El SVG trae un manifiesto C2PA enorme en <metadata>; los rects van después.
41    cuerpo = s[s.rfind("</metadata>") + 11:] if "</metadata>" in s else s
42    celdas = {}
43    for m in re.finditer(
44        r'<rect x="(\d+)" y="(\d+)" width="(\d+)" height="(\d+)" fill="(#[0-9A-Fa-f]{6})"', cuerpo
45    ):
46        celdas[(int(m.group(2)) // 108, int(m.group(1)) // 108)] = m.group(5)
47    if not celdas:
48        sys.exit(f"no encontré celdas en {SVG} — ¿cambió el formato del símbolo?")
49    return celdas, max(r for r, _ in celdas) + 1, max(c for _, c in celdas) + 1
50
51
52def pixeles(escala, margen, juntas=None):
53    """Matriz de colores ya escalada, con el margen de espacio libre alrededor.
54
55    `juntas` deja el último píxel de cada celda en fondo, reproduciendo la
56    separación entre teselas que el símbolo tiene de verdad (en el SVG son
57    cuadrados de 100 con 8 de aire: la tocapu son piezas separadas, no un
58    bloque macizo). Sólo cabe desde escala 3 — a escala 2 la junta se comería
59    la mitad de la tesela, así que ahí se apaga sola.
60    """
61    if juntas is None:
62        juntas = escala >= 3
63    celdas, filas, cols = grilla()
64    alto, ancho = filas * escala + 2 * margen, cols * escala + 2 * margen
65    m = [[None] * ancho for _ in range(alto)]
66    lleno = escala - 1 if juntas else escala
67    for (r, c), f in celdas.items():
68        for dr in range(lleno):
69            for dc in range(lleno):
70                m[margen + r * escala + dr][margen + c * escala + dc] = f
71    return m
72
73
74def medio(escala=3, margen=1):
75    """`▀`: color de frente = píxel de arriba, de fondo = el de abajo."""
76    m = pixeles(escala, margen)
77    if len(m) % 2:
78        m.append([None] * len(m[0]))
79    out = []
80    for y in range(0, len(m), 2):
81        linea = []
82        for x in range(len(m[0])):
83            ar, ab = m[y][x] or OBSIDIANA, m[y + 1][x] or OBSIDIANA
84            fr, fg, fb = rgb(ar)
85            br, bg, bb = rgb(ab)
86            linea.append(f"\x1b[38;2;{fr};{fg};{fb};48;2;{br};{bg};{bb}m▀")
87        out.append("".join(linea) + "\x1b[0m")
88    return "\n".join(out)
89
90
91def bloques():
92    celdas, filas, cols = grilla()
93    out = []
94    for r in range(filas):
95        linea = []
96        for c in range(cols):
97            f = celdas.get((r, c))
98            if f is None:
99                linea.append("  ")
100            else:
101                rr, gg, bb = rgb(f)
102                linea.append(f"\x1b[38;2;{rr};{gg};{bb}m██\x1b[0m")
103        out.append("".join(linea).rstrip())
104    return "\n".join(out)
105
106
107def plano():
108    celdas, filas, cols = grilla()
109    return "\n".join(
110        "".join(DENSIDAD[celdas[(r, c)]] * 2 if (r, c) in celdas else "  " for c in range(cols)).rstrip()
111        for r in range(filas)
112    )
113
114
115if __name__ == "__main__":
116    a = sys.argv[1:]
117    esc = int(next((x.split("=")[1] for x in a if x.startswith("--escala=")), 3))
118    if "--regenerar" in a:
119        (RAIZ / "docs/marca/simbolo.ansi").write_text(medio(esc) + "\n")
120        (RAIZ / "docs/marca/simbolo-bloques.ansi").write_text(bloques() + "\n")
121        (RAIZ / "docs/marca/simbolo.txt").write_text(plano() + "\n")
122        print("docs/marca/simbolo{.ansi,-bloques.ansi,.txt} regenerados desde el SVG")
123    elif "--plano" in a:
124        print(plano())
125    elif "--bloques" in a:
126        print(bloques())
127    else:
128        print(medio(esc))