【集中講座】Grasshopper: スパゲッティコードを回避して見やすい配線を作る方法

はじめに

Grasshopper でアルゴリズムを組んでいくと、気づくとキャンバス上の配線(ワイヤ)がごちゃごちゃになってしまうことはありませんか?配線が見づらいと、後から見返したとき、アルゴリズムの修正が難しくなります。

見やすい配線を維持することで、後々のアルゴリズムの修正・確認も楽になります。ワイヤが絡み合う『スパゲッティ状態』にお悩みの方は、ぜひ参考にしてみてください。

スパゲッティコード

スパゲッティコードとは、プログラムの処理の流れが絡み合って複雑になり、確認や修正が難しい状態をさす言葉です。Grasshopper のようなワイヤを繋げていくノードベースツールだと、コンポーネント同士を繋ぐワイヤが絡み合い、文字通りスパゲッティのような見た目になります。

右図は点を 2 つ読み込み、そのうちの 1 点を移動して線を作成します。作成した直線をZ軸方向に押し出すといった処理ですが、すでに理解しづらくなっています。

これくらいであれば少し絡まっただけですが、もう少し絡まったスパゲッティ状態を放置すると、どこからどこへ繋がっているかを追うこともできなくなります。一度作って動かすだけなら問題ないかもしれませんが、

  • 数日後に、自分でアルゴリズムを修正
  • チームのメンバーと、ファイルを共有

するときに問題となります。処理の流れがわからないと、そもそもアルゴリズムが正しく動いているのかすら分からなくなります。

前提:情報の流れは「左から右へ」を意識する

前提としてGrasshopperの端子はコンポーネントの左側に入力端子が、右側に出力端子がついています。また人間の目は、左上から右下へと動くとも言われています。

ですので、まずは左から右へと処理順にコンポーネントを配置することで、ワイヤが揃い理解しやすくなります。

先ほどのアルゴリズムのコンポーネントの位置で、修正してみると右図のように少し見やすくなりました。

ステップ1:見た目を整える(視覚的な整理)

コンポーネントの位置を直して、左から右への流れができたら、次はワイヤの表示を工夫して、視覚的な見えづらさを解消してみましょう。

回避方法 A. ワイヤの表示方法を変更

Grasshopper には、ワイヤ自体の表示を変更する機能があります。コンポーネントの端子の上で右クリックし、 Wire Display (ワイヤディスプレイ) を、Default から Hidden (ヒドゥン) に設定すると、ワイヤが非表示になります。

一見するとキャンバス上はすっきりと見やすくなりますが、この方法は使いすぎに注意が必要です。ワイヤを完全に消してしまうと、データの参照元がひと目で分からなくなり、意図しないコンポーネントと接続していても間違いに気づけません。

そこでおすすめなのが、同じメニューにある Faint (フェイント) を使う方法です。Faintを使うと完全にワイヤを消すのではなく、細い線で表示できます。これを使うことで、情報の流れを追いやすくしつつスパゲッティ状態を緩和できます。

回避方法 B. Relayを活用する

ワイヤの表示を消す代わりに、 [ Relay ] (リレー)コンポーネントを利用してワイヤを束ねる方法も効果的です。ワイヤの上でダブルクリックをすると、 [ Relay ] コンポーネントを作成できます。他のコンポーネントにぶつからないよう移動したり、広がっているワイヤを一束にまとめることができます。

さらに、 [ Relay ] は右クリックから名前をつけることができます。上流で付けた名前は下流に作成した [ Relay ] にも自動で引き継がれるので、「そのワイヤに何のデータが流れているか」がひと目で分かるようになります。

ただ [ Relay ] コンポーネントの欠点として、コンポーネントをクリックで選択しても、Rhinoの画面上で、作成した形状のプレビュー(緑色にハイライトされる確認表示)ができません。

ワイヤを整理しつつ繋いだ形状の確認もしたい場合は、[ Relay ] の代わりに [ Brep ] や [ Curve ]、[ Point ] 、[ Plane ] などの形状コンポーネントに任意の名前をつける方がおすすめです。

ステップ2:データ構造や計算処理を整える (複数の処理を統合する)

キャンバスの見た目を綺麗にしても、アルゴリズムが大きくなると、複数のワイヤが交差して分かりづらくなってしまいます。

以降は見た目を整えるだけでなく、データの仕組みからみやすくする方法を紹介します。

回避方法 C. Expressionで計算処理を1つにまとめる

足し算や引き算などを行う際、[ Addition ] (アディション) や [ Subtraction ] (サブトラクション) などのコンポーネントを複数繋いで計算していませんか?

四則演算などのコンポーネントは繋げば繋ぐほど、ワイヤが増えて何の計算をしているのかが追いづらくなっていきます。

複雑な計算をスッキリまとめるには、 [ Expression ] (エクスプレッション) コンポーネントを使用できます。入力端子に名前をつけることで、計算式と関連を持たせて確認することができます。

