Assignment 3 -- Animated Object

advertisement
Assignment 3 –Animated Objects
Dean Zeller
CS301
Spring 2015
Objective
20 points
Early Submission: Friday, February 6th by 5:00pm
Due: Friday, February 13th by 5:00pm
The student will use create an animated object with at least five poses, and use it in a demo
animation.
Background: Animated Object Template
This class will involve creating animations that demonstrate a particular algorithm. To do this
effectively, the animated objects cannot use the primitive methods used in assignments 1 and 2. For this
assignment, you will create an animated object with at least five poses, based on a provided template. Create an
object that you would want to use in your demonstrations. You will also have the option to use other students’
objects when completed. Because of this, it is important that programmers follow the given template, leaving it
structurally the same. That way, all students’ objects will run in a similar way.
Instructions
Follow the design standards below to create an object similar to the Face object.
1. Download the assignment files from the course website. ZellerDeanAnimatedFace.py contains the
object definition for an animated smiley face. ZellerDeanAnimatedFaceTester.py contains a
working animation that uses the animated face object. In both files, change Zeller to your last name
and Dean to your first name. As such, change Face to a one-word description of the object implemente,
such as troll, sword, airplane, etc…
2. Design a new object to contain at least five different but related poses. The bulk of the changes will be
in the __init__, draw, and setProperties methods. As long as the code is written properly, there
should be no modifications necessary to the move, moveTo, setPose, nextPose, scale, pause, and
delete methods. If it suits your project, you may make changes to them as necessary.
3. Modify the documentation to indicate you as the programmer. Make sure all block documentation
describes your object, poses, and other functions.
4. Create an animation based on ZellerDeanAnimatedFaceTester.py that demonstrates all of the
methods of your object. Create at least two instances of the object and show each of the methods in
action.
5. Record a videocapture of your animation. It should be about a minute or two in length. Use text
annotations to explain what is going on. Voice annotations are helpful, but not necessary.
6. Make a printout of your object code and animation code, and turn in to your instructor by the due date.
7. Create a videocapture of your finished animation. It should be a minute or two in length. Name your
screencapture ZellerDeanAnimatedFace, where Zeller is replaced with your last name, Dean with
your first name, and Face is replaced with a one-word description of the object. Include your object
code file, animation code file, and the screencapture file into a single zip file, and submit to Blackboard
by the due date.
Grading
You will be graded on the following criteria:
Accuracy
Correctly using the template to create a new object
Reusability
Correctly modifying the documentation to match implementation
details
Creativity
Imagination and aesthetic quality of the animated object created
Methods
A method is a function designed within an object. It is called by the user program to perform a specific task.
Methods are called using the “dot” notation. The following methods are defined in the Face object:
dean = Face(parameters)
Defines a new Face object with the specified parameters. Parameters available:
Canvas parameter:
canvas
The canvas to draw the picture on
Position parameters: left, top
Coordinates of the left-top corner of the picture
Size parameters:
width, height
Width and height of the picture
Color parameters:
color1='blue'
Optional parameters to change colors
Name parameters:
name='blank'
Given name of the object
printName=1
1 = print the name, 0 = do not print the name
dean.draw()
Draws the face at its stored location, size, and colors.
dean.move(deltaX, deltaY, delay)
Moves the face relative to its current location, after a delay.
dean.moveTo(x, y, steps, delay)
Moves the face to a specific location, taking a number of steps, with a delay between each step.
for i in range(10):
dean.move(10,10, delay=0)
dave.move(25,25, delay=50)
Uses a for loop to move two objects at the same time.
dean.setProperties(faceColor='blue',eyeColor='green', pupilColor='black')
Allows the user to change the colors of the face, eyes, and/or pupils. If they are unspecified, then they
will not change.
dean.setPose(pose=Emotions.SAD)
Changes the pose of the face to the specified emotion.
dean.nextPose()
Cycles the face’s pose to the next emotion defined in the enumerated list.
dean.scale(xscale,yscale)
Changes the size of the face, according to the xscale and yscale parameters supplied.
dean.delete()
Deletes the face from the canvas. Note that the object is still defined, and can be drawn again in the
future. While deleted, it can be moved or altered as necessary.
ZellerDeanAnimatedFace.py
###########################################################################
#
Animated Face
#
#
#
#
Programmed by Dean Zeller (02-01-2015)
#
#
Classes: CG120, CS301
#
#
Instructor: Dean Zeller
#
#
#
#
Description: The file contains an animated graphic object and an
#
#
accompanying enumerated data type to define the
#
#
different poses.
#
#
Objects:
#
#
Pose
Enumerated data type for nine different emotions. #
#
Face
A traditional smiley face with different faces
#
#
(poses) for each emotion defined in the Emotions #
#
class.
#
#
#
#
Copyright:
#
#
This file and all code within is copyright (c) 2015 Dean Zeller. #
#
Permission to use for educational purposes only.
#
###########################################################################
from tkinter import *
###########################################################################
#
Pose
#
#
#
#
Description: This class defines nine different emotions, for use
#
#
in the Face class. The emotions include:
#
#
SAD, ANGRY, NEUTRAL, GLAD, HAPPY, SMIRK, ECSTATIC,
#
#
DRUNK, and ANXIOUS.
#
#
#
###########################################################################
class Pose: #enumerated data type
SAD=0
ANGRY=1
NEUTRAL=2
GLAD=3
HAPPY=4
SMIRK=5
ECSTATIC=6
DRUNK=7
ANXIOUS=8
numPoses=9
# number of poses, necessary for nextPose method
###########################################################################
#
Face
#
#
#
#
Description: This uses the tkinter graphics library to create a
#
#
classic smiley face, based on the user's parameters.
#
#
#
#
Parameters:
#
#
canvas
Canvas to draw the picture
#
#
left, top
Left and top of the entire picture
#
#
width, height Width and height of the entire picture
#
#
faceColor
Color of the face
#
#
eyeColor
Color of the eyes
#
#
pupilColor
Color of the pupils
#
#
pose
Emotion poses (see Pose class above)
#
#
name
Name of the face
#
#
displayName
Boolean to display the character name
#
#
0 -> does not display name
#
#
1 -> displays name
#
#
#
###########################################################################
class Face:
# class attributes
objectName = "ZellerDeanAnimatedFace"
objectNum = 0
# Title for this object
# Number of instances created
#######################################################################
# __init__ constructor
#
#
#
# Initializes all attributes to given parameters.
#
#######################################################################
def __init__ (self, canvas, left=0,top=0,width=100,height=100,
faceColor="yellow", eyeColor="white", pupilColor="black",
pose=Pose.HAPPY, name="blank", displayName=0):
# attributes directly from parameters
self.c = canvas
self.left = left
self.top = top
self.width = width
self.height = height
self.faceColor = faceColor
self.eyeColor = eyeColor
self.pupilColor = pupilColor
self.pose = pose
self.name = name
self.displayName = displayName
# calculated attributes
self.tag = Face.objectName + str(Face.objectNum)
Face.objectNum += 1
self.right = self.left + self.width
self.bottom = self.top + self.height
self.center = (self.left + self.right) / 2.0
self.middle = (self.top + self.bottom) / 2.0
#######################################################################
# draw method
#
#
#
# Draws the face according to the attributes, including size,
#
# position, colors, and pose portrayed.
#
#
#
# Common coordinates:
#
#
face
#
#
eye1, eye2
#
#
pupil1, pupil2 (default position defined)
#
#
#
# Variable coordinates:
#
#
smile
#
#
brow1, brow2
#
#
pupil1, pupil2 (overwritten by five emotions)
#
#
#
# Drawing:
#
#
Draw all shapes in their assigned coordinates. The smile is
#
#
a line for all faces except ECSTATIC, for which it is a
#
#
smoothed polygon.
#
#######################################################################
def draw(self):
# create X-coordinate guide list
xspaces = 20 # number of guides in the guide list
xgridwidth = 1.0 * self.width / xspaces
x = []
for i in range(xspaces+1):
x.append(self.left + i*xgridwidth)
# create Y-coordinate guide list
yspaces = 20 # number of guides in the guide list
ygridwidth = 1.0 * self.height / yspaces
y = []
for i in range(yspaces+1):
y.append(self.top + i*ygridwidth)
###################################################################
#
Common Coordinates
#
###################################################################
# face guides (two points)
face = (
(x[1],y[0]) , (x[19],y[20])
# eye guides (two points each)
eye1 = ( ( x[5],y[5]) , ( x[9],y[9])
eye2 = ( (x[11],y[5]) , (x[15],y[9])
)
)
)
# pupil guides (two points each, will be changed by some poses)
pupil1 = ( ( x[6],y[6]) , (x[8],y[8]) )
pupil2 = ( (x[12],y[6]) , (x[14],y[8]) )
###################################################################
#
Variable Coordinates
#
###################################################################
# smile, eyebrow, and pupil guides
if self.pose == Pose.HAPPY:
# smiling mouth, raised and arched eyebrows
smile = ( ( x[4],y[12]) , ( x[6],y[16]) , (x[14],y[16]) , (x[16],y[12])
brow1 = ( ( x[5], y[4]) , ( x[6], y[2]) , ( x[8], y[2]) , ( x[9], y[4])
brow2 = ( (x[11] ,y[4]) , (x[12] ,y[2]) , (x[14], y[2]) , (x[15], y[4])
elif self.pose == Pose.GLAD:
# mildly smiling mouth, raised eyebrows
smile = ( ( x[4],y[14]) , ( x[6],y[16]) , (x[14],y[16]) , (x[16],y[14])
brow1 = ( ( x[5], y[4]) , (x[6], y[3]) , ( x[8], y[3]) , ( x[9], y[4])
brow2 = ( (x[11] ,y[4]) , (x[12] ,y[3]) , (x[14], y[3]) , (x[15], y[4])
elif self.pose == Pose.NEUTRAL:
# straight mouth, straight eyebrows
smile = ( ( x[4],y[14]) , ( x[6],y[14]) , (x[14],y[14]) , (x[16],y[14])
brow1 = ( ( x[5], y[4]) , (x[6], y[4]) , ( x[8], y[4]) , ( x[9], y[4])
brow2 = ( (x[11] ,y[4]) , (x[12] ,y[4]) , (x[14], y[4]) , (x[15], y[4])
elif self.pose == Pose.ANGRY:
# mild frowning mouth, eyebrows angled in, pupils crossed
smile = ( ( x[4],y[15]) , ( x[6],y[13]) , (x[14],y[13]) , (x[16],y[15])
brow1 = ( ( x[5], y[4]) , ( x[6], y[3]) , ( x[8], y[3]) , ( x[9], y[5])
brow2 = ( (x[11], y[5]) , (x[12], y[3]) , (x[14], y[3]) , (x[15], y[4])
pupil1 = ( ( x[7], y[6]) , ( x[9], y[8]) )
pupil2 = ( (x[11], y[6]) , (x[13], y[8]) )
elif self.pose == Pose.SAD:
# frowning mouth, eyebrows angled out, pupils down
smile = ( ( x[4],y[16]) , ( x[6],y[12]) , (x[14],y[12]) , (x[16],y[16])
brow1 = ( ( x[5], y[5]) , ( x[6], y[3]) , ( x[8], y[3]) , ( x[9], y[4])
brow2 = ( (x[11], y[4]) , (x[12], y[3]) , (x[14], y[3]) , (x[15], y[5])
pupil1 = ( ( x[6], y[7]) , ( x[8], y[9]) )
pupil2 = ( (x[12], y[7]) , (x[14], y[9]) )
elif self.pose == Pose.SMIRK:
# asymetrically curved mouth and eyebrows, one pupil looking up
)
)
)
)
)
)
)
)
)
)
)
)
)
)
)
smile = ( ( x[4],y[14]) , ( x[6],y[12]) , (x[14],y[16]) , (x[16],y[14]) )
brow1 = ( ( x[5], y[4]) , ( x[6], y[3]) , ( x[8], y[3]) , ( x[9], y[2]) )
brow2 = ( (x[11], y[3]) , (x[12], y[2]) , (x[14], y[3]) , (x[15], y[4]) )
pupil1 = ( ( x[6], y[6]) , ( x[8], y[8]) )
pupil2 = ( (x[12], y[5]) , (x[14], y[7]) )
elif self.pose == Pose.ECSTATIC:
# open mouth, eyebrows arched high, pupils looking up
smile = ( ( x[4],y[13]) , ( x[6],y[16]) , (x[14],y[16]) ,
(x[16],y[13]) , ( x[4],y[13]) )
brow1 = ( ( x[5], y[4]) , ( x[6], y[2]) , ( x[8], y[2]) , ( x[9], y[4]) )
brow2 = ( (x[11], y[4]) , (x[12], y[2]) , (x[14], y[2]) , (x[15], y[4]) )
pupil1 = ( ( x[6], y[5]) , ( x[8], y[7]) )
pupil2 = ( (x[12], y[5]) , (x[14], y[7]) )
elif self.pose == Pose.DRUNK:
# curvy smile, eyebrows angled, one pupil looking down, one pupil looking in
smile = ( ( x[4],y[13]) , ( x[6],y[15]) , (x[10],y[13]) ,
(x[14],y[15]) , (x[16],y[13]) )
brow1 = ( ( x[5], y[5]) , ( x[6], y[4]) , ( x[8], y[4]) , ( x[9], y[3]) )
brow2 = ( (x[11], y[3]) , (x[12], y[4]) , (x[14], y[4]) , (x[15], y[5]) )
pupil1 = ( ( x[6], y[7]) , ( x[8], y[9]) )
pupil2 = ( (x[11], y[6]) , (x[13], y[8]) )
elif self.pose == Pose.ANXIOUS:
# curvy frown, eyebrows angled slightly
smile = ( ( x[4],y[15]) , ( x[6],y[13]) , (x[10],y[15]) ,
(x[14],y[13]) , (x[16],y[15]) )
brow1 = ( ( x[5], y[4]) , ( x[6] ,y[4]) , ( x[8], y[4]) , ( x[9], y[5]) )
brow2 = ( (x[11], y[5]) , (x[12], y[4]) , (x[14], y[4]) , (x[15], y[4]) )
else: # print warning message, unimplemented emotion
print ("Error -- unimplemented pose")
###################################################################
#
Drawing
#
###################################################################
# draw face
self.faceID = self.c.create_oval(face, fill=self.faceColor, tag=self.tag)
# draw eyes
self.leftEyeID = self.c.create_oval(eye1, fill=self.eyeColor, tag=self.tag)
self.rightEyeID = self.c.create_oval(eye2, fill=self.eyeColor, tag=self.tag)
# draw pupils
self.leftPupilID = self.c.create_oval(pupil1, fill=self.pupilColor, tag=self.tag)
self.rightPupilID = self.c.create_oval(pupil2, fill=self.pupilColor,
tag=self.tag)
# draw smile (Variable Shape)
if self.pose == Pose.ECSTATIC: # Ecstatic has closed polygon for mouth)
self.c.create_polygon(smile, width=2, fill="black", smooth=1, tag=self.tag)
else: # all other faces have open lines for mouth
self.c.create_line(smile, width=2, fill="black", smooth=1, tag=self.tag)
# draw brows
self.c.create_line(brow1, width=2, fill="black", smooth=1, tag=self.tag)
self.c.create_line(brow2, width=2, fill="black", smooth=1, tag=self.tag)
# draw name
if self.displayName: # only display if displayName flag is set
self.c.create_text(self.center, self.bottom, anchor=N,
text=self.name, tag=self.tag)
self.c.update()
#######################################################################
# move method
#
#
#
# Moves the face on the canvas, relative to its current position,
#
# after the specified delay. Attributes are adjusted accordingly.
#
#######################################################################
def move (self, deltaX=0, deltaY=0, delay=5):
# adjust attributes
self.left += deltaX
self.top += deltaY
self.right += deltaX
self.bottom += deltaY
self.center += deltaX
self.middle += deltaY
# move face
self.c.after(delay)
self.c.move(self.tag,deltaX,deltaY)
self.c.update()
#######################################################################
# moveTo method
#
#
#
# Moves the face on the canvas to a specific location, broken
#
# into a series of steps, each after the specified delay.
#
#######################################################################
def moveTo (self, x=0, y=0, steps=10, delay=5):
# calculate deltaX and deltaY
distX = x - self.left
deltaX = distX * 1.0 / steps
distY = y - self.top
deltaY = distY * 1.0 / steps
# move face in steps
for i in range(steps):
self.move(deltaX=deltaX, deltaY=deltaY, delay=delay)
#######################################################################
# setProperties method
#
#
#
# Changes the colors of the face, eyes, and pupils. If a color is
#
# not specified, then it remains unchanged.
#
#######################################################################
def setProperties (self, faceColor="default", eyeColor="default",
pupilColor="default"):
if faceColor != "default":
self.faceColor=faceColor
self.c.itemconfig(self.faceID, fill=self.faceColor)
self.c.update()
if eyeColor != "default":
self.eyeColor=eyeColor
self.c.itemconfig(self.leftEyeID, fill=self.eyeColor)
self.c.itemconfig(self.rightEyeID, fill=self.eyeColor)
self.c.update()
if pupilColor != "default":
self.pupilColor=pupilColor
self.c.itemconfig(self.leftPupilID, fill=self.pupilColor)
self.c.itemconfig(self.rightPupilID, fill=self.pupilColor)
self.c.update()
#######################################################################
# setPose method
#
#
#
# Changes the pose of the face to the specified pose.
#
#######################################################################
def setPose (self, pose=Pose.HAPPY, delay=5):
self.c.after(delay)
self.delete(delay=0)
self.pose = pose
self.draw()
self.c.update()
#######################################################################
# nextPose method
#
#
#
# Changes the pose of the face to the next in the defined list.
#
#######################################################################
def nextPose (self, delay=5):
self.c.after(delay)
self.delete(delay=0)
self.pose = (self.pose + 1) % Pose.numPoses
self.draw()
self.c.update()
#######################################################################
# scale method
#
#
#
# Changes the size of the object, according to the xscale and
#
# yscale parameters, after a given delay.
#
#
#
# Note: There is a bug in this method. The left, right, top, and
#
# bottom are not recalculated. For most cases, this will not affect #
# animation playback. However, the potential for problems exist.
#
#######################################################################
def scale(self, xscale=1.0, yscale=1.0, delay=5):
self.c.after(delay)
self.c.scale(self.tag, self.center, self.middle, xscale, yscale)
self.width = self.width * xscale
self.height = self.height * yscale
self.c.update()
#######################################################################
# pause method
#
#
#
# Delays the animation, according to the after parameter specified. #
#######################################################################
def pause(self, delay):
self.c.after(delay)
self.c.update()
#######################################################################
# delete method
#
#
#
# Deletes the face from the canvas. Note that the object still
#
# still exists after it is deleted; it is just not displayed.
#
#######################################################################
def delete(self, delay=5):
self.c.after(delay)
self.c.delete(self.tag)
self.c.update()
ZellerDeanAnimatedFaceTester.py
###########################################################################
#
Dean's Animation
#
#
#
# Programmed by Dean Zeller (02-01-2015)
#
#
#
# The following is a animated story of Dean and his two minions, Megan
#
# and Cody. The purpose of this animation is to demonstrate to CG120
#
# students how to complete Assignment 3.
#
#
#
# EXTERNAL FILES
#
# The following external files are used in the process of running the
#
# animation.
#
#
ZellerDeanAnimatedFace, written by Dean Zeller
#
#
Face -- An animated smiley face
#
#
Pose -- The available emotions for the face
#
#
#
###########################################################################
from ZellerDeanAnimatedFace import Face
from ZellerDeanAnimatedFace import Pose
import random
from tkinter import *
c = Canvas(width=500, height=500, bg='white')
c.pack(expand=YES, fill=BOTH)
# delay value between actions, in milliseconds (1000 = 1 second)
longDelay = 1000
mediumDelay = 250
shortDelay = 50
###########################################################################
# Delete all lines of code beneath this line
#
###########################################################################
# define objects used in animation, along with initial position and size
title = c.create_text(250,30, text="It's another animation!",
font=("Helvetica",36))
dean = Face(c, left=200, top=100, width=100, height=100,
faceColor="purple", eyeColor="white",
pose=Pose.HAPPY)
megan = Face(c, left=300, top=100, width=100, height=100,
faceColor="red", eyeColor="white",
pose=Pose.DRUNK)
cody = Face(c, left=100, top=100, width=100, height=100,
faceColor="green", eyeColor="red",
pose=Pose.SMIRK)
# draw faces
c.itemconfig(title, text="Draw the Faces")
c.update()
dean.draw()
c.after(longDelay)
c.update()
deantextID = c.create_text(250,220,text="Hi, I'm Dean.", justify=CENTER)
c.update()
c.after(longDelay)
c.update()
megan.draw()
c.after(longDelay)
c.update()
megantextID = c.create_text(350,220,text="Hello. I'm Megan.", justify=CENTER)
c.update()
c.after(longDelay)
c.update()
cody.draw()
c.after(longDelay)
c.update()
codytextID = c.create_text(150,220,text="My name's Cody.", justify=CENTER)
c.update()
c.after(longDelay)
c.update()
# Move faces with the move method
c.itemconfig(title, text="The move Method")
c.itemconfig(codytextID,text="")
c.itemconfig(megantextID,text="")
c.itemconfig(deantextID,text="")
c.update()
c.after(longDelay)
c.itemconfig(codytextID,text="I don't like Dean.")
c.update()
c.after(longDelay)
cody.move(-100,0)
c.itemconfig(codytextID,text="")
c.after(longDelay)
c.update()
c.itemconfig(megantextID,text="Yeah, I don't either.")
c.update()
c.after(longDelay)
megan.move(100,0)
c.itemconfig(megantextID,text="")
c.after(longDelay)
c.update()
c.itemconfig(deantextID,text="I'm so sad...")
c.update()
c.after(longDelay)
c.update()
dean.setPose(pose=Pose.SAD)
c.itemconfig(deantextID,text="")
c.update()
# move faces with moveTo method
c.itemconfig(title, text="The moveTo Method")
c.update()
dean.moveTo(50,100, steps=50, delay=10)
megan.moveTo(350,100, steps=50, delay=10)
cody.moveTo(200,100, steps=50, delay=10)
# change emotions
c.itemconfig(title, text="Changing Poses")
c.update()
dean.setPose(Pose.SAD, delay=longDelay)
megan.setPose(Pose.ANGRY, delay=longDelay)
cody.setPose(Pose.NEUTRAL, delay=longDelay)
dean.setPose(Pose.GLAD, delay=longDelay)
megan.setPose(Pose.HAPPY, delay=longDelay)
cody.setPose(Pose.SMIRK, delay=longDelay)
dean.setPose(Pose.ECSTATIC, delay=longDelay)
megan.setPose(Pose.DRUNK, delay=longDelay)
cody.setPose(Pose.ANGRY, delay=longDelay)
dean.pause(longDelay)
# change emotions in a for loop
c.itemconfig(title, text="Cycling Poses")
c.update()
for i in range(20):
dean.nextPose(delay=shortDelay)
megan.nextPose(delay=shortDelay)
cody.nextPose(delay=shortDelay)
# delete and draw
c.itemconfig(title, text="Delete and Draw")
c.update()
dean.delete(delay=longDelay)
megan.delete(delay=longDelay)
cody.delete(delay=longDelay)
dean.pause(longDelay)
dean.moveTo(350,200, steps=1, delay=0)
megan.moveTo(50,200, steps=1, delay=0)
cody.moveTo(200,200, steps=1, delay=0)
dean.draw()
dean.pause(longDelay)
megan.draw()
megan.pause(longDelay)
cody.draw()
cody.pause(longDelay)
# set colors
c.itemconfig(title, text="Setting Colors")
c.update()
dean.setProperties(faceColor="blue", eyeColor="pink", pupilColor="white")
megan.setProperties(faceColor="orange", eyeColor="blue")
cody.setProperties(eyeColor="black")
c.after(longDelay)
c.update()
# use for loops to move in parallel
c.itemconfig(title, text="Movement in Parallel")
c.update()
for i in range(10):
dean.move(0,15,delay=shortDelay)
megan.move(0,-15,delay=shortDelay)
cody.move(-15,15,delay=shortDelay)
# move while changing emotions
c.itemconfig(title, text="Moving With Poses")
c.update()
for i in range(40):
megan.move(10,0,delay=shortDelay)
megan.nextPose()
# resize
c.itemconfig(title, text="Changing Size")
c.update()
dean.scale(.5, .5, delay=longDelay)
megan.scale(1.5, 1.5, delay=longDelay)
cody.scale(2.5, 2.5, delay=longDelay)
# the end
c.itemconfig(title, text="THE END")
c.update()
Download