CS152
Lab Exercise 7: Working with Objects
Before you get started you might want to view this helpful video provided for you by Prof. Maxwell in order to understand the difference between simulation (Euclidean) and
visualization (Zelle Graphics window) coordinates: Simulation versus Visualization Coordinates
Create a class to represent a Ball for a physics simulation
L1. Create a new file named physics_objects.py
L2. Create a Ball class in your physics_objects.py file
Write a Ball class that will represent a ball in your simulation. Since this is a physics simulation, you will want to represent all of its relevant attributes such as mass, radius, position, velocity, and acceleration.
L3. Write the init method of the Ball class
Remember that we specify all the class attributes in the init method of the class.
The init method should have at least two parameters: self and a GraphWin object which refers to the window object (e.g., win). You are free to add optional arguments such as position and radius, but these are not required. Inside the init method, create fields to hold each of the following pieces of information.
1. self.mass – give it an initial value of 1.
2. self.radius – give it an initial value of 1.
3. self.position – this will be a two-element list, representing the x and y location values. Give the x and y positions initial values of 0.
4. self.velocity – a two-element list, with initial values of 0.
5. self.acceleration – a two-element list, with initial values of 0.
6. self.win – a GraphWin object (the win parameter).
Finally, two other attributes are needed in the init method. They both require a bit more explanation. These attributes are the “glue” that hold the simulation and the visualization coordinate systems together.
Self.Scale
In addition to the physical parameters, each Ball object needs to know how to draw itself to a visualization window. We want to be able to use different units for the physical parameters versus the visualization parameters. Aside: Have you ever made a model of something? For example, a model of a T. Rex dinosaur. Was it 12 feet tall and 40 feet in length? I doubt it! It had a scaling factor built into the model so that it could fit on your desk. In the case of our physics ball, we will add a scale field to the Ball class and give it an initial value of 10. The scale will transform. simulation coordinates to screen coordinates.
Self.Vis
This is the place in our code where the internal and external representations meet. We will use a Zelle graphics object to represent the physics ball on the screen.
Create a field named vis and assign it a list with one element, which is a Zelle Graphics Circle object.
A Circle object needs an anchor point for its center and a radius. For the anchor point x and y values use the Ball’s position x-value multiplied by the scale field.
The y position needs adjustment because the screen coordinates are different from the physics coordinates. In the physics coordinates a move in the positive y direction is up, but on the screen coordinates a move in the positive y direction is down (the upper left corner of the screen is at position (0,0)).
This means that whenever we translate from physics coordinates to screen coordinates, we need to subtract the physics y position from the window’s height.
Therefore, the proper y coordinate for the Graphics Circle is win.get Height() minus the product of the Ball’s position y value and the scale field.
The final parameter for the Circle-creation function is the radius, which should be the Ball’s radius value multiplied by the scale field. The entire statement might look like the following, depending on what you named your fields and how you imported the graphics module.
self.vis = [ gr.Circle( gr.Point(self.pos[0]*self.scale,
win.getHeight()-self.pos[1]*self.scale),
self.radius * self.scale ) ]
L4. Create a Draw method
Create a draw method def draw(self):
The method should loop over the self.vis list and have each item call its draw method just like we did in the lab prep exercise test2() function.
L5. Create Getters and Setters
Create getter and setter methods for each of the physical attributes of the Ball. For example, the get Mass method should return the value of the mass field of the object. The set Mass method should take in a new value as one of the parameters and assign it to the mass field of the object.
When writing accessor methods, you want to avoid returning important encapsulated fields. For example, if the following get Position method returns a reference to the list being used by the object to store its position
# a bad example
def get Position(self):
return self.position
By returning a reference to the list, another program can now change the values stored in self.position. Instead, return a copy of the list.
NOTE:
DO NOT FOLLOW THE ABOVE EXAMPLE CODE IT’S AN EXAMPLE OF WHAT NOT TO DO.
# a good example
def get Position(self):
return self.position[:]
By returning a copy, another program can’t unexpectedly edit the Ball’s internal list.
Use the following definitions for your getter/setter methods. You must follow these specifications or the test files will not work without modification.
def get Position(self): # returns a 2-element tuple with the x, y position.
def set Position(self, px, py): # px and py are the new x,y values
def getVelocity(self): # returns a 2-element tuple with the x and y velocities.
def setVelocity(self, vx, vy): # vx and vy are the new x and y velocities
def getAcceleration(self): # returns a 2-element tuple with the x and y acceleration values.
def setAcceleration(self, ax, ay): # ax and ay are new x and y accelerations.
def get Mass(self): # Returns the mass of the object as a scalar value
def set Mass(self, m): # m is the new mass of the object
def get Radius(self): # Returns the radius of the Ball as a scalar value
def set Radius(self, r): # (**Optional** You might implement this later when you are making your ball class fancier) r is the new radius of the Ball object.
Note: This function will need to undraw the circle, create a new circle with the new radius, and draw it back into the window.
setPosition and setRadius require a little bit of thought as they not only update the appropriate field, but also move or change the visualization.
If a Circle is in screen space location A and you want it to be in screen space location B, then moving the object by the amount (B – A) does what you want to do. To calculate B-A, calculate the difference in simulation space and then multiply by the scale factor for x and by the negative scale factor for y.
def set Position(self, px, py):
# assign to x_old the current x position # assign to y_old the current y position
# assign to the x coordinate in self.pos the new x coordinate # assign to the y coordinate in self.pos the new y coordinate
# assign to dx the change in the x position times self.scale # assign to dy the change in the y position times -self.scale
# for each item in the vis field of self
# call the move method of the item, passing in dx and dy
Once you have completed these steps, download the file testBall_1.py . The test function has code to test some of the getter/setter functions, but it is not complete. As you write get/set functions, add test code to the function to make sure your functions work properly.
L6. Create an Update method
Write an update method that implements Newtonian physics.
Write a method, update that adjusts the internal position and velocity values based on current accelerations and forces. The method should use the equations of motion under uniform
acceleration to update the velocity and position. The method also needs to move the
visualization. The function will take in a time step, dt, that indicates how much time to model.
The following is a step-by-step algorithm for updating the ball. def update(self, dt):
# assign to x_old the current x position
# assign to y_old the current y position
# update the x position to be x_old + x_vel*dt + 0.5*x_acc * dt*dt
# update the y position to be y_old + y_vel*dt + 0.5*y_acc * dt*dt
# assign to dx the change in the x position times the scale factor (self.scale)
# assign to dy the negative of the change in the y position times the scale factor (self.scale) # for each item in self.vis
# call the move method of the graphics object with dx and dy as arguments..
# update the x velocity by adding the acceleration times dt to its old value
# update the y velocity by adding the acceleration times dt to its old value
To test the update method, uncomment the last section of the test Ball_1.py main function. It should implement Brownian motion of the ball like the explore.py program.
L7. Write a test file for the Ball class
Write a test file that will create a Ball in the center of the screen, give it a random initial velocity and an acceleration of (0, -20), and then loop until the ball leaves the screen, calling the ball’s update function inside the loop.
Note: Falling with 0 acceleration in the horizontal direction and -20 in the vertical direction, means that the ball is falling downward in the visualization window after being spawned.
If the ball goes out of bounds (that is x component of position is less than 0 or the x component is greater than the width of the win object, or the y component of position is less than 0 or the y component of position is greater than the height of the win object), then the program should reposition the ball to the center of the screen and give it a new random velocity between -10 and 10.
You can use fall.py as a template.
Test your code and make sure you have a ball falling down, then re-spawning.
Time to contemplate what we have done in this lab. Note that what you are seeing moving on
the screen in test Ball and fall is no longer just a Zelle Circle object like the one in the explore.py program. We can’t move the ball any way we choose, but instead we have to follow the rules of physics that are defined in the equations of motion and implemented in our update method. One could say that the Zelle Graphics behaviors are wrapped into our physics ball class and are only indirectly accessible via the methods of the ball class. Food for thought:)
Reviews
There are no reviews yet.