vicc blog

株式会社ヴィックの技術ブログです。

複数のRhinoファイルに複数のGrasshopperファイルを実行する方法

はじめに

こんにちは。Viccでアルバイトをしている佐藤と申します。本記事では、RhinoPythonを用いてRhino上で複数のGrasshopperファイルを実行する方法を探りました。最後まで読んでいただければ幸いです。

背景と現状の課題

まずはこの調査をしようと思った背景について少しご説明します。

blog.syntegrate.jp

この記事で書かれていることと同様ですが、Viccでは大規模なプロジェクトにて部品それぞれの製作図を作図する際、ファイルサイズが大きくなりすぎないようにRhinoファイルを分割して運用するという工夫をしています。

またGHファイルに関しても、複数の機能を1つにまとめた巨大なスクリプトをつくるのではなく、機能毎に個別のGHファイルを作成した方が、開発効率やPCの処理能力のどちらの面にも良いと考えています。


先述した記事の事例では、ElefrontとRhinoファイルを”開いて・保存し・閉じる”というRhinoPythonスクリプト(open_save.py)の組み合わせにより、1つのGHファイルを複数のRhinoファイルに連続的に適用する手法が確立され、数十ファイル単位での半自動作図が可能になりました。

しかし、現状の手法では、N個のRhinoファイルに対して1つのGHファイルしか実行することができないため、この操作を実行したいそれぞれのGHファイルごとに繰り返し行わなければなりませんでした。


そこでこの操作を効率化するため、N個のRhinoファイルを連続的に処理しながら、その中でM個のGHファイルを適用する手法、”N対Mの関係”を研究していくというのが本記事のねらいです。

下調べ

まずは下調べとして、Rhino側からGHを操作する方法を調査しました。

いくつか方法があるとは思いますが、今回はRhino7から導入された新機能のGrasshopper Playerと、オールドスタイルなRhinoPythonスクリプトについて調べてみたので簡単にそれぞれの紹介をします。


まず、GHPlayerについて。こちらはRhino7で新しく導入された、GHファイルをRhinoコマンド化することが可能な機能です。GHファイルをRhinoのコマンドに設定することで、GHファイルを開かずにプログラムを動かすことができます。


しかしこの機能を軽く調べた限り、純正のContext Bakeコンポーネントを利用しBakeを行う作例が殆どで、Elefrontを利用したLayer や UserText などの詳細な設定、KeyValue の書き換えができそうにないということもあり、今回は使用を見送りました。


次にRhinoPythonスクリプトについてですが、こちらは使われている方も多いのではないでしょうか。Python言語でスクリプトを書くことで、Rhinoコマンドの実行だけでなく繰り返しの操作の自動化や、アルゴリズムを利用したジオメトリの作成など様々なことができるのがRhinoPythonの利点です。

今回はそんなRhinoPythonならGHの操作・実行もできるのではないかという予想から調査をしてみようと思いましたが、そんな必要もなくSyntegrateの記事にありました。

blog.syntegrate.jp

これなら通常通りにElefrontを使ったプログラムを組んで実行することができそうです。


ということで、ここからは上記の記事を参考にGH_RhinoScriptInterfaceを利用して、RhinoPythonで複数のGHファイルを順番に実行する方法を検証していこうと思います。

(参考にしたドキュメント)
GH_RhinoScriptInterface Class


技術検証

動作環境は下記のとおりです。

  • Windows10
  • Rhinoceros 7
  • Grasshopper 1.0.0007
  • Elefront_Beta, Version=5.1.2.0

まずは簡単な動作検証から。

・Test1ーRhinoPythonでGHを起動実行してみる+KVを付けてBakeしてみる(1対1)

Rhino上の点をElefrontのReference by Layerコンポーネントで取得し、その点をベースとした四角形を作成、KeyValueをつけてBakeするというGHファイルを作成しました。

GHファイルではBoolという名前のBooleanコンポーネントの中身は空になっています。

RhinoPythonで以下のスクリプトを記述し実行します。

# -*- coding: utf-8 -*-

import Rhino
import rhinoscriptsyntax as rs

#GHファイルへのパス
path_gh = "test01.gh"

#GH_RhinoScriptInterfaceの取得
gh = Rhino.RhinoApp.GetPlugInObject("Grasshopper")

#grasshopperを開いて表示
gh.LoadEditor()
gh.CloseAllDocuments()
gh.ShowEditor()

#ghファイルの読み込み
gh.OpenDocument(path_gh)

#Boolianをセット
gh.AssignDataToParameter("Bool", True)

#実行
gh.RunSolver(True)

#ghを閉じる
gh.CloseAllDocuments()
gh.HideEditor()

このようにBoolコンポーネントにTrueを代入するスクリプトを書くことで、KeyValueを付けたオブジェクトを自動的にBakeさせることができました。


では次のステップとして、1つのRhinoファイルに対する複数のGHファイルの実行を試してみます。

・Test2ーRhinoPythonでループを回し複数のGHを起動実行してみる(1対M)

GHファイルは先ほどの四角形を作成するファイルに加えて、同様に円を作成するGHファイル、Boxを作成するGHファイルの3つのファイルを用意し、同じフォルダ内に保存しました。

今回の例では3つのGHファイルでのそれぞれの操作に依存関係がなく順番は不問であるため、上の画像にあるような名前になっていますが、GHファイルの実行する順序を指定したい場合はファイル名の先頭に番号を付けることで、希望した順番にGHファイルを実行できます。

今回は以下のスクリプトをRhinoPythonで実行します。

# -*- coding: utf-8 -*-

import os
import rhinoscriptsyntax as rs
import Rhino
import scriptcontext as sc

