TouchDesigner | Modules as Reference Expressions

modHaving the flexibility to write expressions in reference fields for an operator is an incredibly powerful feature in TouchDesigner. This can free you from the necessity of doing computation outside of your reference, making for much tidier networks and programming. However, if you’ve ever tried to write a long expression, or a multi line logical test you might find yourself scratching your head about how to best make that work. Today we’ll look at how you can write a function as a module which you can then call in a parameter. First we’ll take a look at the basic pieces of this idea, and then we’ll explore what that might mean as a more complex exploration of this kind of idea.

One of the primary ingredients to this approach is to understand the basics of writing your own functions in Python. If this is new to you, start by look over these other examples:

Before we get in too deep, let’s first write a simple function in a text DAT, and see how that works in TouchDesigner. Let’s open up a new network, and add a Text DAT to our empty project. Now we’ll write a simple function. Write this following, or copy and paste, into your text DAT:

def simple():
 
    print( 'This is Hard coded text' )
    
    return

Great, but what is all of this? First we defined our function with the name simple and then we indicated that there aren’t any parameters that we want to change in the function by leaving our parentheses empty. Inside of our function we’ve written a print statement that is hard coded into our function – that is to say that we can only change this text by re-writing our function. If you try to run this text DAT as is, nothing should appear in your text port. What gives? Well, we’ve defined our function, but we haven’t actually asked TouchDesigner to run it – to do that, we need to call the function. We can do that by adding one more line of code to our text DAT:

def simple():
 
    print( 'This is Hard coded text' )
    
    return

simple()

Our last line calls the function we just defined. Now, if you run this DAT, you should see our text print in the text port.

mod2Congratulations, you’ve written your first Python Function here in TouchDesigner. This is all well and good, but lets push a little harder. Let’s change this to be a simple multiplication operation. Let’s make a simple function that converts feet to centimeters. If we do a little googling, we can quickly find that 1 foot is equal to 30.48 centimeters. Imagine we want to feed this function feet as a unit, and get centimeters back.

This time we can write a function that looks like this:

def simple( feet ):
 
    centimeters = feet * 30.48
 
    print( centimeters )
 
    return

This prints out centimeters, when we input feet:mod3

This is pretty good, but let’s look at two other ways to get information out of this function. Rather than just printing out a float (floating point number, a value with numbers after the decimal), what if we wanted this to print out a full statement like, “X feet is Y centimeters.” Let’s change our function so we can make that happen:

def simple( feet ):
 
    centimeters = feet * 30.48
 
    print( '%s feet is %s centimeters' %(feet, centimeters) )
 
    return

mod4

Alright, this is all well and good, but let’s consider that we just want to return a value, that we can then plug into something else. To do this, we’ll change our function, and then write a print command that integrates our new function. Here’s our new function:

def simple( feet ):
 
    centimeters = feet * 30.480000
 
    return centimeters

We can read this as completing the operation, and then returning the variable centimeters. We can now add the following line to our text DAT to actually print out our conversion string:

print( '%s feet is %s centimeters' %( 2, simple(2) ) )

mod5

Alright this is an awful lot of simple function writing, but not much integration work, what gives? Now that we have some fundamental pieces of function writing under  our belt, let’s look at what this means for using functions in parameter references.

Let’s start with a simple case where we hard code a value into our function. Let’s start by adding a text DAT to our network, lets also make sure we give it a unique name – I’m going to call mine simpleMod1. Here’s our simple function:

def simple():
 
    a = 100
 
    return a

We should have something like looks like this in our network:

simpleMod1

Great, now we just need to reference this in a parameter. Let’s add a constant CHOP into our network, and write the following reference:

me.mod.simpleMod1.simple()

Here we can see the syntax is:

  • me.mod – we’re asking for a module
  • simleMod1 – the name of the text DAT we’re referencing
  • simple() – the name of the function in the text DAT

Looking at this we should be excited to realize that we could write multiple functions in a single text DAT, and call them by name – we wont’ look at that here today, but feel free to experiment on your own. As long as everything’s working, you should now see that you’ve successfully retrieved your value from your function in your constant CHOP. You should have something that looks like this:

simpleMod2

Whew! Alright, we’ve now successfully written a function, that we’re retrieving in a constant CHOP. This, however, is highly impractical. You would probably never retrieve a constant value from a module like this – that said, it’s a helpful for us to see some of the fundamental elements that go into making something like this work. Now that we have some general concepts down, lets look at a few more complex examples.

