Browse Source

*** empty log message ***

Mark Mine 25 years ago
parent
commit
ae843d2401

+ 21 - 18
direct/src/interval/AnimInterval.py

@@ -4,43 +4,46 @@ from PandaModules import *
 from Interval import *
 
 class AnimInterval(Interval):
-
+    # Name counter
     animNum = 1
-
-    # special methods
-    
+    # Class methods
     def __init__(self, animControl, loop=0, name=None):
         """__init__(name)
         """
+        # Record class specific variables
 	self.animControl = animControl
-	duration = (float(animControl.getNumFrames()) / 
-			animControl.getFrameRate())
 	self.loop = loop
+        # Generate unique name if necessary
 	if (name == None):
-	    n = 'Anim-%d' % self.animNum
-	    self.animNum = self.animNum + 1
-	else:
-	    n = name
-	Interval.__init__(self, n, duration)
+	    name = 'Anim-%d' % AnimInterval.animNum
+	    AnimInterval.animNum += 1
+        # Compute anim duration
+	duration = (float(animControl.getNumFrames()) /
+			animControl.getFrameRate())
+        # Initialize superclass
+	Interval.__init__(self, name, duration)
 
-    def setT(self, t, entry=0):
-	""" setT(t, entry)
+    def updateFunc(self, t, event = IVAL_NONE):
+	""" updateFunc(t, event)
 	    Go to time t
 	"""
-	if (t < 0):
-	    return
-	elif (t > self.duration):
+        # Update animation based upon current time
+        if (t == self.getDuration()):
 	    if (self.isPlaying == 1):
 		self.isPlaying = 0
 		if (self.loop):
 		    self.animControl.stop()
-	    return
-	elif (entry == 1):
+        else:
+            # Set flag
 	    self.isPlaying = 1
 	    # Determine the current frame
 	    frame = int(self.animControl.getFrameRate() * t)
+            # Pose anim
 	    if (self.loop):
 		self.animControl.pos(frame)	
 		self.animControl.loop(0)
 	    else:
 		self.animControl.play(frame, self.animControl.getNumFrames())
+
+
+

+ 22 - 23
direct/src/interval/FunctionInterval.py

@@ -5,33 +5,28 @@ from Interval import *
 from MessengerGlobal import *
 
 class FunctionInterval(Interval):
-
+    # Name counter
     functionIntervalNum = 1
-    
-    # special methods
-    def __init__(self, function, name = None):
+    # Class methods
+    def __init__(self, function, name = None, openEnded = 1):
         """__init__(function, name = None)
         """
-	duration = 0.0
-	self.prevt = 0.0
+        # Record instance variables
 	self.function = function
-        
+        # Create a unique name for the interval if necessary
 	if (name == None):
 	    name = 'FunctionInterval-%d' % FunctionInterval.functionIntervalNum
 	    FunctionInterval.functionIntervalNum += 1
-
-	Interval.__init__(self, name, duration)
-
-    def setT(self, t, entry=0):
-	""" setT(t, entry)
+        # Initialize superclass
+        # Set openEnded true if calls after end time cause interval
+        # function to be called
+	Interval.__init__(self, name, duration = 0.0, openEnded = openEnded)
+    def updateFunc(self, t, event = IVAL_NONE):
+	""" updateFunc(t, event)
 	    Go to time t
 	"""
-	if (t < 0):
-	    self.prevt = t
-	    return
-	elif (t == 0) or (self.prevt < 0):
-	    self.function()
-	    self.prevt = 0.0
+        # Evaluate the function
+        self.function()
 
 ### FunctionInterval subclass for throwing events ###
 class EventInterval(FunctionInterval):
@@ -42,7 +37,8 @@ class EventInterval(FunctionInterval):
         def sendFunc(event = event, sentArgs = sentArgs):
             messenger.send(event, sentArgs)
         # Create function interval
-	FunctionInterval.__init__(self, sendFunc, name = event)
+	FunctionInterval.__init__(self, sendFunc, name = event,
+                                  openEnded = 0)
 
 ### FunctionInterval subclass for accepting hooks ###
 class AcceptInterval(FunctionInterval):
@@ -54,7 +50,8 @@ class AcceptInterval(FunctionInterval):
             print "accepting..."
             dirObj.accept(event, function)
         # Create function interval
