Objectives

This project continues our work with classes within the domain of physical simulations.

This is the second part of a multi-part project where we will look at increasingly complex physical simulations. This week we add more sophisticated collisions with more types of objects.

Setting Up

If you have not already done so, mount your personal space, create a new project9 folder and bring up TextWrangler and a Terminal. As with last week, you will need the Zelle graphics package file graphics.py.

The Zelle documentation, is also available.

Project 08 Fix

In project 8, we let the ball drop through the floor. We can fix this with two small changes. First, we want to make sure the ball can come to rest. Second, we want to make sure accelleration is not applied during every update during a collision, which can force a ball resting on the floor to take on a negative velocity.

Here is a fixed-up Ball.update(), which uses a boolean flag (the colliding parameter) to determine whether or not acceleration should be applied:

```
def update( self, dt, colliding=False ):
"""Computes the ball's next state and and updates the ball
visualization on the screen. If colliding is True, then
acceleration will not be applied."""

# Turn off accelleration during collisions to keep ball
# from sinking through the floor
if not colliding:
a = self.getAcceleration()
else:
a = [0, 0]

# How much the ball will move (in physics coordinates) during
# this timestep, based on its current velocity and acceleration
dx = self.vel[0]*dt + 0.5*a[0]*dt**2
dy = self.vel[1]*dt + 0.5*a[1]*dt**2

# Ball's new position in physics coordinates
self.pos[0] += dx
self.pos[1] += dy

# Ball visualization's new position in graphics coordinates
for shape in self.vis:
shape.move(dx*self.scale, -dy*self.scale)

# New velocity
self.vel[0] += a[0]*dt
self.vel[1] += a[1]*dt

# An applied force produces a temporary acceleration
self.vel[0] += self.f[0] / self.m
self.vel[1] += self.f[1] / self.m

# Simulate damping by reducing x and y velocities
self.vel[0] *= 0.998
self.vel[1] *= 0.998
```

Here is a fixed-up bucket.py.

Dictionaries

Dictionary: a data structure

A dictionary is like a list, but the information in a dictionary is not stored in sequential order. Instead, each value stored in a dictionary has a unique key. In a dictionary, the key plays the same role as the index in a list. The difference is that a key can be any Python type that is immutable (can't be modified). These include the types int, str, float, and tuple. Examples of valid keys are: 1, 42, '42', 'ice cream', 'chocolate', 'cookie', 1.7, and (5, 'blueberry', 'muffins').

Create a new Python file called wordmap.py. Put your name and a date at the top, then start a main function (no parameters). The goal of this program is to give the user a set of word prompts and then record their answer. To record the answer, use a dictionary with the word as the key and the response as the value.

1. Print out a prompt for the user that tells them what to do.
2. Create a list of about ten words, call it words.
3. Create an empty dictionary, calling it mapping.
4. Loop over the words. Each time through the loop, do two things. First, assign to response the return value of input(), using the word as the input to input(). Second, using the word as the key, assign to your dictionary the response (the output from input()).
5. Finally, loop over the keys in mapping (mapping.keys()) and print out the key/response pairs to show the user their results.

Inheritance

Inheritance: Sharing the capabilities of one class with one or more other classes.

This week we are going to use inheritance to simplify the code in the physics_objects.py file and make it easier to create new kinds of shapes. The concept is to create a parent class, Thing, that has all of the common fields and methods for physical objects. Then we create child classes, like Ball, Wall, and Floor, that make use of the parent Thing class.

Create a new file, physics_objects.py, in your project 9 directory. You may want to also have a copy of your physics_objects.py file from last week open, since you will be able to re-use a good bit of the code.

Start a new class, Thing. The __init__() method for Thing should have two required parameters, win and the_type. Then you can add any number of default parameters for the remaining fields. The most useful default parameters to have are radius and position.

Next, in your __init__() method, created the following fields and assign them either from the parameter list or reasonable default values.

 type A string, indicating the type of the object. mass A scalar value indicating the mass of the object. radius A scalar value indicating the radius of the enclosing circle. position A 2-element list indicating the current position of the object. velocity A 2-element list indicating the current velocity of the object. acceleration A 2-element list indicating acceleration acting on the object. force A 2-element list indicating a force acting on the object. elasticity The amount of energy retained after a collision. scale The scale factor between the simulation space and pixels. window A reference to the GraphWin object representing the window. vis A list of Zelle graphics objects for visualization.

Gets & Sets

Create "get" and "set" methods for all of the above fields except vis and win. For the most part, you can copy and paste these from the Ball class from last week. The exception is setPosition() which will be unique for each child class. Leave setPosition() in the Ball class. You can make a setPosition() for Thing that does nothing but set the position field values (does not move the graphics objects).

draw( ) & update( )

Create two methods draw() and update(), again copying from last week's Ball class methods. The draw() method should loop through the vis objects and draw them into the window. The update() method should move the simulation of the object forward by the given time step by modifying the position and velocity based on the current acceleration and force fields.

The Inheritance of Things

Create a new Ball class that inherits the Thing class.

`class Ball(Thing):`

Your Ball class will have two methods, __init__() and setPosition(). The __init__() method should have win as the only required argument, but you may also want to add x0, y0, mass, and radius as default arguments so you can create a Ball in a location (x0, y0), of a particular size and mass.

The first step in the __init__() method is to call the Thing (parent) __init__() method. Call it by using the name of the class, Thing, followed by the name of the method, __init__(), connected by a dot. The first three arguments should be self, win, and the string "ball". Then you may also want to pass in all of the other Ball parameters if you have them as optional arguments to the Thing __init__() method: position, mass, and radius. For example:

`Thing.__init__(self, win, "ball", pos = [x0, y0], mass = mass, radius = radius)`

The second step is to assign to self.vis a new list with a Circle graphics object in it. You can copy and paste your code from last week here. If you want, you can then also set the color of the ball.

The setPosition() method in the ball class should be unchanged from last week

Once you have completed this, download the files test_ballclass.py and collision.py. Running test_ballclass.py should create two balls and send them into a collision.

When you are done with the lab exercises, you may start on the rest of the project.