Welcome, guest | Sign In | My Account | Store | Cart

This class implements a generator, which returns consecutive floats which are incremented by a speed * (time between calls).

Python, 36 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import time

class GovernedRange(object):
    def __init__(self, low, high, speed=1):
        """
        Returns a range of floats, where each consecutive number is 
        incremented at a speed * time between iterations, rather than 
        a set value.
        low: start of range
        high: top of range
        speed: amount to increment, per second.
        """
        self.speed = float(speed)
        self.low = float(low)
        self.high = float(high)
        self._i = self.low
        self._t = time.time()
    
    def __iter__(self):
        while True:
            yield self.next()
            
    def next(self):
        speed = float(self.speed)
        inc = speed * (time.time() - self._t)
        i = self._i
        i += inc
        self._t = time.time()
        if i >= self.high: raise StopIteration
        self._i = i
        return i
        

for x in GovernedRange(1,5,speed=1):
    print x
    time.sleep(0.1)
    

I use this recipe to create linear ramps in realtime simulations. This is especially useful for creating animated sprite translations, rotations and other transformations regardless of the client machine's framerate.

The speed attribute can be changed by another GovernedRange, which will provide smooth, linear accelerations.

I also find it useful for providing smooth, framerate independent frame advancement of sprite strip animations.

3 comments

Raymond Hettinger 19 years, 4 months ago  # | flag

Various improvements. The code is faster and more concise when expressed as a generator rather than as a class based iterator. Most of the gains arise from using local variables rather than instance variables.

On Windows platforms, timer resolution is improved by using time.clock instead of time.time (taking a lesson from the timeit module).

Readability is further improved by use of clearer variable names.

import sys

def GovernedRange(low, high, speed=1.0):
    high, speed = float(high), float(speed)
    if sys.platform == 'win32':
        timer = time.clock
    else:
        timer = time.time
    now = timer()
    timelimit = now + (high - low) / speed
    while now < timelimit:
        yield (now - timelimit) * speed + high
        now = timer()
Raymond Hettinger 19 years, 4 months ago  # | flag

Acceleration. The last recipe can be modified so that speed gets multiplied by a global variable to allow for smooth accelerations.

S W (author) 19 years, 4 months ago  # | flag

Acceleration, with global variable. This is actually the reason I used a class, rather than a generator.

If I have multiple GovernedRange generators , I would need multiple global variables. Using a class to implement the GovernedRange creates a more usuable, encapsulated object.

Created by S W on Tue, 14 Dec 2004 (PSF)
Python recipes (4591)
S W's recipes (20)

Required Modules

Other Information and Tasks