-	FunctionInterval.__init__(self, acceptFunc, name = name)
+	FunctionInterval.__init__(self, acceptFunc, name = name,
+                                  openEnded = 0)
 
 ### FunctionInterval subclass for throwing events ###
 class IgnoreInterval(FunctionInterval):
@@ -66,7 +63,8 @@ class IgnoreInterval(FunctionInterval):
             print "ignoring..."
             dirObj.ignore(event)
         # Create function interval
-	FunctionInterval.__init__(self, ignoreFunc, name = name)
+	FunctionInterval.__init__(self, ignoreFunc, name = name,
+                                  openEnded = 0)
 
 ### Function Interval subclass for adjusting scene graph hierarchy ###
 class ParentInterval(FunctionInterval):
@@ -97,7 +95,8 @@ class WrtParentInterval(FunctionInterval):
             nodePath.wrtReparentTo(parent)
         # Determine name
 	if (name == None):
-	    name = 'WrtParentInterval-%d' % WrtParentInterval.wrtParentIntervalNum
+	    name = ('WrtParentInterval-%d' %
+                    WrtParentInterval.wrtParentIntervalNum)
 	    WrtParentInterval.wrtParentIntervalNum += 1
         # Create function interval
 	FunctionInterval.__init__(self, wrtReparentFunc, name = name)
@@ -210,7 +209,6 @@ class PosHprScaleInterval(FunctionInterval):
         # Create function interval
         FunctionInterval.__init__(self, posHprScaleFunc, name = name)
 
-
 """
 SAMPLE CODE
 from IntervalGlobal import *
@@ -226,6 +224,7 @@ def printHello():
 i3 = FunctionInterval(printHello)
 # Create track
 t1 = Track([(0.0, i1), (2.0, i2), (4.0, i3)], name = 'demo')
+
 # Play track
 t1.play()
 

+ 84 - 15
direct/src/interval/Interval.py

@@ -4,6 +4,10 @@ from DirectObject import *
 import ClockObject
 import Task
 
+# Interval events
+IVAL_NONE = 0
+IVAL_INIT = 1
+
 class Interval(DirectObject):
     """Interval class: Base class for timeline functionality"""
 
@@ -13,13 +17,17 @@ class Interval(DirectObject):
 
     # special methods
     
-    def __init__(self, name, duration):
+    def __init__(self, name, duration, openEnded = 1):
         """__init__(name, duration)
         """
 	self.name = name
 	self.duration = duration
 	self.clock = ClockObject.ClockObject.getGlobalClock()
 	self.curr_t = 0.0
+	self.prev_t = 0.0
+        self.setTHooks = []
+        # Set true if interval responds to setT(t): t>duration
+        self.fOpenEnded = openEnded
 
     def getName(self):
 	""" getName()
@@ -31,16 +39,40 @@ class Interval(DirectObject):
 	"""
 	return self.duration
 
-    def setT(self, t, entry=0):
-	""" setT(t, entry)
+    def setfOpenEnded(self, openEnded):
+        """ setfOpenEnded(openEnded)
+        """
+        self.fOpenEnded = openEnded
+
+    def getfOpenEnded(self):
+        """ getfOpenEnded()
+        """
+        return self.fOpenEnded
+
+    def setT(self, t, event = IVAL_NONE):
+	""" setT(t, event)
 	    Go to time t
 	"""
-	pass
+        # Update current time
+	self.curr_t = t
+        # Perform interval actions
+        self.updateFunc(t, event)
+        # Call setT Hook
+        for func in self.setTHooks:
+            func(t)
+        # Record t for next time around
+        self.prev_t = self.curr_t
+
+    def updateFunc(self, t, event = IVAL_NONE):
+        pass
+
+    def setTHook(self, t):
+        pass
 
     def setFinalT(self):
 	""" setFinalT()
 	"""
-	self.setT(self.getDuration(), entry=1)
+	self.setT(self.getDuration(), event=IVAL_NONE)
 
     def play(self, t0=0.0, duration=0.0, scale=1.0):
         """ play(t0, duration)
@@ -51,10 +83,10 @@ class Interval(DirectObject):
 	self.scale = scale
 	self.firstTime = 1
         if (duration == 0.0):
-            self.playDuration = self.duration
+            self.endTime = self.offset + self.duration
         else:
-            self.playDuration = duration
-	assert(t0 <= self.playDuration)
+            self.endTime = self.offset + duration
+	assert(t0 <= self.endTime)
         taskMgr.spawnMethodNamed(self.__playTask, self.name + '-play')
 
     def stop(self):
@@ -68,16 +100,15 @@ class Interval(DirectObject):
         """
         t = self.clock.getFrameTime()
         te = self.offset + ((t - self.startT) * self.scale)
