"""
Version: 1.71  August 22, 2005

Developed in-house for an OSCON talk.  Demonstrates
suitability of Pygame for coding a programmatically
controlled, scene-based presentation manager (not a
new idea -- a reimplementation of an old idea).

Fixed some typos after the show.

-- Kirby

(c) 2005, 4D Solutions, GNU Public License.

"""

import pygame
pygame.font.init()
pygame.display.init()
pygame.mixer.init()
from pygame.locals import *
import threading
import time
import sys
import random

class Content(object):
    """
    Used to pass media content
    """
    step = 1,1
    milliseconds = 100
    fontsize = 20
    scrolldirection = 1
    scrollwidth = 600
    fontcolor = 'black'
    font = 'verdana'
    
    def __getattr__(self, prop):
        return None
	
class Resource( object ):
    """
    Standard API for all Resource objects.  Some don't respond to some keys,
    so the default method in every case is to do nothing
    """
    def run(self): pass
    def stop(self): pass
    def pause(self): pass
    def unpause(self): pass
    def restart(self): pass
    def start(self): pass
    def next(self): pass
    def prev(self): pass
    def join(self): pass
    def isAlive(self): pass

class Stillimage( Resource ):
    """
    Place a still Image at params.topleft or autocenter)
    """
    
    def __init__(self, params, scene):
        self.scene = scene
        self.screen = scene.screen
        self.theimage = pygame.image.load(params.filename).convert()
        self.topleft = params.topleft

    def start (self):		
        w,h = self.theimage.get_size()
        if self.topleft:
            lc = self.topleft
        else:
            lc = 512 - w//2, 384 - h//2
        self.rect = pygame.Rect(lc,(w,h))
        self.screen.blit(self.theimage, self.rect)
        pygame.display.update()

class Stilltext( Resource ):
    """
    Place still text at params.topleft or autocenter)
    """
    
    def __init__(self, params, scene):
        self.scene = scene
        self.screen = scene.screen
        self.filename = params.filename
        self.fontsize = params.fontsize
        self.font = pygame.font.SysFont('verdana',self.fontsize)
        self.topleft = params.topleft

    def start (self):
        lines = open(self.filename).readlines()
        numlines = len(lines)
        leading = self.fontsize//4
        surfaces = []

        for i in range(numlines):
            theline = lines[i][:-1]
            theline = theline.replace('\t','    ')
            surfaces.append ( self.font.render(theline,1,
                                pygame.color.Color('black')) )
            row = self.topleft[0], self.topleft[1] + ((self.fontsize +
                                                       leading) * i)
            self.screen.fill(pygame.color.Color(self.scene.bg),
                            pygame.Rect(row,(600,self.fontsize + leading)) )
            self.screen.blit(surfaces[i],
                            pygame.Rect(row,(600,self.fontsize + leading)),
                            pygame.Rect(0,0,600, self.fontsize + leading) )


        pygame.display.update()


class Movingimage( threading.Thread, Resource ):
    """
    Image moves around the screen
    """
    moving = True
    looping = True
    
    def __init__(self, params, scene):
        self.scene = scene
        self.screen = scene.screen
        self.posrect = params.posrect
        self.theimage = pygame.image.load(params.filename).convert()
        self.step = params.step
        threading.Thread.__init__(self)

    def run (self):		
        hstep, vstep = self.step
        while self.looping:
            if self.moving:
                if self.posrect.right>1040 or self.posrect.left<-10:
                    hstep = -hstep
                if self.posrect.bottom>700 or self.posrect.top<60:
                    vstep = -vstep
                self.screen.fill(pygame.color.Color(self.scene.bg), self.posrect)				
                self.posrect = self.posrect.move(hstep,vstep)			
                self.screen.blit(self.theimage, self.posrect)
                pygame.display.update()

    def restart(self):
        self.moving = True

    def pause (self):
        self.moving = False

    def stop (self):
        self.looping = False
            
class Flipimage (Resource ):

    def __init__ (self, params, scene ):
        self.scene = scene
        self.screen = scene.screen
        self.imagelist = params.imagelist
        self.imagenum = 0
        self.rect = None
        self.topleft = params.topleft
            
    def start(self):
        if self.rect:
            self.screen.fill(pygame.color.Color(self.scene.bg), self.rect)
        self.theimage = pygame.image.load(self.imagelist[self.imagenum]).convert()
        w,h = self.theimage.get_size()
        if self.topleft:
            lc = self.topleft
        else:
            lc = 512 - w//2, 384 - h//2
        self.rect = pygame.Rect(lc,(w,h))
        self.screen.blit(self.theimage, self.rect)
        pygame.display.update()

    def run(self):
        self.start()
            
    def next(self):
        self.imagenum += 1
        if self.imagenum == len(self.imagelist):
            self.imagenum = 0
        self.start()

    def prev(self):
        self.imagenum -= 1
        if self.imagenum == -1:
            self.imagenum = len(self.imagelist)-1
        self.start()

