Tag Archives: TouchDesigner Tutorial

Python in TouchDesigner | Intro to Functions | TouchDesigner

Core Concepts

  • Functions as a concept
  • Anatomy of a function
  • Writing functions
  • Calling functions
  • Returning values
  • Passing arguments


Before we can tackle CHOP executes we need to take a moment to learn about functions. There’s a lot to learn about with functions, so we’re not going to dive too deep just yet… yet. We are, however, going to peer into this idea so we can better understand part of what we’ll see next as we move into the exciting world of executes.

Let’s start by looking at what a function actually is:

“A function is a block of organized, reusable code that is used to perform a single, related action. Functions provide better modularity for your application and a high degree of code reusing.

As you already know, Python gives you many built-in functions like print(), etc. but you can also create your own functions.”
Tutorials Point

Great! But… how can we better understand that? For a moment let’s first appreciate that we have a wide variety of functions that we do on a regular basis… we just don’t think of them as functions. Most of us know how to calculate a tip, or gas mileage, or estimate travel time, or pack a suitcase, or make lunch, or or or, and and and. We don’t think of these as functions, but if we had to write out very specific instructions about how to complete one of these tasks we’d actually be close to starting to wrestle with the idea of what a function is – it’s okay if that doesn’t make sense yet. Hang on tight, because we’re gonna get there.

Let’s first look at a simple example that examines the anatomy of a function. Next we’ll write a few simple functions. Then we’ll look at why that’s important when it comes to thinking about CHOP executes.

Starting with Anatomy.

Here we go, we’re going to write a dead simple function:

def first_function():
 
    print( 'Hello World' )

    return

There we go. We did it. Now, if we were to run this in TouchDesigner, nothing would happen… so at first glance it would seem like we didn’t really write a function after all. That might be a good guess, but the reason nothing happened is because we never actually called our function, we just defined it – we wrote out all of the instructions, but we never asked TouchDesigner to actually run the function. To see anything happen, we need to actually call the function – we need to tell TouchDesigner that we need to run it. Let’s modify our example to see what that would look like.

def first_function():
 
    print( 'Hello World' )

    return

first_function()

Okay… time to take this all apart and see what makes it tick.

  • We’re started out by indicating that we were going to define a function… that’s really what we meant when we wrote “def.”
  • Next we gave that function a name, in our case we called it “first_function.”
  • Next we specified that we weren’t going to pass in any arguments or parameters by writing “()” – don’t worry, we’re going to learn more about that in a second.
  • Then we indicated that we were going to outline what was in the function with our “:”
  • The next line is indented one tab space and here we print out “Hello World”
  • We ended the function with a return statement, which in this case didn’t return anything.
  • Finally, we summoned our function into action by saying its name… well, writing its name “first_function()”

At this point we’ve written a very simple function that just prints out “Hello World.” We started with this simple example so we could just talk about its anatomy. Before we can move on to something a little more interesting, we need to unpack a few things. Specifically, we need to talk more about what it means to *return* something, and what an argument or parameter is when it comes to functions.

Let’s start with *return*. Like it’s name suggests, to return something is to give it back, or deliver something. Seems straightforward enough, right? We might imagine that sometimes we don’t want to print out the result of a function, but we do want to get something out the other side to use in another process. In this case, we want something returned to us after the function has run. Let’s look at that in a concrete way.

We’re going to use our same example first function, but make a few changes.

def first_function():
 
    text = 'Hello World'

    return text

first_function()

Okay, here we can see that we changed our function so we don’t actually print out “Hello World” anymore, instead we return it at the end. If we run our function, we encounter our same problem that we saw earlier… it would seem as if nothing happened. What gives. Let’s change our function in one small way and see what we end up with:

def first_function():
 
    text = 'Hello World'

    return text

print( first_function() )

The small change to print out first_function() means that we’re now printing out what’s returned from this function. It might feel like a small difference, but it means that we’re able to control what comes out of our function when we summon it into action. That’s actually a very important thing, and we’ll see why shortly.

If we can control what comes out of our functions, surely we can control what goes into them… right? In fact, you are right.

Now that we now how to get something out of our function, let’s pass it some information do to something with. We’re going to write another simple function, this time to do some simple math.

def percent( val1 ):
 
    calculation = val1 * 0.01

    return calculation

print( percent( 50 ) )

Alright, what do we have here? Let’s imagine we want to change an integer into it’s float equivalent as a percentage. 50% as becomes 0.5, 10% would be 0.1, and so on and so on. Here we’ve written a function to do just that. In this case we’ve specified that our function accepts one argument which is named val1. We later see in our function that “calculation” is val1 * 0.01. Finally, we return calculation. This means we can give percent any number, and get a float value in return. Not bad.

Okay, let’s look at two more examples. Next we’ll write a simple function to calculate a tip based on a total bill. At the end of this we want to see our tip and our total bill – using our new found lingo, we’re going to return these values.

Okay, let’s make some Python magic happen. If you’re playing along at home, trying writing this yourself before you look at how I did it.

def tip_calculator( total , tip_percentage ):

    tip = total * ( tip_percentage / 100 )
    total_bill = total + tip
    return tip , total_bill

print( tip_calculator( 50 , 15 ) )

Here we want two things back, our tip and our total_bill. We start by calculating the tip, and then by adding that to our total. Finally we return these two values.

Let’s try one more idea on for size. This next time around you’re challenge is to use the function we just wrote, and to write another function as a compliment. This second function is going to print out these values to our text port so we can see them. By writing this as two separate functions we decide when we want to print out our results, and when we want to just return our tip and total_bill. As an extra challenge, see if you can write your new function to accept only a single argument.

Okay, let’s look at how you might solve this problem:

def tip_calculator( total , tip_percentage ):
    tip = total * ( tip_percentage / 100 )
    total_bill = total + tip
    return tip , total_bill

def display_total( tip_and_total_bill ):
    dotted_line = '- ' * 10
    tip_text = "Your total tip is {}"
    total_bill_text = "Your total bill is {}"
    print( dotted_line )
    print( tip_text.format( tip_and_total_bill[ 0 ] ) )
    print( total_bill_text.format( tip_and_total_bill[ 1 ] ) )
    print( dotted_line )

    return

total = 100
tip_percentage = 20

