📚 The CoCalc Library - books, templates and other resources
License: OTHER
""" Code example from Think Complexity, by Allen Downey.12Original code by Matt Aasted, modified by Allen Downey.34Based on Reynolds, "Flocks, Herds and Schools" and5Flake, "The Computational Beauty of Nature."67Copyright 2011 Allen B. Downey.8Distributed under the MIT License.9"""10try:11from vpython import *12except:13print("This program requires VPython 7, which you can read about")14print("at http://vpython.org/. If you are using Anaconda, you can")15print("install VPython by running the following on the command line:")16print("conda install -c vpython vpython")17import sys18sys.exit()1920import numpy as np212223null_vector = vector(0,0,0)242526def random_vector(a, b):27"""Create a vector with each element uniformly distributed in [a, b)."""28coords = np.random.uniform(a, b, size=3)29return vector(*coords)303132def limit_vector(vect):33"""If the magnitude is greater than 1, set it to 1"""34if vect.mag > 1:35vect.mag = 136return vect373839class Boid(cone):40"""A Boid is a VPython cone with a velocity and an axis."""4142def __init__(self, radius=0.03, length=0.1):43pos = random_vector(0, 1)44self.vel = random_vector(0, 1).norm()45cone.__init__(self, pos=pos, radius=radius, length=length)46self.axis = length * self.vel4748def get_neighbors(self, boids, radius, angle):49"""Return a list of neighbors within a field of view.5051boids: list of boids52radius: field of view radius53angle: field of view angle in radians5455returns: list of Boid56"""57neighbors = []58for boid in boids:59if boid is self:60continue61offset = boid.pos - self.pos6263# if not in range, skip it64if offset.mag > radius:65continue6667# if not within viewing angle, skip it68diff = self.vel.diff_angle(offset)69if abs(diff) > angle:70continue7172# otherwise add it to the list73neighbors.append(boid)7475return neighbors7677def center(self, boids, radius=1, angle=1):78"""Find the center of mass of other boids in range and79return a vector pointing toward it."""80neighbors = self.get_neighbors(boids, radius, angle)81vecs = [boid.pos for boid in neighbors]82return self.vector_toward_center(vecs)8384def vector_toward_center(self, vecs):85"""Vector from self to the mean of vecs.8687vecs: sequence of vector8889returns: Vector90"""91if vecs:92center = np.mean(vecs)93toward = vector(center - self.pos)94return limit_vector(toward)95else:96return null_vector9798def avoid(self, boids, carrot, radius=0.3, angle=np.pi):99"""Find the center of mass of all objects in range and100return a vector in the opposite direction, with magnitude101proportional to the inverse of the distance (up to a limit)."""102objects = boids + [carrot]103neighbors = self.get_neighbors(objects, radius, angle)104vecs = [boid.pos for boid in neighbors]105return -self.vector_toward_center(vecs)106107def align(self, boids, radius=0.5, angle=1):108"""Return the average heading of other boids in range.109110boids: list of Boids111"""112neighbors = self.get_neighbors(boids, radius, angle)113vecs = [boid.vel for boid in neighbors]114return self.vector_toward_center(vecs)115116def love(self, carrot):117"""Returns a vector pointing toward the carrot."""118toward = carrot.pos - self.pos119return limit_vector(toward)120121def set_goal(self, boids, carrot):122"""Sets the goal to be the weighted sum of the goal vectors."""123124# weights for various rules125w_avoid = 10126w_center = 3127w_align = 1128w_love = 10129130self.goal = (w_center * self.center(boids) +131w_avoid * self.avoid(boids, carrot) +132w_align * self.align(boids) +133w_love * self.love(carrot))134self.goal.mag = 1135136def move(self, mu=0.1, dt=0.1):137"""Update the velocity, position and axis vectors.138139mu: how fast the boids can turn (maneuverability).140dt: time step141"""142143self.vel = (1-mu) * self.vel + mu * self.goal144self.vel.mag = 1145self.pos += dt * self.vel146self.axis = self.length * self.vel147148149class World(object):150151def __init__(self, n=10):152"""Create n Boids and one carrot.153154tracking: indicates whether the carrot follows the mouse155"""156self.boids = [Boid() for i in range(n)]157self.carrot = sphere(pos=vector(1,0,0),158radius=0.1,159color=vector(1,0,0))160self.tracking = False161162def step(self):163"""Compute one time step."""164# move the boids165for boid in self.boids:166boid.set_goal(self.boids, self.carrot)167boid.move()168169# if we're tracking, move the carrot170if self.tracking:171self.carrot.pos = scene.mouse.pos172173174n = 20175size = 5176177world = World(n)178scene.center = world.carrot.pos179scene.autoscale = False180181def toggle_tracking(evt):182"""If we're currently tracking, turn it off, and vice versa.183"""184world.tracking = not world.tracking185186# when the user clicks, toggle tracking.187scene.bind('click', toggle_tracking)188189while 1:190rate(10)191world.step()192193194