We might imagine a circumstance where we want to perform a set of computations on values in a table, and then return them in a parameter. Let’s start by laying out all of the operators we need, First let’s add a Table DAT, a text DAT, and a constant CHOP. I’m going to name my text DAT simpleMod10. Let’s keep our initial function, but make one small change. Let’s change it to accept a parameter called val. That means our function should now look like this:

def simple(val):
 
    a = 10 * val
 
    return a

Now we’re ready to start cooking. In our table, let’s add a second row, and place two values in the two rows. You should have something like this:

table1

Now, we’ll write an expression in a constant CHOP that passes in a reference to these two cells to our function. In our constant CHOP, let’s write the following expression:

me.mod.simpleMod10.simple( op('table2')[0,0] )

Looking at what this might mean in English, we see that we’re calling the module simpleMod10 and the function called simple() we can also see that we’re passing it a value from table2 and the cell in row 0, column 0. Let’s do the same thing for the next row in our table. This means we should have something that looks like this:

multiTableRef

We’ve got simple referencing and value passing down, now let’s consider the circumstance where we want to use a slider in a control interface, but instead of a constant float we instead want to only get 4 values. From 0 – 0.25 we want the value 10, from 0.25 – 0.5 we want the value 15, from 0.5 – 0.75 we want the value 20, and from 0.75 – 1.0 we want 25. First let’s start by adding a few things to our network. First we’ll need a text DAT, a slider COMP, and a constant CHOP. Let’s write our function in our Text DAT. I’m going to name my Text DAT simpleMod8. We can use some simple if and else if statements return different values based on our ranges. With that in mind our function should look something like this:

def simple(val):
 
    if val < 0.25:
        a = 10
    elif val < 0.5:
        a = 15
    elif val < 0.75:
        a = 20
    elif val <= 1.0:
        a = 25
    
    return a

Next, in our constant we’ll use the following expression to make sure that we pass in the value from our slider:

me.mod.simpleMod8.simple( op( 'slider1/out1' )[ 'v1' ] )

There we go, we should now end up with values that we’ve specified based on a logical test in our function. You should have something that looks like this:

sliderOutputVals

Returning values is excellent, but we can also return strings using this same method. Let’s set up another similar example. In this one we’ll need a slider COMP, a Text DAT, an Eval DAT, and a Text TOP. Let’s start with our function from the last example, but change our returned values to strings.

def simple(val):

    if val < 0.25:
        a = 'apple'
 
    elif val < 0.5:
        a = 'kiwi'
 
    elif val < 0.75:
        a = 'orange'
 
    elif val <= 1.0:
        a = 'grape'

    return a

Now in both the text field of our Text TOP, and in the expression field of our Eval DAT let’s use the following expression:

me.mod.simpleMod9.simple( op('slider2/out1')['v1'] )

We should have something that looks like this, and as the slider moves returns strings:

stringReturn

stringReturn

Alright, last but not least, let’s look at how we might use this same idea to test strings and return values. This time around we’ll want a Text DAT, a field COMP, and a constant CHOP. I’m going to name my Text DAT simpleMod7. Using the same ideas we’ve explored already, let’s write a function that tests our field COMP’s output and returns an integer.

def simple( val ):

    if val == 'kiwi':
        a = 15
 
    elif val == 'apple':
        a = 10
 
    else:
        a = 0

    return a

This should feel like a pretty routine drill now, let’s set our constant to use the module and pass in our field value with the following expression:

me.mod.simpleMod7.simple(val = op( 'field1/out1' )[ 0,0 ])

With everything up and working, you should see something like this:

stringReturnsInt

The way our function works, if we type ‘apple’ or ‘kiwi’ into our field, we’ll see 10 or 15 returned. In all other cases, we’ll see 0.

stringReturnsInt

If you want to see a set of examples head over to GitHub and look through the source examples.

TD-Examples on GitHub

TouchDesigner | Getting More out of the CHOP Execute DAT

One of the most challenging elements for me to wrap my heard around when using TouchDesigner was the Execute DATs. When scripting was still new to me, it was hard to understand where to start, or what to write. It was also hard to understand what kind of options I had when dealing with the execute family of DATs. Let’s take a closer look at some of the interesting elements that you can extract from a CHOP Execute DAT, and hopefully learn a few things along the way.
LFO Trail

CHOP Execute DATs allow us to run a script at particular events. The functions that you see in the CHOP Execute specify what kind of event, and what kind of information gets passed into those events that you might take advantage of. Looking at the CHOP Execute DAT we can see the following kinds of events that we might want to take advantage of:

  • Off to On
  • While On
  • On to Off
  • While Off
  • Value Change

