# Flocking (cardumes e revoadas) ### Preâmbulo - Vamos usar py5 pra variar ;-) - https://abav.lugaralgum.com/material-aulas/ - https://py5coding.org - Python Fluente https://pythonfluente.com/2/ ### O assunto do dia - Craig Reynolds (1986) - https://en.wikipedia.org/wiki/Boids - ver também Ichiro Aoki (1982) ![image](https://hackmd.io/_uploads/HkimiWoVge.png) - No Nature of Code - https://natureofcode.com/autonomous-agents/#complex-systems - Exemplo do Rohola em Python https://github.com/roholazandie/boids/tree/master - Sketch-a-day com linhas https://github.com/villares/sketch-a-day/blob/main/2023/sketch_2023_10_31/sketch_2023_10_31.py - Sketch-a-day com **Numpy**... https://github.com/villares/sketch-a-day/blob/main/2024/sketch_2024_01_19/sketch_2024_01_19.py Brincadeira com mouse repelindo ```python= import py5 from py5 import Py5Vector as V boids = [] def setup(): py5.size(800, 600) for i in range(30): boids.append(Boid(py5.random(py5.width), py5.random(py5.height))) def draw(): py5.background(255, 0, 255) # py5.fill(py5.random(255), # py5.random(255), # py5.random(255)) # py5.circle(py5.mouse_x, py5.mouse_y, 50) mv = V(py5.mouse_x, py5.mouse_y) for b in boids: b.update(mv) b.desenhar() class Boid: vel_maxima = 8 def __init__(self, x, y): self.pos = V(x, y) self.vel = V(0, 0) self.acc = V(0, 0) def apply_force(self, mouse_vector): f = self.pos - mouse_vector f.normalize() return f * 0.001 def update(self, mouse_vector): self.acc = self.apply_force(mouse_vector) self.vel += self.acc self.vel.set_limit(self.vel_maxima) self.pos += self.vel self.acc.set_mag(0) self.pos.x = self.pos.x % py5.width self.pos.y = self.pos.y % py5.height def desenhar(self): py5.circle(self.pos.x, self.pos.y, 2 + self.vel.mag_sq * 2) py5.run_sketch() ``` Primeira aproximação do flocking ![image](https://hackmd.io/_uploads/rkp2MQjEgx.png) ```python= import py5 from py5 import Py5Vector as V import numpy as np boids = [] def setup(): py5.size(800, 600) for i in range(50): boids.append(Boid(py5.random(py5.width), py5.random(py5.height))) def draw(): py5.background(255, 0, 255) # py5.fill(py5.random(255), # py5.random(255), # py5.random(255)) # py5.circle(py5.mouse_x, py5.mouse_y, 50) mv = V(py5.mouse_x, py5.mouse_y) for b in boids: b.update(mv) b.desenhar() class Boid: vel_maxima = 8 forca_maxima = 0.3 perception = 100 def __init__(self, x, y): self.pos = V(x, y) self.vel = V(0, 0) self.acc = V(0, 0) def apply_force(self, mouse_V): f = self.pos - mouse_V f.normalize() return f * 0.001 def update(self, mouse_V): #self.acc = self.apply_force(mouse_V) self.acc += self.align(boids) self.acc += self.cohesion(boids) self.acc += self.separation(boids) self.vel += self.acc self.vel.set_limit(self.vel_maxima) self.pos += self.vel self.acc.set_mag(0) self.pos.x = self.pos.x % py5.width self.pos.y = self.pos.y % py5.height def desenhar(self): py5.circle(self.pos.x, self.pos.y, 10) def align(self, boids): steering = V(0, 0) total = 0 avg_V = V(0, 0) for boid in boids: if boid != self and (boid.pos - self.pos).mag < self.perception: avg_V += boid.vel total += 1 if total > 0: avg_V /= total if avg_V.mag_sq > 0: avg_V = avg_V.norm * self.vel_maxima steering = avg_V - self.vel return steering def cohesion(self, boids): steering = V(0, 0) total = 0 center_of_mass = V(0, 0) for boid in boids: if boid != self and (boid.pos - self.pos).mag < self.perception: center_of_mass += boid.pos total += 1 if total > 0: center_of_mass /= total #center_of_mass = V(*center_of_mass) vec_to_com = center_of_mass - self.pos if vec_to_com.mag_sq > 0: vec_to_com = vec_to_com.norm * self.vel_maxima steering = vec_to_com - self.vel steering.set_limit(self.forca_maxima) return steering def separation(self, boids): steering = V(0, 0) total = 0 avg_V = V(0, 0) for boid in boids: distance = (boid.pos - self.pos).mag if self != boid and distance < self.perception: diff = self.pos - boid.pos diff /= distance avg_V += diff total += 1 if total > 0: avg_V /= total #avg_V = V(*avg_V) if avg_V.mag_sq > 0: avg_V = avg_V.norm * self.vel_maxima steering = avg_V - self.vel steering.set_limit(self.forca_maxima) return steering py5.run_sketch() ``` Exemplo com linhas ```python! import py5 from py5 import Py5Vector as V import numpy as np boids = [] def setup(): py5.size(800, 600) for i in range(50): boids.append(Boid(py5.random(py5.width), py5.random(py5.height))) def draw(): py5.background(200) mv = V(py5.mouse_x, py5.mouse_y) py5.lines(b.update(mv) for b in boids) py5.window_title(str(int(py5.get_frame_rate()))) class Boid: vel_maxima = 8 forca_maxima = 0.3 perception = 100 def __init__(self, x, y): self.pos = V(x, y) self.vel = V(0, 0) self.acc = V(0, 0) def apply_force(self, mouse_V): f = self.pos - mouse_V f.normalize() return f * 0.001 def update(self, mouse_V): #self.acc = self.apply_force(mouse_V) self.acc += self.align(boids) self.acc += self.cohesion(boids) self.acc += self.separation(boids) self.vel += self.acc self.vel.set_limit(self.vel_maxima) self.pos += self.vel self.acc.set_mag(0) self.pos.x = self.pos.x % py5.width self.pos.y = self.pos.y % py5.height return ( # devolve coordenadas de uma linha self.pos.x, self.pos.y, self.pos.x + self.vel.x * 2, self.pos.y + self.vel.y * 2 ) def align(self, boids): steering = V(0, 0) total = 0 avg_V = V(0, 0) for boid in boids: if boid != self and (boid.pos - self.pos).mag_sq < self.perception ** 2: avg_V += boid.vel total += 1 if total > 0: avg_V /= total if avg_V.mag_sq > 0: avg_V = avg_V.norm * self.vel_maxima steering = avg_V - self.vel return steering def cohesion(self, boids): steering = V(0, 0) total = 0 center_of_mass = V(0, 0) for boid in boids: if boid != self and (boid.pos - self.pos).mag_sq < self.perception ** 2: center_of_mass += boid.pos total += 1 if total > 0: center_of_mass /= total #center_of_mass = V(*center_of_mass) vec_to_com = center_of_mass - self.pos if vec_to_com.mag_sq > 0: vec_to_com = vec_to_com.norm * self.vel_maxima steering = vec_to_com - self.vel steering.set_limit(self.forca_maxima) return steering def separation(self, boids): steering = V(0, 0) total = 0 avg_V = V(0, 0) for boid in boids: distance = (boid.pos - self.pos).mag if self != boid and distance < self.perception: diff = self.pos - boid.pos diff /= distance avg_V += diff total += 1 if total > 0: avg_V /= total #avg_V = V(*avg_V) if avg_V.mag_sq > 0: avg_V = avg_V.norm * self.vel_maxima steering = avg_V - self.vel steering.set_limit(self.forca_maxima) return steering py5.run_sketch() ```