-	self.curr_t = te
-        if (te <= self.playDuration):
+        if (te <= self.endTime):
 	    if (self.firstTime):
-		self.setT(te, entry=1)
+		self.setT(te, event = IVAL_INIT)
 		self.firstTime = 0
 	    else:
             	self.setT(te)
             return Task.cont
         else:
-	    self.setT(self.playDuration)
+	    self.setT(self.endTime)
             return Task.done
 
     def __repr__(self, indent=0):
@@ -86,16 +117,54 @@ class Interval(DirectObject):
 	space = ''
 	for l in range(indent):
 	    space = space + ' '
-	return (space + self.name + ' dur: %.2f\n' % self.duration)
+	return (space + self.name + ' dur: %.2f' % self.duration)
 
     def popupControls(self):
+        import fpformat
+        import string
         # I moved this here because Toontown does not ship Tk
         from Tkinter import *
         import Pmw
         import EntryScale
         tl = Toplevel()
+        tl.title(self.getName() + ' Interval Controls')
         es = EntryScale.EntryScale(
-            tl, min = 0, max = self.duration,
+            tl, text = 'Time',
+            min = 0, max = string.atof(fpformat.fix(self.duration, 2)),
             command = lambda t, s = self: s.setT(t))
-        es.onPress = lambda s=self: s.setT(s.curr_t, entry = 1)
+        es.onRelease = lambda s=self, es = es: s.setT(es.get(),
+                                                      event = IVAL_INIT)
+        es.onReturnRelease = lambda s=self, es = es: s.setT(es.get(),
+                                                            event = IVAL_INIT)
         es.pack(expand = 1, fill = X)
+        f = Frame(tl)
+        def toStart(s=self, es=es):
+            s.stop()
+            s.setT(0.0, event = IVAL_INIT)
+        def toEnd(s=self):
+            s.stop()
+            s.setT(s.getDuration(), event = IVAL_INIT)
+        jumpToStart = Button(tl, text = '<<', command = toStart)
+        stop = Button(tl, text = 'Stop',
+                      command = lambda s=self: s.stop())
+        play = Button(
+            tl, text = 'Play',
+            command = lambda s=self, es=es: s.play(es.get()))
+        jumpToEnd = Button(tl, text = '>>', command = toEnd)
+        jumpToStart.pack(side = LEFT, expand = 1, fill = X)
+        play.pack(side = LEFT, expand = 1, fill = X)
+        stop.pack(side = LEFT, expand = 1, fill = X)
+        jumpToEnd.pack(side = LEFT, expand = 1, fill = X)
+        f.pack(expand = 1, fill = X)
+        # Add hook to update slider on changes in curr_t
+        def update(t,es=es):
+            es.set(t, fCommand = 0)
+        self.setTHooks.append(update)
+        # Clear out hook on destroy
+        def onDestroy(e, s=self, u=update):
+            if u in s.setTHooks:
+                s.setTHooks.remove(u)
+        tl.bind('<Destroy>', onDestroy)
+
+        
+        

+ 1 - 1
direct/src/interval/IntervalTest.py

@@ -50,7 +50,7 @@ waterEventTrack.setIntervalStartTime('water-is-done', eventTime)
 
 mtrack = MultiTrack([boatTrack, dockTrack, soundTrack, waterEventTrack])
 # Print out MultiTrack parameters
-mtrack
+print(mtrack)
 
 def handleWaterDone():
     print 'water is done'

+ 44 - 51
direct/src/interval/LerpInterval.py

@@ -9,21 +9,20 @@ class LerpInterval(Interval):
     def __init__(self, name, duration, functorFunc, blendType='noBlend'):
         """__init__(name, duration, functorFunc, blendType)
         """
+        self.lerp = None
 	self.functorFunc = functorFunc
 	self.blendType = self.getBlend(blendType)
 	Interval.__init__(self, name, duration)
