MaySiXth (MSX Day)

2026-05-06

"""2026-05-06
MaySiXth (MSX Day)
Lembranças dos tempos de MSX
ericof.com|https://paulwratt.github.io/programmers-palettes/HW-MSX/HW-MSX-palettes.html
png
Sketch,py5,CreativeCoding,MaySiXth
"""

from sketches.utils.draw import canvas
from sketches.utils.draw.cores.paletas import gera_paleta
from sketches.utils.draw.grade import cria_grade
from sketches.utils.helpers import sketches as helpers

import py5


sketch = helpers.info_for_sketch(__file__, __doc__)

cor_fundo = py5.color(0)
paleta = gera_paleta("msx1", False)


def estrela(r: float = 200, ri: float = 180) -> py5.Py5Shape:
    """Cria uma estrela de 5 pontas centrada na origem.

    Gera o shape alternando vértices externos (raio ``r``) e internos
    (raio ``ri``) ao longo de 5 passos de ``an = 2·pi/5``. O shape fica
    em coordenadas locais — a translação é responsabilidade de quem
    desenha via :func:`py5.shape`. Cada iteração emite o vértice
    externo no ângulo ``a`` seguido do interno em ``a + an/2``; o
    shape fechado conecta o último ponto ao primeiro, formando 10
    vértices (5 pontas + 5 reentrâncias).

    :param r: raio externo da estrela em pixels.
    :param ri: raio interno da estrela em pixels.
    :returns: ``Py5Shape`` fechado pronto para ser desenhado.
    """
    an = py5.PI * 2 / 5
    a = 0
    forma = py5.create_shape()
    with forma.begin_closed_shape():
        while a < py5.PI * 2 - 0.1:
            x0 = r * py5.cos(a)
            y0 = r * py5.sin(a) * -1
            x2 = ri * py5.cos(a + an / 2)
            y2 = ri * py5.sin(a + an / 2) * -1
            forma.vertices([
                [x0, y0],
                [x2, y2],
            ])
            a += an
    return forma


def cria_estrelas() -> list[tuple[float, float, py5.Py5Shape, float, int, int]]:
    """Monta a lista de estrelas posicionadas numa grade alternada.

    Para cada célula de uma grade alternada de ``80x80`` pixels que
    cobre o canvas externo, sorteia:

    - ``cor`` e ``traco`` da paleta MSX1 (com os dois tons de preto
      iniciais já removidos via slice ``[2:]``), garantindo via
      reamostragem que sejam **diferentes** — caso contrário o
      contorno desapareceria.
    - ``angulo`` em **graus** via Gaussiana ``mu=0, sigma=60``,
      convertido para radianos antes de aplicar em ``forma.rotate``.
    - ``ri`` (raio interno) com pequeno jitter Gaussiano em torno de
      ``r/2`` (``sigma=5%``), variando o "estilo" de cada estrela.

    :returns: lista de tuplas ``(x, y, forma, ri, traco, cor)`` para
        cada estrela posicionada.
    """
    celula_x = 80
    celula_y = 80
    r = celula_x * 0.5
    estrelas = []
    paleta_msx = paleta[2:]
    for x, y in cria_grade(*helpers.DIMENSOES.external, 0, 0, celula_x, celula_y, True):
        cor = py5.color(py5.random_choice(paleta_msx))
        traco = py5.color(py5.random_choice(paleta_msx))
        while traco == cor:
            traco = py5.color(py5.random_choice(paleta_msx))
        angulo = py5.random_gaussian(0, 60)
        ri = r * py5.random_gaussian(0.5, 0.05)
        forma = estrela(r, ri)
        forma.rotate(py5.radians(angulo))
        estrelas.append((x, y, forma, ri, traco, cor))
    return estrelas


def setup():
    global estrelas
    py5.size(*helpers.DIMENSOES.external, py5.P3D)
    py5.shape_mode(py5.CORNER)
    estrelas = cria_estrelas()


def draw():
    py5.background(cor_fundo)
    with py5.push():
        py5.translate(*helpers.DIMENSOES.pos_interno)
        for x, y, forma, ri, traco, cor in estrelas:
            forma.set_stroke(traco)
            forma.set_fill(cor)
            py5.shape(forma, x, y)
            with py5.push():
                py5.stroke("#000")
                py5.stroke_weight(ri / 2)
                py5.point(x, y)
    # Credits and go
    canvas.sketch_frame(
        sketch,
        cor_fundo,
        "large_transparent_white",
        "transparent_white",
        version=2,
    )


def key_pressed():
    key = py5.key
    if key == " ":
        save_and_close()


def save_and_close():
    py5.no_loop()
    canvas.save_sketch_image(sketch)
    py5.exit_sketch()


if __name__ == "__main__":
    py5.run_sketch()