print( tip_calculator( total , tip_percentage ) )

display_total( tip_calculator( total , tip_percentage ) )

How did you do? We can see that our first function stayed the same. Our second function accepts a single argument – tip_and_total_bill. This tuple (a series of values) is then used by our second function when printing out to our textport. This probably isn’t the best way to solve this problem… but for the sake of a simple example our chances of getting into trouble are pretty slim.

Okay, so why do all of this?! Well, let’s take a sneak peak at what’s coming next. If we look at the contents of a CHOP execute we see:

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

def offToOn(channel, sampleIndex, val, prev):
    return

def whileOn(channel, sampleIndex, val, prev):
    return

def onToOff(channel, sampleIndex, val, prev):
    return

def whileOff(channel, sampleIndex, val, prev):
    return

def valueChange(channel, sampleIndex, val, prev):
    return

We should now recognize the contents of these DATs as functions… and not only are they functions, they’re functions with four named incoming arguments. Now we can really start to have fun.

Learn more about functions in Python

Download the sample files from github

Python in TouchDesigner | Data Structures – Dictionaries | TouchDesigner

Part 1 Core Concepts

  • Dictionaries – a structure and a concept
  • Looking at Dictionaries and Lists side by side
  • What are key value pairs
  • Retrieving values from dictionaries
  • Retrieving .keys() and .values()
  • Adding items to dictionaries
  • Nested data structures
  • A better text formatting approach with .format() (a big thank you to Willy Nolan for setting me on the right path with text formatting in Python 3)


Part 2 Core Concepts

  • Dictionaries – a structure and a concept
  • A practical look at dictionaries in TouchDesigner
  • Nested data structures – dictionaries in dictionaries
  • Using Dictionaries as a preset structure
  • Using Python to set parameters
  • Using Python variables in scripts to generalize our code


This isn’t the first time I’ve been on a tear about using Dictionaries in TouchDesigner – THP 494 & 598, Presets. That said, sometimes it’s easier to understand a concept if we back down a little bit and start from the beginning. With that in mind, let’s turn the speed down to 0.1 before we turn it up to 12 again.

Dictionaries are another type of data structure that we can use in Python. They’re similar to lists in that we can store information in them, and retrieve them easily. Dictionaries, however, are distinctly different from lists. Where lists are index based – which is to say that they have a specific order – dictionaries are key based.

What does that mean, and why do we care?! We think of dictionaries as being a pair of things a key, and a value. We might think of this as a name and its corresponding piece of information. Let’s look at something simple to get started. To get started let’s go back to our grocery example when we were talking about lists. In making a list for our trip to the grocery store we listed all of the items we needed from the store. We didn’t however, make any notes about quantity. Let’s quickly make that list again:

grocery_list = [ 'eggs' , 'milk' , 'bread' , 'butter' , 'coffee' ]

This is great, and it tells us lots of information, but maybe not all of the information we need. If I’m going to the store myself, this is a fine list. If I’m asking someone else to pick up these things for me, well then I need at least one other piece of information – quantity. If we’re using lists, we might do something clever, like make a list of lists with two items – the grocery item, and the desired quantity. That might look something like this:

grocery_list = [ 
    ['eggs' , '1 dozen' ] , 
    ['milk' , '1 pint' ] , 
    ['bread' , '2 loaves' ] , 
    ['butter' , '1 lb' ] , 
    ['coffee', '2 lbs' ]
]

This works fine, and might be a great way to hold onto this information. We can, however, use a dictionary to do this same thing. In this case we’re going to think of our grocery items as a keys, and quantities as values. Let’s look at what means:

grocery_list = {
    'eggs' : '1 dozen' , 
    'milk' : '1 pint' , 
    'bread' : '2 loaves' , 
    'butter' : '1 lb' , 
    'coffee': '2 lbs'
}

It’s important to note that I’ve used some indenting to make this easier to read, but another perfectly valid way to write this dictionary would be:

grocery_list = { 'eggs' : '1 dozen' , 'milk' : '1 pint' , 'bread' : '2 loaves', 'butter' : '1 lb' , 'coffee': '2 lbs' }

I just happen to think that anytime you can make something easier to read by a human, the better.

Okay, let’s talk about syntax here for a second. So the first thing we did was declare our dictionary as a variable. Next we used curly brackets to open our dictionary ( {} – these are curly brackets ). Next we wrote out our dictionary as key and value pairs separated by a colon – keys on the left, values on the right. Now in this example all of our values were strings, but they could just as easily have been integers, floats, booleans, lists, or even other dictionaries.

This is all well and good, but how do we get things out of our dictionary? We know how to retrieve things from a list, but a dictionary is a little different. When retrieving something from a dictionary we typically use a key. Let’s consider our first example again for a second. Let’s say we want to print out the quantity of eggs that we’re supposed to get from the store. We can do that like this:

print( grocery_list[ 'eggs' ] )

We can also retrieve the contests of dictionary with .keys() and .values():

print( grocery_list.keys() )
print( grocery_list.values() )

In both of these cases we get a list of keys or values.

It’s also important to know how to add items to our dictionary. There are a few ways to go about this, but let’s just look at one for now. We should start by creating an empty dictionary:

my_dictionary = {}

Now that we have an empty dictionary, we can add items to it. We do this by starting with the dictionary name, then placing the key in square brackets ([] these things), followed by an equal sign, and then what we want to be placed into the dictionary as the value. Let’s look at an example:

my_dictionary[ 'new_item1' ] = "cookies"

Okay, now that we’ve added one key value pair, let’s print out the keys and values in our list (to practice), and add a few more items:

my_dictionary = {}

my_dictionary[ 'new_item1' ] = "cookies"

print( my_dictionary.keys() )
print( my_dictionary.values() )

my_dictionary[ 'new_item2' ] = "cell_phones"

print( my_dictionary.keys() )
print( my_dictionary.values() )

my_dictionary[ 'new_item3' ] = 55

print( my_dictionary.keys() )
print( my_dictionary.values() )

my_dictionary[ 'new_item4' ] = [ 1 , 2 , 3 ]

print( my_dictionary.keys() )
print( my_dictionary.values() )