右図は L が全体の長さ、o が両端の余白、n が分割数として、ルーバーのようなものを配置するときのそれぞれのピッチを求めた計算式です。

 [ Expression ] の使用手順

  1. [ Expression ] コンポーネントを配置します。

2. コンポーネントにズームして、+-アイコンをクリックして入力端子を必要な数だけ増やし、それぞれの端子に計算式内で使用する名前をつけます。

3. 中央の数式部分をダブルクリックして、エディタを開きます。

4. (L – o * 2) / n のように、行いたい計算式を直接テキストで記述します。計算値が、R端子から出力されます。

※端子名の変更と表示設定の注意点

 [ Expression ] の入力端子は、右クリックから 任意の名前(変数名)に変更して使うのが基本です。 以前のバージョンのGrasshopper では、画面上部のメニュー Display > Draw Full Names (ドローフルネーム) の表示モードをオンにしていると、元々の x や y という端子名が強制的に Variable x のような長い名前に上書き表示されてしまいます。この状態になると、数式内に入力した x や y という文字と端子名が一致しなくなり、計算がエラーになってしまうという罠がありました。

そのため、[ Expression ] を使う際は必ず入力端子の名前を 任意の名前に変更したうえで、数式もそれに合わせて記述するように習慣づけておくと安心です。

回避方法 D. Entwineで複数のデータを扱う

4 つ目の回避方法は、 [ Entwine ] (エントワイン) コンポーネントを使用して、階層ごとに複数のデータをまとめる方法です。複数のデータをまとめることで、ワイヤの本数自体を減らすことができます。

また [ Entwine ] は [ Merge ] コンポーネントと似たデータをまとめる働きをしますが、

  •  [ Merge ] : 元々持っている Path 値ごとにデータをまとめる
  •  [ Entwine ] : 入力端子で設定した Path 値になるようにデータをまとめる

といった違いがあります。右図では [ Merge ] は入力データが持つPath 値の { 1 },{ 2 } にまとまりまります。一方、[ Entwine ] は入力端子ごとに{ 0 ; 0 } , { 0 ; 1 } とPath 値を変更してまとめているのが分かります。

注意 :  [ Entwine ] の Flatten Inputs オプションを外すと、入力したデータが持つ階層を残しつつ、さらに Graft を行うような処理となります。このページでは Flatten Inputs オプションをつけた状態で行ってください。

複数データの登録手順

  1. 複数のデータを [ Entwine ] コンポーネントの入力端子にそれぞれ接続します。入力端子は、コンポーネントにズームして +/- マークをクリックすることで増減できます 。 

2. データは入力端子ごとに、異なる Path を持つ階層にそれぞれ配置され、1 つのデータツリーにまとまります 。

3. 呼び出し時に楽に呼び出せるよう、出力に [ Simplify ] オプションを設定しPath 値を単純化します。それによりここでは、{ 0 ; 0 } を { 0 } のように単純化しています。

必要なデータをすべて取り出す手順

  1.  [ Explode Tree ] ( エクスプロード ツリー) コンポーネントに接続します。

2. 右クリックから Match Outputs を実行し、Path の階層値のデータをそれぞれの端子から出力します 。

右図は それぞれの出力端子から各種データを取得した図。出力端子数が足りない場合は注意(オレンジ色)で表示されるので、 Match Outputs を実行する必要があります。

指定したPath値のデータのみ取り出す手順

  1. データを抽出したい Path 値を [ Construct Path ] (コンストラクトパス) コンポーネントを使用して、数値から作成します(Simplify オプションを設定していない場合は、0 と 0 や 0 と 1 など複数の値を入力する必要があります)。

 2. [ Tree Branch ] (ツリーブランチ) コンポーネントに接続して、設定した Path の階層値のデータを取得します 。

右図は { 1 } 内にある Data2 という文字を取得した図。階層内に複数データがあるときも、まとめて取り出すことができます。

[ Entwine ] を使用する際の注意点

複数のデータを 1 つのデータツリーにまとめられる便利な [ Entwine ] コンポーネントですが 、使用するにあたって下記のような欠点もあります 。

  • 順番の変更に弱い: [ Entwine ] に接続するデータの順番が変わると、割り当てられる階層の順番も変わってしまいます 。
    入力順に基づいて { 0 } や { 1 } のようにデータツリーの Path 値が自動的に割り振られるため、順番を変えた場合は、同じデータを取り出すには、呼び出し側の番号を変更し直さなければなりません。異なるデータを最後ではなく途中に挿入した場合も、端子の番号が変わるため注意が必要です。
  •  可読性が低くなる: データを取得する際に Path をインデックス番号などで指定するため、後から見返したときに「何のデータを呼び出しているのか」がわかりづらくなります 。

もしこれらの問題によって管理が難しくなる場合は、次に紹介する「 Python での辞書」を利用した解決方法も効果的です。 

回避方法 E. Pythonの辞書で複数のデータを扱う

