TouchDesigner プリセットを作る

[TouchDesigner Advent Calendar 2018](https://qiita.com/advent-calendar/2018/touchdesigner) 16日目の記事です。よろしくお願いします。

## はじめに
リアルタイムで生成されるビジュアルのパラメータを変えて調整したり、気に入ったパラメータを保存したりすると思います。保存したものをプリセットとして使えたらいいなと思いつつ、`Palette` > `Derivative` > `Techniques` > `presets` が好いのかなと見てみるもあまりピンと来なかったので、自分で作ってみました。

## サンプルファイル
githubにアップしましたので必要に応じて参照してください。
https://github.com/chimanaco/touchdesigner-advent-calendar-2018

## ベースとして使用しているUI
UIのベースは`Palette` > `Derivative` > `UI` > `gal`に入っているものです。これまで`Palette` > `Derivative` > `UI` > `TUIK`内にあるものを使ってUIを作っていたのですが、中身を少し弄ろうとするとTscriptで実装されていてよく分かんないからPythonで実装されているgalにしとけとDiscordでアドバイスを頂きました。使ってみると確かにTUIKと比較して自分のやりたい用途としては使いやすかったです。

## サンプルの構造

![q4e.PNG](https://qiita-image-store.s3.amazonaws.com/0/58558/1776d568-78c7-04dd-a641-3ef5b17a5ae9.png)

今回作ったプリセットの仕組みとしては0-9の各ボタンをクリックすると保存されているプリセットの値が反映され、UIで値を変更した後に`Save`ボタンをクリックすると現在のUIの値が保存されるというものです。これらの値を使って`Ramp Top`をアニメーションさせています。

![q1.png](https://qiita-image-store.s3.amazonaws.com/0/58558/239408fd-570b-cd65-c4eb-5e036e71516f.png)

VJツールのように何種類かビジュアルを用意するとして、`UI_private`にそのビジュアル固有のプリセットを入れておいて、各ビジュアルごとにパラメータの名前や値の範囲などを個別に設定していくのを想定しています。`UI_all`のほうは各ビジュアル共通の内容を保持しているもので、現状プリセットを選択するラジオボタンが入っていて、`UI_private`の中身を一緒に表示しています。

## プリセットのアップデート
`table_all_values`に全てのプリセットのデータが入っていて、`preset_save`の中の`chopexec_save`からプリセットのアップデートをしています。
1列目が各プリセットの名前で、名前が一致した時にindexに該当する値を追加、または更新をします。
新しいプリセットが追加されたら新しい行（row）を追加するようになっています。

```python
null_current = op('null_current_values')
chopto_current = op('chopto_current_values')
null_index = op('null_index')
table_all = op('../table_all_values')

def offToOn(channel, sampleIndex, val, prev):
	# get index to update
	presetIndex = int(null_index[0] + 1)
	# get a list channels
	chans = null_current.chans()
	# reset index
	index = 0

	for c in chans:
		done = False

		for i in range(table_all.numRows):
			table_name = table_all[i, 0]
			# check if a row for this value exists
			if(c.name == table_name):
				table_name.val = chopto_current[index, 0]
				updateValue(c, index, presetIndex)
				done = True
				break
		# if a row for this value doesn't exist
		if(done == False):
			table_all.appendRow([c.name])	
			updateValue(c, index, presetIndex)
			done = True
		index += 1	
	return

def updateValue(chan, index, presetIndex):
	# update value 
	param = table_all[index, presetIndex]
	param.val = chan
```

プリセットにテーブルを使うのはおなじみMatthew Raganさんの記事を参考にしています。
[TouchDesigner | FB HelpGroup | Presets | Matthew Ragan](https://matthewragan.com/2015/07/29/touchdesigner-fb-helpgroup-presets/)

## UIのアップデート

![q3.png](https://qiita-image-store.s3.amazonaws.com/0/58558/98f0b948-1d74-0168-4085-dd2b4bfe8396.png)

プリセット番号をクリックした時に、`chopexec_switch`で一度プリセットからの値を反映させた後、`op('text_reset_switch').run(delayFrames = 60)`で60フレーム後にUIから値を変更できる状態に戻します。

```python
constant_switch = op('constant_switch')
def onValueChange(channel, sampleIndex, val, prev):
	constant_switch.par.value0 = 1
	op('text_reset_switch').run(delayFrames = 60)
	return
```

```python
constant = op('constant_switch')
constant.par.value0 = 0
```

![q2.png](https://qiita-image-store.s3.amazonaws.com/0/58558/736481d9-e7bc-296d-5a97-84fb2e1a18e5.png)

`UI_private`内の各`CHOP Execute`たちはそれぞれすぐ右側にあるUIのValueを直接アップデートしています。UIから変更した時にもプリセットから変更したときにもUI上の値が変更されてほしいのでこのようにしています。

## 改善したい点
* jsonとかにexportして`table DAT`に読み込めないですかね？
* Pythonでcontainer内から外の`table DAT`を参照しているのと、`CHOP Execute`から各UIのValueを直接アップデートしているのをどうにかしたい（単純にネットワークとコードをきれいにしたい）。

## 最後に
プリセットこんな風にやってるよーという方おられたら、ご教示頂ければ幸いです。
ありがとうございました。