Fall 2006»Boids


# boid.py    Version 1.0   9-oct-06    (bzm)
# Models a spherical boid, in terms of its position, velocity,
# and color in VPython.

from visual import *
from math import *
from random import *

# screen setup
universe = display(title='Boids')
universe.fullscreen = True
universe.autoscale = False
rate(24)                    # max allowable iterations per second

class Boid(sphere):
    "Models a spherical boid, in terms of its position, velocity, radius, and color."

    def __init__(self, pos=vector(0,0,0), velocity=vector(0,0,0), radius=0.2, color=color.blue):
        "Construct a boid."
        sphere.__init__(self, pos=pos, radius=radius, color=color)
        # self.pos    # inherited attribute used to store boid position

    def setVelocity(self, velocity):
        "Set boid's velocity."
        self.velocity = velocity

    def getVelocity(self):
        "Returns boid's velocity."
        return self.velocity

    def setPos(self, pos):
        "Set boid's position."
        self.pos = pos

    def getPos(self):
        "Get boid's position."
        return self.pos

    def move(self, timePassed):
        "Move boid to new position based on its velocity and time passed."
        self.pos = self.pos + self.velocity * timePassed

    def distance(self, other):
        "Returns our (3D Euclidean) distance from the other boid."
        return sqrt((self.pos.x - other.pos.x)**2 +
                    (self.pos.y - other.pos.y)**2 +
                    (self.pos.z - other.pos.z)**2)

def randomBoids(boids, numBoids, radius, color):
    "Returns a list of 'numBoids' boids with random position and velocity."

    # initialize boids
    for i in range(0, numBoids):
        randPos = randomVector(-1, 2)       # get a random vector
        randVelocity = randomVector(-1, 2)  # get a random vector
        # create a boid with random position
        boids.append( Boid(pos=randPos, velocity=randVelocity, radius=radius, color=color) )
    return boids

def randomVector(low, high):
    "Returns a vector with random components, each ranging between low and high-1."
    x = randrange(low, high)
    y = randrange(low, high)
    z = randrange(low, high)
    return vector(x,y,z)

def placeBoidsViaMouse(boids, numBoids, radius, color):
    "Returns a list of 'numBoids' boids with position and velocity entered via mouse clicks."

    # initialize boids
    boidsCreated = 0
    # wait for mouse clicks
    while boidsCreated < numBoids:
        if universe.mouse.clicked:   # if mouse was clicked
            mouseClick = universe.mouse.getclick()
            pos = mouseClick.pos     # get its coordinates
            # create a boid with specified position
            boids.append( Boid(pos=pos, radius=radius, color=color) )
            boidsCreated = boidsCreated + 1   # update
    return boids

def moveBoidViaMouse(boids):
    "Allows boid repositioning via mouse drag-n-drop."
    # see http://www.vpython.org/webdoc/visual/mouse_drag.html

    boidPicked = None # no boid picked out of the scene yet

    # is there some mouse activity?
    if universe.mouse.events:

        event = universe.mouse.getevent() # yes, so obtain mouse event
        # if clicked on a boid
        if event.drag and event.pick in boids:

            boidPicked = event.pick     # remember which boid was picked
            drag_pos = event.pickpos    # where on the boid the mouse was
            universe.cursor.visible = 0 # make cursor invisible

        elif event.drop:         # released mouse button at end of drag
            boidPicked = None           # release boid
            universe.cursor.visible = 1 # cursor visible again

        if boidPicked:   # is there a boid to reposition?
            #new_pos = universe.mouse.project(normal=(0,1,0)) # project onto xz plane
            new_pos = universe.mouse.pos    # get mouse position
            if new_pos != drag_pos:         # if the mouse has moved since last time
                offset = (new_pos - drag_pos) + boidPicked.getPos()
                boidPicked.setPos(offset)      # update boid position using offset
                drag_pos = new_pos             # remember where we drag this boid

def test():
    "Place a few boids randomly on the screen and move the around."

    boids = []    # list of boids
    numBoids = input("Enter number of boids: ")

    #boids = randomBoids(boids, numBoids, radius=0.2, color=color.yellow)
    boids = placeBoidsViaMouse(boids, numBoids, radius=0.2, color=color.yellow)

    # animate boids (press 'Escape' to end)
    while (True):

        # move them around a bit   
        for boid in boids:
            boid.move(0.005)   # adjust boid position
        print "another cycle..."

        # check mouse events and update boids as specified

        #wait = input("Paused (press a key and Enter to continue)")


if __name__ == '__main__':