-    def setT(self, t, entry=0):
-	""" setT(t, entry)
+    def updateFunc(self, t, event = IVAL_NONE):
+	""" updateFunc(t, event)
 	"""
-	if (t < 0):
-	    return
-	elif (entry == 1):
+        # First check to see if we need to create the lerp
+	if (event == IVAL_INIT):
 	    self.lerp = Lerp.Lerp(self.functorFunc(), self.duration, 
-						self.blendType)
-	if (entry == 1) and (t > self.duration):
-	    self.lerp.setT(self.duration)
-	elif (t <= self.duration):
-	    self.lerp.setT(t)
+                                  self.blendType)
+        # Now evaluate the lerp
+        if self.lerp:
+            self.lerp.setT(t)
     def getBlend(self, blendType):
         """__getBlend(self, string)
         Return the C++ blend class corresponding to blendType string
@@ -42,9 +41,9 @@ class LerpInterval(Interval):
 		'Error: LerpInterval.__getBlend: Unknown blend type')
 
 class LerpPosInterval(LerpInterval):
-
+    # Name counter
     lerpPosNum = 1
-
+    # Class methods
     def __init__(self, node, duration, pos, startPos=None,
 				other=None, blendType='noBlend', name=None):
 	""" __init__(node, duration, pos, startPos, other, blendType, name)
@@ -66,18 +65,15 @@ class LerpPosInterval(LerpInterval):
             	functor = PosLerpFunctor.PosLerpFunctor(
                     node, startPos, pos)
 	    return functor
-
+        # Generate unique name if necessary
 	if (name == None):
-	    n = 'LerpPosInterval-%d' % LerpPosInterval.lerpPosNum
+	    name = 'LerpPosInterval-%d' % LerpPosInterval.lerpPosNum
 	    LerpPosInterval.lerpPosNum += 1
-	else:
-	    n = name
-
-	LerpInterval.__init__(self, n, duration, functorFunc, blendType) 
+        # Initialize superclass
+	LerpInterval.__init__(self, name, duration, functorFunc, blendType) 
 
 class LerpHprInterval(LerpInterval):
-
-    # Interval counter
+    # Name counter
     lerpHprNum = 1
     # Class methods
     def __init__(self, node, duration, hpr, startHpr=None,
@@ -101,14 +97,12 @@ class LerpHprInterval(LerpInterval):
             	functor = HprLerpFunctor.HprLerpFunctor(
                     node, startHpr, hpr)
 	    return functor
-
+        # Generate unique name if necessary
 	if (name == None):
-	    n = 'LerpHprInterval-%d' % LerpHprInterval.lerpHprNum
+	    name = 'LerpHprInterval-%d' % LerpHprInterval.lerpHprNum
 	    LerpHprInterval.lerpHprNum += 1
-	else:
-	    n = name
-
-	LerpInterval.__init__(self, n, duration, functorFunc, blendType) 
+        # Initialize superclass
+	LerpInterval.__init__(self, name, duration, functorFunc, blendType) 
 
 class LerpScaleInterval(LerpInterval):
 
@@ -137,13 +131,12 @@ class LerpScaleInterval(LerpInterval):
                     node, startScale, scale)
 	    return functor
 
+        # Generate unique name if necessary
 	if (name == None):
-	    n = 'LerpScaleInterval-%d' % LerpScaleInterval.lerpScaleNum
+	    name = 'LerpScaleInterval-%d' % LerpScaleInterval.lerpScaleNum
 	    LerpScaleInterval.lerpScaleNum += 1
-	else:
-	    n = name
-
-	LerpInterval.__init__(self, n, duration, functorFunc, blendType) 
+        # Initialize superclass
+	LerpInterval.__init__(self, name, duration, functorFunc, blendType) 
 
 class LerpPosHprInterval(LerpInterval):
     # Interval counter
@@ -179,13 +172,12 @@ class LerpPosHprInterval(LerpInterval):
                     startHpr, hpr)
 	    return functor
 
+        # Generate unique name if necessary
 	if (name == None):
-	    n = 'LerpPosHpr-%d' % LerpPosHprInterval.lerpPosHprNum
+	    name = 'LerpPosHpr-%d' % LerpPosHprInterval.lerpPosHprNum
 	    LerpPosHprInterval.lerpPosHprNum += 1
-	else:
-	    n = name
-
-	LerpInterval.__init__(self, n, duration, functorFunc, blendType)
+        # Initialize superclass
+	LerpInterval.__init__(self, name, duration, functorFunc, blendType)
 
 class LerpPosHprScaleInterval(LerpInterval):
     # Interval counter
@@ -226,14 +218,13 @@ class LerpPosHprScaleInterval(LerpInterval):
                     node, startPos, pos, startHpr, hpr, startScale, scale)
 	    return functor
 
+        # Generate unique name if necessary
 	if (name == None):
-	    n = ('LerpPosHprScale-%d' %
-                 LerpPosHprScaleInterval.lerpPosHprScaleNum)
+	    name = ('LerpPosHprScale-%d' %
+                    LerpPosHprScaleInterval.lerpPosHprScaleNum)
 	    LerpPosHprScaleInterval.lerpPosHprScaleNum += 1
-	else:
-	    n = name
-
-	LerpInterval.__init__(self, n, duration, functorFunc, blendType)
+        # Initialize superclass
+	LerpInterval.__init__(self, name, duration, functorFunc, blendType)
 
 # Class used to execute a function over time.  Function can access fromData
 # and toData to perform blend
@@ -245,27 +236,29 @@ class LerpFunctionInterval(Interval):
                  blendType = 'noBlend', name = None):
         """__init__(function, duration, fromData, toData, name)
         """
+        # Record instance variables
 	self.function = function
         self.fromData = fromData
         self.toData = toData
 	self.blendType = self.getBlend(blendType)
+        # Generate unique name if necessary
 	if (name == None):
 	    name = ('LerpFunctionInterval-%d' %
                     LerpFunctionInterval.lerpFunctionIntervalNum)
             LerpFunctionInterval.lerpFunctionIntervalNum += 1
         # Initialize superclass
 	Interval.__init__(self, name, duration)
-    def setT(self, t, entry=0):
-	""" setT(t, entry)
+    def updateFunc(self, t, event = IVAL_NONE):
+	""" updateFunc(t, event)
 	"""
