:

Due: , 11:59 pm

Objectives

The purpose of this lab is to get you working with the Zelle graphics
objects, which are a somewhat higher level graphics package than the
turtle. Using the Zelle graphics primitives you can create more
complex objects, move them, and change their color. A scene is a
collection of complex objects.

Tasks

We'll continue to use the graphics.py
package, but we'll start using features other than Pixmaps. You
probably want to bring up the
documentation in a separate tab in the browser.

We are also providing a modified version of the graphics module (graphicsPlus.py)
that adds two new methods to the shape objects. They are getOutline()
and getFill() and they return the outline and fill
colors, respectively, in case you need to access those values. For
example, you may want to query an object as to its current color, then
change the color for a period of time before having it revert to its
original color. Use either graphics.py or graphicsPlus.py, but not both.

Setup

Create a new project6 directory for today's lab, download the
graphics.py file, start up a terminal and cd to your working
directory. Open a new python file and call it test.py. We'll use this
file to experiment with the graphics package. Start by importing the
graphics, time, and random packages.

import time
import random
import graphics as gr

main()

Define a main function. Then flesh out your main function as below.
The purpose of the function is to make a Circle and then have it move
randomly around the screen until the user clicks in the window. You
will need to make a Circle object, and can look up the syntax for it
in the graphics documentation.

def main():
# assign to win a GraphWin object made with title, width, height, and the value False
# (note the final parameter is not one we have used before. It makes it so the
# window doesn't attempt to redraw after every change).
# assign to shapes an empty list
# assign to c a new Circle object at (250, 250) with radius 10
# call the draw function of the circle object stored in c
# append the variable c to the list shapes
# while True
# call time.sleep with a half-second delay (0.5)
# for each thing in shapes
# assign to dx a random integer between -10 and 10
# assign to dy a random integer between -10 and 10
# call the move method of the object referred to by thing, passing in dx and dy
# tell the window to update its display (call win.update())
# if win.checkMouse() is not None
# break out of the while loop
# close the window

Set up a call to your main function inside the conditional that runs
only if the file is run on the command line. Always put these two
lines at the bottom of your code.

if __name__ == "__main__":
main()

Run your test.py program. The shape should bounce around the screen
in Brownian motion, which is another way of saying it should move
randomly around the window.

Controlling color

Next, experiment with changing object colors. Inside the inner for loop
in your main program, generate a random color and set the fill color
of the circle to the new color.

# assign to r a random value in [0, 255]
# assign to g a random value in [0, 255]
# assign to b a random value in [0, 255]
# assign to color the result of calling color_rgb( r, g, b)
# call the setFill method of the object in thing, passing in color

Cloning

The next step is to explore the clone method, which duplicates an
object. After the for loop, but inside the while loop, clone one of
the circles, with some probability, and add it to the shapes list.

# if a call to random.random is less than 0.2
# assign to oldthing the result of calling the function random.choice with shapes as its input
# assign to newthing the result of calling the clone method on oldthing
# call the draw method of newthing, passing in the window object in win
# append newthing to the shapes list

Try out your test.py program again and see what happens.

Complex Shapes

Make a new python file called complex_shape.py. Import the graphics, time and
random packages. Then define a function called spaceship_init(x,
y, scale). The function should have the following overall structure.

Note that when you create a graphics object that
requires points you need to use the graphics Point
object. For example, to create a Rectangle, you would
pass it two points as below.

r = gr.Rectangle( gr.Point(0, 0), gr.Point(20, 20) )

def spaceship_init( x, y, scale ):
'''Creates and returns a list of graphics objects to make up a spaceship'''
# assign to shapes the empty list
# assign to r a Rectangle to represent the body of the ship, use (x-scale*10, y) and (x+scale*10, y-scale*80)
# Set the Rectangle to have a grey color (185, 185, 185), use the setFill of the Rectangle object in r
# append to the shapes list the Rectangle r
# assign to p a Polygon for the nose, use ( x - scale*10, y-scale*80 ), (x, y-scale*100 ), and (x + scale*10, y-scale*80 )
# Set the Polygon to have a grey-blue color (150, 170, 200 )
# append to the shapes list the Polygon p
# assign to p a Polygon for the left fin, use ( x - scale*10, y ), (x-scale*10, y-scale*20 ), and (x - scale*25, y )
# Set the Polygon to have a grey-red color (200, 170, 150 )
# append to the shapes list the Polygon p
# assign to p a Polygon for the right fin, use ( x + scale*10, y ), (x+scale*10, y-scale*20 ), and (x + scale*25, y )
# Set the Polygon to have a grey-red color (200, 170, 150 )
# append to the shapes list the Polygon p
# return the shapes list

