[TouchDesigner]gitでのバージョン管理を支援するtox

この記事は[TouchDesigner Advent Calendar 2020](https://qiita.com/advent-calendar/2020/touchdesigner)の2日目の記事です。

前日（1日目）の記事は @miwa_maroon さんの「[[Touchdesigner]新卒でもハンドトラッキングできるんじゃ! leapmotionとともに](https://qiita.com/miwa_maroon/items/d35519152cf392dfaf71)」です。

翌日（3日目）の記事は @shks さんの「[ParsecTOP : ParsecをTouchDesignerで使って低遅延60FPSストリーミング送受信](https://qiita.com/shks/items/411cc41ae69d9551d31b) 」です。

---
さてみなさんいきなりですが、__toeファイルのバージョン管理、諦めてませんか？__

```shell
> git log --oneline --name-status
f4b6ef7 (HEAD -> main) パラメータをいい感じに変更
M       proj/main.toe
dfc410b みやすく整理
M       proj/main.toe
d4425b2 いい感じのフィルタを追加
M       proj/main.toe
78f5ca4 テクスチャサイズをちょっと小さくした
M       proj/main.toe
```

コミットメッセージを細かく書こうにも限界があるし、自動マージ無理だしコンフリクトなんて起きたときはもう、ねえ？

要は変更の差分が見えれば良いわけなので、それができるものを作ってみました。

https://github.com/nariakiiwatani/VersioningTouchDesignerNetwork

## 使い方

`NetworkExporter.tox`をどこかに置くだけ！
![スクリーンショット 2020-11-24 14.23.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/dc777ca2-08c4-b053-1abe-2867c3dba763.png)


## できること

- 監視対象(デフォルト:`/`)とその下層のオペレーターの情報（オペレータータイプ、位置、サイズ、タグ、コメント、各種フラグ、OP間の接続、パラメータ）をJSONファイルにexport
- 上記処理をPreSave/PostSaveからフック

上のスクショの状態だと、書き出されるファイル群はこんな感じになります。
オペレーター毎にJSONファイルがあり、COMPに対しては同名のフォルダがくっつき、下層の情報がフォルダ内に書き出されています。
※`network_structure`はtoxのパラメータで指定できる書き出しパス
![スクリーンショット 2020-11-24 14.07.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/96401e47-434c-c7c6-2275-e32325f87e94.png)

エスケープシーケンスで読み辛いけど書き出しのスクリプトはこんな感じ（ちなみにこれは書き出しスクリプトが書いてあるTextDATのエクスポート結果。TouchDesignerのファイルの中身をこういう形で見せられるの、すごくよくない？）
[export_script](https://github.com/nariakiiwatani/VersioningTouchDesignerNetwork/blob/4076f1aa7ce43ac28aeb38f548088212cc576b9f/network_structure/NetworkExporter/export_script.json)
[module_extract](https://github.com/nariakiiwatani/VersioningTouchDesignerNetwork/blob/4076f1aa7ce43ac28aeb38f548088212cc576b9f/network_structure/NetworkExporter/module_extract.json)

JSONファイルはデータ毎にいちいち改行するフォーマットになっているので、gitでdiffを見るのに向いていると思います。
TextDATやTableDATの内容もちょっと冗長な感じ（JSON string arrayなので……）になってしまいますが一応行ごとに差分が取れる作りになってます。  
こういうファイルがオペレーター毎に作られます。


```json:execute1.json
{
	"name": "execute1",
	"OPType": "executeDAT",
	"comment": "",
	"nodeX": 100,
	"nodeY": 275,
	"nodeWidth": 130,
	"nodeHeight": 90,
	"storage": {},
	"tags": [],
	"replicator": null,
	"activeViewer": false,
	"allowCooking": true,
	"bypass": false,
	"cloneImmune": false,
	"render": false,
	"viewer": true,
	"inputConnectors": [
		{}
	],
	"outputConnectors": [
		{}
	],
	"par": [
		{
			"name": "projectpresave",
			"val": false,
			"expr": "op('par1')['Exportonpresaving']",
			"enableExpr": null,
			"exportOP": null,
			"exportSource": null,
			"bindExpr": null,
			"bindReferences": [],
			"mode": "EXPRESSION"
		},
		{
			"name": "projectpostsave",
			"val": false,
			"expr": "op('par1')['Exportonpostsaving']",
			"enableExpr": null,
			"exportOP": null,
			"exportSource": null,
			"bindExpr": null,
			"bindReferences": [],
			"mode": "EXPRESSION"
		}
	],
	"text": [
		"# me - this DAT",
		"# ",
		"# frame - the current frame",
		"# state - True if the timeline is paused",
		"# ",
		"# Make sure the corresponding toggle is enabled in the Execute DAT.",
		"",
		"def onStart():",
		"\treturn",
		"",
		"def onCreate():",
		"\treturn",
		"",
		"def onExit():",
		"\treturn",
		"",
		"def onFrameStart(frame):",
		"\treturn",
		"",
		"def onFrameEnd(frame):",
		"\treturn",
		"",
		"def onPlayStateChange(state):",
		"\treturn",
		"",
		"def onDeviceChange():",
		"\treturn",
		"",
		"def onProjectPreSave():",
		"\top('export_script').run()",
		"\treturn",
		"",
		"def onProjectPostSave():",
		"\top('export_script').run()",
		"\treturn",
		"",
		"\t"
	]
}
```

実際のgit差分は例えばこんなふうに見えます。
※`NetworkExporter`という名前の`baseCOMP`の位置を動かして、`Watchtarget`という名前のパラメータを変更したコミット
([View this commit on GitHub](https://github.com/nariakiiwatani/VersioningTouchDesignerNetwork/commit/0497655fae201487abd7c3bf408fa2a9e82f220c#diff-0ff38a35498c61163e48c96dac173beb4320a52d8e442f12cbcf410f2826ad95))
![git diff](https://user-images.githubusercontent.com/1306139/100744334-022cfd00-3421-11eb-9ce0-1d9f3684dd36.png)

## できないこと

- exportした情報のimport  
とりあえずunsupportedですが、やりたいところではありますね・・・。  
ちなみに公式ツールの`toeexpand`/`toecollapse`を使えばできるらしい。  
教えていただきました。
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">あまり知られてませんが公式にもこう知った機能があります、、<a href="https://t.co/TTgcY1KnT9">https://t.co/TTgcY1KnT9</a></p>&mdash; Joe Ohara / じょー (@Joe0hara) <a href="https://twitter.com/Joe0hara/status/1327607586670088192?ref_src=twsrc%5Etfw">November 14, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### `toeexpand`/`toecollapse`について
これらを使いたい場合はこちらに、`git commit`をフックして`toeexpand`を叩くスクリプトを作ったのでこれを使うと捗るかもしれません。
https://github.com/nariakiiwatani/toeexpand-git-pre-commit
macのみ対応してますが、`toeexpand`のパスを書き換えるだけでWindowsでも動くんじゃないかと思うのでそのうちやるかもしれません。

`toeexpand`も、各オペレーターやそれらの構造をテキストファイルに展開してくれるツールなのでgit管理のために使おうと思えば使えると思うんですが、個人的には、あんまりこのツールで書き出した情報が人間に読みやすいかというとそうでもない気がしてるので、やはりやるなら自前でも作っておきたいかな、と思っています。

## その他

- `WatchTarget`を`/`にして書き出すと、ルートに`root`という名前のbaseCOMPがあるのがわかりました。普通に使ってると目に入りませんがそういう作りになってるんですねえふむふむ。
これもしかして唯一の「名前とネットワークパスとが一致しないオペレーター」なのでは？
https://github.com/nariakiiwatani/VersioningTouchDesignerNetwork/blob/4076f1aa7ce43ac28aeb38f548088212cc576b9f/network_structure/.json

- Pro版の[Save Private](https://docs.derivative.ca/Privacy)で保存されたtoxはどう見えるんですかね・・・？

- もし興味を持って使ってみたいという方がいらっしゃれば、ベータテスター的な感じで使用感や改善点の感想をもらえると嬉しいです。
もしインポートできるツールを作るなら有料で公開かなと思っているんですが、なんらかフィードバックくださった方には無償で提供します。（結局作らなかったり、気が変わって無料で公開したらすみません・・・！）