Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/pilas/pytweener.py
diff options
context:
space:
mode:
Diffstat (limited to 'pilas/pytweener.py')
-rw-r--r--pilas/pytweener.py739
1 files changed, 739 insertions, 0 deletions
diff --git a/pilas/pytweener.py b/pilas/pytweener.py
new file mode 100644
index 0000000..ff66a62
--- /dev/null
+++ b/pilas/pytweener.py
@@ -0,0 +1,739 @@
+# pyTweener
+#
+# Tweening functions for python
+#
+# Heavily based on caurina Tweener: http://code.google.com/p/tweener/
+#
+# Released under M.I.T License - see above url
+# Python version by Ben Harling 2009
+import math
+
+class Tweener(object):
+ def __init__(self, duration = 0.5, tween = None):
+ """Tweener
+ This class manages all active tweens, and provides a factory for
+ creating and spawning tween motions."""
+ self.currentTweens = []
+ self.defaultTweenType = tween or Easing.Linear.easeNone
+ self.defaultDuration = duration or 1.0
+
+ def hasTweens(self):
+ return len(self.currentTweens) > 0
+
+ def addTweenNoArgs(self, obj, function, initial_value, value, **kwargs):
+ "Similar a addTween, solo que se especifica la funcion y el valor de forma explicita."
+ args = {function: value, 'initial_value': initial_value}
+
+ if "tweenTime" in kwargs:
+ t_time = kwargs.pop("tweenTime")
+ else: t_time = self.defaultDuration
+
+ if "tweenType" in kwargs:
+ t_type = kwargs.pop("tweenType")
+ else: t_type = self.defaultTweenType
+
+ if "onCompleteFunction" in kwargs:
+ t_completeFunc = kwargs.pop("onCompleteFunction")
+ else: t_completeFunc = None
+
+ if "onUpdateFunction" in kwargs:
+ t_updateFunc = kwargs.pop("onUpdateFunction")
+ else: t_updateFunc = None
+
+ if "tweenDelay" in kwargs:
+ t_delay = kwargs.pop("tweenDelay")
+ else: t_delay = 0
+
+ if kwargs:
+ raise ValueError("No puede llamar a esta funcion con argumentos nombrados, use addTween en su lugar.")
+
+ tw = Tween(obj, t_time, t_type, t_completeFunc, t_updateFunc, t_delay, **args)
+ if tw:
+ self.currentTweens.append( tw )
+ return tw
+
+ def addTween(self, obj, **kwargs):
+ """ addTween( object, **kwargs) -> tweenObject or False
+
+ Example:
+ tweener.addTween( myRocket, throttle=50, setThrust=400, tweenTime=5.0, tweenType=tweener.OUT_QUAD )
+
+ You must first specify an object, and at least one property or function with a corresponding
+ change value. The tween will throw an error if you specify an attribute the object does
+ not possess. Also the data types of the change and the initial value of the tweened item
+ must match. If you specify a 'set' -type function, the tweener will attempt to get the
+ starting value by call the corresponding 'get' function on the object. If you specify a
+ property, the tweener will read the current state as the starting value. You add both
+ functions and property changes to the same tween.
+
+ in addition to any properties you specify on the object, these keywords do additional
+ setup of the tween.
+
+ tweenTime = the duration of the motion
+ tweenType = one of the predefined tweening equations or your own function
+ onCompleteFunction = specify a function to call on completion of the tween
+ onUpdateFunction = specify a function to call every time the tween updates
+ tweenDelay = specify a delay before starting.
+ """
+ if "tweenTime" in kwargs:
+ t_time = kwargs.pop("tweenTime")
+ else: t_time = self.defaultDuration
+
+ if "tweenType" in kwargs:
+ t_type = kwargs.pop("tweenType")
+ else: t_type = self.defaultTweenType
+
+ if "onCompleteFunction" in kwargs:
+ t_completeFunc = kwargs.pop("onCompleteFunction")
+ else: t_completeFunc = None
+
+ if "onUpdateFunction" in kwargs:
+ t_updateFunc = kwargs.pop("onUpdateFunction")
+ else: t_updateFunc = None
+
+ if "tweenDelay" in kwargs:
+ t_delay = kwargs.pop("tweenDelay")
+ else: t_delay = 0
+
+ tw = Tween( obj, t_time, t_type, t_completeFunc, t_updateFunc, t_delay, **kwargs )
+ if tw:
+ self.currentTweens.append( tw )
+ return tw
+
+ def removeTween(self, tweenObj):
+ if tweenObj in self.currentTweens:
+ tweenObj.complete = True
+ #self.currentTweens.remove( tweenObj )
+
+ def getTweensAffectingObject(self, obj):
+ """Get a list of all tweens acting on the specified object
+ Useful for manipulating tweens on the fly"""
+ tweens = []
+ for t in self.currentTweens:
+ if t.target is obj:
+ tweens.append(t)
+ return tweens
+
+ def removeTweeningFrom(self, obj):
+ """Stop tweening an object, without completing the motion
+ or firing the completeFunction"""
+ for t in self.currentTweens:
+ if t.target is obj:
+ t.complete = True
+
+ def finish(self):
+ #go to last frame for all tweens
+ for t in self.currentTweens:
+ t.update(t.duration)
+ self.currentTweens = []
+
+ def update(self, timeSinceLastFrame):
+ removable = []
+ for t in self.currentTweens:
+ t.update(timeSinceLastFrame)
+
+ if t.complete:
+ removable.append(t)
+
+ for t in removable:
+ self.currentTweens.remove(t)
+
+
+class Tween(object):
+ def __init__(self, obj, tduration, tweenType, completeFunction, updateFunction, delay, **kwargs):
+ """Tween object:
+ Can be created directly, but much more easily using Tweener.addTween( ... )
+ """
+ #print obj, tduration, kwargs
+ self.duration = tduration
+ self.delay = delay
+ self.target = obj
+ self.tween = tweenType
+ self.tweenables = kwargs
+ self.delta = 0
+ self.completeFunction = completeFunction
+ self.updateFunction = updateFunction
+ self.complete = False
+ self.tProps = []
+ self.tFuncs = []
+ self.paused = self.delay > 0
+ self.decodeArguments()
+
+ def decodeArguments(self):
+ """Internal setup procedure to create tweenables and work out
+ how to deal with each"""
+
+ if len(self.tweenables) == 0:
+ # nothing to do
+ print "TWEEN ERROR: No Tweenable properties or functions defined"
+ self.complete = True
+ return
+
+ assert(len(self.tweenables) == 2)
+
+ initial_value = self.tweenables.pop('initial_value')
+
+
+ for k, v in self.tweenables.items():
+
+ # check that its compatible
+ if not hasattr( self.target, k):
+ print "TWEEN ERROR: " + str(self.target) + " has no function " + k
+ self.complete = True
+ break
+
+ prop = func = False
+ startVal = 0
+ newVal = v
+
+ try:
+ startVal = self.target.__dict__[k]
+ prop = k
+ propName = k
+
+ except:
+ func = getattr( self.target, k)
+ funcName = k
+
+ if func:
+ try:
+ getFunc = getattr(self.target, funcName.replace("set", "get") )
+ startVal = getFunc()
+ print getfunc
+ except:
+ # no start value, assume its 0
+ # but make sure the start and change
+ # dataTypes match :)
+ startVal = newVal * 0
+
+ startVal = initial_value
+ tweenable = Tweenable( startVal, newVal - startVal)
+ newFunc = [ k, func, tweenable]
+
+ #setattr(self, funcName, newFunc[2])
+ self.tFuncs.append( newFunc )
+
+
+ if prop:
+ tweenable = Tweenable( startVal, newVal - startVal)
+ newProp = [ k, prop, tweenable]
+ self.tProps.append( newProp )
+
+ """
+ for k, v in self.tweenables.items():
+
+ # check that its compatible
+ if not hasattr( self.target, k):
+ print "TWEEN ERROR: " + str(self.target) + " has no function " + k
+ self.complete = True
+ break
+
+ prop = func = False
+ startVal = 0
+ newVal = v
+
+ try:
+ startVal = self.target.__dict__[k]
+ prop = k
+ propName = k
+
+ except:
+ func = getattr( self.target, k)
+ funcName = k
+
+ if func:
+ try:
+ getFunc = getattr(self.target, funcName.replace("set", "get") )
+ startVal = getFunc()
+ print getfunc
+ except:
+ # no start value, assume its 0
+ # but make sure the start and change
+ # dataTypes match :)
+ startVal = newVal * 0
+ tweenable = Tweenable( startVal, newVal - startVal)
+ newFunc = [ k, func, tweenable]
+
+ #setattr(self, funcName, newFunc[2])
+ self.tFuncs.append( newFunc )
+
+
+ if prop:
+ tweenable = Tweenable( startVal, newVal - startVal)
+ newProp = [ k, prop, tweenable]
+ self.tProps.append( newProp )
+ """
+
+
+ def pause( self, numSeconds=-1 ):
+ """Pause this tween
+ do tween.pause( 2 ) to pause for a specific time
+ or tween.pause() which pauses indefinitely."""
+ self.paused = True
+ self.delay = numSeconds
+
+ def resume( self ):
+ """Resume from pause"""
+ if self.paused:
+ self.paused=False
+
+ def update(self, ptime):
+ """Update this tween with the time since the last frame
+ if there is an update function, it is always called
+ whether the tween is running or paused"""
+
+ if self.complete:
+ return
+
+ if self.paused:
+ if self.delay > 0:
+ self.delay = max( 0, self.delay - ptime )
+ if self.delay == 0:
+ self.paused = False
+ self.delay = -1
+ if self.updateFunction:
+ self.updateFunction()
+ return
+
+ self.delta = min(self.delta + ptime, self.duration)
+
+
+ for propName, prop, tweenable in self.tProps:
+ self.target.__dict__[prop] = self.tween( self.delta, tweenable.startValue, tweenable.change, self.duration )
+ for funcName, func, tweenable in self.tFuncs:
+ func( self.tween( self.delta, tweenable.startValue, tweenable.change, self.duration ) )
+
+
+ if self.delta == self.duration:
+ self.complete = True
+ if self.completeFunction:
+ self.completeFunction()
+
+ if self.updateFunction:
+ self.updateFunction()
+
+
+
+ def getTweenable(self, name):
+ """Return the tweenable values corresponding to the name of the original
+ tweening function or property.
+
+ Allows the parameters of tweens to be changed at runtime. The parameters
+ can even be tweened themselves!
+
+ eg:
+
+ # the rocket needs to escape!! - we're already moving, but must go faster!
+ twn = tweener.getTweensAffectingObject( myRocket )[0]
+ tweenable = twn.getTweenable( "thrusterPower" )
+ tweener.addTween( tweenable, change=1000.0, tweenTime=0.4, tweenType=tweener.IN_QUAD )
+
+ """
+ ret = None
+ for n, f, t in self.tFuncs:
+ if n == name:
+ ret = t
+ return ret
+ for n, p, t in self.tProps:
+ if n == name:
+ ret = t
+ return ret
+ return ret
+
+ def Remove(self):
+ """Disables and removes this tween
+ without calling the complete function"""
+ self.complete = True
+
+
+class Tweenable:
+ def __init__(self, start, change):
+ """Tweenable:
+ Holds values for anything that can be tweened
+ these are normally only created by Tweens"""
+ self.startValue = start
+ self.change = change
+
+
+"""Robert Penner's easing classes ported over from actionscript by Toms Baugis (at gmail com).
+There certainly is room for improvement, but wanted to keep the readability to some extent.
+
+================================================================================
+ Easing Equations
+ (c) 2003 Robert Penner, all rights reserved.
+ This work is subject to the terms in
+ http://www.robertpenner.com/easing_terms_of_use.html.
+================================================================================
+
+TERMS OF USE - EASING EQUATIONS
+
+Open source under the BSD License.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the author nor the names of contributors may be used
+ to endorse or promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+class Easing:
+ class Back:
+ @staticmethod
+ def easeIn(t, b, c, d, s = 1.70158):
+ t = t / d
+ return c * t**2 * ((s+1) * t - s) + b
+
+ @staticmethod
+ def easeOut (t, b, c, d, s = 1.70158):
+ t = t / d - 1
+ return c * (t**2 * ((s + 1) * t + s) + 1) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d, s = 1.70158):
+ t = t / (d * 0.5)
+ s = s * 1.525
+
+ if t < 1:
+ return c * 0.5 * (t**2 * ((s + 1) * t - s)) + b
+
+ t = t - 2
+ return c / 2 * (t**2 * ((s + 1) * t + s) + 2) + b
+
+ class Bounce:
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d
+ if t < 1 / 2.75:
+ return c * (7.5625 * t**2) + b
+ elif t < 2 / 2.75:
+ t = t - 1.5 / 2.75
+ return c * (7.5625 * t**2 + 0.75) + b
+ elif t < 2.5 / 2.75:
+ t = t - 2.25 / 2.75
+ return c * (7.5625 * t**2 + .9375) + b
+ else:
+ t = t - 2.625 / 2.75
+ return c * (7.5625 * t**2 + 0.984375) + b
+
+ @staticmethod
+ def easeIn (t, b, c, d):
+ return c - Easing.Bounce.easeOut(d-t, 0, c, d) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ if t < d * 0.5:
+ return Easing.Bounce.easeIn (t * 2, 0, c, d) * .5 + b
+
+ return Easing.Bounce.easeOut (t * 2 -d, 0, c, d) * .5 + c*.5 + b
+
+
+
+ class Circ:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ t = t / d
+ return -c * (math.sqrt(1 - t**2) - 1) + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d - 1
+ return c * math.sqrt(1 - t**2) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ t = t / (d * 0.5)
+ if t < 1:
+ return -c * 0.5 * (math.sqrt(1 - t**2) - 1) + b
+
+ t = t - 2
+ return c*0.5 * (math.sqrt(1 - t**2) + 1) + b
+
+
+ class Cubic:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ t = t / d
+ return c * t**3 + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d - 1
+ return c * (t**3 + 1) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ t = t / (d * 0.5)
+ if t < 1:
+ return c * 0.5 * t**3 + b
+
+ t = t - 2
+ return c * 0.5 * (t**3 + 2) + b
+
+
+ class Elastic:
+ @staticmethod
+ def easeIn (t, b, c, d, a = 0, p = 0):
+ if t==0: return b
+
+ t = t / d
+ if t == 1: return b+c
+
+ if not p: p = d * .3;
+
+ if not a or a < abs(c):
+ a = c
+ s = p / 4
+ else:
+ s = p / (2 * math.pi) * math.asin(c / a)
+
+ t = t - 1
+ return - (a * math.pow(2, 10 * t) * math.sin((t*d-s) * (2 * math.pi) / p)) + b
+
+
+ @staticmethod
+ def easeOut (t, b, c, d, a = 0, p = 0):
+ if t == 0: return b
+
+ t = t / d
+ if (t == 1): return b + c
+
+ if not p: p = d * .3;
+
+ if not a or a < abs(c):
+ a = c
+ s = p / 4
+ else:
+ s = p / (2 * math.pi) * math.asin(c / a)
+
+ return a * math.pow(2,-10 * t) * math.sin((t * d - s) * (2 * math.pi) / p) + c + b
+
+
+ @staticmethod
+ def easeInOut (t, b, c, d, a = 0, p = 0):
+ if t == 0: return b
+
+ t = t / (d * 0.5)
+ if t == 2: return b + c
+
+ if not p: p = d * (.3 * 1.5)
+
+ if not a or a < abs(c):
+ a = c
+ s = p / 4
+ else:
+ s = p / (2 * math.pi) * math.asin(c / a)
+
+ if (t < 1):
+ t = t - 1
+ return -.5 * (a * math.pow(2, 10 * t) * math.sin((t * d - s) * (2 * math.pi) / p)) + b
+
+ t = t - 1
+ return a * math.pow(2, -10 * t) * math.sin((t * d - s) * (2 * math.pi) / p) * .5 + c + b
+
+
+ class Expo:
+ @staticmethod
+ def easeIn(t, b, c, d):
+ if t == 0:
+ return b
+ else:
+ return c * math.pow(2, 10 * (t / d - 1)) + b - c * 0.001
+
+ @staticmethod
+ def easeOut(t, b, c, d):
+ if t == d:
+ return b + c
+ else:
+ return c * (-math.pow(2, -10 * t / d) + 1) + b
+
+ @staticmethod
+ def easeInOut(t, b, c, d):
+ if t==0:
+ return b
+ elif t==d:
+ return b+c
+
+ t = t / (d * 0.5)
+
+ if t < 1:
+ return c * 0.5 * math.pow(2, 10 * (t - 1)) + b
+
+ return c * 0.5 * (-math.pow(2, -10 * (t - 1)) + 2) + b
+
+
+ class Linear:
+ @staticmethod
+ def easeNone(t, b, c, d):
+ return c * t / d + b
+
+ @staticmethod
+ def easeIn(t, b, c, d):
+ return c * t / d + b
+
+ @staticmethod
+ def easeOut(t, b, c, d):
+ return c * t / d + b
+
+ @staticmethod
+ def easeInOut(t, b, c, d):
+ return c * t / d + b
+
+
+ class Quad:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ t = t / d
+ return c * t**2 + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d
+ return -c * t * (t-2) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ t = t / (d * 0.5)
+ if t < 1:
+ return c * 0.5 * t**2 + b
+
+ t = t - 1
+ return -c * 0.5 * (t * (t - 2) - 1) + b
+
+
+ class Quart:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ t = t / d
+ return c * t**4 + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d - 1
+ return -c * (t**4 - 1) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ t = t / (d * 0.5)
+ if t < 1:
+ return c * 0.5 * t**4 + b
+
+ t = t - 2
+ return -c * 0.5 * (t**4 - 2) + b
+
+
+ class Quint:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ t = t / d
+ return c * t**5 + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ t = t / d - 1
+ return c * (t**5 + 1) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ t = t / (d * 0.5)
+ if t < 1:
+ return c * 0.5 * t**5 + b
+
+ t = t - 2
+ return c * 0.5 * (t**5 + 2) + b
+
+ class Sine:
+ @staticmethod
+ def easeIn (t, b, c, d):
+ return -c * math.cos(t / d * (math.pi / 2)) + c + b
+
+ @staticmethod
+ def easeOut (t, b, c, d):
+ return c * math.sin(t / d * (math.pi / 2)) + b
+
+ @staticmethod
+ def easeInOut (t, b, c, d):
+ return -c * 0.5 * (math.cos(math.pi * t / d) - 1) + b
+
+
+ class Strong:
+ @staticmethod
+ def easeIn(t, b, c, d):
+ return c * (t/d)**5 + b
+
+ @staticmethod
+ def easeOut(t, b, c, d):
+ return c * ((t / d - 1)**5 + 1) + b
+
+ @staticmethod
+ def easeInOut(t, b, c, d):
+ t = t / (d * 0.5)
+
+ if t < 1:
+ return c * 0.5 * t**5 + b
+
+ t = t - 2
+ return c * 0.5 * (t**5 + 2) + b
+
+
+
+class TweenTestObject:
+ def __init__(self):
+ self.pos = 20
+ self.rot = 50
+
+ def update(self):
+ print self.pos, self.rot
+
+ def setRotation(self, rot):
+ self.rot = rot
+
+ def getRotation(self):
+ return self.rot
+
+ def complete(self):
+ print "I'm done tweening now mommy!"
+
+
+if __name__=="__main__":
+ import time
+ T = Tweener()
+ tst = TweenTestObject()
+ mt = T.addTween( tst, setRotation=500.0, tweenTime=2.5, tweenType=T.OUT_QUAD,
+ pos=-200, tweenDelay=0.4, onCompleteFunction=tst.complete,
+ onUpdateFunction=tst.update )
+ s = time.clock()
+ changed = False
+ while T.hasTweens():
+ tm = time.clock()
+ d = tm - s
+ s = tm
+ T.update( d )
+ if mt.delta > 1.0 and not changed:
+
+ tweenable = mt.getTweenable( "setRotation" )
+
+ T.addTween( tweenable, change=-1000, tweenTime=0.7 )
+ T.addTween( mt, duration=-0.2, tweenTime=0.2 )
+ changed = True
+ #print mt.duration,
+ print tst.getRotation(), tst.pos
+ time.sleep(0.06)
+ print tst.getRotation(), tst.pos