Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

📚 The CoCalc Library - books, templates and other resources

132922 views
License: OTHER
1
""" Code example from Think Complexity, by Allen Downey.
2
3
Original code by Matt Aasted, modified by Allen Downey.
4
5
Based on Reynolds, "Flocks, Herds and Schools" and
6
Flake, "The Computational Beauty of Nature."
7
8
Copyright 2011 Allen B. Downey.
9
Distributed under the MIT License.
10
"""
11
try:
12
from vpython import *
13
except:
14
print("This program requires VPython 7, which you can read about")
15
print("at http://vpython.org/. If you are using Anaconda, you can")
16
print("install VPython by running the following on the command line:")
17
print("conda install -c vpython vpython")
18
import sys
19
sys.exit()
20
21
import numpy as np
22
23
24
null_vector = vector(0,0,0)
25
26
27
def random_vector(a, b):
28
"""Create a vector with each element uniformly distributed in [a, b)."""
29
coords = np.random.uniform(a, b, size=3)
30
return vector(*coords)
31
32
33
def limit_vector(vect):
34
"""If the magnitude is greater than 1, set it to 1"""
35
if vect.mag > 1:
36
vect.mag = 1
37
return vect
38
39
40
class Boid(cone):
41
"""A Boid is a VPython cone with a velocity and an axis."""
42
43
def __init__(self, radius=0.03, length=0.1):
44
pos = random_vector(0, 1)
45
self.vel = random_vector(0, 1).norm()
46
cone.__init__(self, pos=pos, radius=radius, length=length)
47
self.axis = length * self.vel
48
49
def get_neighbors(self, boids, radius, angle):
50
"""Return a list of neighbors within a field of view.
51
52
boids: list of boids
53
radius: field of view radius
54
angle: field of view angle in radians
55
56
returns: list of Boid
57
"""
58
neighbors = []
59
for boid in boids:
60
if boid is self:
61
continue
62
offset = boid.pos - self.pos
63
64
# if not in range, skip it
65
if offset.mag > radius:
66
continue
67
68
# if not within viewing angle, skip it
69
diff = self.vel.diff_angle(offset)
70
if abs(diff) > angle:
71
continue
72
73
# otherwise add it to the list
74
neighbors.append(boid)
75
76
return neighbors
77
78
def center(self, boids, radius=1, angle=1):
79
"""Find the center of mass of other boids in range and
80
return a vector pointing toward it."""
81
neighbors = self.get_neighbors(boids, radius, angle)
82
vecs = [boid.pos for boid in neighbors]
83
return self.vector_toward_center(vecs)
84
85
def vector_toward_center(self, vecs):
86
"""Vector from self to the mean of vecs.
87
88
vecs: sequence of vector
89
90
returns: Vector
91
"""
92
if vecs:
93
center = np.mean(vecs)
94
toward = vector(center - self.pos)
95
return limit_vector(toward)
96
else:
97
return null_vector
98
99
def avoid(self, boids, carrot, radius=0.3, angle=np.pi):
100
"""Find the center of mass of all objects in range and
101
return a vector in the opposite direction, with magnitude
102
proportional to the inverse of the distance (up to a limit)."""
103
objects = boids + [carrot]
104
neighbors = self.get_neighbors(objects, radius, angle)
105
vecs = [boid.pos for boid in neighbors]
106
return -self.vector_toward_center(vecs)
107
108
def align(self, boids, radius=0.5, angle=1):
109
"""Return the average heading of other boids in range.
110
111
boids: list of Boids
112
"""
113
neighbors = self.get_neighbors(boids, radius, angle)
114
vecs = [boid.vel for boid in neighbors]
115
return self.vector_toward_center(vecs)
116
117
def love(self, carrot):
118
"""Returns a vector pointing toward the carrot."""
119
toward = carrot.pos - self.pos
120
return limit_vector(toward)
121
122
def set_goal(self, boids, carrot):
123
"""Sets the goal to be the weighted sum of the goal vectors."""
124
125
# weights for various rules
126
w_avoid = 10
127
w_center = 3
128
w_align = 1
129
w_love = 10
130
131
self.goal = (w_center * self.center(boids) +
132
w_avoid * self.avoid(boids, carrot) +
133
w_align * self.align(boids) +
134
w_love * self.love(carrot))
135
self.goal.mag = 1
136
137
def move(self, mu=0.1, dt=0.1):
138
"""Update the velocity, position and axis vectors.
139
140
mu: how fast the boids can turn (maneuverability).
141
dt: time step
142
"""
143
144
self.vel = (1-mu) * self.vel + mu * self.goal
145
self.vel.mag = 1
146
self.pos += dt * self.vel
147
self.axis = self.length * self.vel
148
149
150
class World(object):
151
152
def __init__(self, n=10):
153
"""Create n Boids and one carrot.
154
155
tracking: indicates whether the carrot follows the mouse
156
"""
157
self.boids = [Boid() for i in range(n)]
158
self.carrot = sphere(pos=vector(1,0,0),
159
radius=0.1,
160
color=vector(1,0,0))
161
self.tracking = False
162
163
def step(self):
164
"""Compute one time step."""
165
# move the boids
166
for boid in self.boids:
167
boid.set_goal(self.boids, self.carrot)
168
boid.move()
169
170
# if we're tracking, move the carrot
171
if self.tracking:
172
self.carrot.pos = scene.mouse.pos
173
174
175
n = 20
176
size = 5
177
178
world = World(n)
179
scene.center = world.carrot.pos
180
scene.autoscale = False
181
182
def toggle_tracking(evt):
183
"""If we're currently tracking, turn it off, and vice versa.
184
"""
185
world.tracking = not world.tracking
186
187
# when the user clicks, toggle tracking.
188
scene.bind('click', toggle_tracking)
189
190
while 1:
191
rate(10)
192
world.step()
193
194