Python
TouchDesigner

TouchDesigner Pythonでoperatorをコントロールする

More than 1 year has passed since last update.

TouchDesigner Advent Calendar 2017 9日目の記事です。よろしくお願いします。

はじめに

TouchDesigner(以下Touch)で作業していると、人それぞれここはPython(C++かも)でやったほうがいいなと思う箇所が出て来ると思います。俺の場合はDesigner Mode(新規ファイルを作ると出ている画面)でoperatorのサイズを揃えたり等間隔で置いたりといった調整が手動でやりにくいと感じ、そのストレス軽減が最初の目的でした。また、Touchをちょっと触ってみたくらいの時に市原湖畔美術館でのCarsten Nicolaiの展示で作品の転換時に画面が見えて、あ、これTouchDesignerなんだ〜かっこいい〜と思ったのと、ライブパフォーマンスの際にDesigner Modeを見せちゃう人とかもいると知ったことも色々調べるきっかけとなりました。

この記事での座標、サイズ指定などは全てDesigner Mode上のものですので、Perform Modeでの表示には影響しません。そんなわけで以下ご紹介する内容は地味だったり無意味だったりしますが、TouchでのPythonの使い方を理解する一助となると思います。

Touchでの基本的なPythonの使い方は同一Advent CalendarのTouchDesignerでのPython入門(基礎編)TouchDesignerでのPython入門(応用編)で丁寧に書かれているのでそちらを参考にしてください。俺もPythonが超得意ってわけではないのでもっと効率的な書き方などありましたらツッコミ頂けるとうれしいです。

サンプルファイル

githubにアップしましたので必要に応じて参照してください。
https://github.com/chimanaco/touchdesigner-advent-calendar-2017

対象となるoperatorの指定

まず、対象となるoperatorを指定するには以下のものがあります。

名前で指定

以下のコードではmoviefilein1というファイル名のoperatorが対象となります。

o = op('moviefilein1')
// your code

同一階層のoperator全部

以下のコードでは実行するスクリプトと同一階層にある全てのoperatorが対象となります。

c = parent().children
for o in c:
    // your code

名前に文字列を含むoperatorを指定

以下のコードではmoviefileinという文字列を含むoperatorが対象となります。

c = parent().children
for o in c:
  if "moviefilein" in o.name:
    // your code

familyで指定

以下のコードではTOP familyのoperatorが対象となります。

c = parent().children
for o in c:
  if(o.family == 'TOP'):
      // your code

labelで指定

以下のコードではMovie File Inのoperatorが対象となります。

c = parent().children
for o in c:
  if(o.label == 'Movie File In'):
      // your code

typeで指定

以下のコードではmoviefilein typeのoperatorが対象となります。labelとの使い分けがよく分かっていません。

c = parent().children
for o in c:
  if(o.type == 'moviefilein'):
      // your code

位置の指定

座標を指定して配置します。
以下のコードではmoviefilein1 operatorの座標を取得し、左揃えでmoviefilein2 operatorの位置を指定します。

marginY = 20

o1 = op('moviefilein1')
x1 = o1.nodeX
y1 = o1.nodeY

o2 = op('moviefilein2')
o2.nodeX = x1
o2.nodeY = y1 - o1.nodeHeight - marginY

Position

ちなみに Edit -> Preferences -> Network の下の方の設定でグリッドにスナップができるようになることに最近気づきました。

サイズのリセット

拡大縮小したり他所からコピペしたoperatorを手動でうまくサイズ調整することができなかったため、リセットボタンを作りました。
例えばmoviefilein1というoperatorは以下のコードでリセットできます。ドラッグでリサイズする時にもグリッドが出てくれるとありがたいんですが出ないのかしら…。

op('moviefilein1').resetNodeSize()

Reset

サイズを指定する

設定する値の単位はピクセル?HelpPython Exampleui_examplesを見てみると1 pixel = 1 tile unitとあるんですが1 tileって何でしょうね。
以下のコードではmoviefilein1 operatorの幅を500、高さを50に指定します。

o = op('moviefilein1')
o.nodeWidth = 500
o.nodeHeight = 50

Resize

テストする時には、いじっては戻しいじっては戻しを繰り返したいのでサイズ変更ボタンとリセットボタンを並べて置いています。Python実行だとUndo(Ctrl+z)が効かないので。

複数operatorのサイズを更新する

以下のコードでは幅12016個のConstantTopのoperatorを作成、横並びに配置します。

w = 120
for i in range(0, 16):
    o = parent().create(constantTOP)

    # size
    o.nodeWidth = w

    # position
    o.nodeX = xPos * i    

以下のコードでは複数のConstantTopのoperatorの高さをPositionという名前のTableから取得して更新に使用しています。Positionの値を定期的に変更するとアニメーションになります。この場合作成したoperatorを絞り込む条件としてo.label=='Constant'o.family=='TOP'を指定していますが、同一階層上に既存のConstantTop operatorがあった場合それらにも影響してしまいますので条件に注意してください。

c = parent().children
index = 0
for o in c:
  if o.label=='Constant' and o.family=='TOP':
      # size
      cell = op('Position')[index, 0].val
      height = int(float(cell))
      o.nodeHeight = height
      index += 1