class Autoflipimage ( threading.Thread, Resource ):

    def __init__ (self, params, scene ):
        self.scene = scene
        self.screen = scene.screen
        self.imagelist = params.imagelist
        self.usercenter = params.usercenter
        self.imagenum = 0
        self.rect = None
        self.topleft = None
        self.milliseconds = params.milliseconds
        if not self.milliseconds:
            self.milliseconds = 100
        self.topleft = params.topleft
        self.looping = True
        self.flipping = True
        threading.Thread.__init__(self)
            
    def run (self):
        while self.looping:
            if self.flipping:
                self.theimage = pygame.image.load(self.imagelist[self.imagenum]).convert()				
                if self.rect:
                    self.screen.fill(pygame.color.Color(self.scene.bg), self.rect)				
                w,h = self.theimage.get_size()
                if self.topleft:
                    lc = self.topleft
                elif self.usercenter:
                    lc = self.usercenter[0] - w//2, self.usercenter[1] - h//2
                else:
                    lc = 512 - w//2, 384 - h//2
                self.rect = pygame.Rect(lc,(w,h))
                self.screen.blit(self.theimage, self.rect)
                pygame.display.update()
                self.imagenum += 1
                if self.imagenum == len(self.imagelist):
                    self.imagenum = 0
                pygame.time.delay(self.milliseconds)

    def restart(self):
        self.imagenum = 0
        self.flipping = True

    def pause (self):
        self.flipping = False

    def unpause (self):
        self.flipping = True

    def stop (self):
        self.looping = False

class Scrolltextfile ( threading.Thread, Resource ):

    def __init__ (self, params, scene ):
        self.scene = scene
        self.screen = scene.screen
        self.filename = params.filename
        self.milliseconds = params.milliseconds
        self.displaylines = params.displaylines
        self.fontsize = params.fontsize
        self.rect = None
        self.looping = True
        self.scrolling = True
        self.font = params.font
        self.font = pygame.font.SysFont(self.font,self.fontsize)        
        self.fontcolor = params.fontcolor        
        self.topleft = params.topleft
        self.scrolldirection = params.scrolldirection
        self.scrollwidth = params.scrollwidth
        if not self.topleft:
            self.topleft = 100,100
        threading.Thread.__init__(self)
            
    def run (self):
        lines = open(self.filename).readlines()
        lines.append("--------------------------------------")
        lines.append("  ")		
        self.offset = 0
        numlines = len(lines)
        leading = self.fontsize//4
        if not self.displaylines:
            self.displaylines = 600//(self.fontsize + leading)
        if self.scrolldirection<>1:
            therange = list(reversed(range(self.displaylines)))
        else:
            therange = range(self.displaylines)
   
        while self.looping:
            if self.scrolling:
                surfaces = []
                
                for i in therange:
                    theline = lines[(i+self.offset) % numlines][:-1]
                    theline = theline.replace('\t','    ')
                    surfaces.append ( self.font.render(theline,1,
                                                       pygame.color.Color(self.fontcolor)) )

                for i in therange:	
                    row = self.topleft[0], self.topleft[1]+((self.fontsize + leading) * i)
                    self.screen.fill(pygame.color.Color(self.scene.bg),
                                     pygame.Rect(row,(self.scrollwidth, self.fontsize + leading)) )

                    self.screen.blit(surfaces[i],
                                     pygame.Rect(row,(600,self.fontsize + leading)),
                                     pygame.Rect(0,0,self.scrollwidth, self.fontsize + leading) )

                self.offset += 1
                pygame.display.update()
                pygame.time.delay(self.milliseconds)

    def restart(self):
        self.offset = 0
        self.scrolling = True

    def pause (self):
        self.scrolling = False

    def unpause (self):
        self.scrolling = True

    def stop (self):
        self.looping = False	

