Category Archives: Class

textport for performance | TouchDesiger

consoleText

I love a good challenge, and today on the TouchDesigner slack channel there was an interesting question about how you might go about getting the contents of the textport into a texture to display. That’s a great question, and I can imagine a circumstance where that might be a fun and interesting addition to a set. Sadly, I have no idea about how you might make that happen. I looked through the wiki a bit to see if there were any leads, and it’s difficult to see if there’s actually a good way to grab the contents of the textport.

What do we do then?!

Well, it just so happens that this might be another great place to look at how to take advantage of using extensions in TouchDesigner. Here our extension is going to do some double duty for us. The big picture idea is that we’ll want to be able to use a single call to either display a string, or print and display a string. If you only wanted to print it you could just use print(), so we’ll leave that one out of the mix for now.

Let’s take a look at the extension and then pull apart what’s happening in side.

'''
author | matthew ragan
web | matthewragan.com

The idea behind the display class comes from a question on the TouchDesiger 
slack channel about how to handle displaying text for performance. While you
might choose to display the console, the challenge here is that you'll end 
up with another window open on your output machine. Ideally, you only draw
a single openGL context - this results in the best possible performance
from TouchDesigner. With that in mind, how might you approach wanting to use
the textport in an artistic way for your project? 

This example tackles that challenge with a set of methods designed to 
print to a texture intended for display, as well as to the text port.
'''

class disp():

    def __init__( self ):
        ''' our init method does a bit of set-up and starting organization.

        We need a few things to be available to us in this method. It's handy
        to have the target table, the target text TOP, a message with a placeholder
        and some information for our start and end rows to be displayed.
        '''
        self.TextTarget = op( 'table_target' )
        self.DisplayText = op( 'text_for_display' )
        self.MessagePrompt = ">>> {msg}"
        self.StartRow = 0
        self.EndRow = 11
        self.TextTarget.clear()

        return

    def Display( self, inputMsg="hello world" ):
        ''' The display method will populate a table which is used to feed a text TOP

        The big idea here is that we need to fill in a table DAT that will then feed
        a text TOP. Why a table DAT instead of a text TOP? Well, you can do whatever you
        want. My idea here is that a table DAT is a little easier to think of in terms 
        of single rows that can fill up a text TOP. We can also do fancy things like use
        a select to make sure that our text is always displayed even if our start and end
        rows get away from us. A text DAT will just keep filling our text TOP, and
        we might end up with squashed text we can't see. 

        In this method we format our MessagePrompt with the supplied text. We also do a 
        quick check to see if our text will fit in the display. If we have more rows than
        can be displayed, we update our class variables to so that our last lines will
        remain visible.

        Finally, we return our formattedMsg - why? This is handy if we want to use another
        method to actually print our message to the textport as well.
        '''
        formattedMsg = self.MessagePrompt.format( msg = inputMsg )

        self.TextTarget.appendRow( [ formattedMsg ] )

        if self.TextTarget.numRows > 11:
            self.StartRow += 1
            self.EndRow += 1

        else:
            pass

        return formattedMsg

    def PrintAndDisplay( self, inputMsg="hello world" ):
        ''' The PrintAndDisplay method will both add the message to the text TOP, and 
        print the same message to the console.

        You may be wondering why we return the formatted message - the trick here is that
        we can take advantage of the work that arleady happens in the Display method.
        Why write that whole bit again, if we can just use the same logic and work 
        we've already done, and just add a final step of printing to the textport as well.
        '''

        forConsole = self.Display( inputMsg )
        print( forConsole )
        return

        def ClearDisplay( self ):
        ''' Clear display dumps out the contents of our target table.

        This method seems silly, but the idea is that you're likely to want
        a fast and clean way of cleaning out the contents of your display window.
        This method does only that simple little task. This will also reset our 
        start and end bounds to make sure that we're getting the correct selection
        for our text TOP.
        '''
        self.TextTarget.clear()
        self.__init__()
        return

Okay, so what exactly are we doing here?!

