changeset 122:2ed9bfd1dd0e

I totally wrecked Timeline so that it can run the show. (I hope it can I totally wrecked Timeline so that it can run the show. (I hope it can at least do that.) Sleepy...
author dmcc
date Fri, 13 Jun 2003 15:06:14 +0000
parents 2f48cb9219ed
children 41c0ec6cd10a
files flax/KeyboardComposer.py flax/Timeline.py
diffstat 2 files changed, 24 insertions(+), 227 deletions(-) [+]
line wrap: on
line diff
--- a/flax/KeyboardComposer.py	Fri Jun 13 14:01:07 2003 +0000
+++ b/flax/KeyboardComposer.py	Fri Jun 13 15:06:14 2003 +0000
@@ -60,7 +60,7 @@
 
             # another what a hack!
             keylabel = Label(keyhintrow, text='%s\n%s' % (upkey, downkey), 
-                width=3, font=('Arial Bold', 12), bg='red', fg='white')
+                width=3, font=('Arial', 12), bg='red', fg='white')
             keylabel.pack(side=LEFT, expand=1, fill=X)
             col += 1
 
--- a/flax/Timeline.py	Fri Jun 13 14:01:07 2003 +0000
+++ b/flax/Timeline.py	Fri Jun 13 15:06:14 2003 +0000
@@ -8,79 +8,8 @@
 Dana:  Okay, but after dinner, I don't want you putting any of your old cheap 
        moves on me. 
 Peter: Ohhhh no! I've got all NEW cheap moves.