-	if (t < 0):
-	    return
-	if (entry == 1) and (t > self.duration):
-	    self.function(self.toData)
-	elif (t <= self.duration):
+        # Evaluate the function
+        if (t == self.duration):
+            # Set to end value
+	    self.function(self.toData)            
+        else:
+            # In the middle of the lerp, compute appropriate value
             try:
-                #bt = self.blendType(t/self.duration)
-                bt = t/self.duration
+                bt = self.blendType(t/self.duration)
                 data = (self.fromData * (1 - bt)) + (self.toData * bt)
                 self.function(data)
             except ZeroDivisionError:

+ 13 - 19
direct/src/interval/MopathInterval.py

@@ -5,31 +5,25 @@ from Interval import *
 import Mopath
 
 class MopathInterval(Interval):
-
+    # Name counter
     mopathNum = 1
-
-    # special methods
-    
+    # Class methods
     def __init__(self, mopath, node, name=None):
         """__init__(mopath, node, name)
         """
-	self.node = node	
 	self.mopath = mopath 
-	duration = self.mopath.getMaxT()
+	self.node = node
+        # Generate unique name if necessary
 	if (name == None):
-	    n = 'Mopath-%d' % self.mopathNum
-	    self.mopathNum = self.mopathNum + 1
-	else:
-	    n = name
-	Interval.__init__(self, n, duration)
+	    name = 'Mopath-%d' % MopathInterval.mopathNum
+	    MopathInterval.mopathNum += 1
+        # Compute duration
+	duration = self.mopath.getMaxT()
+        # Initialize superclass
+	Interval.__init__(self, name, duration)
 
-    def setT(self, t, entry=0):
-	""" setT(t, entry)
+    def updateFunc(self, t, event = IVAL_NONE):
+	""" updateFunc(t, event)
 	    Go to time t
 	"""
-	if (t < 0):
-	    return
-	elif (entry == 1) and (t > self.duration):
-	    self.mopath.goTo(self.node, self.duration)
-	else:
-	    self.mopath.goTo(self.node, t)
+        self.mopath.goTo(self.node, t)

+ 4 - 6
direct/src/interval/MultiTrack.py

@@ -35,19 +35,17 @@ class MultiTrack(Interval):
 		duration = dur
 	return duration
 
-    def setT(self, t, entry=0):
-	""" setT(t, entry)
+    def updateFunc(self, t, event = IVAL_NONE):
+	""" updateFunc(t, event)
 	    Go to time t
 	"""
-	if (t > self.duration):
-            pass
 	for track in self.tlist:
-	    track.setT(t, entry)
+	    track.setT(min(t, track.getDuration()), event)
 
     def __repr__(self, indent=0):
 	""" __repr__(indent)
 	"""