The big picture is that we want a way to be able to log something to a text  object that can be displayed. In this case I choose a table DAT. The reasoning here is that a table DAT before being converted to just a text DAT allows us to do some simple clean up and line adjustments. Each new entry is posted in a row – which makes for an easy way to limit the number of displayed rows. We can do this with a select DAT – which is where we use our StartRow and EndRow members.

Why exactly do we use these? Well, this helps ensure that we can keep our newest row displayed. A text TOP can accept a text DAT of any length, but at some point the text will spill off the bottom – unless you use adaptive sizing. The catch there is that at some point the text will become impossible to read. A top and bottom boundary ensures that we can always have something portion of our text displayed. We use a simple logical test in our Display() method to see if we’ve hit that boundary yet, and if we have we can update our members plus one… moving them both along at the same time.

You may also notice that we have a separate method to display and print… why not just do this in a single method. Well, that’s a great question. We could just use a single method for this with another argument. That’s probably a better way to tackle this challenge, but I wanted to use this opportunity to show how we might call another method from within our class. This can be helpful in a number of different situations, and while this application is a little too simple to really take advantage of that technique, it gives you a peak into how it might work.

Want to download the tox and take it for a test drive? You can find the source code here.

Python in TouchDesigner | Variables | TouchDesigner

Core Concepts

  • Understanding variables in Python (different form what we mean with TouchDesigner variables)
  • Referencing by using variables in a script
  • simple scripting
  • simple preset recall


There are lots of resources on the web that describe variables better than I might:

The essential idea here, however, is that you have something that you want to reference by name. That something might be a sentence, it might be a quantity, it could be anything really. Again, it’s more important for us in this moment to consider that our something (whatever it is) happens to be a piece of information that we want re-use.

Let’s look at a dead simple example, to help us get started. If variables aren’t new to you feel free to skip ahead.

Let’s imagine you own a toy store. That toy store happens to sell marbles. That’s great, good for you – you’re a marvelous little capitalist. Now, let’s imagine that you want to do an inventory of all of your marbles. You have several different varieties of marbles, and you’d like to be able to think of them as different, while also having a total count. In this situation we might keep track of your marbles by using some variables:

red_marbles = 10
blue_marbles = 5
green_cat_eyes = 6
blue_cat_eyes = 12

Nice work. Now, we can print out each one of those, and get back our stored quantity. We could also do something like this:

total_marbles = red_marbles + blue_marbles + green_cat_eyes + blue_cat_eyes

Now we also know the total quantity of marbles. Super. Finally, we might want to see all of that. Let’s look at what that might look like:

print( "Currently in your inventory you have:" )
print( "%d red marbles" % red_marbles )
print( "%d blue marbles" % blue_marbles )
print( "%d green cat eye marbles" % green_cat_eyes )
print( "%d blue cat eye marbles" % blue_cat_eyes )
print( "-" * 10 )
print( "That makes for %d total marbles" % total_marbles )

That’s great, and hopefully you’re a careful shop keeper and you don’t loose any of your marbles… it was a long set-up for that bad joke.

What does this do for us here in TouchDesigner? When we’re scripting in Touch it’s often useful to be able to assign variables for all sorts of things. This especially useful when referencing operators.

Let’s quickly consider one example. We might, have a level TOP that we want to make changes to. Starting with a simple task, lets imagine we want to use a script to change the opacity of a level TOP to 0. We could easily write something like this to solve this need:

op( 'level1' ).par.opacity = 0

That’s short and simple and gets the job done. Love it. Now, let’s imagine a slightly more complicated world where I want to change lots of parameters for this operator. I want to change the invert, black level, brightness 1, gamma 1, contrast, and opacity. That’s great. Let’s write all of that out and see what we end up with:

op( 'level1' ).par.invert = 0.31
op( 'level1' ).par.blacklevel = 0.27
op( 'level1' ).par.brightness1 = 1.45
op( 'level1' ).par.gamma1 = 0.5
op( 'level1' ).par.contrast = 1.76
op( 'level1' ).par.opacity = 0.782

That’s not too bad, but we could make that a little less error prone if we were to simplify some of our script:

level = op( 'level1' )