Wait… what did we just do there with item4?! Most of that should look pretty straightforward, and hopefully that makes sense for the most part. It is not, however, the most exciting part of using dictionaries. Dictionaries become the most exciting when we start to see how we can nest other lists or dictionaries inside of them.

Let’s look at a dictionary of mixed contents:

my_dictionary = { 
    "apple" : "these are delicious" , 
    "orange" : 12 , 
    "kiwi" : 55.5 , 
    "lots_of_things" : [
        "paper" , 
        "pens" ,
        44 , 
        10.4
    ] 
}

So we know how to get to the values associated with “apple” , “orange” , and “kiwi” , but how do we get to the contents of that list? Well, we can write something like this:

print( my_dictionary[ 'lots_of_things' ][ 0 ] )
print( my_dictionary[ 'lots_of_things' ][ 1 ] )
print( my_dictionary[ 'lots_of_things' ][ 2 ] )
print( my_dictionary[ 'lots_of_things' ][ 3 ] )

Here we see the same syntax that we use when retrieving list items.

That’s wonderful! So what about when we store dictionaries inside of dictionaries? Let’s look at a simple example. We can start by creating a dictionary with fruit’s as our keys. Each fruit will have a corresponding dictionary of quantity, origin, and if the fruit is organic. Okay, what would that look like:

my_dictionary_of_dictionaries = { 
    "apple" : {
        "quantity" : 10 ,
        "origin" : "Vermont" ,
        "organic" : True 
    } , 
    "orange" : {
        "quantity" : 20 ,
        "origin" : "California" ,
        "organic" : False
    } , 
    "kiwi" : {
        "quantity" : 26 ,
        "origin" : "Mexico" ,
        "organic" : False
    } , 
    "grapes" : {
        "quantity" : 50 ,
        "origin" : "Peru" ,
        "organic" : True
   }
}

That’s pretty snazzy and all, but how do we pull things out of this data structure? We can follow the example we learned with lists, but instead of using index values, we can instead use keys. Let’s look at just apple to get started:

print( "Let's just look at apple" )
print( "quanitity -" , my_dictionary_of_dictionaries[ 'apple' ][ 'quantity' ] )
print( "origin -" , my_dictionary_of_dictionaries[ 'apple' ][ 'origin' ] )
print( "organic -" , my_dictionary_of_dictionaries[ 'apple' ][ 'organic' ] )

Here we can see that we start with our dictionary, with a key in square brackets, followed by another key in square brackets. Practice retrieving other keys – what about grapes, or oranges? Also practice by adding other keys inside of the fruit dictionaries. Don’t forget to pay careful attention to where you’ve placed your commas and, remember that keys are strings so they need quotation marks.

Once you’ve done that, let’s consider how we might use something like a dictionary here in TouchDesigner. We’re going to look something a little complex, but still relatively simple to help us get our bearings. Dictionaries can be a great help to us when we want to do things like creating save states. Let’s first think about what it would mean to set the properties of a text TOP with the contents of a dictionary.

Let’s start by making our dictionary. I’m going to use the same names for our dictionary keys that we find in our parameter names – just to make sure we know exactly where a value is going.

top_dictionary = { 
    "text" : 'monkey' , 
    "fontsizex" : 15 ,
    "alignx" : 1 ,
    "aligny" : 1 ,
    "fontcolorr" : 1.0 ,
    "fontcolorg" : 0.0 ,
    "fontcolorb" : 0.0 ,
    "fontalpha" : 1.0 ,
    "bgcolorr" : 0.0 ,
    "bgcolorg" : 0.0 ,
    "bgcolorb" : 0.0 ,
    "bgalpha" : 1.0
}

Now let’s flesh out our script to change the parameters of our TOP:

target_text = op( 'text1' )

target_text.par.text = top_dictionary[ 'text' ]

target_text.par.fontsizex = top_dictionary[ 'fontsizex' ]
target_text.par.alignx = top_dictionary[ 'alignx' ]
target_text.par.aligny = top_dictionary[ 'aligny' ]
target_text.par.fontcolorr = top_dictionary[ 'fontcolorr' ]
target_text.par.fontcolorg = top_dictionary[ 'fontcolorg' ]
target_text.par.fontcolorb = top_dictionary[ 'fontcolorb' ]
target_text.par.fontalpha = top_dictionary[ 'fontalpha' ]
target_text.par.bgcolorr = top_dictionary[ 'bgcolorr' ]
target_text.par.bgcolorg = top_dictionary[ 'bgcolorg' ]
target_text.par.bgcolorb = top_dictionary[ 'bgcolorb' ]
target_text.par.bgalpha = top_dictionary[ 'bgalpha' ]

That’s pretty great – but goodness that’s a lot of work just to change some settings. How might we think about using this idea to create a preset system? We’re not that far off form this idea at this point, so let’s dig in a little deeper. To really make this work, we need to revisit our dictionary. Specifically, we need to encapsulate our presets inside another layer. We need to make them their own dictionary as a set of values for another key. For example, we might want a named structure like “preset1” , “preset2” etc. to be how we retrieve settings. Let’s change our dictionary to make that happen:

top_dictionary = { 
    "preset1" : {
        "text" : 'monkey' , 
        "fontsizex" : 15 ,
        "alignx" : 1 ,
        "aligny" : 1 ,
        "fontcolorr" : 1.0 ,
        "fontcolorg" : 0.0 ,
        "fontcolorb" : 0.0 ,
        "fontalpha" : 1.0 ,
        "bgcolorr" : 0.0 ,
        "bgcolorg" : 0.0 ,
        "bgcolorb" : 0.0 ,
        "bgalpha" : 1.0
    } ,
    "preset2" : {
        "text" : 'pig' , 
        "fontsizex" : 80 ,
        "alignx" : 1 ,
        "aligny" : 0 ,
        "fontcolorr" : 0.0 ,
        "fontcolorg" : 0.0 ,
        "fontcolorb" : 1.0 ,
        "fontalpha" : 1.0 ,
        "bgcolorr" : 1.0 ,
        "bgcolorg" : 1.0 ,
        "bgcolorb" : 1.0 ,
        "bgalpha" : 1.0
    }
}

Not bad. Now, how can apply these presets to our top? To do this we’re going to do one tricky thing. We’re going to write our scripts so that a python variable can stand in our first key. This will mean that we only need to change a single variable before re-running our script. That would look like this:

