Manchas concêntricas 01

2026-03-18

"""2026-03-18
Manchas concêntricas 01
Distribuição de círculos concêntricos
ericof.com
png
Sketch,py5,CreativeCoding
"""

from dataclasses import dataclass
from sketches.utils.draw import canvas
from sketches.utils.draw.cores import lerp_color_rgba
from sketches.utils.draw.cores.paletas import gera_paleta
from sketches.utils.helpers import sketches as helpers

import py5


sketch = helpers.info_for_sketch(__file__, __doc__)

cor_fundo = py5.color(0)
paleta: list[int] = gera_paleta("mondrian")


@dataclass
class CirculoRaio:
    raio: float
    traco: float
    cor: int


@dataclass
class CirculosConcentricos:
    x: float
    y: float
    z: float
    circulos: list[CirculoRaio]


CIRCULOS: list[CirculosConcentricos] = []


def calcula_raios(
    num: int,
    cor_1: int,
    cor_2: int,
    inicial: float,
    final: float,
) -> list[CirculoRaio]:
    """Considerando valores para raio inicial e final, retorna lista com num CirculoRaio

    * Primeiramente calculamos a distância média entre os raios
    * Iteramos por range(num)
        * raio será a distância média + random_int
        * traco será (distância média /2) * random(0.2, 0.75)

    """
    raios = []
    dist_media = (final - inicial) / num
    traco_base = dist_media / 2
    t_minmax = (0, num)
    for idx in range(num):
        cor = lerp_color_rgba(cor_1, cor_2, idx, t_minmax)
        raios.append(
            CirculoRaio(
                raio=(dist_media + py5.random_int(-3, 3)) * idx + inicial,
                cor=cor,
                traco=traco_base * py5.random(0.2, 0.75),
            )
        )
    return raios


def calcula_circulos(
    x_range: tuple[float, float],
    y_range: tuple[float, float],
    z_range: tuple[float, float],
    r_range: tuple[float, float],
    total: int,
    cor_1: int,
    cor_2: int,
) -> CirculosConcentricos:
    x = py5.random(*x_range)
    y = py5.random(*y_range)
    z = py5.random(*z_range)
    circulos = calcula_raios(total, cor_1, cor_2, *r_range)
    return CirculosConcentricos(x=x, y=y, z=z, circulos=circulos)


def setup():
    py5.size(*helpers.DIMENSOES.external, py5.P3D)
    meio_x = helpers.DIMENSOES.external[0] / 2
    meio_y = helpers.DIMENSOES.external[1] / 2
    x_range = -meio_x, meio_x + 1
    y_range = -meio_y, meio_y + 1
    z_range = -400, 20
    for _ in range(300):
        r_min = py5.random_int(20, 50)
        r_range = r_min, py5.random_int(int(r_min * 1.5), int(r_min * 3))
        total = py5.random_int(5, 11)
        cor_1 = py5.random_choice(paleta)
        cor_2 = py5.random_choice(paleta)
        while cor_1 == cor_2:
            cor_2 = py5.random_choice(paleta)
        CIRCULOS.append(
            calcula_circulos(x_range, y_range, z_range, r_range, total, cor_1, cor_2)
        )


def draw():
    py5.background("#444444")
    with py5.push():
        py5.translate(*helpers.DIMENSOES.centro, -40)
        py5.no_fill()
        for figura in CIRCULOS:
            for info in figura.circulos:
                with py5.push():
                    py5.translate(0, 0, figura.z)
                    raio = info.raio
                    py5.stroke_weight(info.traco)
                    py5.stroke(info.cor)
                    py5.circle(figura.x, figura.y, raio)

    # 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()