import pygame
from pygame.locals import USEREVENT, OPENGL, DOUBLEBUF, QUIT
from OpenGL.GL import *

width, height = 640, 480
speed = 0.1
xRange = 16.0
yRange = 8.0
xStep = xRange / 240.0
maxTerms = 50

def factorialFunc(x):
    if x <= 1: return 1
    else: return factorialFunc(x-1)*x
# A brief recursive factorial function.
# -Python2.3 handles long integers insanely fast,
# factorial(999) takes less than a tenth of a second.
# Store factorials in a table, just to be most efficient
factorial = [factorialFunc(x) for x in range(maxTerms)]

def displayInit():
    """Set up the display and pygame"""
    pygame.init()
    pygame.display.set_caption('Taylor Sine Approximation')
    pygame.display.set_mode((width, height), OPENGL|DOUBLEBUF)
    glClearColor(0.5, 0.5, 0.5, 0.0)
    glDisable(GL_DEPTH_TEST)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(0.0, width, height, 0.0, -1.0, 1.0)
    glTranslated(0.0, height/2.0, 0.0)
    glScaled(width/xRange, height/yRange, 1.0)
    glLineWidth(2.0)

class SineApprox:
    """Hold the data for the interpolation and do the drawing approximation"""
    def __init__(self):#Could be a generator function, for the sake of twistedness
        self.terms = 0
        self.time = 0.0
    def step(self):
        glClear(GL_COLOR_BUFFER_BIT)
        glBegin(GL_LINE_STRIP)
        x = 0.0
        time = self.time
        terms = self.terms
        while x < xRange:
            y = 0.0
            sign = 1
            for n in range(1,terms,2):
                y += sign*(x**n)/factorial[n]
                sign *= -1
            y += sign*((x**(terms+1))/factorial[terms+1])*time #terms is even
            glVertex2d(x,y)
            x += xStep
        glEnd()
        pygame.display.flip()
        self.time += speed
        if self.time >= 1.0:
            self.terms += 2
            self.time = 0.0
            print "Terms:", self.terms

if __name__ == '__main__':
    print 'An exciting display of the continued approximation of a sine function'
    print 'It interpolates between Taylor approximations of increasing numbers of terms'
    displayInit()           
    mySine = SineApprox()
    STEPEVENT = USEREVENT+1
    pygame.time.set_timer(STEPEVENT,50) #An event at regulat intervals
    event = pygame.event.wait()
    while event.type != QUIT and mySine.terms < maxTerms: #Loop on it, yah?
        if event.type == STEPEVENT:
            mySine.step()
        event = pygame.event.wait()
    print 'Ah, brilliant show'
