Package pilas :: Module pytweener
[hide private]
[frames] | no frames]

Source Code for Module pilas.pytweener

  1  # pyTweener 
  2  # 
  3  # Tweening functions for python 
  4  # 
  5  # Heavily based on caurina Tweener: http://code.google.com/p/tweener/ 
  6  # 
  7  # Released under M.I.T License - see above url 
  8  # Python version by Ben Harling 2009  
  9  import math 
10 11 -class Tweener(object):
12 - def __init__(self, duration = 0.5, tween = None):
13 """Tweener 14 This class manages all active tweens, and provides a factory for 15 creating and spawning tween motions.""" 16 self.currentTweens = [] 17 self.defaultTweenType = tween or Easing.Linear.easeNone 18 self.defaultDuration = duration or 1.0
19
20 - def hasTweens(self):
21 return len(self.currentTweens) > 0
22
23 - def addTweenNoArgs(self, obj, function, initial_value, value, **kwargs):
24 "Similar a addTween, solo que se especifica la funcion y el valor de forma explicita." 25 args = {function: value, 'initial_value': initial_value} 26 27 if "tweenTime" in kwargs: 28 t_time = kwargs.pop("tweenTime") 29 else: t_time = self.defaultDuration 30 31 if "tweenType" in kwargs: 32 t_type = kwargs.pop("tweenType") 33 else: t_type = self.defaultTweenType 34 35 if "onCompleteFunction" in kwargs: 36 t_completeFunc = kwargs.pop("onCompleteFunction") 37 else: t_completeFunc = None 38 39 if "onUpdateFunction" in kwargs: 40 t_updateFunc = kwargs.pop("onUpdateFunction") 41 else: t_updateFunc = None 42 43 if "tweenDelay" in kwargs: 44 t_delay = kwargs.pop("tweenDelay") 45 else: t_delay = 0 46 47 if kwargs: 48 raise ValueError("No puede llamar a esta funcion con argumentos nombrados, use addTween en su lugar.") 49 50 tw = Tween(obj, t_time, t_type, t_completeFunc, t_updateFunc, t_delay, **args) 51 if tw: 52 self.currentTweens.append( tw ) 53 return tw
54
55 - def addTween(self, obj, **kwargs):
56 """ addTween( object, **kwargs) -> tweenObject or False 57 58 Example: 59 tweener.addTween( myRocket, throttle=50, setThrust=400, tweenTime=5.0, tweenType=tweener.OUT_QUAD ) 60 61 You must first specify an object, and at least one property or function with a corresponding 62 change value. The tween will throw an error if you specify an attribute the object does 63 not possess. Also the data types of the change and the initial value of the tweened item 64 must match. If you specify a 'set' -type function, the tweener will attempt to get the 65 starting value by call the corresponding 'get' function on the object. If you specify a 66 property, the tweener will read the current state as the starting value. You add both 67 functions and property changes to the same tween. 68 69 in addition to any properties you specify on the object, these keywords do additional 70 setup of the tween. 71 72 tweenTime = the duration of the motion 73 tweenType = one of the predefined tweening equations or your own function 74 onCompleteFunction = specify a function to call on completion of the tween 75 onUpdateFunction = specify a function to call every time the tween updates 76 tweenDelay = specify a delay before starting. 77 """ 78 if "tweenTime" in kwargs: 79 t_time = kwargs.pop("tweenTime") 80 else: t_time = self.defaultDuration 81 82 if "tweenType" in kwargs: 83 t_type = kwargs.pop("tweenType") 84 else: t_type = self.defaultTweenType 85 86 if "onCompleteFunction" in kwargs: 87 t_completeFunc = kwargs.pop("onCompleteFunction") 88 else: t_completeFunc = None 89 90 if "onUpdateFunction" in kwargs: 91 t_updateFunc = kwargs.pop("onUpdateFunction") 92 else: t_updateFunc = None 93 94 if "tweenDelay" in kwargs: 95 t_delay = kwargs.pop("tweenDelay") 96 else: t_delay = 0 97 98 tw = Tween( obj, t_time, t_type, t_completeFunc, t_updateFunc, t_delay, **kwargs ) 99 if tw: 100 self.currentTweens.append( tw ) 101 return tw
102
103 - def removeTween(self, tweenObj):
104 if tweenObj in self.currentTweens: 105 tweenObj.complete = True
106 #self.currentTweens.remove( tweenObj ) 107
108 - def getTweensAffectingObject(self, obj):
109 """Get a list of all tweens acting on the specified object 110 Useful for manipulating tweens on the fly""" 111 tweens = [] 112 for t in self.currentTweens: 113 if t.target is obj: 114 tweens.append(t) 115 return tweens
116
117 - def removeTweeningFrom(self, obj):
118 """Stop tweening an object, without completing the motion 119 or firing the completeFunction""" 120 for t in self.currentTweens: 121 if t.target is obj: 122 t.complete = True
123
124 - def removeTweeningFromObjectField(self, obj, field):
125 """Stop tweening an object, without completing the motion 126 or firing the completeFunction""" 127 for t in self.currentTweens: 128 if t.target is obj: 129 for f in t.tFuncs: 130 if f[0] == field: 131 t.complete = True
132
133 - def finish(self):
134 #go to last frame for all tweens 135 for t in self.currentTweens: 136 t.update(t.duration) 137 self.currentTweens = []
138
139 - def update(self, timeSinceLastFrame):
140 removable = [] 141 for t in self.currentTweens: 142 t.update(timeSinceLastFrame) 143 144 if t.complete: 145 removable.append(t) 146 147 for t in removable: 148 self.currentTweens.remove(t)
149
150 - def eliminar_todas(self):
151 a_eliminar = [] 152 for t in self.currentTweens: 153 a_eliminar.append(t) 154 155 for x in a_eliminar: 156 self.currentTweens.remove(x)
157
158 159 160 -class Tween(object):
161 - def __init__(self, obj, tduration, tweenType, completeFunction, updateFunction, delay, **kwargs):
162 """Tween object: 163 Can be created directly, but much more easily using Tweener.addTween( ... ) 164 """ 165 #print obj, tduration, kwargs 166 self.duration = tduration 167 self.delay = delay 168 self.target = obj 169 self.tween = tweenType 170 self.tweenables = kwargs 171 self.delta = 0 172 self.completeFunction = completeFunction 173 self.updateFunction = updateFunction 174 self.complete = False 175 self.tProps = [] 176 self.tFuncs = [] 177 self.paused = self.delay > 0 178 self.decodeArguments()
179
180 - def decodeArguments(self):
181 """Internal setup procedure to create tweenables and work out 182 how to deal with each""" 183 184 if len(self.tweenables) == 0: 185 # nothing to do 186 print "TWEEN ERROR: No Tweenable properties or functions defined" 187 self.complete = True 188 return 189 190 assert(len(self.tweenables) == 2) 191 192 initial_value = self.tweenables.pop('initial_value') 193 194 195 for k, v in self.tweenables.items(): 196 197 # check that its compatible 198 if not hasattr( self.target, k): 199 print "TWEEN ERROR: " + str(self.target) + " has no function " + k 200 self.complete = True 201 break 202 203 prop = func = False 204 startVal = 0 205 newVal = v 206 207 try: 208 startVal = self.target.__dict__[k] 209 prop = k 210 propName = k 211 212 except: 213 func = getattr( self.target, k) 214 funcName = k 215 216 if func: 217 try: 218 getFunc = getattr(self.target, funcName.replace("set", "get") ) 219 startVal = getFunc() 220 print getfunc 221 except: 222 # no start value, assume its 0 223 # but make sure the start and change 224 # dataTypes match :) 225 startVal = newVal * 0 226 227 startVal = initial_value 228 tweenable = Tweenable( startVal, newVal - startVal) 229 newFunc = [ k, func, tweenable] 230 231 #setattr(self, funcName, newFunc[2]) 232 self.tFuncs.append( newFunc ) 233 234 235 if prop: 236 tweenable = Tweenable( startVal, newVal - startVal) 237 newProp = [ k, prop, tweenable] 238 self.tProps.append( newProp ) 239 240 """ 241 for k, v in self.tweenables.items(): 242 243 # check that its compatible 244 if not hasattr( self.target, k): 245 print "TWEEN ERROR: " + str(self.target) + " has no function " + k 246 self.complete = True 247 break 248 249 prop = func = False 250 startVal = 0 251 newVal = v 252 253 try: 254 startVal = self.target.__dict__[k] 255 prop = k 256 propName = k 257 258 except: 259 func = getattr( self.target, k) 260 funcName = k 261 262 if func: 263 try: 264 getFunc = getattr(self.target, funcName.replace("set", "get") ) 265 startVal = getFunc() 266 print getfunc 267 except: 268 # no start value, assume its 0 269 # but make sure the start and change 270 # dataTypes match :) 271 startVal = newVal * 0 272 tweenable = Tweenable( startVal, newVal - startVal) 273 newFunc = [ k, func, tweenable] 274 275 #setattr(self, funcName, newFunc[2]) 276 self.tFuncs.append( newFunc ) 277 278 279 if prop: 280 tweenable = Tweenable( startVal, newVal - startVal) 281 newProp = [ k, prop, tweenable] 282 self.tProps.append( newProp ) 283 """
284 285
286 - def pause( self, numSeconds=-1 ):
287 """Pause this tween 288 do tween.pause( 2 ) to pause for a specific time 289 or tween.pause() which pauses indefinitely.""" 290 self.paused = True 291 self.delay = numSeconds
292
293 - def resume( self ):
294 """Resume from pause""" 295 if self.paused: 296 self.paused=False
297
298 - def update(self, ptime):
299 """Update this tween with the time since the last frame 300 if there is an update function, it is always called 301 whether the tween is running or paused""" 302 303 if self.complete: 304 return 305 306 if self.paused: 307 if self.delay > 0: 308 self.delay = max( 0, self.delay - ptime ) 309 if self.delay == 0: 310 self.paused = False 311 self.delay = -1 312 if self.updateFunction: 313 self.updateFunction() 314 return 315 316 self.delta = min(self.delta + ptime, self.duration) 317 318 319 for propName, prop, tweenable in self.tProps: 320 self.target.__dict__[prop] = self.tween( self.delta, tweenable.startValue, tweenable.change, self.duration ) 321 for funcName, func, tweenable in self.tFuncs: 322 func( self.tween( self.delta, tweenable.startValue, tweenable.change, self.duration ) ) 323 324 325 if self.delta == self.duration: 326 self.complete = True 327 if self.completeFunction: 328 self.completeFunction() 329 330 if self.updateFunction: 331 self.updateFunction()
332 333 334
335 - def getTweenable(self, name):
336 """Return the tweenable values corresponding to the name of the original 337 tweening function or property. 338 339 Allows the parameters of tweens to be changed at runtime. The parameters 340 can even be tweened themselves! 341 342 eg: 343 344 # the rocket needs to escape!! - we're already moving, but must go faster! 345 twn = tweener.getTweensAffectingObject( myRocket )[0] 346 tweenable = twn.getTweenable( "thrusterPower" ) 347 tweener.addTween( tweenable, change=1000.0, tweenTime=0.4, tweenType=tweener.IN_QUAD ) 348 349 """ 350 ret = None 351 for n, f, t in self.tFuncs: 352 if n == name: 353 ret = t 354 return ret 355 for n, p, t in self.tProps: 356 if n == name: 357 ret = t 358 return ret 359 return ret
360
361 - def Remove(self):
362 """Disables and removes this tween 363 without calling the complete function""" 364 self.complete = True
365
366 367 -class Tweenable:
368 - def __init__(self, start, change):
369 """Tweenable: 370 Holds values for anything that can be tweened 371 these are normally only created by Tweens""" 372 self.startValue = start 373 self.change = change
374 375 376 """Robert Penner's easing classes ported over from actionscript by Toms Baugis (at gmail com). 377 There certainly is room for improvement, but wanted to keep the readability to some extent. 378 379 ================================================================================ 380 Easing Equations 381 (c) 2003 Robert Penner, all rights reserved. 382 This work is subject to the terms in 383 http://www.robertpenner.com/easing_terms_of_use.html. 384 ================================================================================ 385 386 TERMS OF USE - EASING EQUATIONS 387 388 Open source under the BSD License. 389 390 All rights reserved. 391 392 Redistribution and use in source and binary forms, with or without modification, 393 are permitted provided that the following conditions are met: 394 395 * Redistributions of source code must retain the above copyright notice, 396 this list of conditions and the following disclaimer. 397 * Redistributions in binary form must reproduce the above copyright notice, 398 this list of conditions and the following disclaimer in the documentation 399 and/or other materials provided with the distribution. 400 * Neither the name of the author nor the names of contributors may be used 401 to endorse or promote products derived from this software without specific 402 prior written permission. 403 404 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 405 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 406 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 407 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 408 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 409 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 410 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 411 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 412 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 413 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 414 """
415 -class Easing:
416 - class Back:
417 @staticmethod
418 - def easeIn(t, b, c, d, s = 1.70158):
419 t = t / d 420 return c * t**2 * ((s+1) * t - s) + b
421 422 @staticmethod
423 - def easeOut (t, b, c, d, s = 1.70158):
424 t = t / d - 1 425 return c * (t**2 * ((s + 1) * t + s) + 1) + b
426 427 @staticmethod
428 - def easeInOut (t, b, c, d, s = 1.70158):
429 t = t / (d * 0.5) 430 s = s * 1.525 431 432 if t < 1: 433 return c * 0.5 * (t**2 * ((s + 1) * t - s)) + b 434 435 t = t - 2 436 return c / 2 * (t**2 * ((s + 1) * t + s) + 2) + b
437
438 - class Bounce:
439 @staticmethod
440 - def easeOut (t, b, c, d):
441 t = t / d 442 if t < 1 / 2.75: 443 return c * (7.5625 * t**2) + b 444 elif t < 2 / 2.75: 445 t = t - 1.5 / 2.75 446 return c * (7.5625 * t**2 + 0.75) + b 447 elif t < 2.5 / 2.75: 448 t = t - 2.25 / 2.75 449 return c * (7.5625 * t**2 + .9375) + b 450 else: 451 t = t - 2.625 / 2.75 452 return c * (7.5625 * t**2 + 0.984375) + b
453 454 @staticmethod
455 - def easeIn (t, b, c, d):
456 return c - Easing.Bounce.easeOut(d-t, 0, c, d) + b
457 458 @staticmethod
459 - def easeInOut (t, b, c, d):
460 if t < d * 0.5: 461 return Easing.Bounce.easeIn (t * 2, 0, c, d) * .5 + b 462 463 return Easing.Bounce.easeOut (t * 2 -d, 0, c, d) * .5 + c*.5 + b
464 465 466
467 - class Circ:
468 @staticmethod
469 - def easeIn (t, b, c, d):
470 t = t / d 471 return -c * (math.sqrt(1 - t**2) - 1) + b
472 473 @staticmethod
474 - def easeOut (t, b, c, d):
475 t = t / d - 1 476 return c * math.sqrt(1 - t**2) + b
477 478 @staticmethod
479 - def easeInOut (t, b, c, d):
480 t = t / (d * 0.5) 481 if t < 1: 482 return -c * 0.5 * (math.sqrt(1 - t**2) - 1) + b 483 484 t = t - 2 485 return c*0.5 * (math.sqrt(1 - t**2) + 1) + b
486 487
488 - class Cubic:
489 @staticmethod
490 - def easeIn (t, b, c, d):
491 t = t / d 492 return c * t**3 + b
493 494 @staticmethod
495 - def easeOut (t, b, c, d):
496 t = t / d - 1 497 return c * (t**3 + 1) + b
498 499 @staticmethod
500 - def easeInOut (t, b, c, d):
501 t = t / (d * 0.5) 502 if t < 1: 503 return c * 0.5 * t**3 + b 504 505 t = t - 2 506 return c * 0.5 * (t**3 + 2) + b
507 508
509 - class Elastic:
510 @staticmethod
511 - def easeIn (t, b, c, d, a = 0, p = 0):
512 if t==0: return b 513 514 t = t / d 515 if t == 1: return b+c 516 517 if not p: p = d * .3; 518 519 if not a or a < abs(c): 520 a = c 521 s = p / 4 522 else: 523 s = p / (2 * math.pi) * math.asin(c / a) 524 525 t = t - 1 526 return - (a * math.pow(2, 10 * t) * math.sin((t*d-s) * (2 * math.pi) / p)) + b
527 528 529 @staticmethod
530 - def easeOut (t, b, c, d, a = 0, p = 0):
531 if t == 0: return b 532 533 t = t / d 534 if (t == 1): return b + c 535 536 if not p: p = d * .3; 537 538 if not a or a < abs(c): 539 a = c 540 s = p / 4 541 else: 542 s = p / (2 * math.pi) * math.asin(c / a) 543 544 return a * math.pow(2,-10 * t) * math.sin((t * d - s) * (2 * math.pi) / p) + c + b
545 546 547 @staticmethod
548 - def easeInOut (t, b, c, d, a = 0, p = 0):
549 if t == 0: return b 550 551 t = t / (d * 0.5) 552 if t == 2: return b + c 553 554 if not p: p = d * (.3 * 1.5) 555 556 if not a or a < abs(c): 557 a = c 558 s = p / 4 559 else: 560 s = p / (2 * math.pi) * math.asin(c / a) 561 562 if (t < 1): 563 t = t - 1 564 return -.5 * (a * math.pow(2, 10 * t) * math.sin((t * d - s) * (2 * math.pi) / p)) + b 565 566 t = t - 1 567 return a * math.pow(2, -10 * t) * math.sin((t * d - s) * (2 * math.pi) / p) * .5 + c + b
568 569
570 - class Expo:
571 @staticmethod
572 - def easeIn(t, b, c, d):
573 if t == 0: 574 return b 575 else: 576 return c * math.pow(2, 10 * (t / d - 1)) + b - c * 0.001
577 578 @staticmethod
579 - def easeOut(t, b, c, d):
580 if t == d: 581 return b + c 582 else: 583 return c * (-math.pow(2, -10 * t / d) + 1) + b
584 585 @staticmethod
586 - def easeInOut(t, b, c, d):
587 if t==0: 588 return b 589 elif t==d: 590 return b+c 591 592 t = t / (d * 0.5) 593 594 if t < 1: 595 return c * 0.5 * math.pow(2, 10 * (t - 1)) + b 596 597 return c * 0.5 * (-math.pow(2, -10 * (t - 1)) + 2) + b
598 599
600 - class Linear:
601 @staticmethod
602 - def easeNone(t, b, c, d):
603 return c * t / d + b
604 605 @staticmethod
606 - def easeIn(t, b, c, d):
607 return c * t / d + b
608 609 @staticmethod
610 - def easeOut(t, b, c, d):
611 return c * t / d + b
612 613 @staticmethod
614 - def easeInOut(t, b, c, d):
615 return c * t / d + b
616 617
618 - class Quad:
619 @staticmethod
620 - def easeIn (t, b, c, d):
621 t = t / d 622 return c * t**2 + b
623 624 @staticmethod
625 - def easeOut (t, b, c, d):
626 t = t / d 627 return -c * t * (t-2) + b
628 629 @staticmethod
630 - def easeInOut (t, b, c, d):
631 t = t / (d * 0.5) 632 if t < 1: 633 return c * 0.5 * t**2 + b 634 635 t = t - 1 636 return -c * 0.5 * (t * (t - 2) - 1) + b
637 638
639 - class Quart:
640 @staticmethod
641 - def easeIn (t, b, c, d):
642 t = t / d 643 return c * t**4 + b
644 645 @staticmethod
646 - def easeOut (t, b, c, d):
647 t = t / d - 1 648 return -c * (t**4 - 1) + b
649 650 @staticmethod
651 - def easeInOut (t, b, c, d):
652 t = t / (d * 0.5) 653 if t < 1: 654 return c * 0.5 * t**4 + b 655 656 t = t - 2 657 return -c * 0.5 * (t**4 - 2) + b
658 659
660 - class Quint:
661 @staticmethod
662 - def easeIn (t, b, c, d):
663 t = t / d 664 return c * t**5 + b
665 666 @staticmethod
667 - def easeOut (t, b, c, d):
668 t = t / d - 1 669 return c * (t**5 + 1) + b
670 671 @staticmethod
672 - def easeInOut (t, b, c, d):
673 t = t / (d * 0.5) 674 if t < 1: 675 return c * 0.5 * t**5 + b 676 677 t = t - 2 678 return c * 0.5 * (t**5 + 2) + b
679
680 - class Sine:
681 @staticmethod
682 - def easeIn (t, b, c, d):
683 return -c * math.cos(t / d * (math.pi / 2)) + c + b
684 685 @staticmethod
686 - def easeOut (t, b, c, d):
687 return c * math.sin(t / d * (math.pi / 2)) + b
688 689 @staticmethod
690 - def easeInOut (t, b, c, d):
691 return -c * 0.5 * (math.cos(math.pi * t / d) - 1) + b
692 693
694 - class Strong:
695 @staticmethod
696 - def easeIn(t, b, c, d):
697 return c * (t/d)**5 + b
698 699 @staticmethod
700 - def easeOut(t, b, c, d):
701 return c * ((t / d - 1)**5 + 1) + b
702 703 @staticmethod
704 - def easeInOut(t, b, c, d):
705 t = t / (d * 0.5) 706 707 if t < 1: 708 return c * 0.5 * t**5 + b 709 710 t = t - 2 711 return c * 0.5 * (t**5 + 2) + b
712
713 714 715 -class TweenTestObject:
716 - def __init__(self):
717 self.pos = 20 718 self.rot = 50
719
720 - def update(self):
721 print self.pos, self.rot
722
723 - def setRotation(self, rot):
724 self.rot = rot
725
726 - def getRotation(self):
727 return self.rot
728
729 - def complete(self):
730 print "I'm done tweening now mommy!"
731 732 733 if __name__=="__main__": 734 import time 735 T = Tweener() 736 tst = TweenTestObject() 737 mt = T.addTween( tst, setRotation=500.0, tweenTime=2.5, tweenType=T.OUT_QUAD, 738 pos=-200, tweenDelay=0.4, onCompleteFunction=tst.complete, 739 onUpdateFunction=tst.update ) 740 s = time.clock() 741 changed = False 742 while T.hasTweens(): 743 tm = time.clock() 744 d = tm - s 745 s = tm 746 T.update( d ) 747 if mt.delta > 1.0 and not changed: 748 749 tweenable = mt.getTweenable( "setRotation" ) 750 751 T.addTween( tweenable, change=-1000, tweenTime=0.7 ) 752 T.addTween( mt, duration=-0.2, tweenTime=0.2 ) 753 changed = True 754 #print mt.duration, 755 print tst.getRotation(), tst.pos 756 time.sleep(0.06) 757 print tst.getRotation(), tst.pos 758