Never mind; it’s working now with an updated tweening module. I also added a start_time, in case you need to pop immediately to the middle of a tween (like I did…). If anyone else is interested in manipulating any numeric values this way, here’s my current tween module:
[code]# tdTweener
Tweening functions for TouchDesigner
Modified piTweener:
which is a fork of pyTweener:
Released under the MIT License - see above url
Original Python version by Ben Harling 2009
Bugfixes and fork by Benjamin Woodruff 2010
Copyright (c) Ben Harling (2009), Benjamin Woodruff (2010)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
import math
import time
class TweenerEquations(object):
“”"A set of predefined interpolation (tweening) equations. They are ported
from the work of Robert Penner. Each equation takes 4 arguments, the
current time from start, the start value, the desired final change from
the start value, and the total duration of the tween. As is the nature of
interpolation, no equation has units.
**Note:** When using the tdTweener class, you should use the tdTweener class'
instance copies of the equations (tdTweener extends TweenerEquations)
"""
def OUT_EXPO(self, t, b, c, d):
if t == d:
return b + c
return c * (-2 ** (-10 * t / d) + 1) + b;
def LINEAR(self, t, b, c, d):
return c * t / d + b
def IN_QUAD(self, t, b, c, d):
t /= d
return c * t * t + b
def OUT_QUAD(self, t, b, c, d):
t /= d
return -c * t * (t - 2) + b
def IN_OUT_QUAD(self, t, b, c, d):
t /= d * .5
if t < 1.:
return c * .5 * t * t + b
t -= 1.
return -c * .5 * (t * (t - 2.) - 1.) + b
def OUT_IN_QUAD(self, t, b, c, d):
if t < d * .5:
return self.OUT_QUAD(t * 2, b, c * .5, d)
return self.IN_QUAD(t * 2 - d, b + c * .5, c * .5, d)
def IN_CUBIC(self, t, b, c, d):
t /= d
return c * t * t * t + b
def OUT_CUBIC(self, t, b, c, d):
t = t / d - 1
return c * (t * t * t + 1) + b
def IN_OUT_CUBIC(self, t, b, c, d):
t /= d * .5
if t < 1:
return c * .5 * t * t * t + b
t -= 2
return c * .5 * (t * t * t + 2) + b
def OUT_IN_CUBIC(self, t, b, c, d ):
if t < d * .5:
return self.OUT_CUBIC (t * 2., b, c * .5, d)
return self.IN_CUBIC(t * 2. - d, b + c * .5, c * .5, d)
def IN_QUART(self, t, b, c, d):
t /= d
return c * t * t * t * t + b
def OUT_QUART(self, t, b, c, d):
t = t / d - 1
return -c * (t * t * t * t - 1) + b
def IN_OUT_QUART(self, t, b, c, d):
t /= d * .5
if t < 1:
return c * .5 * t * t * t * t + b
t -= 2
return -c / 2 * (t * t * t * t - 2) + b
def OUT_ELASTIC(self, t, b, c, d):
if t == 0:
return b
t /= d
if t == 1:
return b + c
p = d * .3 # period
a = 1. # amplitude
if a < abs(c):
a = c
s = p / 4
else:
s = p / (2. * math.pi) * math.asin(c / a)
return (a * 2. ** (-10. * t) * math.sin((t * d - s) * (2. * math.pi)
/ p) + c + b)
class tdTweener(TweenerEquations):
“”“This class manages all active tweens, and provides a factory for
creating and spawning tween motions.
“””
def __init__(self):
self.current_tweens = []
self.default_tween_type = self.IN_OUT_QUAD
self.default_duration = 1.0
self.prev_time = time.time()
self.default_start_time = 0.0
def has_tweens(self):
"""Returns ``True`` if there are any tweens (paused or unpaused),
``False`` otherwise. This function can be useful to determine if a
redraw should be done or not.
"""
return len(self.current_tweens) > 0
def add_tween(self, obj, **kwargs):
"""Returns Tween object or False
Example::
tweener.add_tween(my_rocket, throttle=50, set_thrust=400,
tween_time=5.0, tween_type=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.
* ``tween_time``: the duration of the motion
* ``tween_type``: a predefined tweening equation or your own function
* ``on_complete_function``: called on completion of the tween
* ``on_update_function``: called every time the tween updates
* ``tween_delay``: a delay before starting.
"""
if (len(self.current_tweens) == 0): self.prev_time = time.time()
if "tween_time" in kwargs:
t_time = kwargs.pop("tween_time")
else:
t_time = self.default_duration
if "start_time" in kwargs:
print(‘HAS START TIME’)
t_start_time = kwargs.pop("start_time")
else:
print(‘NO START TIME’)
t_start_time = self.default_start_time
if "tween_type" in kwargs:
t_type = kwargs.pop("tween_type")
else:
t_type = self.default_tween_type
if "on_complete_function" in kwargs:
t_complete_func = kwargs.pop("on_complete_function")
else:
t_complete_func = None
if "on_update_function" in kwargs:
t_update_func = kwargs.pop("on_update_function")
else:
t_update_func = None
if "tween_delay" in kwargs:
t_delay = kwargs.pop("tween_delay")
else:
t_delay = 0
tw = Tween(obj, t_time, t_start_time, t_type, t_complete_func, t_update_func, t_delay,
**kwargs)
if tw:
self.current_tweens.append(tw)
return tw
def remove_all_tweens(self):
"Stops and removes every tween associated with this tdTweener object."
for i in self.current_tweens:
i.complete = True
self.current_tweens = []
def remove_tween(self, tween_obj):
"Stops and removes the Tween instance passed."
if tween_obj in self.current_tweens:
tween_obj.complete = True
self.current_tweens.remove(tween_obj)
def get_tweens_affecting_object(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.current_tweens:
if t.target is obj:
tweens.append(t)
return tweens
def remove_tweening_from(self, obj):
"""Stop tweening an object, without completing the motion or firing the
complete_function.
"""
# TODO: This loop could be optimized a bit
for t in self.current_tweens[:]:
if t.target is obj:
t.complete = True
self.current_tweens.remove(t)
def update(self, time_since_last_frame=None):
"""Update every 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.
``time_since_last_frame`` is the change in time in seconds. If no
value is passed, the change in time is measured with time.time() from
the previous call of this function. If it is the first time calling
this function, timing is measured from the construction of the
tdTweener engine.
"""
print ('update start:', len(self.current_tweens))
current_time = time.time()
if time_since_last_frame is None:
time_since_last_frame = current_time - self.prev_time
self.prev_time = current_time
for t in self.current_tweens:
t.update(time_since_last_frame)
if t.complete:
self.current_tweens.remove(t)
print ('update end:', len(self.current_tweens))
class Tween(object):
def init(self, obj, tduration, t_start, tween_type, complete_function,
update_function, delay, **kwargs):
“”“Tween object:
Can be created directly, but much more easily using
tdTweener.add_tween(...)
“””
self.duration = tduration
self.tstart = t_start
self.delay = delay
self.target = obj
self.tween = tween_type
self.tweenables = kwargs
self.delta = 0
self.complete_function = complete_function
self.update_function = update_function
self.complete = False
self.t_props = []
self.t_funcs = []
self.paused = self.delay > 0
self.decode_arguments()
def decode_arguments(self):
"""Internal setup procedure to create tweenables and work out how to
deal with each
"""
if len(self.tweenables) == 0:
# nothing to do
raise BaseException("No Tweenable properties or functions defined")
self.complete = True
return
for k, v in self.tweenables.items():
# check that it's compatible
if not hasattr(self.target, k):
raise AttributeError(str(self.target) + " has no function " + k)
self.complete = True
continue
prop = func = False
start_val = 0
change = v
var = getattr(self.target, k)
if hasattr(var, "__call__"):
func = var
func_name = k
else:
start_val = var
change = v - start_val
prop = k
prop_name = k
if func:
try:
get_func = getattr(self.target,
func_name.replace("set", "get"))
start_val = get_func()
change = v - start_val
except:
# no start value, assume its 0
# but make sure the start and change
# datatypes match :)
startVal = change * 0
tweenable = Tweenable(start_val, change)
new_func = [k, func, tweenable]
self.t_funcs.append(new_func)
if prop:
tweenable = Tweenable(start_val, change)
new_prop = [k, prop, tweenable]
self.t_props.append(new_prop)
def pause(self, seconds = -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 = seconds
def resume(self):
"Resume from pause"
if self.paused:
self.paused = False
def update(self, ptime=None):
"""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. ptime is the change in time in seconds.
"""
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.update_function:
self.update_function()
return
self.delta = min(self.delta + ptime + self.tstart, self.duration)
self.tstart = 0
if not self.complete:
for prop_name, prop, tweenable in self.t_props:
setattr(self.target, prop,
self.tween(self.delta, tweenable.start_value,
tweenable.change, self.duration))
for func_name, func, tweenable in self.t_funcs:
func(
self.tween(self.delta, tweenable.start_value,
tweenable.change, self.duration)
)
if self.delta == self.duration:
self.complete = True
if self.complete_function:
self.complete_function()
if self.update_function:
self.update_function()
def get_tweenable(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.get_tweens_affecting_object(my_rocket)[0]
tweenable = twn.get_tweenable("thruster_power")
tweener.addTween(tweenable, change=1000.0, tween_time=0.4,
tween_type=tweener.IN_QUAD)
"""
ret = None
for n, f, t in self.t_funcs:
if n == name:
ret = t
return ret
for n, p, t in self.t_props:
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(object):
def init(self, start, change):
“”“Tweenable:
Holds values for anything that can be tweened
these are normally only created by Tweens
“””
self.start_value = start
self.change = change
[/code]
Just point self.tweener at tdTweener.tdTweener() and call self.tweener.update() in your enterframe or exitframe of an execute DAT. Useage example:
self.tweener.addTween(op('transform1').par.tx, val=0.25, tweenTime=4)
does a 4 second tween from whatever tx currently is to 0.25 using the default in/out quad function.