target_text = op( 'text1' )
dictionary_preset = 'preset2'

target_text.par.text = top_dictionary[ dictionary_preset ][ 'text' ]
target_text.par.fontsizex = top_dictionary[ dictionary_preset ][ 'fontsizex' ]
target_text.par.alignx = top_dictionary[ dictionary_preset ][ 'alignx' ]
target_text.par.aligny = top_dictionary[ dictionary_preset ][ 'aligny' ]
target_text.par.fontcolorr = top_dictionary[ dictionary_preset ][ 'fontcolorr' ]
target_text.par.fontcolorg = top_dictionary[ dictionary_preset ][ 'fontcolorg' ]
target_text.par.fontcolorb = top_dictionary[ dictionary_preset ][ 'fontcolorb' ]
target_text.par.fontalpha = top_dictionary[ dictionary_preset ][ 'fontalpha' ]
target_text.par.bgcolorr = top_dictionary[ dictionary_preset ][ 'bgcolorr' ]
target_text.par.bgcolorg = top_dictionary[ dictionary_preset ][ 'bgcolorg' ]
target_text.par.bgcolorb = top_dictionary[ dictionary_preset ][ 'bgcolorb' ]
target_text.par.bgalpha = top_dictionary[ dictionary_preset ][ 'bgalpha' ]

Alright. Looking closely at the above, we can see that we need only change the variable “dictionary_preset” in order to fetch a whole different set of values. Not bad, right?

Take some time to experiment with these ideas. As we head forward we’re going to start to look at how we can use executes and for loops to see how we can really start to make headway in using Python. We’ve laid a lot of ground work so we can really plow ahead.

Learn more about Python Data Structures

Download the sample files from github

Python in TouchDesigner | Data Structures – Lists | TouchDesigner

Part 1 Core Concepts

  • Lists – a structure and a concept
  • Creating lists – syntax and structure
  • Retrieving items from a list – Syntax
  • Adding items to a list .append() and .extend()
  • Lists of Lists


Part 2 Core Concepts

  • Lists – a structure and a concept
  • Why Lists matter in TouchDesigner
  • The Channel Class – seeing CHOPs as lists
  • The Point Class – thinking of geometry as lists
  • The COMP Class and .findChildren() – pulling apart returned lists
  • More about how to read the TouchDesigner wiki


Lists are the bees knees, they’re the cat’s pajamas, they’re almost better than sliced bread. There are a few important things for us to think about before we dive into the Python of lists. Python lists are just like the lists you might make on a piece of paper. They’re a sequential ordering of items. A grocery list might be:

  • eggs
  • milk
  • bread
  • butter
  • coffee

We often make lists, and while the order of our grocery list might be arbitrary, there are plenty of lists that are not. Frequently a todo list has a specific order:

  1. Have preliminary discussion with collaborators
  2. Check schedule for availability
  3. Block off time for new project
  4. Coordinate schedules
  5. Build a preliminary budget
  6. Draft contracts
  7. Confirm costs
  8. Book space
  9. Purchase equipment

While this is a silly example, the important consideration is that here you wouldn’t purchase equipment before you started a preliminary discussion with your collaborators. Of course that seems obvious – but remember that you have a sense of linearity, a sense of time, a sense of order, and a idiomatic frame that you subconsciously constructed based on the content of the list items. Alright, semiotics aside, the more important idea here is that lists have order. Now while that may seem obvious, we’ll see later that dictionaries don’t necessarily work in the way – and in fact this is an important distinction we need to make early on.

Let’s go back to our grocery list. What might that look like in Python?

grocery_list = [ 'eggs' , 'milk' , 'bread' , 'butter', 'coffee' ]

You’ll notice that our items are enclosed in matching foot or inch marks: ” or “”. We can remember back to our first lesson on printing that this helps us see that these are strings. That’s wonderful. What if we want to print the whole list? Well we can do this:

print( grocery_list )

That prints our whole list. That’s pretty swanky, but what if we just want a single item from our list? How can we just print that? Well, we’ll remember that 0 is still a number for us here in Python. That means the indexing of our list items looks like: 0 1 2 3 4

We can print a single item in our list by indicating the index of the item we want:

print( grocery_list[ 0 ] )

Let’s look at that a little more closely and print out all of the items we have in our list:

print( grocery_list[ 0 ] )
print( grocery_list[ 1 ] )
print( grocery_list[ 2 ] )
print( grocery_list[ 3 ] )
print( grocery_list[ 4 ] )

Let’s go one step further and really make that as explicit as possible – just to make sure we understand.

print( "The item in the 0 position of our list is %r" % grocery_list[ 0 ] )
print( "The item in the 1 position of our list is %r" % grocery_list[ 1 ] )
print( "The item in the 2 position of our list is %r" % grocery_list[ 2 ] )
print( "The item in the 3 position of our list is %r" % grocery_list[ 3 ] )
print( "The item in the 4 position of our list is %r" % grocery_list[ 4 ] )

We can make lists out of just about anything. Let’s make a list out of all of the data types we’ve talked about so far:

my_int_list = [ 1 , 2 , 3 , 4 ]
my_float_list = [ 1.235 , 1.5679 , 9.454 , 4.23485 ]
my_string_list = [ 'apple' , 'kiwi' , 'orange' , 'pineapple' ]
my_bool_list = [ True , True , False , True, False ]
my_mixed_list = [ 1.234 , 5 , 'apple' , True , 3.45 ]

One question we might have is how long is our list? Well, there happens to be an easy way for us to figure that out with len() – as in length.

len( my_int_list )

Practice printing the length of all of your lists.

We can also build lists from scratch. First we need to create an empty list.

my_list = []

print( 'As we go, we will print our list at each' )
print( 'step along the way' )
print( 'My List' , my_list )

Next we can add items to our list with .append( theValueOrStringToBeAddedHere ).

my_list.append( 1 )

# this is here to make a line break
print( '\n' )

print( 'So we just added a single number out our list' )
print( 'what does that look like now?' )
print( 'My List' , my_list )

We can even add multiple items at once with .extend( aListofItemsHere ):

my_list.extend( [ 45 , 2 , 100 , 6 ] )