-	str = Interval.__repr__(self, indent)
+	str = Interval.__repr__(self, indent) + '\n'
 	for t in self.tlist:
 	    str = str + t.__repr__(indent+1)
         return str

+ 24 - 25
direct/src/interval/SoundInterval.py

@@ -4,43 +4,42 @@ from PandaModules import *
 from Interval import *
 
 class SoundInterval(Interval):
-
+    # Name counter
     soundNum = 1
-
-    # special methods
-    
+    # Class methods
     def __init__(self, sound, loop=0, name=None):
         """__init__(sound, loop, name)
         """
 	self.sound = sound
-	duration = self.sound.length() 
 	self.loop = loop
 	self.isPlaying = 0
+        # Generate unique name if necessary
 	if (name == None):
-	    n = 'Sound-%d' % self.soundNum
-	    self.soundNum = self.soundNum + 1
-	else:
-	    n = name
-	self.prevt = 0.0
-	Interval.__init__(self, n, duration)
+	    name = 'Sound-%d' % SoundInterval.soundNum
+	    SoundInterval.soundNum += 1
+        # Calculate duration
+	duration = self.sound.length()
+        # Initialize superclass
+	Interval.__init__(self, name, duration)
 
-    def setT(self, t, entry=0):
-	""" setT(t, entry)
-	    Go to time t
+    def updateFunc(self, t, event = IVAL_NONE):
+	""" updateFunc(t, event)
+        Go to time t
 	"""
-	if (t < 0):
-	    self.prevt = t
-	    return
-	elif (t > self.duration):
+        if(t == self.duration):
+            # Stop sound if necessary
 	    if (self.isPlaying == 1):
-		AudioManager.stop(self.sound)
 		self.isPlaying = 0
-	    return
-	elif (entry == 1):
+		AudioManager.stop(self.sound)
+        elif (event == IVAL_INIT):
+            # Set flag
 	    self.isPlaying = 1
-	    if (self.prevt < 0.0):
-	    	AudioManager.play(self.sound)
-	    else:
-		AudioManager.play(self.sound, t)
+            # If its within a 20th of a second of the start,
+            # start at the beginning
+            if (t < 0.05):
+                t = 0.0
+            # Start sound
+            AudioManager.play(self.sound, t)
+            # Loop in necessary
 	    if (self.loop):
 		AudioManager.setLoop(self.sound, 1)

+ 117 - 73
direct/src/interval/Track.py

@@ -8,33 +8,39 @@ PREVIOUS_END = 1
 PREVIOUS_START = 2
 TRACK_START = 3
 
-class Track(Interval):
+IDATA_IVAL = 0
+IDATA_TIME = 1
+IDATA_TYPE = 2
+IDATA_START = 3
+IDATA_END = 4
 
+class Track(Interval):
+    # Name counter
     trackNum = 1
-
-    # special methods
-    
+    # Class methods
     def __init__(self, intervalList, name=None):
         """__init__(intervalList, name)
         """
-	if (name == None):
-	    n = 'Track-%d' % Track.trackNum
-	    Track.trackNum = Track.trackNum + 1
-	else:
-	    n = name
+        # Record instance variables
 	self.__buildIlist(intervalList)
-	duration = self.__computeDuration(len(self.ilist))
 	self.currentInterval = None
-	Interval.__init__(self, n, duration)
+        # Generate unique name if necessary
+	if (name == None):
+	    name = 'Track-%d' % Track.trackNum
+	    Track.trackNum = Track.trackNum + 1
+        # Compute duration
+	duration = self.__computeDuration()
+        # Initialize superclass
+	Interval.__init__(self, name, duration)
 
     def __getitem__(self, item):
-        return self.ilist[item][0]
+        return self.ilist[item]
 
     def __buildIlist(self, intervalList):
 	self.ilist = []
 	for i in intervalList:
             if isinstance(i, Interval):
-                self.ilist.append((i, 0.0, PREVIOUS_END))
+                self.ilist.append([i, 0.0, PREVIOUS_END, 0.0, 0.0])
             elif (isinstance(i, types.ListType) or
                   isinstance(i, types.TupleType)):
                 t0 = i[0]
@@ -43,21 +49,22 @@ class Track(Interval):
                     type = i[2]
                 except IndexError:
                     type = TRACK_START
-                self.ilist.append((ival, t0, type))
+                self.ilist.append([ival, t0, type, 0.0, 0.0])
             else:
                 print 'Track.__buildIlist: Invalid intervallist entry'
 
-    def __computeDuration(self, length):
-	""" __computeDuration(length)
+    def __computeDuration(self):
+	""" __computeDuration()
 	"""