ResizeMulti

サンプルではnoiseで高さをコントロールしていますが、サウンドリアクティブにしたりセンサーから取得した値を使用したりするともっと楽しくなると思います。

operatorを形に沿って配置する

Pythonで座標を計算して図形を描く場合と、SOPの頂点座標を使用する2パターンやってみました。

Pythonで座標を計算する

以下のコードではoperatorを螺線形に配置します。

radius = 0

for i in range(0, 200):
    radius += i * 0.1
    addOpSpiral(i, total, radius)
def addOpSpiral(index, total, radius):
    angleSpeed = 5

    o = parent().create(constantTOP)

    # size
    setOpSize(o, 120, 67.5)

    # positon
    angle = toRadians(index * 360 / total * angleSpeed)
    x = math.cos(angle) * radius
    y = math.sin(angle) * radius
    setOpPosition(o, x, y)
    return

# Common utilities
def setOpSize(op, w, h):
    op.nodeWidth = w
    op.nodeHeight = h
    return

def setOpPosition(op, x, y):
    op.nodeX = x
    op.nodeY = y
    return

def toRadians(angle):
    return angle * (math.pi / 180)

Spriral

ここでは螺旋形に配置していますが、興味ある方は好きな形に配置してみてください。

SOPの頂点座標を使用する

FontSOPResampleSOPSOPtoCHOPMathCHOPChoptoDATという流れでテキストの頂点データをPositionと名付けたTableDATにインプットし、以下のコードでoperatorの座標に指定します。

c = parent().children
index = 0
for o in c:
  if o.label=='Constant' and o.family=='TOP':
      # position
      x = op('Position')[index, 0].val
      y = op('Position')[index, 1].val
      setOpPosition(o, x, y)

TextSOP

サンプルではSequence BlendSOPを使用して元となるテキストを切り替えています。

ネストされたoperatorを追加する

Replicatorを使うのがTouchらしいやり方な気がしますが、Pythonでも同じようなことができます。operatorを追加していくだけですが、connectメソッドでoperator同士をつなげられるところが好きです。
以下のコードはcontainerCOMPを追加、その中にconstantoutの二つのTOPを追加し、outTOPからの出力を予め配置しておいたswitch1につなげています。

w = 240
h = 135
yPos = -(h + 10)
total = 5

for i in range(0, total):
    # add container
    c = parent().create(containerCOMP)

    # size
    c.nodeWidth = w
    c.nodeHeight = h

    # position
    c.nodeY = yPos * i

    # add op to container
    addOpToContainer(c, i, total)

    # connect
    c.outputConnectors[0].connect(op('switch1'))
def addOpToContainer(op, index, total):
    # add
    t1 = op.create(constantTOP)

    t2 = op.create(outTOP)
    t2.nodeX = 240

    # connect
    t1.outputConnectors[0].connect(t2)
    return

Container

Designer Modeでズームする

こちらは実際にoperatorを操作するわけではないのですが、関連ということで。
panezoomの数値を指定することでDesigner Modeのズーム具合を指定できます。
以下のコードは画面の中心(座標0,0)にある幅240、高さ130のoperatorを中心にズームする場合です。

p = ui.panes.current # 現在のpaneを指定
p.zoom = 1  #8くらいでフルスクリーン
p.x = 120 # ターゲットにしたいoperatorのwidth/2
p.y = 67.5 # ターゲットにしたいoperatorのheight/2

Zoom

zoomの値を定期的に変更することでアニメーションになります。サンプルは拡大のみですが、一覧表示時、縮小時などにアニメーションをつけたり画面を覆うタイミングでPerform Modeに切り替えたりするなどして作り込むとプレゼンテーションに使えたり、Carsten Nicolaiの展示のような動き(一覧を表示→次の作品にフォーカス→次の作品をフルスクリーンで表示、を繰り返す)も作れます。サンプルのようにズームさせていると各operatorに触りにくいので停止用ショートカットを設定するとか、MIDIコントローラーに停止ボタンを設定しておくなどしたほうが好いかもしれないです。

うまくいかなかったこと

op.activeViewer = 1

という設定があってoperatorのviewerを全域使うためにこれを使用しているのですが、 operatorを作ったすぐのタイミングでは設定できないことが多かったので、アニメーションとして定期的に呼ぶメソッドに設定を入れています。operatorがcookされる前に設定しようとしているからダメだとかかしら。

おまけ

俺得のoperatorのresize、reset、locate、destroyができるtoxを作りました。
https://github.com/chimanaco/touchdesigner-tox にあるOperator_adjust.tox というやつです。確認メッセージは未実装でボタンを押したら即実行されてundoできないので、試される方おられたら現状destroyは注意が必要です。

最後に

お読み頂きありがとうございました。他にもデフォルトのoperatorのサイズがCOMP > TOP > CHOP = SOP = MAT = DAT となっているのとかも気になるところですが、Designer Modeをライブコーディングで使ってみたいのでもっと楽しく魅せる編集画面を目指します(その割にサンプルファイル美しくないですごめんなさい)。

明日は@ToyoshiMoriokaさんの登場です!
引き続きこの後も盛り沢山なTouchDesigner Advent Calendar 2017を楽しんでいきましょう!