Due: , 11:59 pm
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.
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.
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*dt + 0.5*a*dt**2 dy = self.vel*dt + 0.5*a*dt**2 # Ball's new position in physics coordinates self.pos += dx self.pos += 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 += a*dt self.vel += a*dt # An applied force produces a temporary acceleration self.vel += self.f / self.m self.vel += self.f / self.m # Simulate damping by reducing x and y velocities self.vel *= 0.998 self.vel *= 0.998
Here is a fixed-up bucket.py.
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.
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.|
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).
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.
Create a new Ball class that inherits the Thing class.
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
When you are done with the lab exercises, you may start on the rest of the project.
© 2018 Caitrin Eaton.