複数のデータを整理するときに有効なのが、 Python (パイソン) の辞書というデータ構造を活用する方法です。 Python と聞くと難しそうに感じるかもしれませんが、たった数行のコードをコピー&ペーストするだけで、[ Entwine ] より簡単にデータを扱うことができます。

Pythonの辞書は現実の辞書と同様に、

  • 検索する文字列 Key (キー)
  • 対応する各種データ Value (値)

をペアとして組み合わせて設定します。また [ Entwine ] のように階層の番号で管理するのではなく、自分が設定した名前でデータを検索できるため、取り出すときにも何を取り出しているかが分かりやすくなります。

データの登録手順

  1. [ Python 3 Script ] コンポーネントを配置します。

2. ズームして +- マークをクリックし、入力端子を 2 つ設定します。

3. 入力端子の上で右クリックして、端子の名前を keys と values に変更します。また出力端子名は、result_dict とします。

4. keys の端子を右クリックして List Access (リストアクセス) に変更し、 Type hints (タイプヒント) から str を指定します。

5. values の端子を右クリックして Tree Access (ツリーアクセス) に変更します。

6. values に[ Entwine ] で階層を分けたデータを繋ぎます。また keys に [ Panel ] コンポーネントなどで values が持つ階層と同じ数だけキー値を作成し 端子にそれぞれ接続します。

7. [ Python 3 Script ] コンポーネントをダブルクリックし、右のコードを記述します。

注意:

・入力・出力端子名は上記以外の任意の名前にも設定可能ですが、その場合はコードも同名に合わせる必要があります。

・端子名やコードの中では、大文字と小文字も区別されます。keys と入力した場合は、中のコードも key や Keys ではなく keys と端子名と合わせて書く必要があります。

・起こりがちなエラーとして、Type hint の指定忘れや List Access、Tree Access の指定し忘れがあります。正しい型や入力を指定しないと、意図した通りに動作しない可能性があります。ご注意ください。

# 辞書を作成
dictionary = {}

# valuesはTree(階層があることを想定)なので、階層ごとにList構造で取り出す
value_list = list(values.Branches) 

# keysの長さを利用してインデックス i を回す
# keysの数とvaluesの階層の数が等しいことを前提
for i in range(len(keys)):
    k = keys[i]
    v = value_list[i]
    dictionary[k] = v

# 出力端子に接続。GHの仕様上そのまま辞書で出力すると、キャンバスで展開される。
# 展開されないよう[]で括って、複数のデータを明示して渡す。
result_dict = [dictionary]

データの呼び出し手順

データを取り出したい箇所で、辞書を作成したのとは別に [ Python 3 Script ] コンポーネントを配置し、

  • 作成済みの辞書データ
  • 取り出したいデータの Key

 をそれぞれ入力します。手順は下記の通りです。

  1. データを取り出したい場所に新しく [ Python 3 Script ] コンポーネントを配置します。
  2. 入力端子を 2 つにし、名前を dictionary と key に変更します。出力端子名をvalue とします。
  3. key 端子の Type hint を str に指定します。

4. dictionary 端子には、先に登録した辞書データからでているワイヤを接続します。

5. key 端子には取り出したい名前を [ Panel ] コンポーネントなどで記述して接続します。

6. 右のコードをコンポーネントをダブルクリックして記述します。

# 辞書とキーから値を取得します。
value = dictionary[key]

辞書に登録した[テスト値]や[テスト曲線群]といったキー名から、対応した値を取り出しているのがわかります。

この方法なら、キーを変更しない限り検索するデータが崩れづらいです。複数のワイヤを引っ張ってくることなく、名前で検索するだけでデータを呼び出せるため、アルゴリズムの管理がしやすくなります。

まとめ

本記事では、 Grasshopper のスパゲッティコードを回避し、ワイヤを視覚的・データをまとめることで見やすくする方法を紹介しました。整理された理解しやすいデータにしておくことで、後から見返したときや、他の人に共有する際にもスムーズに作業を進めることができます。

ぜひ、スパゲッティにならないコンポーネント配置を試してみてください。


補足

また、今回はワイヤやデータの流れを綺麗にする手法を中心に解説しましたが、アルゴリズムがさらに大規模になった場合には、コンポーネント自体をひとまとめにする機能も便利です。代表的な方法として [ Cluster ] (クラスター) や [ Hops ] (ホップス) 機能があります。

[ Cluster ] は、キャンバス上で複雑になったコンポーネント群を、 1 つの収納箱にまとめて管理する機能です。これは通常通り Grasshopper の計算として処理されます。

一方 [ Hops ] は、外部の Grasshopper ドキュメントを呼び出して使用する機能です。こちらは開いている Grasshopper とは別にバックグラウンドで並列計算や非同期処理を行うことができ、時間がかかる重い処理にも向いています。

まずは本記事の配線整理からマスターし、さらにステップアップしたいときにこれらの機能も活用してみてください。関連情報は、下記リンクを参照ください。