Colisões 3

2024-08-29

"""2024-08-29
Colisões 3
Exercício utilizando pymunk e colisões entre partículas e os limites do sketch
png
Sketch,py5,CreativeCoding
"""

from collections import deque

import py5
import pymunk

from utils import helpers

space = pymunk.Space()
space.gravity = (0, 9)


MARGEM = 100
MARGEM_INTERNA = int(MARGEM * 1.01)

POSICAO_MURO = [
    ((MARGEM, MARGEM), (helpers.LARGURA - MARGEM, MARGEM)),
    (
        (helpers.LARGURA - MARGEM, MARGEM),
        (helpers.LARGURA - MARGEM, helpers.ALTURA - MARGEM),
    ),
    (
        (helpers.LARGURA - MARGEM, helpers.ALTURA - MARGEM),
        (MARGEM, helpers.ALTURA - MARGEM),
    ),
    ((MARGEM, helpers.ALTURA - MARGEM), (MARGEM, MARGEM)),
]

PARTICULAS = None
MAX_LEN = 350
LIMITES = []
PONTOS = []

sketch = helpers.info_for_sketch(__file__, __doc__)


def cria_particula(space, x0, y0, raio, massa, cor) -> pymunk.Circle:
    inercia = pymunk.moment_for_circle(massa, 0, raio, (0, 0))
    body = pymunk.Body(massa, inercia)
    body.position = x0, y0
    shape = pymunk.Circle(body, raio, (0, 0))
    shape._color = cor
    shape.elasticity = 0.7
    shape.friction = 0.95
    space.add(body, shape)
    return shape


def adiciona_rastro(space, pos, raio, posicoes):
    limite = pymunk.Segment(space.static_body, pos, pos, raio)
    limite.elasticity = 0.0
    limite.friction = 0.95
    space.add(limite)
    if len(posicoes) == MAX_LEN:
        space.remove(posicoes[0][1])
    posicoes.append((pos, limite))


def cria_limites(space):
    for inicio, final in POSICAO_MURO:
        limite = pymunk.Segment(space.static_body, inicio, final, 20)
        limite.elasticity = 0.0
        limite.friction = 0.95
        space.add(limite)
        LIMITES.append((inicio, final))


def setup():
    global PARTICULAS
    py5.size(helpers.LARGURA, helpers.ALTURA, py5.P3D)
    py5.color_mode(py5.HSB, 360, 100, 100)
    PARTICULAS = {}
    cria_limites(space)
    s = 80
    b = 80
    i = 0
    meio = py5.width // 2
    x0 = meio + 10
    x1 = meio - 10
    pontos = [
        (
            (x0 if idx % 2 else x1) + ((1 if idx % 2 else -1) * diff),
            py5.height - MARGEM_INTERNA - idx,
            (1 if idx % 2 else -1) * py5.random_int(800, 1200),
            py5.random_int(-2000, -800),
        )
        for idx, diff in enumerate(range(0, 250, 5))
    ]
    for idx, (x0, y0, impulso_x, impulso_y) in enumerate(pontos):
        key = f"particula_{i:02d}"
        raio = 5
        massa = raio * py5.random_int(1, 8)
        h = py5.random_int(0, 30) + (idx % 2 * 180)
        cor = (h, s, b)
        particula = cria_particula(space, x0, y0, raio=raio, massa=massa, cor=cor)
        particula.body.apply_impulse_at_local_point((impulso_x, impulso_y))
        PARTICULAS[key] = {"particula": particula, "posicoes": deque(maxlen=MAX_LEN)}
        i += 1


def draw():
    py5.background(0)
    with py5.push_style():
        for (x0, y0), (xf, yf) in LIMITES:
            py5.stroke(200, 0, 100)
            py5.stroke_weight(4)
            py5.line(x0, y0, xf, yf)
    for particula in PARTICULAS.values():
        p = particula["particula"]
        pos = (p.body.position.x, p.body.position.y)
        posicoes = particula["posicoes"]
        adiciona_rastro(space, pos, p.radius, posicoes)
        h, s, b = p._color
        cor = py5.color(h, s, b)
        py5.stroke(cor)
        py5.stroke_weight(p.radius * 0.1)
        total = len(posicoes)
        for idx, rastro in enumerate(posicoes):
            mult = idx / total
            py5.stroke_weight(p.radius * mult)
            (x, y), _ = rastro
            if idx == total - 1:
                py5.circle(x, y, p.radius)
            elif not idx:
                py5.point(x, y)
            else:
                cor = py5.color(h, s, b)
                py5.stroke(cor)
                x0, y0 = particula["posicoes"][idx - 1][0]
                py5.line(x0, y0, x, y)

    # Avança a simulação
    space.step(1 / py5.get_frame_rate())
    helpers.write_legend(sketch=sketch)


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


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


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