level.par.invert = 0.31
level.par.blacklevel = 0.27
level.par.brightness1 = 1.45
level.par.gamma1 = 0.5
level.par.contrast = 1.76
level.par.opacity = 0.782

That’s pretty swanky, but let’s imagine a situation where I’ve made a table full of presets that I want to be able to reference. Let’s look at how we might tackle something like that:

# define our variables:
presets = op( 'table_presets' )
level = op( 'level1' )
row_ref = 'preset1'

# change some parameters
level.par.invert = presets[ row_ref , 'invert' ]
level.par.blacklevel = presets[ row_ref , 'blacklevel' ]
level.par.brightness1 = presets[ row_ref , 'brightness1' ]
level.par.gamma1 = presets[ row_ref , 'gamma1' ]
level.par.contrast = presets[ row_ref , 'contrast' ]
level.par.opacity = presets[ row_ref , 'opacity' ]

Okay… so what happened here? First we defined created a variable called “presets” that stands in for op( ‘table_presets’ ). We also made one called “level” and one called “row_ref”.

Next we wrote a generalized set of instructions to change some parameters using our variables. For the sake of seeing it all written out let’s look write it out long-form:

op( 'level1' ).par.invert = op( 'table_presets' )[ 'preset1' , 'invert' ]
op( 'level1' ).par.blacklevel = op( 'table_presets' )[ 'preset1' , 'blacklevel' ]
op( 'level1' ).par.brightness1 = op( 'table_presets' )[ 'preset1' , 'brightness1' ]
op( 'level1' ).par.gamma1 = op( 'table_presets' )[ 'preset1' , 'gamma1' ]
op( 'level1' ).par.contrast = op( 'table_presets' )[ 'preset1' , 'contrast' ]
op( 'level1' ).par.opacity = op( 'table_presets' )[ 'preset1' , 'opacity' ]

This works just the same… so why use variables. Well, in this case I used variables to keep my code a little more tidy. I also did this because it means I’m less likely to make an error if I’m using shorter names. Most importantly, we did this because we’ve now created a variable called row_ref. This means we can change how this script works, just by altering this single variable. Let’s say that we have two different presets. It would be far less fun to write the same set of scripts all over again just to reference a different preset. Instead, we can just change our variable to indicate which preset to use. That means that by making this single change:

row_ref = 'preset2'

We’ve actually made this change:

op( 'level1' ).par.invert = op( 'table_presets' )[ 'preset2' , 'invert' ]
op( 'level1' ).par.blacklevel = op( 'table_presets' )[ 'preset2' , 'blacklevel' ]
op( 'level1' ).par.brightness1 = op( 'table_presets' )[ 'preset2' , 'brightness1' ]
op( 'level1' ).par.gamma1 = op( 'table_presets' )[ 'preset2' , 'gamma1' ]
op( 'level1' ).par.contrast = op( 'table_presets' )[ 'preset2' , 'contrast' ]
op( 'level1' ).par.opacity = op( 'table_presets' )[ 'preset2' , 'opacity' ]

This is only the tip of the iceberg, but helps us see how useful using variables in Python can be.

Download the sample files from github

Python in TouchDesigner | Printing | TouchDesigner

Core Concepts

  • Using the text port
  • Running scripts
  • print()
  • strings, integers, floats, and booleans
  • printing and joining
  • simple substitution in strings


Printing out lines isn’t especially interesting on the face of it. That being said, this is one of the most powerful places to get your bearings. I almost always start any python related task by printing out bits of pieces of what I’m up to. It lets me see into the otherwise invisible process of code execution. Many folks will swear by this or that debugger, but at the end of the day your best debugging tool is just printing out what’s happening. Learning the ins and outs of printing will also transfer to a number of different bits and pieces along the way. We’ll start here as a way to get a solid handle on a few basic elements.

Download the sample files from github

THP 494 & 598 | Touch OSC – A Case Study | TouchDesigner

Core Concepts

  • Hexler’s Touch OSC
  • Simple Network Communication with Open Sound Control
  • Sending Floats from TouchOSC to TouchDesigner
  • Sending Floats from TouchDesigner to TouchOSC
  • Sending Messages from TouchDesiger to TouchOSC