STUDIO TAMA


thumbnail

投稿日:2024-11-10

【Grasshopper】Grasshopperのghxファイルを解読してみた

  • #Grasshopper

今回は Grasshopper ファイルの内部構造を解読してみようと思います。

Archifuture2024

で、ある会社の講演を聞いた際 Grasshopper ファイルを読み込むことで誰でもパラメーターを簡単に変更し、形状を確認できるシステムを開発した(してる?)という話がありました。Grasshopper は、Grasshopper に詳しくない人にとっては扱いづらいという問題がありますが、その課題を解決するために開発されたプロダクトだそうです。その話を聞きながら、たぶん Grasshopper のファイルを解析して Rhino Compute とかで API を呼んでるのかなと思ったので、まずは.Grasshopper のファイルの中身を読み込んでみることにしました。

ghx ファイルについて

Grasshopper のデータは通常、gh ファイルとして保存されますが、これは ghx ファイルをバイナリ化した形式です。バイナリ化することでファイルサイズを小さくし、データの読み書きをより高速に行うことができます。ghx ファイルは、バイナリ化される前の XML 形式で記述されており、人間が直接読み書きできる形式です。今回はこの ghx ファイルを読み込んで Grasshopper のファイルの中身がどうなっているかを確認していこうと思います。

とりあえず.ghx で保存して中身を確認してみる

thumbnail

とりあえず 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 部分の解読

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 コンポーネントの中身

とりあえず最初の 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>
  • items タグ内にはコンポーネントの description や id、名前や Nickname などが記述されています。Optional は多分入力端子がある際に入力が必須かどうかを示すものかな・・・確信はないですが・・・、SourceCount は入力されてくる値の数になりますが NumberSlider コンポーネントは入力がないので 0 になります。
  • 次の chunks タグの中身にはコンポーネントのファイル内での位置情報(Attributes)とコンポーネントの設定や値(Slider)が含まれてます。
  • <chunk name="Attributes">部分を見ていくとコンポーネントのサイズや座標(Bounds)、移動時の起点(Pivot)?(多分...)が含まれています。実際に Grasshopper 側でコンポーネントを移動させたりサイズを伸縮させて上書き保存すると値が変わるのでわかるかと思います。
  • <chunk name="Slider">部分に NumberSlider コンポーネントの設定や値が含まれていることがわかります。コンポーネントの Edit 画面から設定や値をを変更して保存してみるとわかりやすいかと思います。

VectorXYZ コンポーネントの中身

次に 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>
  • 最初の items でコンポーネントの id や名前が設定されています。
  • 次の chunks の中の<chunk name="Container">部分には、コンポーネントの情報(description, id ,Name, Nickname,)が設定されており、さらにコンポーネントの Grasshopper ファイル内での位置情報や入力端子と出力端子のまとまりが定義されています。

では次に入力端子 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
  • items の中に入力端子の情報が記述されてます。Source にどの入力端子がつながれているか id で紐づいており繋がっている NumberSlider コンポーネントの id が記述されています。また SourceCount はこの入力端子に接続されているコンポーネントの数で、今回は 1 つだけなので count が1になっています。
  • <chunk name="Attributes">は入力端子の位置情報かな?UI 上は1つのコンポーネントに見えるけど、複数のまとまりを1つのコンポーネントとして見せてるっぽい?
  • <chunk name="PersistentData">に関してはデフォルト値が入ります。この input 端子は何もつながないと 0 という値が1つ、{0}という Tree 構造で入力されます。試しに、入力端子を右クリックで SetNumber あるいは SetMultipleNumber で初期値を変えてあげるとこのあたりの記述も変化します。

Input 端子についてまとめると、

  1. コンポーネントの基本情報と接続元のコンポーネント情報
  2. コンポーネントの位置情報
  3. デフォルト値

といった構成になっていました。

次に 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 呼ぶことで実現しているのかなと勝手に思った次第であります。そのうち実装してみよう。

以上

目 次