mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -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