Skip to content

Commit 9f34b8b

Browse files
authored
flocking examples - CPU bound and GPU bound (#200)
1 parent 77f105b commit 9f34b8b

3 files changed

Lines changed: 718 additions & 0 deletions

File tree

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# Flocking
2+
#
3+
# Ported from Dan Schiffman's Flocking
4+
#
5+
# An implementation of Craig Reynold's Boids program to simulate
6+
# the flocking behavior of birds. Each boid steers itself based on
7+
# rules of avoidance, alignment, and coherence.
8+
#
9+
# Click the mouse to add a new boid.
10+
from mewnala import *
11+
12+
flock = None
13+
14+
15+
def setup():
16+
global flock
17+
size(640, 360)
18+
flock = Flock()
19+
# Add an initial set of boids into the system
20+
for i in range(150):
21+
flock.add_boid(Boid(width / 2, height / 2))
22+
23+
24+
def draw():
25+
background(50)
26+
flock.run()
27+
28+
29+
# Add a new boid into the System
30+
def mouse_pressed():
31+
flock.add_boid(Boid(mouse_x, mouse_y))
32+
33+
34+
# The Flock (a list of Boid objects)
35+
class Flock:
36+
def __init__(self):
37+
self.boids = [] # A list for all the boids
38+
39+
def run(self):
40+
for b in self.boids:
41+
b.run(self.boids) # Passing the entire list of boids to each boid individually
42+
43+
def add_boid(self, b):
44+
self.boids.append(b)
45+
46+
47+
# The Boid class
48+
class Boid:
49+
def __init__(self, x, y):
50+
self.acceleration = Vec2(0, 0)
51+
self.velocity = Vec2.random()
52+
self.position = Vec2(x, y)
53+
self.r = 2.0
54+
self.maxspeed = 2.0 # Maximum speed
55+
self.maxforce = 0.03 # Maximum steering force
56+
57+
def run(self, boids):
58+
self.flock(boids)
59+
self.update()
60+
self.borders()
61+
self.render()
62+
63+
def apply_force(self, force):
64+
# We could add mass here if we want A = F / M
65+
self.acceleration.add(force)
66+
67+
# We accumulate a new acceleration each time based on three rules
68+
def flock(self, boids):
69+
sep = self.separate(boids) # Separation
70+
ali = self.align(boids) # Alignment
71+
coh = self.cohesion(boids) # Cohesion
72+
# Arbitrarily weight these forces
73+
sep.mult(1.5)
74+
ali.mult(1.0)
75+
coh.mult(1.0)
76+
# Add the force vectors to acceleration
77+
self.apply_force(sep)
78+
self.apply_force(ali)
79+
self.apply_force(coh)
80+
81+
# Method to update position
82+
def update(self):
83+
# Update velocity
84+
self.velocity.add(self.acceleration)
85+
# Limit speed
86+
self.velocity.limit(self.maxspeed)
87+
self.position.add(self.velocity)
88+
# Reset acceleration to 0 each cycle
89+
self.acceleration.mult(0)
90+
91+
# A method that calculates and applies a steering force towards a target
92+
# STEER = DESIRED MINUS VELOCITY
93+
def seek(self, target):
94+
desired = target - self.position # A vector pointing from the position to the target
95+
# Scale to maximum speed
96+
desired.set_mag(self.maxspeed)
97+
98+
# Steering = Desired minus Velocity
99+
steer = desired - self.velocity
100+
steer.limit(self.maxforce) # Limit to maximum steering force
101+
return steer
102+
103+
def render(self):
104+
# Draw a triangle rotated in the direction of velocity
105+
theta = self.velocity.heading() + HALF_PI
106+
107+
fill(200, 100)
108+
stroke(255)
109+
push_matrix()
110+
translate(self.position.x, self.position.y)
111+
rotate(theta)
112+
begin_shape(TRIANGLES)
113+
vertex(0, -self.r * 2)
114+
vertex(-self.r, self.r * 2)
115+
vertex(self.r, self.r * 2)
116+
end_shape()
117+
pop_matrix()
118+
119+
# Wraparound
120+
def borders(self):
121+
if self.position.x < -self.r:
122+
self.position.x = width + self.r
123+
if self.position.y < -self.r:
124+
self.position.y = height + self.r
125+
if self.position.x > width + self.r:
126+
self.position.x = -self.r
127+
if self.position.y > height + self.r:
128+
self.position.y = -self.r
129+
130+
# Separation
131+
# Method checks for nearby boids and steers away
132+
def separate(self, boids):
133+
desired_separation = 25.0
134+
steer = Vec2(0, 0)
135+
count = 0
136+
# For every boid in the system, check if it's too close
137+
for other in boids:
138+
d = self.position.dist(other.position)
139+
# If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
140+
if 0 < d < desired_separation:
141+
# Calculate vector pointing away from neighbor
142+
diff = (self.position - other.position).normalize()
143+
diff.div(d) # Weight by distance
144+
steer.add(diff)
145+
count += 1 # Keep track of how many
146+
# Average -- divide by how many
147+
if count > 0:
148+
steer.div(count)
149+
150+
# As long as the vector is greater than 0
151+
if steer.mag() > 0:
152+
# Implement Reynolds: Steering = Desired - Velocity
153+
steer.set_mag(self.maxspeed)
154+
steer.sub(self.velocity)
155+
steer.limit(self.maxforce)
156+
return steer
157+
158+
# Alignment
159+
# For every nearby boid in the system, calculate the average velocity
160+
def align(self, boids):
161+
neighbor_dist = 50.0
162+
sum = Vec2(0, 0)
163+
count = 0
164+
for other in boids:
165+
d = self.position.dist(other.position)
166+
if 0 < d < neighbor_dist:
167+
sum.add(other.velocity)
168+
count += 1
169+
if count > 0:
170+
sum.div(count)
171+
# Implement Reynolds: Steering = Desired - Velocity
172+
sum.set_mag(self.maxspeed)
173+
steer = sum - self.velocity
174+
steer.limit(self.maxforce)
175+
return steer
176+
else:
177+
return Vec2(0, 0)
178+
179+
# Cohesion
180+
# For the average position (i.e. center) of all nearby boids, calculate steering vector towards that position
181+
def cohesion(self, boids):
182+
neighbor_dist = 50.0
183+
sum = Vec2(0, 0) # Start with empty vector to accumulate all positions
184+
count = 0
185+
for other in boids:
186+
d = self.position.dist(other.position)
187+
if 0 < d < neighbor_dist:
188+
sum.add(other.position) # Add position
189+
count += 1
190+
if count > 0:
191+
sum.div(count)
192+
return self.seek(sum) # Steer towards the position
193+
else:
194+
return Vec2(0, 0)
195+
196+
197+
run()

0 commit comments

Comments
 (0)