Screenshot_061015_113742_PM

That’s all well and good, but what do those things actually mean? Let’s create a simple script to see what we end up with. We’ll work mostly with the Off to On Definition – with what we learn here you can start to explore what the other definitions do (when I say definitions, I’m mean the portions of the Execute DAT that begin with def).

Before we can get started we need a CHOP in our network to associate with our CHOP Execute DAT. Let’s add an LFO CHOP, and a CHOP Execute DAT to our network.

Screenshot_061015_114358_PM

Next we need to associate our CHOP with our DAT. We can do this by dragging the LFO CHOP onto the CHOP Execute DAT.

associate CHOP to DAT

Next we need to make sure that in the parameters of our Execute DAT that the flag which corresponds to our definition is turned on.

Screenshot_061015_114629_PMWith my default values, I can see that something isn’t quite right. Here my “Value Change” Flag is turned on, I want to turn off that flag and turn on the Off to On Flag. The parameters for your CHOP Execute should now look like this:

Screenshot_061015_114822_PM

Alright, now that we’re just about set up let’s see what happens when we write some scripts for our Execute DAT. The simplest place for us to start might be to just print a string. Let’s add this line to inside of our definition:

print('Hello World')

Because this is inside of a defined function, we’ll also need to make sure that it’s indented. The whole function should look like this in your DAT:

def offToOn(channel, sampleIndex, val, prev):
    
    print('Hello World')
    
    return

In TouchDesigner that looks like this:

Screenshot_061015_115603_PM

That’s all well and good, but what does that do? Well, if we open up our text port we should see that ever time we cross the 0 threshold in our LFO that we print “Hello World” to the text port.

Hello World

That in itself isn’t very excited, but we might imagine a situation where we want to run a script at a given regular interval, and this is exactly the kind of technique we might use to make that happen. You might also notice that there are several other parameters for this function that we might be interested in.

Before we get in too deep, lets remember to read what’s already in our DAT. We can see that the header comment reads:

# me is this DAT.
# 
# channel is the Channel object which has changed.
# sampleIndex is the index of the changed sample
# val is the numeric value of the changed sample.
# prev is the previous sample value.
# 
# Make sure the corresponding toggle is enabled in the CHOP Execute DAT.

In general I like to think about any scripting with the following kind of mantra – “When in Doubt, Print it Out.” With that in mind, let’s just print out these parameters to see what they are. To do that, we’ll change our function to read like the following:

def offToOn(channel, sampleIndex, val, prev):
 
    print('channel is ' , channel)
    print('sampleIndex is ', sampleIndex)
    print('val is ', val)
    print('prev is ', prev)
 
 return

The results in our text port should look something like this:

Screenshot_061115_121305_AM

This should give us a sense of the kinds of information that we can pull out of our Panel Execute DAT. Before we stop, let’s push a little harder on one of these ideas. First we need to make some changes to our LFO. Let’s first use some pattern matching to add some more channels. We can do this on the Channel Page of our CHOP by specifying that our Channel Names should be chan[1-5]:

Screenshot_061115_122708_AMNext, lets give these unique all a unique phase with the following python expression:

me.chanIndex/me.numChans

Screenshot_061115_122846_AM

Alright, now we should have an LFO that looks something like this:

LFOCHOP

Let’s change up our Execute DAT to see what kinds of interesting information we might be able to extract from this. First let’s just see if we can retrieve the channel index as our wave passes through zero. We can do this by changing our function to read:

def offToOn(channel, sampleIndex, val, prev):
 
    print(channel.index)
 
    return

In TouchDesigner that might look like this:

Screenshot_061115_123416_AMThat’s pretty interesting, but what if we only want to print out a line of text when a certain set of conditions are met? We might imagine a scenery where we want to first match a logical test, then run a script.

Let’s try writing something like this:

def offToOn(channel, sampleIndex, val, prev):
 
    if channel.index == 0:
        print('This will only appear when index 0 fires')
 
    if channel.index == 1:
        print('This will only appear when index 1 fires')
 
    if channel.index == 2:
        print('This will only appear when index 2 fires')
 
    if channel.index == 3:
        print('This will only appear when index 3 fires')
 
    if channel.index == 4:
        print('This will only appear when index 4 fires')
 
 return

We’ve now set up a series of tests where we’ll only see a particular line of text when the if conditions are met. As a result we should see this in the text port:

textPortText

This may initially seem unimpressive, but when you begin to imagine how you might combine the power of CHOPs with the extensible nature of scripting you soon end up with a world of options that wasn’t present before.

Looking for the Example Files? You can find them here on GitHub.

TouchDesigner | Make a Copy of that Table

When working with DATs there are often a whole host of operations that we want to be able to take advantage of that don’t make sense when we initially look at the documentation on the TouchDesigner Wiki. One simple example of this is how copy operations work. A clear example of this might be how copy operations work. For example, I have a table full of information but I’d like to make a copy of it for another part of my network. If we look at the Table DAT Class wiki page we see this:

copy(DAT)

Copy the text or table from the specified DAT operator.

  • OP – The DAT operator whose contents should be copied into the DAT.

How can we make sense of this?

Let’s start by first setting up a simple table – you can fill it with any information that you’d like. We also want an empty Table DAT, as well as a Text DAT.

Screenshot_060615_065054_PM

In our Text DAT we’re going to write the following script:

source = op('table1')
target = op('table2')

target.copy(source)

Looking at the syntax of the operation we can see what we first defined two variables – target and source, named as such to make it easier to see what’s happening. Next we start with the target and then specify what operator we’d like to copy. Now we can right click on the text DAT, run our script, and voila. A little copy magic.

anatomy of a copy script

Need to see it action? Look through the example files on Git Hub.

TouchDesigner | Drop Scripts

We all love some drag and drop action – we use it all the time, and in fact have often come to expect it to be a feature in nearly every application we interact with. With that in mind, how can we think about integrating some drop actions into our work in TouchDesigner? Well, today is your lucky day! Here we’re going to look at some of the fundamental ideas that will help you get started with much more complicated drag and drop functions.

Anatomy of a Drop Script

Let’s start by setting up a container to perform some actions based on when we drop a file onto it – this will help us get our bearings, and see how dropping works. First we need to set up our container to work with a drop script. First add a new container to a network, next let’s go to the Drag page of the container, and look at the sub page labeled “On Dropping into this Component.”

Screenshot_053015_095734_PM

We’ll start by first changing the drop down menu here to be “Allow Drop.” Next we’ll need to specify what script to run when something is dropped onto this Component. Let’s plan to call our script dropScript, and we’ll place it inside of our container. This means that we can specify it’s relative path and name as

./dropScript

Now we should have something that looks like this:

Screenshot_053015_100204_PM

Now we’re ready to start working. First let’s navigate inside of our container, then split our work-space left and right, and open up a text port on one side of our work-space. Inside of container1 let’s add a new Text DAT and call it dropScript – just like we had planed earlier.

Screenshot_053015_100509_PM

To get started, let’s first write a simple script so we can see this whole thing in action. In our Text DAT let’s use the simple script:

print('Hello World')

Screenshot_053015_100738_PM

To this in action, let’s open the viewer for our container and drop a file onto it – we’ll want to keep an eye on our text port as well, because this is where our print command will show up.

drop

Perfect. Here we can see that when we drop something onto our container, low and behold our script runs. This is great, but wouldn’t it be slick if we could instead get the pathway to our file? In fact, we can get this! Let’s change out drop script to this:

print( args )

Now when we drop something on to our container we get a list that should look something like this:

('E:/Dropbox/Camera Uploads/2015-05-26 10.55.18.jpg', '0', '0', '0', '1', 'jpg', '2015-05-26 10.55.18', '/container1')

The first item in this list is what we’re looking for – it’s the pathway to where our dropped file lives on our computer. Let’s change our drop script so we only get that first item in our list.

print( args[0] )

Now when we drop a file onto our container we only get the pathway to the file.

getAFile

That’s all well and good, but what can we do with that?

Changing a Movie File In TOP

Let’s set up a few other items in our container so we can really see this in action. Let’s add a Movie File In TOP, wire that to a Fit TOP, and finally to a Null TOP.

Screenshot_053015_102336_PM

Set the Fit TOP resolution to match that parent container with following expressions:

parent().par.w
parent().par.h

Screenshot_053015_102639_PM

Finally, let’s name the null bg, and set the parent to display this as the background TOP.

Screenshot_053015_102751_PM

Now we’re ready to return to our drop script. Let’s change our drop script to set the parameter “file” on the Movie File In TOP to be the path from args. In python that looks something like this:

op('moviefilein1').par.file = args[0]