-	assert(length <= len(self.ilist))
 	duration = 0.0
 	prev = None
-	for i in self.ilist[0:length]:
-	    ival = i[0]
-	    t0 = i[1]
-	    type = i[2]
+        for idata in self.ilist:
+	    ival = idata[IDATA_IVAL]
+	    t0 = idata[IDATA_TIME]
+	    type = idata[IDATA_TYPE]
 	    assert(t0 >= 0.0)
+            # Compute fill time, time between end of last interval and
+            # start of this one
 	    fillTime = t0 
 	    if (type == PREVIOUS_END):
                 pass
@@ -72,23 +79,27 @@ class Track(Interval):
 	    if (fillTime < 0.0):
 		Interval.notify.error(
 			'Track.__computeDuration(): overlap detected')
-	    duration = duration + fillTime + ival.getDuration()
+            # Compute start time of interval
+            idata[IDATA_START] = duration + fillTime
+            # Compute end time of interval
+            idata[IDATA_END] = idata[IDATA_START] + ival.getDuration()
+            # Keep track of current duration
+	    duration = idata[IDATA_END]
 	    prev = ival
 	return duration
 
     def setIntervalStartTime(self, name, t0, type=TRACK_START):
 	""" setIntervalStartTime(name, t0, type)
 	"""
-	length = len(self.ilist)
 	found = 0
-	for i in range(length):
-	    if (self.ilist[i][0].getName() == name):
-		newi = (self.ilist[i][0], t0, type)	
-		self.ilist[i] = newi
+        for idata in self.ilist:
+	    if (idata[IDATA_IVAL].getName() == name):
+                idata[IDATA_TIME] = t0
+                idata[IDATA_TYPE] = type
 		found = 1
-		break;
+		break
 	if (found):
-	    self.duration = self.__computeDuration(length)	
+	    self.duration = self.__computeDuration()	
 	else:
 	    Interval.notify.warning(
 		'Track.setIntervalStartTime(): no Interval named: %s' % name)
@@ -96,71 +107,104 @@ class Track(Interval):
     def getIntervalStartTime(self, name):
 	""" getIntervalStartTime(name)
 	"""
-	for i in range(len(self.ilist)):
-	    if (self.ilist[i][0].getName() == name):	
-		return (self.__computeDuration(i+1) - 
-				self.ilist[i][0].getDuration())
+	for idata in self.ilist:
+	    if (idata[IDATA_IVAL].getName() == name):
+		return idata[IDATA_START]
 	Interval.notify.warning(
 		'Track.getIntervalStartTime(): no Interval named: %s' % name)
-	return 0.0
+	return None
 
     def __getIntervalStartTime(self, interval):
 	""" __getIntervalStartTime(interval)
 	"""
-	for i in range(len(self.ilist)):
-	    if (self.ilist[i][0] == interval):
-		return (self.__computeDuration(i+1) - interval.getDuration())
+	for idata in self.ilist:
+	    if (idata[IDATA_IVAL] == interval):
+		return idata[IDATA_START]
 	Interval.notify.warning(
 		'Track.getIntervalStartTime(): Interval not found')
-	return 0.0
+	return None
 
     def getIntervalEndTime(self, name):
 	""" getIntervalEndTime(name)
 	"""
-	for i in range(len(self.ilist)):
-	    if (self.ilist[i][0].getName() == name):	
-		return self.__computeDuration(i+1)
+	for idata in self.ilist:
+	    if (idata[IDATA_IVAL].getName() == name):	
+		return idata[IDATA_END]
 	Interval.notify.warning(
 		'Track.getIntervalEndTime(): no Interval named: %s' % name)
-	return 0.0
+	return None
 