# this is here to make a line break
print( '\n' )

print( 'Can we add multiple items at once?' )
print( 'My List' , my_list )

print( 'We sure can, we just need to use .extend' )
print( 'instead of .append' )

That’s great… but what does this mean for me in TouchDesigner? Well, in Touch many things are returned as lists. Samples in CHOPs are often a list, as are points in a SOP. Once we have a fundamental understanding of lists as a data structure we can start to really have a lot of fun.

Let’s look at CHOPs first.
First, make sure you add a noise CHOP to your network called noise1.

# define some variables
noise1 = op( 'noise1' )

# understanding the channel operator make
# a big difference in the way we use TouchDesigner
# lets start by just printing our variable

print( 'If we just print our noise1 variable we see this' )
print( noise1 )

print( 'If we print chan1 in noise1 one we see this' )
print( noise1[ 'chan1' ] )

print( 'we can also access this by using .chan( channelIndexHere )' )
print( noise1.chan( 0 ) )

print( 'Finally, we can see the whole list of values if we use' )
print( '.vals as we... that looks like') 
print( 'noise1.( channelIndexHere ).vals' )
print( noise1.chan( 0 ).vals )

That’s pretty fun… but let’s take that a step further.

# okay, but why do we care?

# define some variables
noise1 = op( 'noise1' )

# we can use what we've learned working with the .chans().vals
# to help us understand a little bit more about our CHOP
# for example, if our channel is a list of values, we can
# access those values just like we might in a list

print( noise1[ 'chan1' ][ 0 ] )
print( noise1[ 'chan1' ][ 1 ] )
print( noise1[ 'chan1' ][ 2 ] )

# we can even do the same things we might do in python here
print( len( noise1[ 'chan1' ] ) )

# though if we look at the wiki, we'll find that there's already
# a method to do just this called .numSamples
# and a method called numChans - which tells us how many channels
# If we think of our CHOP as a list of lists... then we can both
# see how many lists, and the length of the lists.

print( noise1.numSamples )
print( noise1.numChans )

Next let’s add a rectangle SOP to our network.

# define some variables
rectangle1 = op( 'rectangle1' )

# That's great... but what about geometry?
# Let's take a closer look at SOPs

print( 'Like with a sop we can print the path to rectangle1 operator' )
print( rectangle1 )

print( 'We can also look at the member .points' )
print( rectangle1.points )

print( 'Seeing that it is an object by itself, means we can look closer' )
print( 'What happens if we just ask for the first item in this object?' )
print( rectangle1.points[ 0 ] )

print( 'What if we ask to make the whole object a list, and the print it out?' )
print( list( rectangle1.points ) )

While CHOPs and SOPs seem like obvious operators that might have lists, they’re certainly not the only ones. The method .findChildren returns a list of operators when dealing with COMPs. Let’s take a closer look at that while we’re at it. I started by making a container and adding three buttons inside. Make sure that you look at the example file to see what I’ve done to get started.

# define some variables
radio_buttons = op( 'container_radio_buttons' )

# Let's take a look at findChildren
# we can see all of the ops inside of our container with:
print( radio_buttons.findChildren() )

# What if we only wanted to see the buttons??
print( radio_buttons.findChildren( depth = 1 ) )

# That's fine as long as there aren't any other operators
# inside of our conatiner. If we wanted to make sure we only
# got a list of buttons, we could be even more specific with

print( radio_buttons.findChildren( type = buttonCOMP , depth = 1 ) )

# Okay... so?
# Well, what we get back is a list, so what if we did this?

print( radio_buttons.findChildren( type = buttonCOMP , depth = 1 )[ 0 ] )

# Maybe we don't want to see the whole path, we just want to see it's name
print( radio_buttons.findChildren( type = buttonCOMP , depth = 1 )[ 0 ].name )

# Or maybe just its digits
print( radio_buttons.findChildren( type = buttonCOMP , depth = 1 )[ 0 ].digits )

# We could even click on one of our buttons this way
radio_buttons.findChildren( type = buttonCOMP , depth = 1 )[ 0 ].click()

Lists are powerful and also flexible data structures. And this is only the start of what we can do with them. Practice making some lists, accessing their contents, and printing out pieces of them.

Learn more about data structures in Python

Download the sample files from github

Advanced Instancing | Puzzle Pieces | TouchDesigner

Part 1

Core Concepts

  • Instancing geometry
  • Replicators and Clones
  • The Sort SOP
  • Images composed out of component pieces
  • Real time rendering


Part 2

Core Concepts

  • Instanced geometry from pixel data
  • Texture Arrays – the 3D Texture TOP
  • The Sort SOP
  • Images composed out of component pieces
  • Real time rendering

Advanced Instancing | Instancing with the Animation COMP | TouchDesigner

Once upon a time this had an audio track, and then suddenly it didn’t. A fix coming by the end of the week. Sorry for the delay.

Core Concepts

  • Instancing geometry
  • Working with the Animation COMP
  • Building Animation Channels
  • The Shuffle CHOP
  • Real time rendering

Advanced Instancing | Pixel Mapping Geometry | TouchDesigner

Part 1

Core Concepts

  • Instancing geometry
  • Transferring data between operator types
  • Real time rendering


Part 2

Core Concepts

  • Instanced geometry from pixel data
  • Transferring data between operator types
  • Real time rendering


Part 3

Core Concepts

  • Spatial representations of time and images
  • Instanced geometry over time
  • Experiments in understanding depth
  • Real time rendering


Part 4

Core Concepts

  • Camera control
  • Spatial representations of time and images
  • Instanced geometry
  • Real time rendering


Download the Example Code from GitHub

TouchDesigner | Email | Shrink Instance

Original Email – Mon, Jul 13, 2015 at 9:43 PM

Hey Matt!
So I’ve tried banging my brain around 10 different ways to where I’m sure it now resembles a sphereSOP – noiseSOP but I’m still coming up a little short. Naturally, I thought about your THP 494 Shape lesson but I was not able to figure out what I’m missing so I’m coming straight to teacher for some guidance.

I’m trying to recreate the attached image. I’m using a GridSOP, MetaballSOP – MagnetSOP which sort of works but I’m missing some very important part of the process. Can you have a look at this and give me a hint as to what I’m missing?