If you’re following along at home, you might want to consider adding some comments and breaking this up slightly. It may make your code a little more verbose, but when you come back to it later on it will be easier to decode what’s actually going on here. That might end up reading like this:

# identify the movie file in 
# operator that I want to change
movTarget = op('moviefilein1')

# make a new variable called newFile
# assign the args[0] string to this variable
newFile = args[0]

# set the file paramater for the target op
# to be newFile - this means that when I drop
# an image onto this container, that file
# will become the background
movTarget.par.file = newFile

Commenting isn’t always the most fun part of programming, but when you come back to a project or script it can make it much easier to remember what you were thinking and how you were working.

Now, when we drop a file onto our container, we should see the background file change as well.

DropAction

Changing a Directory

That’s great, but how can we push this a little farther? Well, we might imagine that we find ourselves in a situation where we’d like to change the target directory for a Folder DAT. While the Choose a Directory example works well, it you might not want the hassle of navigating to your folder – instead you might just want to drop it onto your container. We can use the same principles from above to do just that.

Let’s add a Folder DAT to our network. Now we’ll change our dropScript to be:

folderDAT = op('folder1')
folderDAT.par.rootfolder = args[0]

Now we can drag an entire folder onto our container and see the Folder DAT update.

folderDop

Bada bing! With a few basics under your belt, now you can have fun and and start to play with doing something exciting with drop scripts.

Want to take a look at some examples? You can find them here on GitHub

TouchDesigner | Choosing a Directory

Sometimes when you’re building an interface for an application you suddenly realize that you need what aught to be a simple feature. Time and again I’ve found myself wanting to be able to quickly select a directory for TouchDesigner to use for a bin of images or movie files. This is a pretty simple feature to add to your network, and we can do this with just a simple panel execute DAT and a few lines of Python. Let’s take a closer look at what goes into make this happen.

To get started we need a few initial ingredients in our network. We need a button (or something to trigger our script), a Folder DAT, and a Panel Execute DAT.

Screenshot_052415_013200_PM

Let’s start by assigning our Button to our Panel Execute DAT. We can do this by editing the Panel field in the Panel Execute, or by dragging and dropping our button onto our Panel Execute DAT.

Screenshot_052415_013334_PM

or

panelExecute Assign

Either of these methods work, and it really just comes down to a matter of personal preference. Next let’s make sure that we’ve set our button to have a “Momentary” Button Type. We don’t want a button that toggles on and off, in this case we just want a button that fires a single pulse when we click on it.

Screenshot_052415_013817_PM

Now we’re finally ready to write a little bit of python. The effect that we’re looking for is to open up a windows dialog box that allows us to select a directory for our folder DAT. To do this, we’re going to use the UI Class in TouchDesigner. They is a ton we can do with this class, and folder or file selection is one of those things. Let’s set up our Panel Execute DAT so that the Off to On Flag is on, and the panel value that we’re watching for is “state.”

Screenshot_052415_014122_PM

We’re going to edit the offToOn definition in this DAT, so it’s important to make sure that you’re editing the correct portion of your script. The first thing we want to do is create a new variable called “userFolder.” Now, in our case, we want this variable to equal to the string that is the pathway to the directory that we want. That’s going to look something like this:

userFolder = ui.chooseFolder(title = 'Choose a Folder')

Inside of our single ticks (or you can use quotes) is the name that we want to see on the pop up window. This is a great start, but at this point we’re not actually assigning our selected directory to our folder DAT. Before we get there we need to consider the following: What happens if I change my mind about selecting a new directory, and just hit cancel? When you do this, instead of a directory being returned, the windows selection dialogue will return a “None” from your folder selection. That’s great, but this could mean that in the middle of a set, you suddenly find that when you hit cancel you loose the directory that you previously navigated to. To prevent this from happening, we can use a simple if test to decide what we’re going to do. In our case, if we get “None” back from our selection, we just want to leave our directory as it is, otherwise we want to change it. Okay, what might that look like?

 userFolder = ui.chooseFolder(title = 'Choose a Folder')
 
 if userFolder == None:
 op('folder1').par.rootfolder = op('folder1').par.rootfolder
 
 else:
 op('folder1').par.rootfolder = userFolder

This is looking pretty good. Let’s review what’s happening here. First we select a new folder, next we do a quick test. If our userFolder is exactly “None” then our folder DAT root folder stays the same. If our userFolder is anything else, then we set the folder DAT root folder to our new address. Simple.

If you want to see this example in action, you can download it from GitHub here: TD Examples.