-
-Timeline idea
-=============
- time    | 0   1   2   3   4   5   6
----------+-----------------------------
-frame    | F       F           F
-blenders |  \  b /  \- b ----/
-
-Update: this is more or less what happened.  However, there are 
-TimelineTracks as well.  FunctionFrames go on their own track.
-LevelFrames must have unique times, FunctionFrames do not.
-
-Level propagation
-=================
-Cue1 is a CueNode.  CueNodes consist of a timeline with any number
-of LevelFrame nodes and LinearBlenders connecting all the frames.
-At time 0, Cue1's timeline has LevelFrame1.  At time 5, the timeline
-has NodeFrame1.  NodeFrame1 points to Node1, which has it's own sets
-of levels.  It could be a Cue, for all Cue1 cares.  No matter what,
-we always get down to LevelFrames holding the levels at the bottom,
-then getting combined by Blenders.
-
-             /------\
-             | Cue1 |                                       
-             |---------------\
-             |  Timeline:    |                                     
-             |  0   ... 5    |                                     
-     /--------- LF1     NF1 -------\                               
-     |       |   \      /    |     |                          
-     |       | LinearBlender |     |                                 
-     |       \---------------/     |                        
-     |                          points to                  
-   /---------------\            a port in Cue1,
-   | blueleft : 20 |            which connects to a Node
-   | redwash  : 12 |                                                       
-   |   .           |                                        
-   |   :           |    
-   |               |
-   \---------------/
-                                                            
-PS. blueleft and redwash are other nodes at the bottom of the tree.
-The include their real channel number for the DimmerNode to process.
-
-When Cue1 requests levels, the timeline looks at the current position.
-If it is directly over a Frame (or Frames), that frame is handled.
-If it is LevelFrame, those are the levels that it returns.  If there is
-a FunctionFrame, that function is activated.  Thus, the order of Frames
-at a specific time is very significant, since the FunctionFrame could
-set the time to 5s in the future.  If we are not currently over any 
-LevelFrames, we call upon a Blender to determine the value between.
-Say that we are at 2.3s.  We use the LinearBlender with 2.3/5.0s = 0.46%
-and it determines that the levels are 1 - 0.46% = 0.54% of LF1 and
-0.46% of NF1.  NF1 asks Node9 for its levels and this process starts
-all over.
-
-Graph theory issues (node related issues, should be moved elsewhere)
-====================================================================
-1.  We need to determine dependencies for updating (topological order).
-2.  We need to do cyclicity tests.
-
-Guess who wishes they had brought their theory book home?
-I think we can do both with augmented DFS.  An incremental version of both
-would be very nice, though hopefully unnecessary.
-
 """
 
-class InvalidFrameOperation(Exception):
-    """You get these when you try to perform some operation on a frame
-    that doesn't make sense.  The interface is advised to tell the user,
-    and indicate that a Blender or FunctionFramea should be disconnected
-    or fixed."""
-    pass
-
 class MissingBlender(Exception):
     """Raised when a TimedEvent is missing a blender."""
     def __init__(self, timedevent):
@@ -93,105 +22,13 @@
 FORWARD = 1
 BACKWARD = -1
 
-class Frame:
-    """Frame is an event that happens at a specific time.  There are two
-    types of frames: LevelFrames and FunctionFrames.  LevelFrames provide
-    levels via their get_levels() function.  FunctionFrames alter the 
-    timeline (e.g. bouncing, looping, speed changes, etc.).  They call
-    __call__'ed instead."""
-    def __init__(self, name):
-        self.name = name
-        self.timeline = DummyClass(use_warnings=0, raise_exceptions=0)
-    def set_timeline(self, timeline):
-        """Tell the Frame who the controlling Timeline is"""
-        self.timeline = timeline
-    def __mul__(self, percent):
-        """Generate a new Frame by multiplying the 'effect' of this frame by
-        a percent."""
-        raise InvalidFrameOperation, "Can't use multiply this Frame"
-    def __add__(self, otherframe):
-        """Combines this frame with another frame, generating a new one."""
-        raise InvalidFrameOperation, "Can't use add on this Frame"
-
-class LevelFrame(Frame):
-    """LevelFrames provide levels.  They can also be combined with other
-    LevelFrames."""
-    def __init__(self, name, levels):
-        Frame.__init__(self, name)
-        self.levels = levels
-    def __mul__(self, percent):
-        """Returns a new LevelFrame made by multiplying all levels by a 
-        percentage.  Percent is a float greater than 0.0"""
-        newlevels = dict_scale(self.get_levels(), percent)
-        return LevelFrame('(%s * %f)' % (self.name, percent), newlevels)
-    def __add__(self, otherframe):
-        """Combines this LevelFrame with another LevelFrame, generating a new 
-        one.  Values are max() together."""
-        theselevels, otherlevels = self.get_levels(), otherframe.get_levels()
-        return LevelFrame('(%s + %s)' % (self.name, otherframe.name),
-                          dict_max(theselevels, otherlevels))
-    def get_levels(self):
-        """This function returns the levels held by this frame."""
-        return self.levels
-    def __repr__(self):
-        return "<%s %r %r>" % (str(self.__class__), self.name, self.levels)
-
-class EmptyFrame(LevelFrame):
-    """An empty LevelFrame, for the purposes of extending the timeline."""
-    def __init__(self, name='Empty Frame'):
-        EmptyFrame.__init__(self, name, {})
-
-class NodeFrame(LevelFrame):
-    """A LevelFrame that gets its levels from another Node.  This must be
-    used from a Timeline that is enclosed in TimelineNode.  Node is a string
-    describing the node requested."""
-    def __init__(self, name, node):
-        LevelFrame.__init__(self, name, {})
-        self.node = node
-    def get_levels(self):
-        """Ask the node that we point to for its levels"""
-        node = self.timeline.get_node(self.node)
-        self.levels = node.get_levels()
-        return self.levels
-
-class FunctionFrame(Frame):
-    def __init__(self, name):
-        Frame.__init__(self, name)
-    def __call__(self, timeline, timedevent, node):
-        """Called when the FunctionFrame is activated.  It is given a pointer
-        to it's master timeline, the TimedEvent containing it, and Node that
-        the timeline is contained in, if available."""
-        pass
-
-# this is kinda broken
-class BounceFunction(FunctionFrame):
-    def __call__(self, timeline, timedevent, node):
-        """Reverses the direction of play."""
-        timeline.reverse_direction()
-        print "boing! new dir:", timeline.direction
-
-# this too
-class LoopFunction(FunctionFrame):
-    def __call__(self, timeline, timedevent, node):
-        timeline.set_time(0)
-        # print 'looped!'
-
-class DoubleTimeFunction(FunctionFrame):
-    def __call__(self, timeline, timedevent, node):
-        timeline.set_rate(2 * timeline.rate)
-        print 'doubled!', timeline.rate
-
-class HalfTimeFunction(FunctionFrame):
-    def __call__(self, timeline, timedevent, node):
-        timeline.set_rate(0.5 * timeline.rate)
-        print 'halved!', timeline.rate
-
 class TimedEvent:
     """Container for a Frame which includes a time that it occurs at,
     and which blender occurs after it."""
