CSC111 Homework 8 Solution
--D. Thiebaut 20:30, 3 April 2010 (UTC)
#hw8.py
#beth mcgann, 111c-ab
#edited by D. Thiebaut
# graphics program that produces a pair of moving balls that bounce around the screen
# in an improvised physics simulation. The two balls will bounce off a white center box
# and disappear into three black holes splayed across the screen.
# user input opens and closes the window.
from math import*
from time import sleep
from graphics import *
import random
W = 900 # the width of the graphics window
H = 600 # the height of the graphics window
#-----------------------------------------------------------------------------------------------------
def waitForClick( win, message ):
""" waitForClick: stops the GUI and displays a message.
Returns when the user clicks the window. The message is erased."""
startMsg = Text( Point( win.getWidth()/2, win.getHeight()/2 ), message )
startMsg.draw( win ) # display message
win.getMouse() # wait
startMsg.undraw() # erase
#-----------------------------------------------------------------------------------------------------
def simul( Balls, wBox, bBoxes ):
"""The main engine of the program. Goes through 300 steps and moves each ball according
to its dx, dy vectors defining its direction and speed. Checks and see if the balls
hit each other, the white rectangle, or the walls of the graphics window, and bounce back
in a natural way if that happens (except when the balls hit each other, which results in
a 180-degree switch. When the balls are totally inside one of the black rectangles, they
stop"""
#--- run simulation for 300 steps ---
for step in range(300):
#--- stop if the two balls are immobile... ---
if (Balls[0][1] == 0) and (Balls[0][2] == 0) \
and (Balls[1][1] == 0) and (Balls[1][2] == 0 ):
break
#--- Move each ball, one after the other ---
for i in range(len(Balls)):
#--- get ball ---
c,dx,dy = Balls[i]
#--- move it ---
c.move( dx, dy )
#--- check graphics walls ---
r = c.getRadius()
x = c.getCenter().getX()
y = c.getCenter().getY()
if (y < r) or (y > H-r):
dy = -dy
if (x < r) or (x > W-r):
dx = -dx
Balls[i] = c, dx, dy
#--- check if the balls are touching each other---
if areTouching(Balls[0][0], Balls[1][0]):
c1, dx1, dy1 = Balls[0]
Balls[0] = c1, -dx1, -dy1
c2, dx2, dy2 = Balls[1]
Balls[1] = c2, -dx2, -dy2
#--- check if balls are in black boxes ---
for i in range(len(Balls)):
c, dx, dy = Balls[i]
if isInBox(bBoxes, c):
Balls[i] = c, 0, 0
Balls[i] = touchingWBox(wBox, Balls[i])
#------------------------------------------------------------------------------------------------------
def distance( P1, P2 ):
"""computes distance between two points"""
return sqrt( pow( P1.getX() - P2.getX(), 2 ) + pow( P1.getY() - P2.getY(), 2 ) )
#------------------------------------------------------------------------------------------------------
def areTouching(Ball1, Ball2):
"""checks to see if balls are touching """
center1 = Ball1.getCenter()
center2 = Ball2.getCenter()
distance1 = distance(center1, center2)
r1 = Ball1.getRadius()
r2 = Ball2.getRadius()
return (distance1 <= r1 + r2)
#------------------------------------------------------------------------------------------------------
def isInBox(bBoxes, c):
"""checks to see if balls are inside black boxes"""
xLeft = c.getCenter().getX() - c.getRadius()
xRight = c.getCenter().getX() + c.getRadius()
yTop = c.getCenter().getY() - c.getRadius()
yBot = c.getCenter().getY() + c.getRadius()
#--- runs through the list bBoxes that controls the properties of the holes ---
isInBox = False
for rectangle in bBoxes:
boxTop = rectangle.getP1().getY()
boxBottom = rectangle.getP2().getY()
boxLeft = rectangle.getP1().getX()
boxRight = rectangle.getP2().getX()
if (xLeft >= boxLeft) and (xRight <= boxRight) \
and (yTop >= boxTop) and (yBot <= boxBottom):
isInBox = True
return isInBox
#------------------------------------------------------------------------------------------------------
def touchingWBox(wBox, Ball):
"""Takes a ball and modify its direction if it is touching the white box"""
c, dx, dy = Ball
#--- get ball center ---
x = c.getCenter().getX()
y = c.getCenter().getY()
#--- create a square around ball ---
xLeft = c.getCenter().getX() - c.getRadius()
xRight = c.getCenter().getX() + c.getRadius()
yTop = c.getCenter().getY() - c.getRadius()
yBot = c.getCenter().getY() + c.getRadius()
#--- get white rectangle coordinates ---
boxTop = wBox.getP1().getY()
boxBottom = wBox.getP2().getY()
boxLeft = wBox.getP1().getX()
boxRight = wBox.getP2().getX()
#--- check if square intersects with rectangle ---
if ((boxBottom >= yBot >= boxTop) or (boxBottom >= yTop >= boxTop)) and ( boxLeft <= x <= boxRight):
dy = -dy
if ((boxRight >= xLeft >= boxLeft) or (boxRight >= xRight >= boxLeft)) and ( boxTop <= y <= boxBottom):
dx = -dx
#--- return the ball with modified direction ---
return c, dx, dy
#------------------------------------------------------------------------------------------------------
# M A I N F U N C T I O N
#------------------------------------------------------------------------------------------------------
def main():
#--- open graphic window ---
win = GraphWin( "111c-ab, Beth McGann: Moving Balls", W, H )
Balls =[]
x = W/4
y = H/4
#--- defines how the ball moves on the dx and dy ---
for i in range(2):
dx = 3-random.randrange( 6 )
dy = 3-random.randrange( 6 )
while dx == 0:
dx = 3-random.randrange(6)
while dy ==0:
dy = 3-random.randrange(6)
Balls.append( (Circle (Point( x, y), 25), dx , dy ))
x = W/3
y = H/3
#--- runs through both circles ---
for circle, dx, dy in Balls:
c = circle
c.setFill("green")
c.draw(win)
#--- the white box ---
wBox = Rectangle(Point(400, 255), Point(500, 355))
wBox.setFill("white")
wBox.draw(win)
#--- list holding the black boxes ---
bBoxes = []
bBoxes.append( Rectangle(Point(400, 499), Point(500, 599)))
bBoxes.append( Rectangle(Point(50, 15), Point(150, 115)))
bBoxes.append( Rectangle(Point(750, 15), Point(850, 115)))
#--- applies contents of list bBoxes to the window ---
for box in bBoxes:
box.setFill("black")
box.draw(win)
#--- the list of functions that control the graphics movement ---
#--- prompts the user to click to begin ---
#--- when the program ends, the user clicks to close the window ---
waitForClick(win, "Click to Begin")
simul(Balls, wBox, bBoxes)
waitForClick( win, "Click to End" )
win.close()
main()