class Movieplayer( threading.Thread, Resource ):
    
    def __init__( self, params, scene):
        self.scene = scene
        self.screen = scene.screen
        self.filename = params.filename
        self.topleft = params.topleft
        self.themovie = None
        threading.Thread.__init__(self)
    
    def run ( self ):
        if not self.themovie:
            self.themovie = pygame.movie.Movie(self.filename)
            w,h = self.themovie.get_size()
            if not self.topleft:
                self.topleft = 512 - w//2, 384 - h//2            
            self.themovie.set_display(self.screen, pygame.Rect(self.topleft, (w,h)))        
        self.themovie.play()        
        
    def restart ( self ):
        if self.themovie.get_busy():
            self.themovie.stop()
        self.themovie.rewind()
        self.themovie.play()
        
    def stop ( self ):
        if self.themovie.get_busy():
            self.themovie.stop()
    
    def unpause(self):  # resume play if not playing

        if self.themovie.get_busy():
            self.themovie.play()
        
    pause = stop

class Coderunner( threading.Thread, Resource ):

    """
    Runs a generator with output rendered to a surface
    """
    
    def __init__( self, params, scene):
        self.scene = scene
        self.screen = scene.screen
        self.usercenter = params.usercenter
        self.topleft = params.topleft
        self.milliseconds = params.milliseconds
        self.generator = params.generator
        self.fontsize = params.fontsize
        self.font = params.font
        self.font = pygame.font.SysFont(self.font,self.fontsize)        
        threading.Thread.__init__(self)
    
    def run ( self ):
        self.f = self.generator()
        self.looping = True
        self.iterating = True
        self.rect = None
        while self.looping:
            if self.iterating == True:
                output = self.f.next()
                if not type(output)==type(''):
                    output = str(output)                    
                surface = self.font.render(output,1,pygame.color.Color('black'))
                if self.rect:
                    self.screen.fill(pygame.color.Color(self.scene.bg), self.rect)
                w,h = surface.get_size()                              
                if self.topleft:
                    lc = self.topleft
                elif self.usercenter:
                    lc = self.usercenter[0] - w//2, self.usercenter[1] - h//2
                else:
                    lc = 512 - w//2, 384 - h//2
                self.screen.blit(surface, pygame.Rect(lc,(w,h)) )
                self.rect = pygame.Rect(lc,(w,h))                                    
                pygame.display.update()
                pygame.time.delay(self.milliseconds)

    def restart ( self ):
        self.f = self.generator()
        self.iterating = True
     
    def stop ( self ):
        self.looping = False
    
    def unpause(self):
        self.iterating = True
        
    def pause ( self ):
        self.iterating = False