Note the structure of the above function. First, create an empty
list. Second, create all of the basic shape objects needed to create
the complex shape. For each of the basic shape objects,
append it to the list. Third, return the list. All of the primitive
objects in your scene will be contained in the list returned by the
corresponding init function.

Test

Copy and paste the test_spaceship function below into
complex_shape.py. The function creates a GraphWin, calls spaceship_init
and assigns its return value to a variable like spaceship. It then
loops over the variable and calls the draw method on each primitive
object. Then it calls the getMouse and close methods of your GraphWin
object. Test your complex_shape.py file.

Draw, Move, Undraw

If all of your complex objects have the same structure--they are all
lists of primitive objects--then we should be able to write some
functions to draw them, move them, undraw them, and clone them. Each
of these functions needs to loop over the elements in the object
list and call the appropriate methods.

To encapsulate this functionality, create functions at the top of your
complex_shape.py file for draw, move, and undraw. The skeletons are
below.

def draw( objlist, win ):
""" Draw all of the objects in objlist in the window (win) """
# for each thing in objlist
# call the draw method on thing with win as the argument
def move( objlist, dx, dy ):
""" Draw all of the objects in objlist by dx in the x-direction
and dy in the y-direction """
# for each item in objlist
# call the move method on item with dx and dy as arguments
def undraw( objlist ):
""" Undraw all of the objects in objlist """
# for each thing in objlist
# call the undraw method on thing

When you are done with that, go back to the
test_spaceship function and replace the for loop that
calls thing.draw (both lines of code) with a single
call to draw( spaceship, win ). Test it.

Animation

Now we're going to animate the spaceship. In complex_shape.py, create
a function spaceship_animate immediately after the spaceship_init
function, with the first argument being the shape list, and the second
argument being a frame number. Then we want to make smoke (circles)
come out of the bottom of the spaceship and move down. Once a smoke
ball gets far enough from the spaceship, it will be undrawn and
removed from the shapes list. At the same time, the spaceship itself
needs to move up.

The spaceship_animate function draws one frame of the animation.
It will be called over and over, with different frame numbers, to
construct the animation

def spaceship_animate( shapes, frame_num, win ):
'''given the spaceship list, a frame number, and a window, move the spaceship as appropriate'''
# assign to p1 the result of calling getP1 on the first item in the shapes list
# assign to p2 the result of calling getP2 on the first item in the shapes list
# assign to dx the width of the spaceship body, which is p2.getX() - p1.getX()
# assign to newx the middle X point of the spaceship body, the average of the x values of p1 and p2
# assign to newy the Y value of p1 (the bottom of the spaceship), use getY()
# repeat twice
# assign to c a new circle located at newx, newy with a radius of 0.4*dx
# use the setFill method of c to color the circle bright light yellow (250, 250, 200), you can add some randomness
# use the draw method of c to draw the circle into the window
# append to the shapes list the circle c
# for the first four items in the shapes list (use a slice)
# call the move method of item with the values 0, -dx*0.25 (moves it up the screen)
# **alternative: call your move function with the shapes slice and the amounts to move
# assign to count the value 4
# for the remaining items in the shapes list (index 4 and above)
# assign to c the result of calling the getCenter method of item
# if the y value of c is less than the y value of p1 plus 5*dx
# if count is even (use the modulo operator to calculate this)
# move the item in X by a small negative random amount and in Y by dx * 0.5
#else
# move the item in X by a small positive random amount and in Y by dx * 0.5
# else
# undraw the item
# remove the shape from the shapes list using the pop method of shapes with the argument count
# decrement count by 1
# increment count

Add the following code to your test function in complex_shape.py,
after the draw and before the getMouse, and try it out.

Test Animation

As a final test, download and
run lab6test.py. It will import your
complex_shape.py file and create a simple animated scene.

At the end of this lab, you will have the file complex_shape.py, which
will contain your complex object functions. The spaceship_init and
spaceship_animate functions are a template for you to design other complex
scene elements. If you make the spaceship more interesting, you may use
it as part of your project.

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