The focus on this project is to provide you with more experience writing classes. Now that you have a working ball class, it's time to create some things for the balls to bounce against. In particular, you will create floors and walls. Each of these objects will be a class.


Floor Class

In your physics_objects.py file, start a new class called Floor. The __init__() method should take in a GraphWin object, the anchor x location (e.g. x0), anchor y location (e.g. y0), length, and thickness of the new floor. The Floor needs to have fields for its position, length, and thickness. As with the ball, you will also need a field for the GraphWin object, a field for scale, and a field to hold a Rectangle graphics object.

You can add other optional arguments to the __init__() method, such as a fill color. If you add an argument, make sure you give it a reasonable default value and use it in your code.

To create the visual representation of a Floor, use a graphics Rectangle. The first point (upper left corner) should be the anchor point, but add half the thickness to the y0 value (i.e. in physics coordinates, it should be at (x0, y0+thickness/2.0)). To create the point in the screen coordinates, multiply both coordinates by self.scale and subtract the y-position from the window's height -- x is x0*self.scale and y is self.win.getHeight()-(y0+thickness/2)*self.scale). The second point defining the Rectangle (lower right corner) should be located at (x0+length, y0-thickness/2) in physics coordinates and ((x0 + length)*self.scale, self.win.getHeight() - (y0 - thickness/2)*self.scale) in screen coordinates.

The next method to write is the draw() method draws all of the elements of the vis list onto the window (self.win). This should be identical to the draw() method of the Ball class.

The final method is the collision() method. We're going to implement the simplest possible method of detecting collisions by assuming that the balls will not be moving fast enough to zip through the floor in a single time step. The collision() method will have self and a Ball object as its arguments. It will return True if there is a collision and False, otherwise.

The concept is as follows. We can get the position of the ball (e.g. ball.getPosition() and we know the y-value of the Floor. We also know the radius of the ball (e.g. ball.getRadius()) and the thickness of the Floor. The amount of material that has to be between the center of the ball and the center-line of the Floor is the radius plus the thickness/2. Therefore, if the center of the ball and the centerline of the Floor are closer than the amount of material that has to be between them, there is a collision. As the floor is horizontal, we need only check the y coordinates to test for a collision. You may want to divide the function into two cases: the position of the ball is less than the position of the wall, and the position of the ball is greater than the position of the wall. Return True if the ball and wall are too close.

Modify your fall.py code. Create a Floor object after making the Ball object, but before the main loop. Place the floor at coordinates [0, 5], with a length of 50 and a thickness of 5. Then draw the floor into the window. You should test it at this point to see if it is there.

Next, add the following to your main loop.

# if there is a collision between the floor and ball (call the floor collision method)
    # assign to v the velocity of the ball, using getVelocity
    # set the velocity of the ball to (v[0], -v[1]*0.95), using setVelocity
    # while there is still a collision
        call the ball's update method with a time step of 0.001 (small)

Test your code with the fall.py program. The ball should bounce against the floor and lose a little energy over time. If you want, trigger the respawning every 300 frames or so.

Wall Class

Create a Wall class. The only differences with the Floor class are the initial arguments to the __init__() method--win, x, y, height, thickness--and the fact that you will need to check the x-coordinate, not the y-coordinate when testing for collisions. The __init__(), draw(), and collision() methods are all you need for now.

The visualization of the wall will also be a Rectangle. Use (x0-thickness/2, y0) as the lower left corner, then use (x0+thickness/2 and y0+ height) as the upper right corner (these are in physics coordinates). Be sure to transform these coordinates to screen coordinates to make the Point objects that are passed into the Rectangle. Note that the "height" referred to in the physics coordinates is the height of the wall -- not the heigh of the window).

Test your code by modifying the fall.py code to include a wall. You will need to create a Wall object, draw it, and then check for collisions with it inside the main loop. A good place to put the wall is on the left side (position 5, 0) with a height of 50 and thickness of 5). The algorithm for handling collisions will be almost identical to the one above, except that it is the x velocity that needs to be modified. Then give your balls an initial x velocity (like -30) so they bounce into the wall.


Using your Ball, Wall, and Floor classes, write a new main program: bucket.py. Have your program make a U-shaped space (2 walls and a floor). Then have your program make five balls (stick them in a list) with randomized starting locations. Give each ball a randomized starting velocity and position (don't forget to draw each ball into the window).

Your main loop should be similar to the one in fall.py. Have a while loop that uses win.checkMouse() == None as its running condition. Inside the while loop, loop over each ball in the balls list. First, update the ball. Second, check for collisions with the floor or the walls. If any collisions occur, handle them as in the fall.py file. The goal is to make a simulation where multiple balls are bouncing around the U-shaped space. The balls will not interact with each other, but they should bounce off the walls and floor.

Create a variable to represent the loss factor (the constant used to reduce the velocity when a collision occurs). Then run your simulation several times with different loss factors. It might be helpful to even make this a parameter of your main function and control it with a command-line argument.

Create a short video of two different simulations with different loss factors. You can do this using the QuickTime Player, which is installed on the lab computers. The basic procedure for recording a movie on your screen is to open QuickTime Player, select "New Screen Recording", press the red square in the window that pops up, then record the screen (either click on the screen to record the entire screen or select a region of the screen to record). While the screen is being recorded a square icon will appear in your menu bar (top right of the screen). Click that to stop the recording. You can then save the file (it has a .mov extension). If you want to record just the graphics window, then you may want to get the window up before starting the Quicktime movie recording. But, you may ask, what happens to the first few second of your animation if you need to start it before the recording? Here is one way to work around the problem of timing. Why not put a call to win.getMouse() before your animation begins? Then your window will appear, you can set up the recording, then you can click in the window and the animation will begin.

Example Extensions


Make a new wiki page for your assignment. Put the label cs152s18project8 on the page. Each of you needs to make your own writeup.

In addition to making the wiki page writeup, put the python files you wrote on the Courses server in your private directory in a folder named project8.

Colby Wiki

In general, your writeup should follow the outline below.

© 2018 Caitrin Eaton.