You certainly do not have to correct the work unless you want to but a point in the right direction (haha) would really help me sleep tonight!


Reply – Tue, Jul 14, 2015 at 12:23 AM,

That was a good brain teaser.

I’m including two different approaches to solve this problem. The first looks at using your magnet approach, and the second is more GPU focused.

Screenshot_072115_120220_AM

Following your model with magnet, you had just about nailed it – all of the information you needed was in that SOP to CHOP, it was just a matter of reformatting it. In the magnet base, I grabbed the tz channel of the SOP to CHOP, scaled it, and renamed it scale, merged it in your instance CHOP, then applied it to the scale parameter of your instances. Pretty right on with your existing approach. The draw back here is that the magnet SOP is very expensive – nearly 7 milliseconds by itself – bottle-necking your performance at about 30 fps. This also keeps you pretty limited in terms of the number of points you can work with – CPU bottlenecks can be tricky to work around.

MetaBall

So, I started to think about how I would solve this problem on the GPU, and remembered that an array of pixels is just a different kind of grid. The second approach translates a circle TOP, and then converts that to CHOP information, merges this with a SOP to CHOP (using the xyz data from a gird), and then instances from there. I was looking at over 1400 instances without a problem. The challenge you’ll encounter, however, is when you try to replicate that many source textures. I did a quick test, and things slowed down when I had that many texture instances drawn using the newer texture instance approach. I was, however, able to get performance back up to 60 FPS if I loaded a 3D Texture Array TOP, and then turned off it’s active parameter. Markus uses this trick often.

circleTOP

Anyway, that’s as far as a I got in the little bit of time that I carved out. The next steps (in terms of mimicking your source image) would be to get the displacement right pushing instances up and down to make an opening in the center of the array.

circleTOPdisplacement

Alright, well I put another hour into this because I got really interested in the idea of displacement. I think this still needs a little more work to really dial it in, but it’s a solid starting point for sure.

Screenshot_072115_120617_AM

Hope this helps.

Best,
Matthew

scaleDisplacementExample

Look at the example file on GitHub – shrinkInstance_locked

TouchDesigner | Understanding Extensions

genGeoClassSo you’ve made a killer component that you love using, but you suddenly find yourself wondering how best to re-use it in future projects. You could make a killer control panel for it, or create a more generalized method for passing in values with in CHOPs or DATs. You could just resign yourself to some more complex scripting – reaching deep into your component to set parameters one at a time. You could hard code it, you’ll probably be making some job specific changes to your custom component anyway, so what’s a little more hard coding? The 50000 series now features custom parameters, or you could use variables, or storage. Any one of these options might be right for your component, or maybe they’re just not quite right. Maybe what you really need is a little better reach with Python, but without as much head scratching specificity. If find yourself feeling this way, than extensions are about to make your TouchDesigner programming life so, so much better.

Using extensions requires a bit of leg work on your part as a programmer, it also means that you’ll want to do this for components that you’ll find yourself reusing time and again – after all, if you’re going to take some time to really think about how you want a reusable piece of code to work in a larger system it only makes sense to do this with something you know will be useful again. That is to say, this approach isn’t right for every circumstance, but the circumstances it is right for will really make a difference for you. We’re going to look at a rather impractical example to give us a lay of the land when it comes to using extensions – that’s okay. In fact, as you’re learning how to apply this approach to your workflow it’s worth practicing several times to make sure you have a handle on the ins and outs of the process.

Before we get too much further, what exactly is this extension business? If you’re to the point with TouchDesigner where you’re comfortable using Python to manipulate your networks, you’ll no doubt have come to rely on a number of methods – anything with a . followed by a call. For example:

  • op(‘moviefilein1’).width – returns the width of the file
  • op(‘moviefilein1’).height– returns the heightof the file
  • op(‘table1’).numRows – returns the number of Rows
  • op(‘table1’).numCols – returns the number of Columns

In each of these examples, it’s the .operation that extends how you might think of working with an operator. Custom extensions, means that you, the programmer, are now free to create your own set of extensions for a given component. The classic example for this kind of custom component and custom extension set for TouchDesigner would be  a movie player. Once you build a movie player that cross fades between two videos, wouldn’t it be lovely to use something like op(‘videoPlayer’).PlayNext() or op(‘videoPlayer’).Loop(). The big idea here is that you should be free to do just that when working with a component, and custom extensions are a big part of that puzzle. This keeps us from reaching deep into the component to change parameters, and instead allows us to write modular code with layers of abstraction. If you’re still not convinced that this is a powerful feature, consider that when you start a car you’re not in the business of specifying how precisely the starter motor sequences each electrical signal to help the car turn over, or which spark plugs fire in which order – you issue a command car.start() with the expectation that the start() function holds all of the necessary information about how the vehicle actually starts. While this example might be reductive, it helps to illustrate the importance of abstraction. It’s impractical for you, the driver, to always be caught up in starting sequences in order to drive a car (one might make an argument against this, but for now let’s roll with the fact that many drivers don’t understand the magic behind a car starting when they turn the key), this embedded function allows the driver to focus on higher order operations – navigation, manipulation, etc. At the end of the day, that’s really what we’re after here – how do add a layer of abstraction that simplifies how we manipulate a given component.

That’s all well and good, but let’s get to the practical application of these concepts. In this case, before we actually start to see this in action, we need to have a working component to start working with. We are going to imagine that we want to build a generative component that’s got faceted torus that we use in lots of live sets. We want to be able to change a number of different elements for this Torus – its texture, background, rotation, deformation, to name a few. Let’s begin by putting together a simple render network to build this component, and then we can look at how extensions complement the work we’ve already done.

First let’s add an empty Base COMP to our network.

emptyBase

Inside of our new base let’s add a Camera, Geo, and Light COMP, as well as a Render TOP connected to an Out TOP. We’re building a simple rendering network, which should feel like old hat.

simpleRender

Let’s add a movie file in TOP, and a Composite TOP to our network. We’ll composite our render over our movie file in, so we have a background. In the image below only the changed parameters for the Composite TOP are shown.

simpleRenderWithComposit