-    def __init__(self, time, frame, blender=None):
+    def __init__(self, time, frame, blender=None, level=1.0):
         make_attributes_from_args('time', 'frame')
         self.next_blender = blender
+        self.level = level
     def __float__(self):
         return self.time
     def __cmp__(self, other):
@@ -200,12 +37,10 @@
         else:
             return cmp(self.time, other.time)
     def __repr__(self):
-        return "<TimedEvent %s at %.2f, next blender=%s>" % \
-            (self.frame, self.time, self.next_blender)
-    def get_levels(self):
-        """Return the Frame's levels.  Hopefully frame is a LevelFrame or
-        descendent."""
-        return self.frame.get_levels()
+        return "<TimedEvent %s at %.2f, time=%.2f, next blender=%s>" % \
+            (self.frame, self.level, self.time, self.next_blender)
+    def get_level(self):
+        return self.level
 
 class Blender:
     """Blenders are functions that merge the effects of two LevelFrames."""
@@ -237,7 +72,7 @@
         0.25 * endframe.  This function is included since many blenders are
         just functions on the percentage and still combine start and end frames
         in this way."""
-        return (startframe * (1.0 - blendtime)) + (endframe * blendtime)
+        return {startframe : (1.0 - blendtime), endframe : blendtime}
 
 class InstantEnd(Blender):
     """Instant change from startframe to endframe at the end.  In other words,
@@ -248,7 +83,7 @@
         # This is because Blenders never be asked for blenders at the endpoints
         # (after all, they wouldn't be blenders if they were). Please see
         # 'Very important note' in Blender.__doc__
-        return startframe
+        return {startframe : 1.0}
 
 class InstantStart(Blender):
     """Instant change from startframe to endframe at the beginning.  In other
@@ -259,13 +94,12 @@
         # This is because Blenders never be asked for blenders at the endpoints
         # (after all, they wouldn't be blenders if they were). Please see
         # 'Very important note' in Blender.__doc__
-        return endframe
+        return {endframe : 1.0}
 
 class LinearBlender(Blender):
     """Linear fade from one frame to another"""
     def __call__(self, startframe, endframe, blendtime):
         return self.linear_blend(startframe, endframe, blendtime)
-        # return (startframe * (1.0 - blendtime)) + (endframe * blendtime)
 
 class ExponentialBlender(Blender):
     """Exponential fade fron one frame to another.  You get to specify
