Tweening by script, can't pass references

I’m working on a game that has little floating video previews, click to select one and it glides and scales to a “play” position and then starts. For the game engine, I’m using an execute DAT and pyTweener, calling the engine’s play() at frameEnd(). To get past all the errors I’ve been hitting, I’ve given my engine a tx, ty, and s that I apply to my Transform CHOP in my play(). It’s totally not scaleable, as I’m going to need to manage about 10 of these when I’m done (and they’re likely going to need alpha fades in and out, etc). Maybe I’ll post what’s working now first, then my failures; for non-tweeners, the addTween function takes an object reference, then property=target, and you can set timing, oncomplete, etc:

[code]import sys
sys.path.append(‘C:\Users\touchdesigner\Documents\tweening\’)
import pyTweener

class Engine(object):

def __init__(self):
	self.tweener = pyTweener.Tweener()
	op('transform1').par.tx = -0.05
	self.tx = -0.05
	self.resetXTween()
	op('transform1').par.ty = 0
	self.ty = 0
	self.resetYTween()
	self.s = 0.1;
	self.resetSTween()

def play(self):
	self.tweener.update(1/60)
	op('transform1').par.tx = self.tx
	op('transform1').par.ty = self.ty
	op('transform1').par.sx = self.s
	op('transform1').par.sy = self.s

def resetXTween(self):
	print('resetXTween')
	if (self.tx < 0):
		self.tweener.addTween(self, tx=0.25, tweenTime=4, onCompleteFunction=self.resetXTween)
	else:
		self.tweener.addTween(self, tx=-0.25, tweenTime=4, onCompleteFunction=self.resetXTween)

def resetYTween(self):
	print('resetYTween')
	if (self.ty > 0.1):
		self.tweener.addTween(self, ty=-0.25, tweenTime=2, onCompleteFunction=self.resetYTween)
	else:
		self.tweener.addTween(self, ty=0.25, tweenTime=2, onCompleteFunction=self.resetYTween)

def resetSTween(self):
	print('resetSTween')
	if (self.s > 0.1):
		self.tweener.addTween(self, s=-0.05, tweenTime=3, onCompleteFunction=self.resetSTween)
	else:
		self.tweener.addTween(self, s=0.1, tweenTime=3, onCompleteFunction=self.resetSTween)

engine = Engine()

def start():
print (‘start’)
return

def create():
print(‘create’)
return

def exit():
print(‘exit’)
return

def frameStart(frame):
return

def frameEnd(frame):
engine.play()
return

def playState(state):
print (‘playstate’)
return
[/code]
So this works but I don’t like holding on to duplicate variables that then have to be reapplied over and over; I’d really rather do something like this:

self.tweener.addTween(op('transform1').par, ty=-0.25, tweenTime=2, onCompleteFunction=self.resetYTween) but that tells me that “td.Par object is not callable.”

...addTween(op('transform1'), ty=... obviously tells me that transform1 has no function ty,

...addTween(op('transform1'), par.ty=... says keyword can’t be an expression.

So (getting desperate), ...addTween(op('transform1').par.ty, val=... : nope, “‘float’ object is not callable”

My pythonoob is showing. Anyhow, I figured I could just accept having a lot of double variables, since tweening self.variables is working fine. But (one final attempt to cut a lot of lines out of my play()) when I tried checking “expression” and try to insert the expression “op(‘execute1’).engine.tx” (and a variety of other ways that say the same thing) into transform1’s tx it just gives me red x’es. I’m stuck having to assign all the parameters I’m playing with on every exitframe. Can I use variables from my engine variable in CHOP parameter expressions? Am I just totally approaching this the wrong way? I see how I can make this work with a long enough play() function and lots and lots of variables in my engine, but it seems like TD should be able to do what I’m trying to do.

ideas/tips? Thanks,
-richard (reading up on lists…)

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

Heavily based on caurina Tweener: Google Code Archive - Long-term storage for Google Code Project Hosting.

Modified piTweener:

GitHub - bgw/PiTweener: A fork of pyTweener (BSD Licensed)

which is a fork of pyTweener:

http://wiki.python-ogre.org/index.php/CodeSnippits_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.

Has anyone tried this with 099? Does it work? I have a background in Flash/AS3 programming and one of my favorite tools was GreenSock TweenMax (www.greensock.com). If I had tweenMax functionality in Touch Designer I would be over the moon with joy.

It should work but it will be really computationally slow compared to other ways of doing animations in TouchDesinger, like using CHOPs. If you’re just getting started, you can def use it to get going, but I’d recommend learning to control animations and channel data with CHOPs.