#GH_RhinoScriptInterfaceの取得
gh = Rhino.RhinoApp.GetPlugInObject("Grasshopper")

#フォルダ内のGHファイルのパスをリスト化
def getGHFile(_dir):
    pathList = []
    for root, dirs, files in os.walk(_dir, topdown=False):
        for name in files:
            path = os.path.join(root, name)
            path = str(path)
            if path.endswith(".gh"):
                pathList.append(path)
    return pathList

#GHファイル起動実行操作
def GHLoop(file):
    #grasshopperを開いて表示
    gh.LoadEditor()
    gh.CloseAllDocuments()
    gh.ShowEditor()
    
    #ghファイルの読み込み
    gh.OpenDocument(file)
    
    #Boolianをセット
    gh.AssignDataToParameter("Bool", True)
    
    #実行
    gh.RunSolver(True)

sc.doc = Rhino.RhinoDoc.ActiveDoc

#FolderLocation
mydir = rs.BrowseForFolder(None,"Sel Folder")

files = getGHFile(mydir)

for f in files:
    GHLoop(f)

#ghを閉じる
gh.CloseAllDocuments()
gh.HideEditor()


実行対象のフォルダを選択すると、フォルダ内に保存した3つのGHファイルが順に実行されました。

ちゃんとElefrontで指定したLayerにそれぞれのオブジェクトがBakeされています。


それでは最終段階として、目標としていた複数のRhinoファイルに複数のGHファイルを実行することができるか試してみましょう。

・Test3ーopen_save.pyと合わせてみる(N対M)

GHファイルはTest2と同じ3つのGHで、今回は操作対象のRhinoファイルを、異なる点を保持した10個のRhinoファイルとします。なお、これらのRhinoファイルは同じフォルダ内に保存しています。

以前の記事のopen_save.pyとTest2のループ処理を合体し、以下のスクリプトを作成しました。

# -*- coding: utf-8 -*-

import os

import rhinoscriptsyntax as rs
import Rhino
import scriptcontext as sc

#フォルダ内のRhinoファイルのパスをリスト化
def get_Rhino_file(_dir):
    pathList = []
    for root, dirs, files in os.walk(_dir, topdown=False):
        for name in files:
            path = os.path.join(root, name)
            path = str(path)
            if path.endswith(".3dm"):
                pathList.append(path)
    return pathList

#フォルダ内のGHファイルのパスをリスト化
def get_GH_file(_dir):
    pathList = []
    for root, dirs, files in os.walk(_dir, topdown=False):
        for name in files:
            path = os.path.join(root, name)
            path = str(path)
            if path.endswith(".gh"):
                pathList.append(path)
    return pathList

#GHファイル起動実行操作
def run_GH(GH_file):
    
    #grasshopperを開いて表示
    gh.LoadEditor()
    gh.CloseAllDocuments()
    gh.ShowEditor()
    
    #ghファイルの読み込み
    gh.OpenDocument(GH_file)
        
    #Booleanをセット
    gh.AssignDataToParameter("RhinoPython_bake", True)
    
    #実行
    gh.RunSolver(True)

#GHファイルのループ処理
def loop_GH(GH_files):
    for f in GH_files:
        run_GH(f)
    #ghを閉じる
    gh.CloseAllDocuments()
    gh.HideEditor()

#指定されたパスのRhinoファイルを開いて..保存
def open_close(Rhino_file, GH_files):
    Rhino_file = '"' + Rhino_file + '"'
    rs.Command('_-Open {}'.format(Rhino_file))
    #rs.Command("_-purge _Enter")
    loop_GH(GH_files)
    rs.Command("_Save")

#GH_RhinoScriptInterfaceの取得
gh = Rhino.RhinoApp.GetPlugInObject("Grasshopper")

sc.doc = Rhino.RhinoDoc.ActiveDoc

### Init
rs.Command("-New \"Large Objects - Millimeters.3dm\"")

#FolderLocation
rhino_files = get_Rhino_file(rs.BrowseForFolder(None,"Sel Folder_(Rhino)"))

#FolderLocation
gh_files = get_GH_file(rs.BrowseForFolder(None,"Sel Folder_(GH)"))

### Loop
for rhino_file in rhino_files:
    open_close(rhino_file, gh_files)

### Init
rs.Command("-New \"Large Objects - Millimeters.3dm\"")
print("Complete_Open_Close!!!!!!!!!!!!")


ポップアップに従って順に操作対象のRhinoフォルダ・GHフォルダを選択すると、10個全てのRhinoファイルに対して3つのGHファイルを適用することができました。

検証結果・まとめ

検証の結果、RhinoPythonスクリプトで複数のRhinoファイルへの複数のGHファイルの適用が可能なことが分かりました。

一方で注意点として、今回のようにBooleanコンポーネントをBakeコンポーネント繋ぎ、その値をRhinoPythonからTrueにセットすることでBakeを行うようにする場合、GHファイルを作成する段階で、Booleanコンポーネントに名前を付けること、また、ループ処理で複数のGHファイルを実行する場合には、そのBooleanコンポーネントの名前を統一することが必要になります。

この点に関して、開発やデバッグの操作性を高めるために、下の図のようにBoolean ToggleとBooleanコンポーネントどちらかがTrueの時Bakeするようにプログラムしておくのが良いのではと、今日の時点では考えています。

以上の調査検証結果から、大規模プロジェクトにおいて処理を複数のファイルに分けて実行する際、RhinoPythonを用いることで複数のGHファイルを自動的に実行することが可能であるとわかったので、手数を減らすことによる作業効率の改善や、作業漏れの防止に繋げることができると期待します。

ここまでお読みいただきありがとうございました。



(編集追記)
下記の記事に続編として連続実行をより明示的に処理する手法が紹介されています。
blog.vicc.jp