Project 8: Object-Oriented Physics Simulation
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.
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
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, -v*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.
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
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.
Each assignment will have a set of suggested extensions. The required tasks constitute about 85% of the assignment, and if you do only the required tasks and do them well you will earn a B+. To earn a higher grade, you need to undertake one or more extensions. The difficulty and quality of the extension or extensions will determine your final grade for the assignment. One complex extension, done well, or 2-3 simple extensions are typical.
- Create more elaborate spaces and developing interesting additional simulation videos.
- Make a new obstacle object, like a block, and place it in the environment. Alternatively, give walls/floors boundaries so a ball can go over a wall, or beside a floor.
- Add the capabilility for balls to collide with one another. Keep it simple and get it working with just 2 balls first.
- Make the simulation more visually stimulating, such as changing the color of the balls when they collide.
- Develop a different method of handling collisions, for example, one that predicts collisions in the next time step and takes appropriate actions.
Write-up and Hand-in
Turn in your code by putting it into your private hand-in directory on the Courses server. All files should be organized in a folder titled "Project 8" and you should include only those files necessary to run the program. We will grade all files turned in, so please do not turn in old, non-working, versions of files.
Make a new wiki page for your assignment. Put the label cs151sf15project8 in the label field on the bottom of the page. But give the page a meaningful title (e.g. Milo's Project 8).
In general, your intended audience for your write-up is your peers not in the class. Your goal should be to be able to use it to explain to friends what you accomplished in this project and to give them a sense of how you did it. Follow the outline below.
- A brief summary of the task, in your own words. This should be no more than a few sentences. Give the reader context and identify the key purpose of the assignment.
- A description of your solution to the tasks, including any text output or images you created. This should be a description of the form and functionality of your final code. Note any unique computational solutions you developed or any insights you gained from your code's output. You may want to incorporate code snippets in your description to point out relevant features. Code snippets should be small segments of code--usually less than a whole function--that demonstrate a particular concept. If you find yourself including more than 5-10 lines of code, it's probably not a snippet.
- A description of any extensions you undertook, including text output or images demonstrating those extensions. If you added any modules, functions, or other design components, note their structure and the algorithms you used.
- A brief description (1-3 sentences) of what you learned. Think about the answer to this question in terms of the stated purpose of the project. What are some specific things you had to learn or discover in order to complete the project?
- A list of people you worked with, including TAs and professors. Include in that list anyone whose code you may have seen, such as those of friends who have taken the course in a previous semester.
- Double-check the label. When you created the page, you should have added a the label cs151f15sproject8. Make sure it is there.