投稿日:2024-11-10
#Grasshopper
今回は Grasshopper ファイルの内部構造を解読してみようと思います。
で、ある会社の講演を聞いた際 Grasshopper ファイルを読み込むことで誰でもパラメーターを簡単に変更し、形状を確認できるシステムを開発した(してる?)という話がありました。Grasshopper は、Grasshopper に詳しくない人にとっては扱いづらいという問題がありますが、その課題を解決するために開発されたプロダクトだそうです。その話を聞きながら、たぶん Grasshopper のファイルを解析して Rhino Compute とかで API を呼んでるのかなと思ったので、まずは.Grasshopper のファイルの中身を読み込んでみることにしました。
Grasshopper のデータは通常、gh ファイルとして保存されますが、これは ghx ファイルをバイナリ化した形式です。バイナリ化することでファイルサイズを小さくし、データの読み書きをより高速に行うことができます。ghx ファイルは、バイナリ化される前の XML 形式で記述されており、人間が直接読み書きできる形式です。今回はこの ghx ファイルを読み込んで Grasshopper のファイルの中身がどうなっているかを確認していこうと思います。
とりあえず Grasshopper のデータを ghx 形式で保存して中身を見てみます。今回は上画像のような簡単なもので検証していこうと思います。保存する際に ghx の拡張子を選んで保存し、VSCode で開いてみます。xml 形式で記述されているのがわかります。
※以降 VSCode と Grasshopper の画面を開き、Grasshopper ファイルを変更し上書き保存しながら.ghx ファイルがどのように変わるかを確認しながら読んでいくとわかりやすいかと思います。
大まかな構造を見てみます。 1 行目に xml のバージョンと文字コードの宣言があり、その次の行の Archive タグに Grasshopper のデータが記述されているようです。 数百行記述がありますが、大まかな構造は以下のようになっているかと思います。
1<?xml version="1.0" encoding="utf-8" standalone="yes"?> 2<Archive name="Root"> 3 <item name="ArchiveVersion" type_name="gh_version" type_code="80"> 4 ...Grasshopperファイルのバージョン情報 5 </item> 6 <chunks count="2"> 7 <chunk name="Definition"> 8 <items count="1"> 9 <item name="plugin_version" type_name="gh_version" type_code="80"> 10 ...Grasshopperプラグインのバージョン情報 11 </item> 12 </items> 13 <chunks count="5"> 14 <chunk name="DocumentHeader"> 15 ...ドキュメントのidやプレビューする時の色などの設定情報 16 </chunk> 17 <chunk name="DefinitionProperties"> 18 ...Grasshopperファイルの作成日時やファイル名などのプロパティ情報 19 ...保存時のレイアウト情報など 20 <chunk name="DefinitionObjects"> 21 ...Grasshopperファイルに配置したコンポーネント情報 22 </chunk> 23 </chunk> 24 </chunks> 25 </chunk> 26 <chunk name="Thumbnail"> 27 ...ファイルのIconかな?プログラムに影響なさそう 28 </chunk> 29 </chunks> 30</Archive>
この中で上記の 20 行目の
<chunk name="DefinitionObjects">
部分がコンポーネントについての記述なのでこの中身を詳しく見ていきます。
DefinitionObjects の中身をざっくり見ていきます。以下のような構造になっているかと思います。
1<chunk name="DefinitionObjects"> 2 <items count="1"> 3 ...コンポーネント数。今回は4つ 4 <item name="ObjectCount"type_name="gh_int32"type_code="3">4<item> 5 </items> 6 <chunks count="4"> 7 <chunk name="Object" index="0"> 8 ...NumberSliderコンポーネント 9 </chunk> 10 <chunk name="Object" index="1"> 11 ...NumberSliderコンポーネント 12 </chunk> 13 <chunk name="Object" index="2"> 14 ...NumberSliderコンポーネント 15 </chunk> 16 <chunk name="Object" index="3"> 17 ...VectorXYZコンポーネント 18 <chunk> 19 </chunk> 20</chunk>
ざっくり見ていくとコンポーネントの数と配置されたコンポーネントについての記述がされていることがわかるかと思います。 今回のファイルは NumberSlider コンポーネントが 3 つと VectorXYZ が 1 つなのでファイルの構造と一致していることがわかります。 次に各コンポーネントの中身を見て行きます。
とりあえず最初の NumberSlider の中身を見てみましょう。以下のようになっております。
1<chunks count="1"> 2 <chunk name="Container"> 3 <items count="6"> 4 <item name="Description" type_name="gh_string" type_code="10">Numeric slider for single values</item> 5 <item name="InstanceGuid" type_name="gh_guid" type_code="9">df5e881e-e006-4dea-90de-3b7d2cd6d76b</item> 6 <item name="Name" type_name="gh_string" type_code="10">Number Slider</item> 7 <item name="NickName" type_name="gh_string" type_code="10"></item> 8 <item name="Optional" type_name="gh_bool" type_code="1">false</item> 9 <item name="SourceCount" type_name="gh_int32" type_code="3">0</item> 10 </items> 11 <chunks count="2"> 12 <chunk name="Attributes"> 13 <items count="2"> 14 <item name="Bounds" type_name="gh_drawing_rectanglef" type_code="35"> 15 <X>30</X> 16 <Y>186</Y> 17 <W>198</W> 18 <H>20</H> 19 </item> 20 <item name="Pivot" type_name="gh_drawing_pointf" type_code="31"> 21 <X>30.12512</X> 22 <Y>186.3333</Y> 23 </item> 24 </items> 25 </chunk> 26 <chunk name="Slider"> 27 <items count="7"> 28 <item name="Digits" type_name="gh_int32" type_code="3">3</item> 29 <item name="GripDisplay" type_name="gh_int32" type_code="3">1</item> 30 <item name="Interval" type_name="gh_int32" type_code="3">1</item> 31 <item name="Max" type_name="gh_double" type_code="6">3</item> 32 <item name="Min" type_name="gh_double" type_code="6">1</item> 33 <item name="SnapCount" type_name="gh_int32" type_code="3">0</item> 34 <item name="Value" type_name="gh_double" type_code="6">1</item> 35 </items> 36 </chunk> 37 </chunks> 38 </chunk> 39</chunks>
次に VectorXYZ コンポーネントの中身を見ていきます。以下のような構造になっています。長いのでざっくりとした構造を記載します。
1<chunk name="Object" index="3"> 2 <items count="2"> 3 <item name="GUID" type_name="gh_guid" type_code="9">56b92eab-d121-43f7-94d3-6cd8f0ddead8</item> 4 <item name="Name" type_name="gh_string" type_code="10">Vector XYZ</item> 5 </items> 6 <chunks count="1"> 7 <chunk name="Container"> 8 <items count="4"> 9 <item name="Description" type_name="gh_string" type_code="10">Create a vector from {xyz} components.</item> 10 <item name="InstanceGuid" type_name="gh_guid" type_code="9">50eb89c2-133b-473e-801a-981064b19be5</item> 11 <item name="Name" type_name="gh_string" type_code="10">Vector XYZ</item> 12 <item name="NickName" type_name="gh_string" type_code="10">Vec</item> 13 </items> 14 <chunks count="6"> 15 <chunk name="Attributes"> 16 ...VectorXYZコンポーネントの位置情報 17 </chunk> 18 <chunk name="param_input" index="0"> 19 ...入力端子Xの情報 20 </chunk> 21 <chunk name="param_input" index="1"> 22 ...入力端子yの情報 23 </chunk> 24 <chunk name="param_input" index="2"> 25 ...入力端子zの情報 26 </chunk> 27 <chunk name="param_output" index="0"> 28 ...出力端子V 29 </chunk> 30 <chunk name="param_output" index="1"> 31 ...出力端子L 32 </chunk> 33 </chunks> 34 </chunk> 35 </chunks> 36</chunk>
では次に入力端子 X を ピックアップしてみてみます。
1<chunk name="param_input" index="0"> 2 <items count="7"> 3 <item name="Description" type_name="gh_string" type_code="10">Vector {x} component</item> 4 <item name="InstanceGuid" type_name="gh_guid" type_code="9">3cbcb950-0cca-4712-b49d-9fd37063e84b</item> 5 <item name="Name" type_name="gh_string" type_code="10">X component</item> 6 <item name="NickName" type_name="gh_string" type_code="10">X</item> 7 <item name="Optional" type_name="gh_bool" type_code="1">false</item> 8 <item name="Source" index="0" type_name="gh_guid" type_code="9">df5e881e-e006-4dea-90de-3b7d2cd6d76b</item> 9 <item name="SourceCount" type_name="gh_int32" type_code="3">1</item> 10 </items> 11 <chunks count="2"> 12 <chunk name="Attributes"> 13 <items count="2"> 14 <item name="Bounds" type_name="gh_drawing_rectanglef" type_code="35"> 15 <X>259</X> 16 <Y>186</Y> 17 <W>14</W> 18 <H>20</H> 19 </item> 20 <item name="Pivot" type_name="gh_drawing_pointf" type_code="31"> 21 <X>267.5</X> 22 <Y>196</Y> 23 </item> 24 </items> 25 </chunk> 26 <chunk name="PersistentData"> 27 <items count="1"> 28 <item name="Count" type_name="gh_int32" type_code="3">1</item> 29 </items> 30 <chunks count="1"> 31 <chunk name="Branch" index="0"> 32 <items count="2"> 33 <item name="Count" type_name="gh_int32" type_code="3">1</item> 34 <item name="Path" type_name="gh_string" type_code="10">{0}</item> 35 </items> 36 <chunks count="1"> 37 <chunk name="Item" index="0"> 38 <items count="1"> 39 <item name="number" type_name="gh_double" type_code="6">0</item> 40 </items> 41 </chunk> 42 </chunks> 43 </chunk> 44 </chunks> 45 </chunk> 46 </chunks> 47</chunk> 48
Input 端子についてまとめると、
といった構成になっていました。
次に output 側も見てみます。
1<chunk name="param_output" index="0"> 2 <items count="6"> 3 <item name="Description" type_name="gh_string" type_code="10">Vector construct</item> 4 <item name="InstanceGuid" type_name="gh_guid" type_code="9">47ac57ad-cdfb-4d4e-93e8-9ee53f01c0d3</item> 5 <item name="Name" type_name="gh_string" type_code="10">Vector</item> 6 <item name="NickName" type_name="gh_string" type_code="10">V</item> 7 <item name="Optional" type_name="gh_bool" type_code="1">false</item> 8 <item name="SourceCount" type_name="gh_int32" type_code="3">0</item> 9 </items> 10 <chunks count="1"> 11 <chunk name="Attributes"> 12 <items count="2"> 13 <item name="Bounds" type_name="gh_drawing_rectanglef" type_code="35"> 14 <X>421</X> 15 <Y>186</Y> 16 <W>17</W> 17 <H>30</H> 18 </item> 19 <item name="Pivot" type_name="gh_drawing_pointf" type_code="31"> 20 <X>429.5</X> 21 <Y>201</Y> 22 </item> 23 </items> 24 </chunk> 25 </chunks> 26</chunk>
こちらのほうがシンプルで Output なので SourceCount が 0 で、あとは位置情報があるだけです。
ざっくり ghx コンポーネントの中身をみて基本的な構造がわかったのですが、この中にはコンポーネント同士の関係性が記述されており、このファイルを解析して Grasshopper 側で処理が実行されているんだろうなとわかりました。 冒頭に述べた内容に戻りますが、Grasshopper ファイルを読み込むませ誰でもパラメーターを簡単に調整し形状を確認できるシステムを、例えば Web 上で行うとして ghx ファイルを読み込み、コンポーネント同士の関係性を解析し Rhino Compute とか使って API 呼ぶことで実現しているのかなと勝手に思った次第であります。そのうち実装してみよう。
以上