Lecture 16

Announcements

Code from Lecture

import random, math


class Dot(object):
    def __init__(self, cx, cy, r):
        self.cx = cx
        self.cy = cy
        self.r = r
        self.color = random.choice(["hotPink", "green2", 
                                    "purple", "dodgerBlue"])
        self.direction = random.choice([(0, 1), (0, -1),
                                        (1, 0), (-1, 0)])
        self.speed = 5

    def moveDot(self):
        dx, dy = self.direction
        self.cx, self.cy = (self.cx + dx*self.speed, 
                                self.cy + dy*self.speed)

    def isCollisionWithWall(self, width, height):
        r = self.r
        return (self.cx + r >= width or self.cx - r <= 0
                    or self.cy + r >= height or self.cy - r <=0)

    def stopDot(self):
        self.speed = 0

    def clickedInsideDot(self, mx, my):
        return math.sqrt((self.cx - mx)**2 + (self.cy - my)**2) <= self.r

    def collidedWithDot(self, other):
        if(isinstance(other, Dot)):
            cx1, cy1, r1 = self.cx, self.cy, self.r
            cx2, cy2, r2 = other.cx, other.cy, other.r
            return math.sqrt((cx1 - cx2)**2 + (cy1 - cy2)**2) <= (r1 + r2)

        else:
            return False

    def grow(self):
        self.r += 5

    def isEaten(self, other):
        if(isinstance(other, Dot)):
            return self.r < other.r
        return False

    def __eq__(self, other):
        if(not isinstance(other, Dot)): return False
        return (self.cx == other.cx and self.cy == other.cy and 
                    self.color == other.color and self.r == other.r and
                        self.direction == other.direction)


    def checkCollision(self, data):
        for dot in data.dots:
            if(dot != self):
                if(self.collidedWithDot(dot)):
                    if(self.isEaten(dot)):
                        data.dots.remove(self)
                        dot.grow()
                        return
                    else:
                        data.dots.remove(dot)
                        self.grow()


    def draw(self, canvas):
        cx, cy, r = self.cx, self.cy, self.r
        canvas.create_oval(cx - r, cy - r, cx + r, cy + r, fill=self.color)


class BouncingDot(Dot):
    def __init__(self, cx, cy, r):
        super().__init__(cx, cy, r)

    def bounce(self):
        dx, dy = self.direction
        self.direction = dx*-1, dy*-1

    def onTimerFired(self, data):
        if(self.isCollisionWithWall(data.width, data.height)):
            self.bounce()
        self.moveDot()
        self.checkCollision(data)

class WrapAroundDot(Dot):
    def __init__(self, cx, cy, r):
        super().__init__(cx, cy, r)

    def wrapAround(self, width, height):
        self.cx = self.cx%width
        self.cy = self.cy%height

    def onTimerFired(self, data):
        self.moveDot()
        self.wrapAround(data.width, data.height)
        self.checkCollision(data)
        
# Animation Starter Code, Focus on timerFired

from tkinter import *

####################################
# customize these functions
####################################

def init(data):
    # load data.xyz as appropriate
    data.paused = False
    data.timer = 0
    data.dots = [] 

def mousePressed(event, data):
    # use event.x and event.y
    if(data.paused): return 
    for dot in data.dots:
        if(dot.clickedInsideDot(event.x, event.y)):
            dot.stopDot()
            return
    radius = random.randint(5, 50)
    if(radius % 2 == 0):
        dot = WrapAroundDot(event.x, event.y, radius)
    else:
        dot = BouncingDot(event.x, event.y, radius)
    data.dots.append(dot)

def keyPressed(event, data):
    # use event.char and event.keysym
    if(event.keysym == "p"):
        data.paused = not data.paused

def timerFired(data):
    if(data.paused): return
    data.timer += 1
    if(data.timer % 100 == 0):
        data.dots = []
    for dot in data.dots:
        dot.onTimerFired(data)

def redrawAll(canvas, data):
    # draw in canvas
    for dot in data.dots:
        dot.draw(canvas)

####################################
# use the run function as-is
####################################

def run(width=300, height=300):
    def redrawAllWrapper(canvas, data):
        canvas.delete(ALL)
        canvas.create_rectangle(0, 0, data.width, data.height,
                                fill='white', width=0)
        redrawAll(canvas, data)
        canvas.update()

    def mousePressedWrapper(event, canvas, data):
        mousePressed(event, data)
        redrawAllWrapper(canvas, data)

    def keyPressedWrapper(event, canvas, data):
        keyPressed(event, data)
        redrawAllWrapper(canvas, data)

    def timerFiredWrapper(canvas, data):
        timerFired(data)
        redrawAllWrapper(canvas, data)
        # pause, then call timerFired again
        canvas.after(data.timerDelay, timerFiredWrapper, canvas, data)
    # Set up data and call init
    class Struct(object): pass
    data = Struct()
    data.width = width
    data.height = height
    data.timerDelay = 100 # milliseconds
    root = Tk()
    init(data)
    # create the root and the canvas
    canvas = Canvas(root, width=data.width, height=data.height)
    canvas.configure(bd=0, highlightthickness=0)
    canvas.pack()
    # set up events
    root.bind("<Button-1>", lambda event:
                            mousePressedWrapper(event, canvas, data))
    root.bind("<Key>", lambda event:
                            keyPressedWrapper(event, canvas, data))
    timerFiredWrapper(canvas, data)
    # and launch the app
    root.mainloop()  # blocks until window is closed
    print("bye!")

run(600, 600)

Lecture Plan