class Scene ( object ):

    footerfont = pygame.font.SysFont('verdana',20)
    headerfont = pygame.font.SysFont('verdana',40)	

    def __init__(self, screen, bg, name, num, hfcolor='black', contents=[]):
        self.screen = screen
        self.bg = bg
        self.name = name
        self.num = num
        self.hfcolor = hfcolor
        self.contents = contents
        self.resources = []
        
    def display(self):

        s = self.screen
        s.fill(pygame.color.Color(self.bg))
        today = time.ctime().split()
        today = "%s, %s %s, %s" % (today[0], today[1], today[2], today[4])
        footer0 = self.footerfont.render(today,1,pygame.color.Color(self.hfcolor))		        
        footer1 = self.footerfont.render('OSCON 2005',1,pygame.color.Color(self.hfcolor))        
        footer2 = self.footerfont.render(str(self.num),1,pygame.color.Color(self.hfcolor))
        header  = self.headerfont.render(self.name,1,pygame.color.Color(self.hfcolor))
        w,h = header.get_size()
        s.blit(footer1, (450,730))
        s.blit(footer0, (20,730))
        s.blit(footer2, (950,730))
        s.blit(header, (512 - w//2, 10))

        self.resources = []
        for o in self.contents:
            if o.content_type == 'movie':
                self.resources.append(Movieplayer( o, self))
            elif o.content_type == 'movingimage':
                self.resources.append(Movingimage( o, self))
            elif o.content_type == 'flipimage':
                self.resources.append(Flipimage( o, self ))
            elif o.content_type == 'autoflip':
                self.resources.append(Autoflipimage( o, self ))				
            elif o.content_type == 'scrollfile':
                self.resources.append(Scrolltextfile( o, self ))				
            elif o.content_type == 'stillimage':
                self.resources.append(Stillimage( o, self ))
            elif o.content_type == 'stilltext':
                self.resources.append(Stilltext( o, self ))
            elif o.content_type == 'coderunner':
                self.resources.append(Coderunner( o, self ))
                
    def run(self):
        for r in self.resources:
            if hasattr(r,"themovie"):
                pygame.mixer.quit()
            r.start()
                    
    def end(self):        
        for r in self.resources:
            r.stop()
            if r.isAlive():
                r.join()
        if not pygame.mixer.get_init():          
            pygame.mixer.init()                
        self.resources = []

# path options


fpath = "c:/Documents and Settings/Kirby/My Documents/Projects/oscon2005/"
# fpath = "C:/Documents and Settings/Kirby/Desktop/oscon2005/"


"""
Each slide or scene is set up inside a function, the purpose of which is
to pass in some slide-level parameters, including a list of resources
called 'contents', a list of surface objects to be displayed.  Many of
these surface objects invoke their own threads, so that the outer keyboard
loop remains in effect (keystrokes get passed to internal content, e.g.
to pause a video clip or whatever).
"""

def setscene_welcome(s, num=0):
    """
    Scroll this source code with animated graphic
    """
    mobj = Content()
    mobj.content_type = 'scrollfile'
    mobj.fontsize = 14
    mobj.filename = fpath + "oscon2005.py"
    contents = [mobj]

    graphics = []
    for pic in ['ch1.gif','ch2.gif','ch3.gif','ch4.gif',
	'ch5.gif','ch6.gif','ch7.gif','ch8.gif','ch9.gif','ch10.gif']:
        graphics.append(fpath + pic)
    mobj = Content()
    mobj.content_type = 'autoflip'
    mobj.imagelist = graphics
    mobj.topleft = 680,450
    contents.append(mobj)

    mobj = Content()
    mobj.content_type = 'stillimage'
    mobj.filename = fpath + 'osconbanner.gif'
    mobj.topleft = 780,100
    contents.append(mobj)
    
    scene = Scene( s, 'white', 'Welcome to OSCON 2005',
                   num, contents = contents)    
    return scene

def setscene_thankyou(s, num=0):
    """
    Your willingness to participate in the free and open source
    revolution has given humanity a new vector, towards a more
    sustainable and happier career as crew of Spaceship Earth.
    """

    contents = []

    for r in range(5):
        mobj = Content()
        mobj.content_type = 'stillimage'
        mobj.filename = fpath + 'osconbanner.gif'
        mobj.topleft = 150 + r*150,100
        contents.append(mobj)

    mobj = Content()   
    mobj.content_type = 'stilltext'
    mobj.filename = fpath + "thankyou.txt"
    mobj.fontsize = 30
    mobj.topleft = (280,400)
    contents.append(mobj)

    mobj = Content()   
    mobj.content_type = 'stilltext'
    mobj.filename = fpath + "optional.txt"
    mobj.fontsize = 30
    mobj.topleft = (330,550)
    contents.append(mobj)
    
    scene = Scene( s, 'white', 'Thank You!',
                   num, contents = contents)    
    return scene

def setscene_rbf(s, num=0):
    """
    RBF slide; intro to a series of collaborations
    """
    graphics = []
    for pic in ['rbfjitter.jpg','vesphere.jpg','domecar.gif',
		'dymaxcar.gif','Mtl-biosphere.jpg','eye.gif','bfitm.png',
                'bmc1.jpg', 'build.gif','buckyworks.gif','epball1.gif',
                'edapple2.jpg','synbook.jpg','buckystampssm.png',
                'ohman12192004.jpg','conf_06.gif', 'conf_02.gif',
                'conf_03.gif']:
        graphics.append(fpath + pic)
    mobj = Content()
    mobj.content_type = 'flipimage'
    mobj.imagelist = graphics
    mobj.milliseconds = 500
    scene = Scene( s, 'white', 'R. Buckminster Fuller (RBF)',
                   num, contents = [mobj])	
    return scene

def setscene_collab(s, num=0):
    """
    A scrolling lists of collaborations and contributions that
    emerged to define the Fuller School (the list goes on,
    and gets longer).  I've been in on a few of these, lots of
    times I'm just an onlooker from afar.
    """
    mobj = Content()
    mobj.content_type = 'scrollfile'
    mobj.filename = fpath + "collablist.txt"
    mobj.fontsize = 20
    mobj.milliseconds = 500
    mobj.fontcolor = 'yellow'
    mobj.font = 'courier'
    mobj.scrollwidth = 820
    contents = [mobj]
    scene = Scene( s, 'black', 'A Web of Collaborations',
                   num, hfcolor='yellow', contents = [mobj])	
    return scene

def setscene_rywalt(s, num=0):
    """
    Chris unfolds the World Game "game board" creating a
    digital library video clip
    """
    mobj = Content()
    mobj.content_type = 'movie'
    mobj.filename = fpath + "dymaxion_2003.animation.mpeg"
    mobj.topleft = (110,70)
    scene = Scene( s, 'white', 'Chris Rywalt Plays World Game',
                   num, contents = [mobj])
    return scene

def setscene_metoo(s, num=0):
    """
    One of my early animations, derived from a POV-Ray tutorial
    with some Fuller School twists
    """
    mobj = Content()
    mobj.content_type = 'movie'
    mobj.filename = fpath + "ghostjit.mpeg"
    scene = Scene( s, 'black', 'Me Too!',
                   num, hfcolor = "orange", contents = [mobj])
    return scene

def setscene_wanderers(s, num=0):
    """
    A cultural phenomenon:  the Math Wars = a breakdown in agreement over
    what and how to teach mathematics.  My view is the open source
    community has plenty to offer, and would provide a valuable service
    by competing in the recruiting of faculty and students to a curriculum
    designed to pass on core skills and values.
    """
    mobj = Content()
    mobj.content_type = 'flipimage'
    graphics = []
    for i in range(2):
        graphics.append(fpath + ('wwwanderers.jpg','demowanderers.jpg')[i])
    mobj.imagelist = graphics
    mobj.usercenter = (512,400)
    scene = Scene( s, 'black', 'Math Through Storytelling',
                   num, hfcolor = 'yellow', contents = [mobj])
    return scene

def setscene_dome(s, num=0):
    """
    Rick Bono started the ball rolling with DOME, an free software
    offering (GPL) built on Hugh Kenner's 'Geodesic Math and How
    to Use It'
    """
    mobj = Content()
    mobj.content_type = 'scrollfile'
    mobj.filename = fpath + "Geodesic.cpp"
    mobj.fontcolor = 'black'
    mobj.fontsize = 14
    contents = [mobj]

    mobj = Content()
    mobj.content_type = 'movingimage'
    mobj.step = 0,1
    mobj.filename = fpath + 'rbono.gif'
    mobj.posrect = pygame.Rect(800,400,113,121)
    contents.append(mobj)
    scene = Scene( s, 'white', 'Dome by Rick Bono',
                   num, hfcolor = 'orange', contents = contents)
    
    return scene

def setscene_bonoballs(s, num=0):
    """
    Illustrates the concept of 'frequency'
    """
    contents=[]
    graphics = []
    pix = ['freq1.png','freq2.png','freq3.png','freq4.png',
    'freq5.png','freq6.png', 'freq7.png', 'freq8.png', 'freq9.png']
    pix.extend( list(reversed(pix[:-1])) )
    for i in pix:
        graphics.append(fpath + i)
    mobj = Content()
    mobj.content_type = 'autoflip'
    mobj.imagelist = graphics
    mobj.milliseconds = 100
    contents.append(mobj)
    scene = Scene( s, 'white', 'Dome by Rick Bono',
                   num, contents = contents)    
    return scene

def setscene_waterman1(s, num=0):
    """
    Waterman Polyhedra include the maximum number of CCP vertices at a
    given radius or less, such that overall convexity is preserved.
    Larger radius polys become ever more spherical in appearance
    """
    mobj = Content()
    mobj.filename = fpath + "w1000.gif"	
    mobj.content_type='movingimage'
    mobj.posrect = pygame.Rect(200,200,490,467)
    scene = Scene( s, 'white', 'Waterman Polyhedra',
                   num, contents = [mobj] )
    return scene

def setscene_waterman2(s, num=0):
    """
    Figuring a convex hull has always been the hard part.  Qhull was
    a godsend.  More recently, QuickHull3D by has meant the whole
    computation may be done within a single Jython script.
    """
    mobj = Content()
    mobj = Content()
    mobj.content_type = 'stillimage'
    mobj.filename = fpath + 'w500.png'
    scene = Scene( s, 'black', 'Waterman Polyhedra',
                   num, hfcolor = 'yellow', contents = [mobj] )
    return scene

def setscene_snelson(s, num=0):
    """
    Kenneth Snelson is an internationally recognized artist.  He has also put
    a lot of skill into a visual atomic model.  He had a stormy relationship
    with Fuller, after a happy start at Black Mountain College, owing to a
    tug-o-war for ownership/priority vis-a-vis tensegrity a.k.a. floating
    compression.  Both took out patents.
    """
    graphics = []
    for pic in ['Ken_Snelsonsmall.gif','eqtower.jpg','29-AltComplexAtm.jpeg.jpg',
                '26-Neon.jpeg.jpg','ForestDevCross.jpg','WoodenXPiece.jpg']:
        graphics.append(fpath + pic)
    mobj = Content()
    mobj.content_type = 'flipimage'
    mobj.imagelist = graphics
    scene = Scene( s, 'white', 'Kenneth Snelson',
                   num, contents = [mobj])	
    return scene

def setscene_eig(s, num=0):
    """
    Kenneth Snelson (and Fuller) inspired Gerald de Jong to create
    Elastic Interval Geometry (EIG), a simple concept with for exploring
    dynamic networks, where the edges resist being pushed or pulled
    from some ideal length.  In Fluidiom, Gerald let EIG networks
    randomly emerge and had his program select those best able to
    cover distance, per some supplied algorithm.

    Karl Erickson and Russell Chu were adept EIG sculptors i.e.
    Struck power users and contributed to the specimens below (Struck
    provided animations in the form of a POV-Ray clock loop).

    SpringDance and Springie came later in the story (i.e. after
    Struck).
    """
    contents = []
    
    mobj = Content()
    mobj.content_type = 'movie'
    mobj.filename = fpath + "Dolphin.mpg"
    mobj.topleft = (10,90)
    contents.append(mobj)
    
    mobj = Content()
    mobj.content_type = 'movie'
    mobj.filename = fpath + "Golem.mpg"
    mobj.topleft = (355,90)
    contents.append(mobj)
    
    mobj = Content()
    mobj.content_type = 'movie'
    mobj.filename = fpath + "IcosaFlip.mpg"
    mobj.topleft = (700,90)
    contents.append(mobj)
       
    mobj = Content()
    mobj.content_type = 'movie'
    mobj.filename = fpath + "Fishy.mpg"
    mobj.topleft = (110,400)
    contents.append(mobj)
    
    mobj = Content()
    mobj.content_type = 'movie'
    mobj.filename = fpath + "Bird.mpg"
    mobj.topleft = (510,400)
    contents.append(mobj)
    
    scene = Scene( s, 'black', 'Elastic Interval Geometry (EIG)',
                   num, hfcolor = 'yellow', contents = contents)	
    return scene

def setscene_bellivm(s, num=0):
    """
    Alexander Graham Bell pioneer the development of a space frame known as
    the octet truss, which has a close relationship with the CCP sphere
    packing.  Fuller found out about this later.
    """
    graphics = []
    contents = []
    pics = [('belltower.jpg',(150,70)),
            ('smbellbigkite.jpg',(550,70)),
            ('truss.gif',(150,450)),
            ('archforum1.jpg',(550,320))
            ]

    for pic in pics:
        mobj = Content()
        mobj.content_type = 'stillimage'
        mobj.filename = fpath + pic[0]
        mobj.topleft = pic[1]
        contents.append(mobj)
    
    scene = Scene( s, 'white', 'Octet Truss',
                   num, contents = contents )	
    return scene

def setscene_quadrays(s, num=0):
    """
    Doing this kind of tetrahedron-focused geometry led David Chako
    and others to invent tetrays or quadray geometry, a way of doing
    vector arithmetic that gives easy integer coordinates to many
    of our concentric hierarchy vertices

    Programming around Quadrays is what led me to explore some new
    (to me) languages, always reimplementing a similar design.  I
    went from FoxPro to Java, then to Python.  What hooked me about
    Python was the interactive shell, plus operator overloading,
    which FoxPro didn't support.  I continued to use Visual FoxPro
    though, in various professional programming jobs.  This was also
    time when I looked fairly seriously at Haskell, Ocaml, Scheme
    and LISP -- and J.
    """
    graphics = []
    graphics.append(fpath + "quadray.png")
    graphics.append(fpath + "rhdodeca.jpg")
    graphics.append(fpath + "povlabels.gif")
    graphics.append(fpath + "quadraycode.jpg")
    graphics.append(fpath + "hoops.gif")
    graphics.append(fpath + "javasamp.jpg")
    graphics.append(fpath + "fox.gif")
    mobj = Content()
    mobj.content_type = 'flipimage'
    mobj.imagelist = graphics	
    scene = Scene( s, 'white', 'Quadrays',
                   num, contents = [mobj] )	
    return scene

def setscene_cosmichierarchy(s, num=0):
    """
    The concentric hierarchy is a way of presenting a sequence of
    polyhedra such that their many logical relationships are made
    clear and memorable
    """
    graphics = []
    for i in range(1,11):
        pic = 'ch'+str(i)+'.gif'
        graphics.append(fpath + pic)
    graphics += list(reversed(graphics[:-1]))
    mobj = Content()
    mobj.content_type = 'autoflip'
    mobj.milliseconds = 200
    mobj.imagelist = graphics
    scene = Scene( s, 'white', 'Concentric Hierarchy',
                   num, contents = [mobj] )	
    return scene
	
def setscene_grunch(s, num=0):
    """
    Towards the end of his life, Fuller contemplated the significance
    of swift globalization, and posited that corporations were in
    a transformative position, both dissolving national boundaries
    for selfish ends, and inadvertently creating the conditions for
    a new management philosophy based on letting intellectual goods
    cooperate to greater synergetic advantage (e.g. the free software
    development model, now recognized and embraced in many tech-savvy
    board rooms).
    """
    graphics = []
    for pic in ['obey1.gif','obey2.gif']:
        graphics.append(fpath + pic)
    mobj = Content()
    mobj.content_type = 'autoflip'
    mobj.imagelist = graphics
    mobj.milliseconds = 444
    scene = Scene( s, 'white', 'Grunch of Giants',
                   num, contents = [mobj])	
    return scene

def setscene_eja(s, num=0):
    """
    Ed was one of Fuller's primary collaborators.  Ed's high level
    of discipline made it possible for Fuller to make deeper forays
    into philosophy and literature, plus Ed gave us the 'Synergetics
    Dictionary' (a four-volume compendium of Fuller's "pattern
    language").
    
    Ed also worked on the Wichita House Project (a.k.a. the DDU, or
    Dymaxion Deployment Unit).
    """
    
    contents = []
    pics = [
            ('cia.png',(600,100)),
            ('ejarbf2.jpg',(150,150)),
            ('eja.jpg',(150,450))
            ]

    for pic in pics:
        mobj = Content()
        mobj.content_type = 'stillimage'
        mobj.filename = fpath + pic[0]
        mobj.topleft = pic[1]
        contents.append(mobj)

    mobj = Content()
    mobj.content_type = 'autoflip'
    mobj.imagelist = fpath + 'eja.gif', fpath + 'june.gif'
    mobj.topleft = (400,100)
    mobj.milliseconds = 2000
    contents.append(mobj)

    mobj1 = Content()   
    mobj1.content_type = 'stilltext'
    mobj1.filename = fpath + "edbio.txt"
    mobj1.fontsize = 20
    mobj1.topleft = 250,450


    contents.append(mobj1)
  
    scene = Scene( s, 'white', 'Ed Applewhite (EJA)',
                   num, contents = contents)	
    return scene

def setscene_europix(s, num=0):
    """
    The intellectual property theme was big at Europython.  Coincidentally,
    the campus where we met features a mascot based on a Disney character,
    similar to University of Oregons's in my neck of the woods:
    http://www.goducks.com/

    Tintin is another important cartoon character in Europe, part of a
    shared heritage.  One of the lawyers present used some Tintin images
    in his slides, saying he knew the rules well enough to know that
    he was operating within them.
    """
    mobj = Content()
    mobj.content_type = 'flipimage'
    graphics = []
    for i in range(9):
        graphics.append(fpath + 'euro'+str(i) + '.jpg')
    graphics.append('prp.jpg')  # People's Repub. of Perl (frm Larry's SOTO)

    mobj.imagelist = graphics
    mobj.usercenter = (512,400)
    scene = Scene( s, 'black', 'Scholarship & IP',
                   num, hfcolor = 'yellow', contents = [mobj])
    return scene

def setscene_gst(s, num=0):
    """
    GST might put a different spin on things than Econ, i.e. is
    reflective of a newer management philosophy that doesn't begin
    from all the same premises
    """
    graphics = []
    for pic in ['gst2.gif','gst3.gif','gst1.gif']:
        graphics.append(fpath + pic)
    mobj = Content()
    mobj.content_type = 'flipimage'
    mobj.imagelist = graphics
    scene = Scene( s, 'white', 'General Systems Theory (GST)',
                   num, contents = [mobj])
    return scene
	
def setscene_eikclip(s, num=0):
    """
    Fuller talking about what he's going to be talking about.
    """
    mobj0 = Content()
    mobj0.content_type = 'movie'
    mobj0.filename = fpath + "04003_new.mpg"
    mobj0.topleft = 350,200
	
    mobj1 = Content()   
    mobj1.content_type = 'scrollfile'
    mobj1.filename = fpath + "04003_new.txt"
    mobj1.fontsize = 20
    mobj1.milliseconds = 4000
    mobj1.displaylines = 7
    mobj1.fontcolor = 'orange'
    mobj1.topleft = 325,450
    scene = Scene( s, 'black', '2 Minute EIK Archive Clip',
                   num, hfcolor = 'orange', contents = [mobj0, mobj1])
    return scene  

def setscene_ccp(s, num=0):
    """
    Demonstrates closest packing of spheres.  An easy place for
    kids to start learning Python, and 21st century math topics,
    is with sequences based on figurate and polyhedral numbers.
    Generators are good for this, and this slide features an
    object that renders generator output to screen.
    """
    graphics = []
    for i in range(1,12):
        filename = "cubanim"+str(i).zfill(2)
        graphics.append(fpath + filename + ".png" )
    mobj0 = Content()
    mobj0.content_type = 'autoflip'
    mobj0.topleft = (400,100)
    mobj0.imagelist = graphics
    mobj0.milliseconds = 100

    mobj1 = Content()   
    mobj1.content_type = 'stilltext'
    mobj1.filename = fpath + "ccp.py"
    mobj1.fontsize = 20
    mobj1.topleft = (60,200)

    mobj2 = Content()
    mobj2.content_type = 'coderunner'
    mobj2.usercenter = (515,350)   
    def ccp():
        shell, accum = 1,1
        yield (shell, accum)
        f = 1
        while True:
            shell = 10*f*f + 2
            f += 1
            accum += shell
            yield shell,accum
    mobj2.generator = ccp
    mobj2.milliseconds = 1000

    mobj3 = Content()
    mobj3.content_type = 'autoflip'
    mobj3.topleft = (350,400)
    mobj3.imagelist = [fpath + 'smbellbigkite.jpg', fpath + 'vesphere.jpg',
                       fpath + 'bellivm.jpg']
    mobj3.milliseconds = 1000

    mobj4 = Content()
    mobj4.content_type = 'stillimage'
    mobj4.topleft = (750,200)
    mobj4.filename = fpath + 'tetrapack.gif'
        
    scene = Scene( s, 'white', 'Closest Packing of Spheres',
                   num, contents = [mobj2, mobj0, mobj1, mobj3, mobj4])	
    return scene

def main(startslide, num=0):
    s = pygame.display.set_mode((1024,768), pygame.FULLSCREEN)
    pygame.mouse.set_visible(0)    
    snd = pygame.mixer.Sound(fpath + "whoosh.wav")
    soundon = True

    # the code below serves as a sorter tray

    thescenes = enumerate([
    setscene_welcome,
    setscene_rbf,
    setscene_collab,
    setscene_rywalt,
    setscene_metoo,
    setscene_wanderers,
    setscene_dome,
    setscene_bonoballs,
    setscene_snelson,
    setscene_eig,
    setscene_bellivm,
    setscene_ccp,
    setscene_waterman1,
    setscene_waterman2,
    setscene_quadrays,
    setscene_cosmichierarchy,
    setscene_grunch,
    setscene_eja,    
    setscene_europix,
    setscene_gst,
    setscene_eikclip,
    setscene_thankyou
    ])	
    # slides have now been sorted...

    
    scenes = []
    for i, f in thescenes:
        scenes.append(f(s,i))
        
    snum = int(startslide)
    currscene = scenes[snum]
    currscene.display()
    if currscene.contents:
        currscene.run()

    pygame.display.update()
    newscene = False

    pygame.event.set_blocked(KEYUP)

    while 1:
        nextkey = pygame.event.wait()
        if not hasattr(nextkey, "key"):
            continue

        if nextkey.key == K_ESCAPE or pygame.event.peek(QUIT):
            print "Escape!"
            currscene.end()
            break

        if nextkey.key == K_r: # Rewind!

            if currscene.contents:
                for r in currscene.resources:
                    r.restart()

        if nextkey.key == K_p: # Pause!

            if currscene.contents:
                for r in currscene.resources:
                    r.pause()

        if nextkey.key == K_u: # Unpause!

            if currscene.contents:
                for r in currscene.resources:
                    r.unpause()
                    
        if nextkey.key == K_x: # neXt!

            # relevant to Flipimages

            if currscene.contents:
                for r in currscene.resources:
                    r.next()

        if nextkey.key == K_v: # preVious! 

            # relevant to Flipimages

            if currscene.contents:
                for r in currscene.resources:
                    r.prev()

        if nextkey.key == K_RIGHT: # next scene

            snum += 1	   
            if snum == len(scenes):
                snum = 0
            newscene = True

        if nextkey.key == K_LEFT: # previous scene	   

            snum -= 1
            if snum == -1:
                snum = len(scenes)-1
            newscene = True

        if newscene:
            currscene.end()
            # newscene sound            

            if soundon:
                if pygame.mixer.get_init():
                    snd.play()            
            print snum
            currscene = scenes[snum]
            currscene.display()
            # initialize content (whether in its own thread or in this event loop)

            if currscene.contents:
                currscene.run()
            newscene = False

        pygame.display.update()

    pygame.quit()
    
if __name__ == '__main__':
    if len(sys.argv)>1:
	startslide = sys.argv[1]
    else:
	startslide = 0
    main(startslide)
    
# code highlighted using py2html.py version 0.8