-    def setT(self, t, entry=0):
-	""" setT(t, entry)
+    def updateFunc(self, t, event = IVAL_NONE):
+	""" updateFunc(t, event)
 	    Go to time t
 	"""
-	if (t < 0):
-	    return
-	elif (entry == 1):
-	    self.currentInterval = None
-	if (len(self.ilist) == 0):
-	    Interval.notify.warning('Track.setT(): track has no intervals')
+        # Make sure track actually contains some intervals
+        if not self.ilist:
+	    Interval.notify.warning(
+                'Track.updateFunc(): track has no intervals')
 	    return
-	elif (entry == 1) and (t > self.duration):
-	    # Anything beyond the end of the track is assumed to be the 
-	    # final state of the last Interval on the track
-	    self.ilist[len(self.ilist)-1][0].setT(t, entry=1)
+        # Deterimine which interval, if any to evaluate
+	if (t < 0):
+            # Before start of track, do nothing
+	    pass
 	else:
-	    for i in self.ilist:
-		# Calculate the track relative start time for the interval
-		ival = i[0]
-		t0 = self.__getIntervalStartTime(ival)
-
-		# Calculate the interval-relative time value for t
-		tc = t - t0
-
-		# There can only be one interval active at any given time
-		# per track, so see if we've crossed over
-		if ((t0 <= t) and (t <= t0 + ival.getDuration()) and
-		    (self.currentInterval != ival)):
-		    ival.setT(tc, entry=1)
-		    self.currentInterval = ival
-		else:
-		    ival.setT(tc)
+            # Initialize local variables
+            currentInterval = None
+            # First entry, re-init instance variables
+            if (event == IVAL_INIT):
+                # Initialize prev_t to max t of track
+                self.prev_t = self.getDuration()
+                # Clear record of currentInterval
+                self.currentInterval = None
+            # Compare t with start and end of each interval to determine
+            # which interval(s) to execute.
+            # If t falls between the start and end of an interval, that
+            # becomes the current interval.  If we've crossed over the end
+            # of an interval ((prev_t < tEnd) and (t > tEnd)) then execute
+            # that interval at its final value.  If we've crossed over the
+            # start of an interval ((prev_t > tStart) and (t < tStart))
+            # then execute that interval at its start value
+	    for ival, itime, itype, tStart, tEnd in self.ilist:
+                # Compare time with ival's start/end times
+                if (t < tStart):
+                    if self.prev_t > tStart:
+                        # We just crossed the start of this interval
+                        # going backwards (e.g. via the slider)
+                        # Execute this interval at its start time
+                        ival.setT(0.0)
+                    # Done checking intervals
+                    break
+                elif (t >= tStart) and (t <= tEnd):
+                    # Between start/end, record current interval
+                    currentInterval = ival
+                    # Make sure event == IVAL_INIT if entering new interval
+                    if ((self.prev_t < tStart) or
+                        (ival != self.currentInterval)):
+                        event = IVAL_INIT
+                    # Evaluate interval at interval relative time
+                    currentInterval.setT(t - tStart, event)
+                    # Done checking intervals
+                    break
+                elif (t > tEnd):
+                    # Crossing over interval end 
+                    if ((self.prev_t < tEnd) or
+                        ((event == IVAL_INIT) and ival.getfOpenEnded())):
+                        # If we've just crossed the end of this interval
+                        # or its an INIT event after the interval's end
+                        # and the interval is openended,
+                        # then execute the interval at its end time
+                        ival.setT(ival.getDuration())
+                     # May not be the last, keep checking other intervals
+            # Record current interval (may be None)
+            self.currentInterval = currentInterval
 
     def __repr__(self, indent=0):
 	""" __repr__(indent)
 	"""
-	str = Interval.__repr__(self, indent)
-	for i in self.ilist:	
-	    str = str + i[0].__repr__(indent+1)
+	str = Interval.__repr__(self, indent) + '\n'
+	for idata in self.ilist:	
+	    str = (str + idata[IDATA_IVAL].__repr__(indent+1) +
+                   (' start: %0.2f end: %0.2f' %
+                    (idata[IDATA_START], idata[IDATA_END])) + '\n'
+                   )
         return str

+ 7 - 9
direct/src/interval/WaitInterval.py

@@ -4,17 +4,15 @@ from PandaModules import *
 from Interval import *
 
 class WaitInterval(Interval):
-
+    # Name counter
     waitNum = 1
-
-    # special methods
-    
+    # Class methods
     def __init__(self, duration, name=None):
         """__init__(duration, name)
         """
+        # Generate unique name if necessary
 	if (name == None):
-	    n = 'Wait-%d' % self.waitNum
-	    self.waitNum = self.waitNum + 1
-	else:
-	    n = name
-	Interval.__init__(self, n, duration)
+	    name = 'Wait-%d' % WaitInterval.waitNum
+	    WaitInterval.waitNum += 1
+        # Initialize superclass
+	Interval.__init__(self, name, duration)