@@ -290,7 +124,7 @@
     """TimelineTrack is a single track in a Timeline.  It consists of a
     list of TimedEvents and a name.  Length is automatically the location
     of the last TimedEvent.  To extend the Timeline past that, add an
-    EmptyTimedEvent."""
+    EmptyTimedEvent (which doesn't exist :-/)."""
     def __init__(self, name, *timedevents):
         self.name = name
         self.events = list(timedevents)
@@ -352,7 +186,7 @@
         before, after = self.get_surrounding_frames(time)
         
         if before == after:
-            return before.frame
+            return {before.frame : 1.0}
         else: # we have a blended value
             diff = after.time - before.time
             elapsed = time - before.time
@@ -362,9 +196,9 @@
             return before.next_blender(before.frame, after.frame, percent)
 
 class Timeline:
-    def __init__(self, tracks, functions, rate=1, direction=FORWARD):
+    def __init__(self, tracks, rate=1, direction=FORWARD):
         """
-        Most of this is old:
+        Most/all of this is old:
 
         You can have multiple FunctionFrames at the same time.  Their
         order is important though, since FunctionFrames will be applied
@@ -377,9 +211,6 @@
         some time if you want to extend a Timeline."""
         
         make_attributes_from_args('tracks', 'rate', 'direction')
-        # the function track is a special track
-        self.fn_track = TimelineTrack('functions', *functions)
-        
         self.current_time = 0
         self.last_clock_time = None
         self.stopped = 1
@@ -387,7 +218,6 @@
         """Length of the timeline in pseudoseconds.  This is determined by
         finding the length of the longest track."""
         track_lengths = [track.length() for track in self.tracks]
-        track_lengths.append(self.fn_track.length())
         return max(track_lengths)
     def play(self):
         """Activates the timeline.  Future calls to tick() will advance the 
@@ -422,21 +252,6 @@
         self.last_clock_time = clock_time
         self.current_time = new_time
         
-        # now, find out if we missed any functions
-        if self.fn_track.has_events():
-            lower_time, higher_time = last_time, new_time
-            if lower_time == higher_time: print "zarg!"
-            if lower_time > higher_time:
-                lower_time, higher_time = higher_time, lower_time
-            
-            events_to_process = self.fn_track.get_range(lower_time, 
-                higher_time, self.direction)
-            
-            for event in events_to_process:
-                # they better be FunctionFrames
-                event.frame(self, event, None) # the None should be a Node, 
-                                               # but that part is coming later
-        
         # now we make sure we're in bounds (we don't do this before, since it
         # can cause us to skip events that are at boundaries.
         self.current_time = max(self.current_time, 0)
@@ -456,17 +271,11 @@
     def get_levels(self):
         """Return the current levels from this timeline.  This is done by
         adding all the non-functional tracks together."""
-        current_level_frame = LevelFrame('timeline sum', {})
-        for t in self.tracks:
-            current_level_frame += t.get_levels_at_time(self.current_time)
-
-        return current_level_frame.get_levels()
+        levels = [t.get_levels_at_time(self.current_time)
+                    for t in self.tracks]
+        return dict_max(*levels)
 
 if __name__ == '__main__':
-    scene1 = LevelFrame('scene1', {'red' : 50, 'blue' : 25})
-    scene2 = LevelFrame('scene2', {'red' : 10, 'blue' : 5, 'green' : 70})
-    scene3 = LevelFrame('scene3', {'yellow' : 10, 'blue' : 80, 'purple' : 70})
-
     T = TimedEvent
 
     linear = LinearBlender()
@@ -475,25 +284,13 @@
     smoove = SmoothBlender()
 
     track1 = TimelineTrack('lights',
-        T(0, scene1, blender=linear),
-        T(5, scene2, blender=quad),
-        T(10, scene3, blender=smoove),
-        T(15, scene2)) # last TimedEvent doesn't need a blender
+        T(0, 'red', blender=linear),
+        T(5, 'blue', blender=quad),
+        T(10, 'red', blender=smoove),
+        T(15, 'blue')) # last TimedEvent doesn't need a blender
 
-    halver = HalfTimeFunction('1/2x')
-    doubler = DoubleTimeFunction('2x')
-    if 0:
-        # bounce is semiworking
-        bouncer = BounceFunction('boing')
-        tl = Timeline([track1], [T(0, bouncer), 
-                                 T(0, halver),
-                                 T(15, bouncer),
-                                 T(15, doubler)])
-    else:
-        looper = LoopFunction('loop1')
-        tl = Timeline([track1], [T(0, doubler),
-                                 T(5, halver),
-                                 T(14, looper)])
+    tl = Timeline([track1])
+
     tl.play()
 
     import Tix
@@ -508,7 +305,7 @@
     for color in colors:
         sv = Tix.DoubleVar()
         scalevars[color] = sv
-        scale = Tix.Scale(colorscalesframe, from_=100, to_=0, bg=color,
+        scale = Tix.Scale(colorscalesframe, from_=1, to_=0, res=0.01, bg=color,
             variable=sv)
         scale.pack(side=Tix.LEFT)