oops, omitted file

This commit is contained in:
David Rose 2002-08-29 17:24:13 +00:00
parent da03dcffc4
commit 7eedaffd4b

View File

@ -0,0 +1,374 @@
from PandaModules import *
from DirectNotifyGlobal import *
import Interval
import Task
import types
PREVIOUS_END = CMetaInterval.RSPreviousEnd
PREVIOUS_START = CMetaInterval.RSPreviousBegin
TRACK_START = CMetaInterval.RSLevelBegin
class MetaInterval(CMetaInterval):
# This is a Python-C++ hybrid class. MetaInterval is a Python
# extension of the C++ class CMetaInterval, which adds some
# Python-specific features (like list management).
# This is the base class of Sequence, Parallel, and Track.
notify = directNotify.newCategory("Interval")
SequenceNum = 1
def __init__(self, *ivals, **kw):
name = None
if len(ivals) == 2 and isinstance(ivals[1], types.StringType):
# If the second parameter is a string, it's the name.
name = ivals[1]
ivals = ivals[0]
else:
# Otherwise, look for the name in the keyword params.
if kw.has_key('name'):
name = kw['name']
del kw['name']
if kw:
self.notify.error("Unexpected keyword parameters: %s" % (kw.keys()))
# We must allow the old style: Track([ ival0, ival1, ... ]) as
# well as the new style: Track(ival0, ival1, ...)
if len(ivals) == 1 and \
(isinstance(ivals[0], types.TupleType) or \
isinstance(ivals[0], types.ListType)):
self.ivals = ivals[0]
else:
self.ivals = ivals
self.__ivalsDirty = 1
if name == None:
name = '%s-%d' % (self.__class__.__name__, self.SequenceNum)
MetaInterval.SequenceNum += 1
CMetaInterval.__init__(self, name)
self.pythonIvals = []
# Functions to make the MetaInterval object act just like a Python
# list of intervals:
def append(self, ival):
# Appends a single interval to the list so far.
if isinstance(self.ivals, types.TupleType):
self.ivals = list(self.ivals)
self.ivals.append(ival)
self.__ivalsDirty = 1
def extend(self, ivals):
# Appends a list of intervals to the list so far.
if isinstance(self.ivals, types.TupleType):
self.ivals = list(self.ivals)
self.ivals.extend(ivals)
self.__ivalsDirty = 1
def __len__(self):
return len(self.ivals)
def __getitem__(self, index):
return self.ivals[index]
def __setitem__(self, index, value):
if isinstance(self.ivals, types.TupleType):
self.ivals = list(self.ivals)
self.ivals[index] = value
self.__ivalsDirty = 1
def __delitem__(self, index):
if isinstance(self.ivals, types.TupleType):
self.ivals = list(self.ivals)
del self.ivals[index]
self.__ivalsDirty = 1
def __getslice__(self, i, j):
if isinstance(self.ivals, types.TupleType):
self.ivals = list(self.ivals)
return self.ivals[i : j]
def __setslice__(self, i, j, s):
if isinstance(self.ivals, types.TupleType):
self.ivals = list(self.ivals)
self.ivals[i : j] = s
self.__ivalsDirty = 1
def __delslice__(self, i, j):
if isinstance(self.ivals, types.TupleType):
self.ivals = list(self.ivals)
del self.ivals[i : j]
self.__ivalsDirty = 1
def __iadd__(self, other):
if isinstance(self.ivals, types.TupleType):
self.ivals = list(self.ivals)
if isinstance(other, MetaInterval):
self.ivals += other.ivals
else:
self.ivals += list(other.ivals)
self.__ivalsDirty = 1
return self
def addSequence(self, list, relTime, relTo):
# Adds the given list of intervals to the MetaInterval to be
# played one after the other.
self.pushLevel(relTime, relTo)
for ival in list:
self.addInterval(ival, 0.0, PREVIOUS_END)
self.popLevel()
def addParallel(self, list, relTime, relTo):
# Adds the given list of intervals to the MetaInterval to be
# played simultaneously.
self.pushLevel(relTime, relTo)
for ival in list:
self.addInterval(ival, 0.0, TRACK_START)
self.popLevel()
def addTrack(self, list, relTime, relTo):
# Adds a "track list". This is a list of tuples of the form:
#
# ( <delay>, <Interval>,
# PREVIOUS_END | PREVIOUS_START | TRACK_START )
#
# where <delay> is a relative time, in seconds, for the
# <Interval> to start, relative to either the end of the
# previous interval (PREVIOUS_END), the start of the previous
# interval (PREVIOUS_START) or the start of the track list
# (TRACK_START). If the relative code is omitted, the default
# is TRACK_START.
self.pushLevel(relTime, relTo)
for tuple in list:
if isinstance(tuple, Interval.Interval) or \
isinstance(tuple, CInterval):
# Actually, it's not a tuple, but just an interval.
# In this case we fall back on the old default of
# assuming a sequential list of intervals. This is a
# temporary feature for backward compatibility.
self.addInterval(tuple, 0.0, PREVIOUS_END)
elif isinstance(tuple, types.TupleType) or \
isinstance(tuple, types.ListType):
relTime = tuple[0]
ival = tuple[1]
if len(tuple) >= 3:
relTo = tuple[2]
else:
relTo = TRACK_START
self.addInterval(ival, relTime, relTo)
else:
self.notify.error("Not a tuple in Track: %s" % (tuple,))
self.popLevel()
def addInterval(self, ival, relTime, relTo):
# Adds the given interval to the MetaInterval.
if isinstance(ival, MetaInterval):
# It's another MetaInterval, so copy in its intervals
# directly to this object. We could just store the
# MetaInterval itself, which would work, but we get a
# performance advantage by flattening out the deeply
# nested hierarchy into a linear list within the root
# CMetaInterval object.
ival.applyIvals(self, relTime, relTo)
elif isinstance(ival, CInterval):
# It's a C++-style Interval, so add it directly.
if getattr(ival, "inPython", 0):
# Actually, it's been flagged to run in Python, even
# though it's a C++ Interval. It's probably got some
# Python functors that must be invoked at runtime to
# define some of its parameters. Treat it as a Python
# interval.
index = len(self.pythonIvals)
self.pythonIvals.append(ival)
self.addExtIndex(index, ival.getName(), ival.getDuration(),
ival.getOpenEnded(), relTime, relTo)
else:
# Nope, a perfectly ordinary C++ interval. Hooray!
self.addCInterval(ival, relTime, relTo)
elif isinstance(ival, Interval.Interval):
# It's a Python-style Interval, so add it as an external.
index = len(self.pythonIvals)
self.pythonIvals.append(ival)
self.addExtIndex(index, ival.getName(), ival.getDuration(),
ival.getOpenEnded(), relTime, relTo)
# Once we have any Python intervals, we must handle this
# interval from Python.
self.inPython = 1
else:
self.notify.error("Not an Interval: %s" % (ival,))
def __updateIvals(self):
# The MetaInterval object does not create the C++ list of
# Intervals immediately; rather, it stores a Python list of
# Intervals that will be compiled into the C++ list the first
# time it is needed.
# This design allows us to avoid creation of the C++ list for
# nested MetaInterval objects, instead copying all nested
# MetaInterval hierarchy into the root CMetaInterval object,
# for a performance benefit.
# This function is called only on the root MetaInterval
# object, when it is time to build the C++ list for itself.
if self.__ivalsDirty:
self.clearIntervals()
self.applyIvals(self, 0, TRACK_START)
self.__ivalsDirty = 0
def clearIntervals(self):
# This overrides the function defined at the C++ level to
# reset the inPython flag. Clearing out the intervals list
# allows us to run entirely in C++ again, at least until a new
# Python interval gets added.
CMetaInterval.clearIntervals(self)
self.inPython = 0
def applyIvals(self, meta, relTime, relTo):
# Add the intervals listed in this object to the given
# MetaInterval object at the C++ level. This will make the
# other MetaInterval object ready to play the intervals.
# This function should be overridden in a derived class to
# change the intepretation of the intervals in this list. In
# the case of a MetaInterval directly, this is valid only if
# the list has only zero or one intervals.
if len(self.ivals) == 0:
pass
elif len(self.ivals) == 1:
meta.addInterval(self.ivals[0], relTime, relTo)
else:
self.notify.error("Cannot build list from MetaInterval directly.")
def __doPythonCallbacks(self):
# This function invokes any Python-level Intervals that need
# to be invoked at this point in time. It must be called
# after any call to setT() or setFinalT() or stepPlay(), or
# some such; basically any function that might invoke an
# interval. The C++ base class will invoke whatever C++
# intervals it can, and then indicate the Python intervals
# that must be invoked through this interface.
while (self.isEventReady()):
index = self.getEventIndex()
t = self.getEventT()
eventType = self.getEventType()
self.popEvent()
ival = self.pythonIvals[index]
if eventType == CInterval.ETStep:
ival.setT(t, Interval.IVAL_NONE)
elif eventType == CInterval.ETFinalize:
ival.setT(ival.getDuration(), Interval.IVAL_DONE)
elif eventType == CInterval.ETReverseFinalize:
ival.setT(0, Interval.IVAL_NONE)
elif eventType == CInterval.ETInitialize or \
eventType == CInterval.ETReverseInitialize:
ival.setT(t, Interval.IVAL_INIT)
elif eventType == CInterval.ETInstant:
ival.setT(ival.getDuration(), Interval.IVAL_INIT)
elif eventType == CInterval.ETReverseInstant:
ival.setT(0, Interval.IVAL_INIT)
else:
self.notify.error("Unhandled event type %s" % (eventType))
def stepPlay(self):
# This function overrides the function defined in the C++
# CInterval code that is called every frame while the interval
# is playing, to check for Python callbacks aftwards.
result = CMetaInterval.stepPlay(self)
self.__doPythonCallbacks()
return result
def setT(self, t, event = Interval.IVAL_NONE):
# This function overrides the C++ function to check for Python
# callbacks afterwards.
self.__updateIvals()
CMetaInterval.setT(self, t, event)
self.__doPythonCallbacks()
def setFinalT(self):
# This function overrides the C++ function to check for Python
# callbacks afterwards.
self.__updateIvals()
CMetaInterval.setFinalT(self)
self.__doPythonCallbacks()
def getDuration(self):
# This function overrides from the parent level to force it to
# update the interval list first, if necessary.
self.__updateIvals()
return CMetaInterval.getDuration(self)
def play(self, *args, **kw):
# This function overrides from the parent level to force it to
# update the interval list first, if necessary.
self.__updateIvals()
return CMetaInterval.play(self, *args, **kw)
def loop(self, *args, **kw):
# This function overrides from the parent level to force it to
# update the interval list first, if necessary.
self.__updateIvals()
return CMetaInterval.loop(self, *args, **kw)
def __repr__(self, *args, **kw):
# This function overrides from the parent level to force it to
# update the interval list first, if necessary.
self.__updateIvals()
return CMetaInterval.__repr__(self, *args, **kw)
def __str__(self, *args, **kw):
# This function overrides from the parent level to force it to
# update the interval list first, if necessary.
self.__updateIvals()
return CMetaInterval.__str__(self, *args, **kw)
class Sequence(MetaInterval):
def applyIvals(self, meta, relTime, relTo):
meta.addSequence(self.ivals, relTime, relTo)
class Parallel(MetaInterval):
def applyIvals(self, meta, relTime, relTo):
meta.addParallel(self.ivals, relTime, relTo)
class Track(MetaInterval):
def applyIvals(self, meta, relTime, relTo):
meta.addTrack(self.ivals, relTime, relTo)
# Temporary for backward compatibility.
class MultiTrack(MetaInterval):
def applyIvals(self, meta, relTime, relTo):
meta.addParallel(self.ivals, relTime, relTo)