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()