# L-5 MCS 275 Fri 25 Jan 2008 : billiards.py

from Tkinter import *
import random,math

class BilliardBall():
   """
   GUI to simulate billiard ball movement.
   """
   def __init__(self,wdw,dimension,increment,delay):
      "determines the layout of the GUI"
      wdw.title('a pool table')
      self.dim = dimension # dimension of the canvas
      self.inc = increment
      self.dly = IntVar()
      self.path = IntVar()
      self.L = Label(wdw,text="delay",justify=LEFT)
      self.L.grid(row=0,column=0)
      self.s = Scale(wdw,orient='vertical',\
        from_=0,to=200,tickinterval=20,resolution=1,\
        length=dimension,variable=self.dly)
      self.s.grid(row=1,column=0)
      self.s.set(delay)
      self.ex = Entry(wdw) # for x value
      self.ex.grid(row=0,column=1)
      self.ex.insert(INSERT,"x = ")
      self.ey = Entry(wdw) # for y value
      self.ey.grid(row=0,column=2)
      self.ey.insert(INSERT,"y = ")
      self.cb = Checkbutton(wdw,text="draw path",\
        variable=self.path,onvalue=1,offvalue=0)
      self.cb.grid(row=0,column=3)
      self.c = Canvas(wdw,width=self.dim,\
        height=self.dim,bg ='green')
      self.c.grid(row=1,column=1,columnspan=3)
      self.go = False # state of animation
      self.b0 = Button(wdw,text='start',\
        command = self.start)
      self.b0.grid(row=2,column=1,sticky=W+E)
      self.b1 = Button(wdw,text='stop',\
        command = self.stop)
      self.b1.grid(row=2,column=2,sticky=W+E)
      self.b2 = Button(wdw,text='clear',\
        command = self.clear)
      self.b2.grid(row=2,column=3,sticky=W+E)
      # initial coordinates of the ball
      self.x = random.randint(10,self.dim-10)
      self.y = random.randint(10,self.dim-10)

   def MapToTable(self,p):
      "keeps the ball on the pool table"
      if p < 0:
         (q,r) = divmod(-p,self.dim)
      else:
         (q,r) = divmod(p,self.dim)
      if q % 2 == 1:
         r = self.dim - r
      return r

   def ShowCoordinates(self,x,y):
      "show coordinates in entry fields"
      sx = 'x = %d' % x
      sy = 'y = %d' % y
      self.ex.delete(0,END)
      self.ex.insert(INSERT,sx)
      self.ey.delete(0,END)
      self.ey.insert(INSERT,sy)

   def DrawBall(self):
      "draws the ball on the pool table"
      x = self.MapToTable(self.x)
      y = self.MapToTable(self.y)
      self.ShowCoordinates(x,y)
      self.c.delete('dot')
      self.c.create_oval(x-6,y-6,x+6,y+6,width=1,\
        outline='black',fill='red',tags='dot')

   def DrawPath(self,x0,y0,x1,y1):
      "draws the line from (x0,y0) to (x1,y1)"
      px0 = self.MapToTable(x0)
      py0 = self.MapToTable(y0)
      px1 = self.MapToTable(x1)
      py1 = self.MapToTable(y1)
      self.c.create_line(px0,py0,px1,py1,width=2)

   def animate(self):
      "performs the animation"
      self.DrawBall()
      angle = random.uniform(0,2*math.pi)
      vx = math.cos(angle)
      vy = math.sin(angle)
      while self.go:
         x = self.x; y = self.y
         self.x = x + vx*self.inc
         self.y = y + vy*self.inc
         self.c.after(self.dly.get())
         self.DrawBall()
         if self.path.get() == 1:
            self.DrawPath(x,y,self.x,self.y)
         self.c.update()

   def start(self):
      "starts the animation"
      self.go = True
      self.animate()

   def stop(self):
      "stops the animation"
      self.go = False

   def clear(self):
      "clears the canvas"
      self.c.delete(ALL)
  
def main():
   top = Tk()
   dimension = 400 # dimension of pool table
   increment = 10  # increment for coordinates
   delay = 50      # how much sleep before update
   show = BilliardBall(top,dimension,increment,delay)
   top.mainloop()

if __name__ == "__main__": main()
