From 73446c8d33a8016120b961add929636b24eef55e Mon Sep 17 00:00:00 2001 From: Alexandre B A Villares Date: Wed, 11 Dec 2019 19:22:48 -0200 Subject: [PATCH] 10 --- 2019/sketch_191210a/boid.py | 195 ++++++++++++++++++++++++ 2019/sketch_191210a/cores.py | 43 ++++++ 2019/sketch_191210a/estrela.py | 99 ++++++++++++ 2019/sketch_191210a/flock.py | 13 ++ 2019/sketch_191210a/sketch_191210a.pyde | 30 ++++ 5 files changed, 380 insertions(+) create mode 100644 2019/sketch_191210a/boid.py create mode 100644 2019/sketch_191210a/cores.py create mode 100644 2019/sketch_191210a/estrela.py create mode 100644 2019/sketch_191210a/flock.py create mode 100644 2019/sketch_191210a/sketch_191210a.pyde diff --git a/2019/sketch_191210a/boid.py b/2019/sketch_191210a/boid.py new file mode 100644 index 00000000..62ccf7bc --- /dev/null +++ b/2019/sketch_191210a/boid.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# The Boid class + +from estrela import * +from cores import * + +class Boid(object): + + def __init__(self, x, y): + self.acceleration = PVector(0, 0) + self.angle = random(TWO_PI) + self.velocity = PVector(cos(self.angle), sin(self.angle)) + self.location = PVector(x, y) + self.r = 2.0 + self.maxspeed = 2 + self.maxforce = 0.03 + + def run(self, boids, i, amp): + self.flock(boids) + self.update() + self.borders() + self.render(i, amp) + + def applyForce(self, force): + # We could add mass here if we want A = F / M + self.acceleration.add(force) + + # We accumulate acceleration each time based on three rules. + def flock(self, boids): + sep = self.separate(boids) # Separation + ali = self.align(boids) # Alignment + coh = self.cohesion(boids) # Cohesion + # Arbitrarily weight these forces. + sep.mult(1.5) + ali.mult(1.0) + coh.mult(1.0) + # Add the force vectors to acceleration. + self.applyForce(sep) + self.applyForce(ali) + self.applyForce(coh) + + # Method to update location. + def update(self): + # Update velocity. + self.velocity.add(self.acceleration) + # Limit speed. + self.velocity.limit(self.maxspeed) + self.location.add(self.velocity) + # Reset accelertion to 0 each cycle. + self.acceleration.mult(0) + + # A method that calculates and applies a steering force towards a target. + # STEER = DESIRED MINUS VELOCITY + def seek(self, target): + # A vector pointing from the location to the target. + desired = PVector.sub(target, self.location) + # Scale to maximum speed. + desired.normalize() + desired.mult(self.maxspeed) + # Above two lines of code below could be condensed with PVector setMag() method. + # Not using this method until Processing.js catches up. + # desired.setMag(maxspeed) + # Steering = Desired minus Velocity + steer = PVector.sub(desired, self.velocity) + steer.limit(self.maxforce) # Limit to maximum steering force. + return steer + + def render(self, ins, amp): + # Draw a triangle rotated in the direction of velocity. + theta = self.velocity.heading2D() + radians(90) + # heading2D() above is now heading() but leaving old syntax until + # Processing.js catches up. + noFill() + stroke(255) + strokeWeight(.1) + self.x, self.y = self.location.x, self.location.y + raio1, raio2 = amp, 25 + # self.tamanho = amp + colorMode(RGB) + cor_final = 255 + stroke(cor_final, 150) + strokeJoin(ROUND) + if ins in (0, 1, 2, 3, 4, 5): + fill(0, 10) # preto bem fraquinho + apply_override() + estrela(self.x, self.y, 7, raio1 * .6, raio2 * .6) + if ins in (6, 7, 8, 9, 10, 11): + pushMatrix() + translate(self.x, self.y) + stroke(cor_final, min(255, 255)) + apply_override() + estrela(0, 0, 4, raio1, raio2) + rotate(QUARTER_PI) + s = 4 if ins == 5 else 5 + # cor2 = paleta(s, cor) + # stroke(cor2, min(255, amp)) + apply_override() + estrela(0, 0, 4, raio1 * .8, raio2 * .8) + popMatrix() + if ins in (12, 13, 14, 15, 16, 17): + stroke(0) + pushMatrix() + translate(self.x, self.y) + rotate(radians(frameCount)) + # stroke(cor_final, 230 - min(230, amp)) + # s = 4 if ins == 5 else 5 + # stroke(paleta(s, cor)) + apply_override() + estrela(0, 0, 10, raio1, 50) + popMatrix() + + # Wraparound + def borders(self): + if self.location.x < -self.r: + self.location.x = width + self.r + if self.location.y < -self.r: + self.location.y = height + self.r + if self.location.x > width + self.r: + self.location.x = -self.r + if self.location.y > height + self.r: + self.location.y = -self.r + + # Separation + # Method checks for nearby boids and steers away. + def separate(self, boids): + desiredseparation = 50.0 + steer = PVector(0, 0, 0) + count = 0 + # For every boid in the system, check if it's too close. + for other in boids: + d = PVector.dist(self.location, other.location) + # If the distance is greater than 0 and less than an arbitrary + # amount (0 when you are yourself). + if 0 < d < desiredseparation: + # Calculate vector pointing away from neighbor. + diff = PVector.sub(self.location, other.location) + diff.normalize() + diff.div(d) # Weight by distance. + steer.add(diff) + count += 1 # Keep track of how many + # Average -- divide by how many + if count == 0: + return PVector(0, 0) + if count > 0: + steer.div(float(count)) + # As long as the vector is greater than 0 + if steer.mag() > 0: + # First two lines of code below could be condensed with PVector setMag() method. + # Implement Reynolds: Steering = Desired - Velocity + steer.normalize() + steer.mult(self.maxspeed) + steer.sub(self.velocity) + steer.limit(self.maxforce) + return steer + + # Alignment + # For every nearby boid in the system, calculate the average velocity. + def align(self, boids): + neighbordist = 50 + sum = PVector(0, 0) + count = 0 + for other in boids: + d = PVector.dist(self.location, other.location) + if 0 < d < neighbordist: + sum.add(other.velocity) + count += 1 + if count == 0: + return PVector(0, 0) + sum.div(float(count)) + # First two lines of code below could be condensed with PVector setMag() method. + # Implement Reynolds: Steering = Desired - Velocity + sum.normalize() + sum.mult(self.maxspeed) + steer = PVector.sub(sum, self.velocity) + steer.limit(self.maxforce) + return steer + + # Cohesion + # For the average location (i.e. center) of all nearby boids, calculate + # steering vector towards that location. + def cohesion(self, boids): + neighbordist = 50 + # Start with empty vector to accumulate all locations. + sum = PVector(0, 0) + count = 0 + for other in boids: + d = PVector.dist(self.location, other.location) + if 0 < d < neighbordist: + sum.add(other.location) # Add location. + count += 1 + if count > 0: + sum.div(count) + return self.seek(sum) # Steer towards the location. + else: + return PVector(0, 0) diff --git a/2019/sketch_191210a/cores.py b/2019/sketch_191210a/cores.py new file mode 100644 index 00000000..0f5d98b3 --- /dev/null +++ b/2019/sketch_191210a/cores.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# Paleta B - grupo 1 +coresB = ((0, 255, 77), (109, 0, 255), (255, 0, 255), + (0, 255, 255), (255, 0, 71), (253, 255, 0)) + +# Paleta A - grupo 2 +coresA = ((255, 82, 0), (255, 247, 0), (0, 238, 255), + (79, 0, 255), (0, 255, 249), (255, 255, 255)) + +# Paleta C - grupo 3 +coresC = ((224, 0, 255), (255, 245, 0), (0, 255, 241), + (255, 0, 95), (255, 250, 0), (255, 255, 255)) + + +def paleta(ins, valor): + opcoes = (coresB[:3], + coresB[3:], + coresA[:3], + coresA[3:], + coresC[:3], + coresC[3:], + ) + if ins > 5: + ins = 5 + a, b, c = opcoes[ins] + return triangulo(color(*a), color(*b), color(*c), int(valor)) + +def triangulo(a, b, c, v): + if 0 <= v < 60 or v == 360: + return a + if 60 <= v < 120: + t = map(v, 60, 120, 0, 1) + return lerpColor(a, b, t) + if 120 <= v < 180: + return b + if 180 <= v < 240: + t = map(v, 180, 240, 0, 1) + return lerpColor(b, c, t) + if 240 <= v < 300: + return c + if 300 <= v < 360: + t = map(v, 300, 360, 0, 1) + return lerpColor(c, a, t) diff --git a/2019/sketch_191210a/estrela.py b/2019/sketch_191210a/estrela.py new file mode 100644 index 00000000..5f13c6d0 --- /dev/null +++ b/2019/sketch_191210a/estrela.py @@ -0,0 +1,99 @@ +22# -*- coding: utf-8 -*- +from cores import * + +class Estrela(): + + """ Classe Estrela, cor sorteada, tamanho sorteado por default """ + full_screen = False + ins_override = -1 + + def __init__(self, x, y, tam=100): + self.x, self.y = x, y + self.tamanho = tam + self.vy = -0.5 + + def desenha(self, ins, cor, amp): + """ Desenha polígono em torno das coordenadas do objeto """ + raio1, raio2 = amp, amp / 4 + self.tamanho = amp + colorMode(RGB) + cor_final = paleta(ins, cor) + stroke(cor_final) + strokeJoin(ROUND) + if Estrela.full_screen: + translate(width / 2, height / 2) + rotate(HALF_PI) + translate(-width / 2, -height / 2) + if ins in (0, 1): + fill(0, 10) # preto bem fraquinho + apply_override() + estrela(self.x, self.y, 7, raio1 * .6, raio2 * .6) + if ins in (2, 3): + pushMatrix() + translate(self.x, self.y) + apply_override() + estrela(0, 0, 4, raio1, raio2) + rotate(QUARTER_PI) + s = 4 if ins == 5 else 5 + cor2 = paleta(s, cor) + stroke(cor2, min(255, amp)) + apply_override() + estrela(0, 0, 4, raio1 * .8, raio2 * .8) + popMatrix() + if ins in (4, 5): + pushMatrix() + translate(self.x, self.y) + rotate(radians(frameCount)) + apply_override() + if amp > 10: + estrela(0, 0, 10, raio1, 50) + else: + estrela(0, 0, 10, raio1, 5) + popMatrix() + if Estrela.full_screen: + translate(width / 2, height / 2) + rotate(-HALF_PI) + translate(-width / 2, -height / 2) + + + def anda(self, tom=None): + """ atualiza a posição do objeto e devolve do lado oposto se sair """ + # if Estrela.full_screen: + # w, h = height, width + # else: + w, h = width, height + if tom is not None: + self.x = map(tom, -24, 24, self.tamanho * 2, w - self.tamanho * 2) + # self.x += self.vx + self.vy = -.35 * 40 / (tom + 40) + self.y += self.vy + tam = self.tamanho + if self.x > w + tam: + self.x = -tam + if self.y > h + tam: + self.y = -tam + if self.x < -tam: + self.x = w + tam + if self.y < -tam: + self.y = h + tam + +def apply_override(): + if Estrela.ins_override == 6: + stroke(255) + noFill() + strokeWeight(1) + +def estrela(cx, cy, pontas, raio1, raio2): + pontos = pontas * 2 + parte = 360. / pontos + beginShape() # comece a forma! + for p in range(pontos): # para cada p + angulo = radians(p * parte) # calcula angulo + if p % 2 == 0: # se for par + raio = raio1 + else: # senão, se for impar + raio = raio2 + x = cx + raio * sin(angulo) + y = cy + raio * cos(angulo) + vertex(x, y) # vertex é um ponto + endShape(CLOSE) # termina forma diff --git a/2019/sketch_191210a/flock.py b/2019/sketch_191210a/flock.py new file mode 100644 index 00000000..2ef7d98f --- /dev/null +++ b/2019/sketch_191210a/flock.py @@ -0,0 +1,13 @@ +# The Flock (a list of Boid objects) +class Flock(object): + + def __init__(self): + self.boids = [] # Initialize a list for all the boids. + + def run(self, amp): + for i, b in enumerate(self.boids): + # Pass the entire list of boids to each boid individually. + b.run(self.boids, i, amp) + + def addBoid(self, b): + self.boids.append(b) diff --git a/2019/sketch_191210a/sketch_191210a.pyde b/2019/sketch_191210a/sketch_191210a.pyde new file mode 100644 index 00000000..e27934fe --- /dev/null +++ b/2019/sketch_191210a/sketch_191210a.pyde @@ -0,0 +1,30 @@ +add_library('sound') # aviso de que vai usar o microfone + +from estrela import Estrela +from boid import Boid +from flock import Flock + +def setup(): + global dados, instrumentos, oscP5, novos_dados, estrelas + global input, loudness + global flock + flock = Flock() + size(800, 600) + # fullScreen() # testar se 1 vai para segundo monitor + background(0) + # Burocracia para receber o som e analisar o volume + source = AudioIn(this, 0) + source.start() + loudness = Amplitude(this) + loudness.input(source) + for i in range(18): + flock.addBoid(Boid(width / 2, height / 2)) + +def draw(): + fill(0, 10) + noStroke() + rect(0, 0, width, height) + volume = loudness.analyze() + tamanho = int(map(volume, 0, 0.5, 30, 350)) + # amp = 200 * noise(frameCount * .02) + flock.run(tamanho)