mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-02 09:52:27 -04:00
oops, omitted file
This commit is contained in:
parent
da03dcffc4
commit
7eedaffd4b
374
direct/src/interval/MetaInterval.py
Normal file
374
direct/src/interval/MetaInterval.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user