forked from rougier/from-python-to-numpy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathboid_python.py
More file actions
189 lines (160 loc) · 5.63 KB
/
boid_python.py
File metadata and controls
189 lines (160 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# -----------------------------------------------------------------------------
# From Numpy to Python
# Copyright (2017) Nicolas P. Rougier - BSD license
# More information at https://github.com/rougier/numpy-book
# -----------------------------------------------------------------------------
import math
import random
from vec2 import vec2
class Boid:
def __init__(self, x, y):
self.acceleration = vec2(0, 0)
angle = random.uniform(0, 2*math.pi)
self.velocity = vec2(math.cos(angle), math.sin(angle))
self.position = vec2(x, y)
self.r = 2.0
self.max_velocity = 2
self.max_acceleration = 0.03
def seek(self, target):
desired = target - self.position
desired = desired.normalized()
desired *= self.max_velocity
steer = desired - self.velocity
steer = steer.limited(self.max_acceleration)
return steer
# Wraparound
def borders(self):
x, y = self.position
x = (x+self.width) % self.width
y = (y+self.height) % self.height
self.position = vec2(x,y)
# Separation
# Method checks for nearby boids and steers away
def separate(self, boids):
desired_separation = 25.0
steer = vec2(0, 0)
count = 0
# For every boid in the system, check if it's too close
for other in boids:
d = (self.position - other.position).length()
# If the distance is greater than 0 and less than an arbitrary
# amount (0 when you are yourself)
if 0 < d < desired_separation:
# Calculate vector pointing away from neighbor
diff = self.position - other.position
diff = diff.normalized()
steer += diff/d # Weight by distance
count += 1 # Keep track of how many
# Average - divide by how many
if count > 0:
steer /= count
# As long as the vector is greater than 0
if steer.length() > 0:
# Implement Reynolds: Steering = Desired - Velocity
steer = steer.normalized()
steer *= self.max_velocity
steer -= self.velocity
steer = steer.limited(self.max_acceleration)
return steer
# Alignment
# For every nearby boid in the system, calculate the average velocity
def align(self, boids):
neighbor_dist = 50
sum = vec2(0, 0)
count = 0
for other in boids:
d = (self.position - other.position).length()
if 0 < d < neighbor_dist:
sum += other.velocity
count += 1
if count > 0:
sum /= count
# Implement Reynolds: Steering = Desired - Velocity
sum = sum.normalized()
sum *= self.max_velocity
steer = sum - self.velocity
steer = steer.limited(self.max_acceleration)
return steer
else:
return vec2(0, 0)
# Cohesion
# For the average position (i.e. center) of all nearby boids, calculate
# steering vector towards that position
def cohesion(self, boids):
neighbor_dist = 50
sum = vec2(0, 0) # Start with empty vector to accumulate all positions
count = 0
for other in boids:
d = (self.position - other.position).length()
if 0 < d < neighbor_dist:
sum += other.position # Add position
count += 1
if count > 0:
sum /= count
return self.seek(sum)
else:
return vec2(0, 0)
def flock(self, boids):
sep = self.separate(boids) # Separation
ali = self.align(boids) # Alignment
coh = self.cohesion(boids) # Cohesion
# Arbitrarily weight these forces
sep *= 1.5
ali *= 1.0
coh *= 1.0
# Add the force vectors to acceleration
self.acceleration += sep
self.acceleration += ali
self.acceleration += coh
def update(self):
# Update velocity
self.velocity += self.acceleration
# Limit speed
self.velocity = self.velocity.limited(self.max_velocity)
self.position += self.velocity
# Reset acceleration to 0 each cycle
self.acceleration = vec2(0, 0)
def run(self, boids):
self.flock(boids)
self.update()
self.borders()
class Flock:
def __init__(self, count=150, width=640, height=360):
self.width = width
self.height = height
self.boids = []
for i in range(count):
boid = Boid(width/2, height/2)
boid.width = width
boid.height = height
self.boids.append(boid)
def run(self):
for boid in self.boids:
# Passing the entire list of boids to each boid individually
boid.run(self.boids)
def cohesion(self, boids):
P = np.zeros((len(boids),2))
for i, boid in enumerate(self.boids):
P[i] = boid.cohesion(self.boids)
return P
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
n=50
flock = Flock(n)
P = np.zeros((n,2))
def update(*args):
flock.run()
for i,boid in enumerate(flock.boids):
P[i] = boid.position
scatter.set_offsets(P)
fig = plt.figure(figsize=(8, 5))
ax = fig.add_axes([0.0, 0.0, 1.0, 1.0], frameon=True)
scatter = ax.scatter(P[:,0], P[:,1],
s=30, facecolor="red", edgecolor="None", alpha=0.5)
animation = FuncAnimation(fig, update, interval=10)
ax.set_xlim(0,640)
ax.set_ylim(0,360)
ax.set_xticks([])
ax.set_yticks([])
plt.show()