Next let’s look inside of our geo COMP, and make a few changes. First let’s change our geo to be a polygon rather than a mesh. We’ll also turn off the display and render flags for the torus (don’t worry, we’ll turn them on further down the chain.

torus

Next we’ll add a noise SOP.

Noise SOP

Next we’ll add a facet SOP, turning on unique points and compute normals.

facetSOP

Finally, let’s add a null SOP. On the null, let’s turn on the display and render flags. When it’s all said and done we should have something that looks like this.

noiseTorusChain

Let’s move up one layer out of our geo, back into the base we started in. Here let’s add a phong Material and apply it to our geo. Let’s also add a movie file in TOP connected to a null TOP, and set it as the color map for our phong.

colorMapAndMaterial

While we’re still thinking about our material, lets make a few changes. Let’s set our diffuse color to white, our specular color to a light gray, and turn up our shininess to 255.

phongNonDefaults

Shown are the non default parameters for the Phong Material.

Let’s also make a few changes to our light COMP. I’m after a kind of shiny faceted torus, so let’s change our light to a cone light, place it overhead and to the right of our geometry, and set it to look at our geo.

Shown are the non default parameters for the Light Component.

Shown are the non default parameters for the Light Component.

I’ve gone ahead and changed file in my movie file in TOP to a different default image so I can see the whole torus. In the end you should have a network that looks something like this.

textureTorus

Thinking ahead, I know that I’m going to want to have the freedom of changing a few parameters for this texture. I’d like to be able to control if it’s monochrome, as well as a few of the levels of the image. Let’s add a monochrome TOP and a level TOP between the movie file in and the null TOP.

postProcess

We’re almost ready to start thinking about extensions, but first we need to build a control network to operate our component. Let’s start by adding a constant CHOP and calling it attrAssign. Here I’m going to start thinking about what I want to control in this component. I want to drive the rotation of the x y and z axis for our geo, I want to control the amplitude of the noise, the saturation of our image, the black level, brightness, and opacity. I’m going to think of those parameters as:

  • rx
  • ry
  • rz
  • noiseAmp
  • monoVal
  • blkLvl
  • bright
  • opacity

I’ll start out my constant CHOP with those channel names, and some starting values.

attrAssign

For this particular component, I want to be able to set values, and have it smartly figure out transitions rather than needing it constantly feed it a set of smoothly changing values. There are a couple of different ways we might set this up, but I’m going to take a rout of using a speed CHOP for one set of operations, and a filter CHOP to smooth everything out. First I want to isolate the rx ry and rz channels, we can do that with a select CHOP. Next we’ll connect that to a speed CHOP. We can merge this changed set of channels back into the stream with a replace CHOP – replacing our static rx ry rz channels with the dynamic ones.

selectSpeedReplace

Finally, we can smooth out our data with a Filter CHOP, and end our chain of operations in a null CHOP.

controlChops

Our last step here is to export or reference to each of our control parameters. Our rotation channels should be referenced by our Geo1 for rx, ry, and rz. The Noise SOP in Geo1 should be connected to the channel noiseAmp, and our image controls should be connected to their respective parameters – Monochrome, Black Level, Brightness, and Opacity. In the end, you should end up with a complete network that looks something like this.

complete BaseCOMP

Alright, we now finally have a basic generative component set up, and we’re ready to start thinking about how we want our extensions to work with this bad boy. Let’s start with the simplest ideas here, and work our way up to something more complex. For starters we need to add a text DAT to our network. I’m going to call mine genGeoClass.

genGeo

Let’s add our first class to our genGeoClass text DAT. Our class is going to contain all of our functions that we want to use with our component. There are a few things we need to keep in mind for this process. First, white space is going to be important – tabs matter, and this is a great place to really learn that the hard way. Namespace also matters. We’re eventually going to promote our extensions (more on that later on down), and in order for that to work correctly our functions need to begin with capital letters. That will make more sense as we see that in practice, but for now it’s important to have that tucked away in your mind.

Let’s begin by adding a simple print command. First we define our class, and place our functions inside of the class. When we’re writing a class in Python we need to explicitly place self in our definitions. There are a number of very good reasons for this, and I’d encourage you to read up on the topic if you’re curious:

Why ‘self’ is used explicitly
Why the explicit self has to stay

For our purposes, let’s get our class started with the following;

class GenGeo:

    def SimplePrint( self ):
 
        print( 'Hello World' )
        
        return

Before we can see this in action, we need to set up our base COMP to use extensions. Let’s back out of our base, and take a look at our component parameters.

baseExtensions

Here I’ve set the module reference to be the operator called genGeoClass inside of base1. We can also see that we’re specifcally referencing the GenGeo() class that we just wrote. I’ve also gone ahead and turned on promote extensions. Make sure you click “Re-Init” Extensions at the bottom of the parameters page, and then we can see our extension in action.

Next let’s add a text DAT to the same directory as our base1. Here we’ll use the following piece of code to call the SimplePrint() function we just defined:

op( 'base1' ).SimplePrint()

Let’s open our text port, and run our text DAT.

SimpleText

That should feel like a little slice of magic. If you choose not to promote your extensions, the syntax for calling a function looks something like this:

op( 'base1' ).ext.GenGeo.SimplePrint()

Okay, this has been a lot of work so far to only print out “Hello World.” How can we make this a little more interesting? I’m so glad you asked. Here’s  a set of functions that I’ve already written. We can copy and paste these into our genGeoClass text DAT, and now we suddenly have a whole new host of functions we can call that perform some meta operations for us.

class GenGeo:

    def SimplePrint( self ):
        print( 'Hello World' )
        return

    def TorusPar( self , rows , columns ):
        op('geo1/torus1').par.rows = rows
        op('geo1/torus1').par.cols = columns
        return

    def TorusParReset( self ):
        op('geo1/torus1').par.rows = 10
        op('geo1/torus1').par.cols = 20 
        return

    def Texture( self , file ): 
        op('moviefilein1').par.file = file
        return

    def TextureReset( self ):
        op('moviefilein1').par.file = app.samplesFolder + '/Map/TestPattern.jpg'
        return

    def Rot( self , rx , ry , rz ):
        attr = op('attrAssign')
 
        attr.par.value0 = rx
        attr.par.value1 = ry
        attr.par.value2 = rz
        return

    def RotReset( self ):
        attr = op('attrAssign')
        speed = op('speed1')
        filterCHOP = op('filter1')
        attr.par.value0 = 0
        attr.par.value1 = 0
        attr.par.value2 = 0
        speed.par.resetpulse.pulse()
        filterCHOP.par.resetpulse.pulse()
        return

    def TorusNoise( self , noiseAmp ):
        op( 'attrAssign' ).par.value3 = noiseAmp
        return

    def Mono( self , monoVal ):
        op( 'attrAssign' ).par.value4 = monoVal
        return

    def Levels( self , blkLvl , bright , opacity ):
        attr = op('attrAssign')
        attr.par.value5 = blkLvl
        attr.par.value6 = bright
        attr.par.value7 = opacity
        return

    def PostProcessReset( self ):
        attr = op('attrAssign')
        attr.par.value4 = 0
        attr.par.value5 = 0
        attr.par.value6 = 1
        attr.par.value7 = 1
        return

    def Background( self , onOff ):
        op('comp1').bypass = onOff
        return

To better understand what all of these do let’s look at a quick cheat sheet that I made:

# Test Print Statement
op( 'base1' ).SimplePrint()

# Set Rows and Columns
op( 'base1' ).TorusPar( 20 , 20 )

# Reset Rwos and Columns to 10 x 20
op( 'base1' ).TorusParReset()

# Set the texture of a movie file in TOP
op( 'base1' ).Texture( 'https://farm4.staticflickr.com/3696/10353390565_1fa6dbf704_o.jpg' )

# Reset the Texture of movie file in TOP
op( 'base1' ).TextureReset()

# Set the Rotation Speed for the x y and / or z axis
op( 'base1' ).Rot( 10 , 15 , 20 )

# Reset the Rotation speed to 0, and the rotation values to 0
op( 'base1' ).RotReset()

# Set the Amplitude paramater of the Noise SOP for the Torus
op( 'base1' ).TorusNoise( 0.8 )

# Make the texture Monochrome
op( 'base1' ).Mono( 1.0 )

# Control the Black Leve, Brightness, and Opacity of the Texture
# that's applied to the Torus
op( 'base1' ).Levels( 0.25 , 1.5 , 0.8 )

# Reset all post process effects
op( 'base1' ).PostProcessReset()

# Turn off Background Image - 0 will turn the Background back on
op( 'base1' ).Background( 1 )

This is wonderful, but there’s one last thing for us to consider. Wouldn’t it be great if we had some initialization values in here, so at start-up or when we made a new instance of this comp we defaulted to a reliable base state? That would be lovely, and we can set that with an __init__ definition. Let’s add the following to our class:

    def __init__( self ):
 
        print( 'Gen Init' )
        attr = op('attrAssign')

        op('moviefilein1').par.file = app.samplesFolder + '/Map/TestPattern.jpg'

        attr.par.value4 = 0
        attr.par.value5 = 0
        attr.par.value6 = 1
        attr.par.value7 = 1

        return

That means our whole class should now look like this:

class GenGeo:

    def __init__( self ):
        print( 'Gen Init' )
        attr = op('attrAssign')

        op('moviefilein1').par.file = app.samplesFolder + '/Map/TestPattern.jpg'

        attr.par.value4 = 0
        attr.par.value5 = 0
        attr.par.value6 = 1
        attr.par.value7 = 1
        return

    def SimplePrint( self ):
        print( 'Hello World' )
        return

    def TorusPar( self , rows , columns ):
        op('geo1/torus1').par.rows = rows
        op('geo1/torus1').par.cols = columns
        return

    def TorusParReset( self ):
        op('geo1/torus1').par.rows = 10
        op('geo1/torus1').par.cols = 20 
        return

    def Texture( self , file ): 
        op('moviefilein1').par.file = file
        return

    def TextureReset( self ):
        op('moviefilein1').par.file = app.samplesFolder + '/Map/TestPattern.jpg'
        return

    def Rot( self , rx , ry , rz ):
        attr = op('attrAssign')
 
        attr.par.value0 = rx
        attr.par.value1 = ry
        attr.par.value2 = rz
        return

    def RotReset( self ):
        attr = op('attrAssign')
        speed = op('speed1')
        filterCHOP = op('filter1')
        attr.par.value0 = 0
        attr.par.value1 = 0
        attr.par.value2 = 0
        speed.par.resetpulse.pulse()
        filterCHOP.par.resetpulse.pulse()
        return

    def TorusNoise( self , noiseAmp ):
        op( 'attrAssign' ).par.value3 = noiseAmp
        return

    def Mono( self , monoVal ):
        op( 'attrAssign' ).par.value4 = monoVal
        return

    def Levels( self , blkLvl , bright , opacity ):
        attr = op('attrAssign')
        attr.par.value5 = blkLvl
        attr.par.value6 = bright
        attr.par.value7 = opacity
        return

    def PostProcessReset( self ):
        attr = op('attrAssign')
        attr.par.value4 = 0
        attr.par.value5 = 0
        attr.par.value6 = 1
        attr.par.value7 = 1
        return

    def Background( self , onOff ):
        op('comp1').bypass = onOff
        return

Alright, so why do we care? Well, this application of extensions frees us to think differently about this component. Let’s say that I want to make a few changes to this component’s behavior. First I want to set a new image to be the texture for the torus, next I want to change the rotation speed on the x and y axis, and finally I want to turn up the noise SOP. Previously, I might think about this by writing a series of scripts that looked something like:

op( 'base1/attrAssign' ).par.value0 = 20
op( 'base1/attrAssign' ).par.value1 = 30
op( 'base1/attrAssign' ).par.value3 = 0.8
op( 'base1/moviefilein1' ).par.file = 'https://farm4.staticflickr.com/3696/10353390565_1fa6dbf704_o.jpg'

Instead, I can now write that like this:

op( 'base1' ).Texture( 'https://farm4.staticflickr.com/3696/10353390565_1fa6dbf704_o.jpg' )
op( 'base1' ).Rot( 20 , 30 , 0 )
op( 'base1' ).TorusNoise( 0.8 )

That might not seem like a huge difference here in our example network, but as we build larger and more complex components, this suddenly becomes hugely powerful as a working approach.

extensionsInAction

Check out the example file on GitHub if you get stuck along the way, or want to see exactly how I made this work.

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.