CPythonからGrasshopperを制御する(練習編)

最終更新日

突然ですがGrasshopperを使用しますか? SyntegrateとViccでは使う機会がたくさんあります。前回のブログ記事では、Rhino PythonからGrasshopperの制御を行いました。

今回はRhinoInsideを用いて、CPythonからGrasshopperを制御してみようと思います。これによってRhino Python(IronPython)の制限がなくなり、CPythonからインストールできるモジュールを利用することができます。また、複数のGrasshopperを制御することができるようになり、Grasshopperの用途を拡張することができます。

では本記事の流れとして、まずCPythonからGrasshopperを扱えるかの確認を行います。そして、練習としてghファイルを開いてコンポーネントに値やオブジェクトをセットします。今回はそこまでやってみます。

あらかじめRhino 7とCPythonをインストールして、以下のCPythonのモジュールをインストールします。

RhinoInside

Pythonet

CPythonからGrasshopperのモジュールを扱う準備

Rhino 7が以下のフォルダにあるかを確認します。

C:\Program Files\Rhino 7\Plug-ins\Grasshopper

もしフォルダが存在していれば、以下のCPythonコードを実行してみます。Grasshopperのバージョンが出力されていれば成功です。

もしなければ、Rhino 7をインストールした時に別の場所に入れた可能性があるので、dir_plugins=”C:\\Program Files\\Rhino 7\\Plug-ins\\Grasshopper”の箇所をRhino 7がインストールしてあるディレクトリパスに修正してください。

### Copyright (c) 2021 Syntegrate
###
### This software is released under the MIT License.
### https://opensource.org/licenses/MIT

import os
import rhinoinside #pip install rhinoinside
import clr         #pip install pythonnet

#Rhino.insideのimport
rhinoinside.load() #これがないとimport Rhinoができない → import Grasshopperができない
import Rhino

#pythonnetを用いて、GlasshopperのDLLを読み込んでimport
dir_plugins = "C:\\Program Files\\Rhino 7\\Plug-ins\\Grasshopper"

path_dll_Grasshopper = os.path.join(dir_plugins, "Grasshopper.dll")
path_dll_GHIO        = os.path.join(dir_plugins, "GH_IO.dll")
path_dll_GHUtil      = os.path.join(dir_plugins, "GH_Util.dll")

clr.AddReference(path_dll_Grasshopper)
clr.AddReference(path_dll_GHIO)
clr.AddReference(path_dll_GHUtil)

#試しにimport, バージョン出力
import Grasshopper
print(Grasshopper.Versioning.Version)

CPythonを実行させて、以下のようなGrasshopperのバージョンが出力されたら準備完了です。

1.0.0007

CPythonからghファイルを開いてGrasshopperに値をセットする

では、前回の投稿と同じように各コンポーネントに値を入力していくところからやってみましょう。パラメータのコンポーネントに入力できたかを確認するために、Pythonコンポーネントの出力”a”から値を取得しています。Panelコンポーネントは確認用であり、特になくても構いません。

Numberコンポーネントに値をセット

(test01.gh) 値が入ってきたのをそのまま出力するだけ
### Copyright (c) 2021 Syntegrate
###
### This software is released under the MIT License.
### https://opensource.org/licenses/MIT

import os
import rhinoinside #pip install rhinoinside
import clr         #pip install pythonnet

#Rhino.insideのimport
rhinoinside.load() #これがないとimport Rhinoができない → import Grasshopperができない
import Rhino  #Grasshopperを使うだけなら必要ないが、メッシュとかを扱うなら必要

#pythonnetを用いて、GlasshopperのDLLを読み込んでimport
dir_plugins = "C:\\Program Files\\Rhino 7\\Plug-ins\\Grasshopper"

path_dll_Grasshopper = os.path.join(dir_plugins, "Grasshopper.dll")
path_dll_GHIO        = os.path.join(dir_plugins, "GH_IO.dll")
path_dll_GHUtil      = os.path.join(dir_plugins, "GH_Util.dll")

clr.AddReference(path_dll_Grasshopper)
clr.AddReference(path_dll_GHIO)
clr.AddReference(path_dll_GHUtil)

#ここまでは同じコード

import Grasshopper

from Grasshopper.Kernel.Types  import GH_Integer #Integerを入れるクラス
from Grasshopper.Kernel.Data   import GH_Path
from Grasshopper.Kernel import GH_SolutionMode

path_gh = "○○\\△△\\test01.gh" #Grasshopperファイルへのパス

#--- Grasshopper.Kernel.Parameters(Integer)にセット
def AssignKernelParameter_Integer(gh_doc, nname_param, val):
    for obj in gh_doc.Objects:  #IGH_DocumentObject
        if obj.NickName != nname_param:
            continue
        obj.AddVolatileData(GH_Path(0), 0, GH_Integer(int(val))) #ValatileDataのセット
        return True
    return False

#--- Grasshopper.Kernel.Parametersにセットされている値を出力
def PrintParameterInput(gh_doc, nname_param):
    for obj in gh_doc.Objects:  #IGH_DocumentObject
        if obj.NickName != nname_param:
            continue
        #出力
        print("input : {}".format(nname_param))

        for branches in obj.VolatileData.Branches:
            for item in branches:
                print("        {}".format(item))
        return True
    return False

#---コンポーネントの出力結果を出力
def PrintComponentOutput(gh_doc, nname_cmp):
    for obj in gh_doc.Objects:  #IGH_DocumentObject
        if obj.NickName != nname_cmp:
            continue
        for outp in obj.Params.Output:
            print("output : {} ({})".format(nname_cmp, type(outp)))
            #謎の処理
            outp.CollectData()
            #計算(いらない?)
            outp.ComputeData()
            for branches in outp.VolatileData.Branches:
                for item in branches:
                    print("        {}".format(item.Value))
        return True
    return False

def main():
    #.ghファイル読み込み
    gh_doc_io = Grasshopper.Kernel.GH_DocumentIO()
    ret = gh_doc_io.Open(path_gh)
    if not ret:
        return

    #GH_Documentの取得
    gh_doc = gh_doc_io.Document

    #Integerコンポーネントにセット
    ret_assign_A = AssignKernelParameter_Integer(gh_doc, "Int", 108)

    #入力コンポーネント(Int)をチェック
    ret_input_A = PrintParameterInput(gh_doc, "Int")

    #計算
    gh_doc.NewSolution(True, GH_SolutionMode.Silent)

    #Pythonコンポーネントから結果を出力
    ret_out_Py = PrintComponentOutput(gh_doc, "Python")

if __name__ == '__main__':
    main()

出力結果です。Pythonコンポーネントの”a”から108が出てきています。

input : Int
        108
output : Python (<class 'Grasshopper.Kernel.Parameters.Param_String'>)
output : Python (<class 'Grasshopper.Kernel.Parameters.Param_GenericObject'>)
        108

Sliderコンポーネントに値をセット

(test02.gh) sliderコンポーネントに”x”と名前をつける
### Copyright (c) 2021 Syntegrate
###
### This software is released under the MIT License.
### https://opensource.org/licenses/MIT

import os
import rhinoinside #pip install rhinoinside
import clr         #pip install pythonnet

#Rhino.insideのimport
rhinoinside.load() #これがないとimport Rhinoができない → import Grasshopperができない
import Rhino  #Grasshopperを使うだけなら必要ないが、メッシュとかを扱うなら必要

#pythonnetを用いて、GlasshopperのDLLを読み込んでimport
dir_plugins = "C:\\Program Files\\Rhino 7\\Plug-ins\\Grasshopper"

path_dll_Grasshopper = os.path.join(dir_plugins, "Grasshopper.dll")
path_dll_GHIO        = os.path.join(dir_plugins, "GH_IO.dll")
path_dll_GHUtil      = os.path.join(dir_plugins, "GH_Util.dll")

clr.AddReference(path_dll_Grasshopper)
clr.AddReference(path_dll_GHIO)
clr.AddReference(path_dll_GHUtil)

#ここまでは同じコード

import Grasshopper

from Grasshopper.Kernel.Types  import GH_Number #Numberを入れるクラス
from Grasshopper.Kernel.Data   import GH_Path
from Grasshopper.Kernel import GH_SolutionMode

path_gh = "○○\\△△\\test02.gh" #Grasshopperファイルへのパス

#--- Grasshopper.Kernel.Parameters(Numというやつ)にセット
def AssignKernelParameter_Number(gh_doc, nname_param, val):

    for obj in gh_doc.Objects:  #IGH_DocumentObject
        if obj.NickName != nname_param:
            continue

        obj.AddVolatileData(GH_Path(0), 0, GH_Number(float(val))) #ValatileDataのセット

        return True
    return False

#--- Grasshopper.Kernel.Parametersにセットされている値を出力
def PrintParameterInput(gh_doc, nname_param):

    for obj in gh_doc.Objects:  #IGH_DocumentObject
        if obj.NickName != nname_param:
            continue

        #出力
        print("input : {}".format(nname_param))

        for branches in obj.VolatileData.Branches:
            for item in branches:
                print("        {}".format(item))

        return True
    return False

#---コンポーネントの出力結果を出力
def PrintComponentOutput(gh_doc, nname_cmp):
    for obj in gh_doc.Objects:  #IGH_DocumentObject
        if obj.NickName != nname_cmp:
            continue

        for outp in obj.Params.Output:

            print("output : {} ({})".format(nname_cmp, type(outp)))

            #謎の処理
            outp.CollectData()

            #計算(いらない?)
            outp.ComputeData()

            for branches in outp.VolatileData.Branches:
                for item in branches:
                    print("        {}".format(item.Value))

        return True
    return False

def main():

    #.ghファイル読み込み
    gh_doc_io = Grasshopper.Kernel.GH_DocumentIO()
    ret = gh_doc_io.Open(path_gh)
    if not ret:
        return

    #.ghの中身取得
    gh_doc = gh_doc_io.Document #GH_Document

    #パラメータコンポーネントxにセット
    ret_assign_X = AssignKernelParameter_Number(gh_doc, "x", 0.108)

    #入力コンポーネントの値チェック
    ret_input_X = PrintParameterInput(gh_doc, "x")

    #計算
    gh_doc.NewSolution(True, GH_SolutionMode.Silent)

    #結果を出力
    ret_out_Py = PrintComponentOutput(gh_doc, "Python")

if __name__ == '__main__':
    main()

出力結果です。Pythonコンポーネントの”a”から0.108が出てきています。

input : x
        0.108
output : Python (<class 'Grasshopper.Kernel.Parameters.Param_String'>)
output : Python (<class 'Grasshopper.Kernel.Parameters.Param_GenericObject'>)
        0.108

Brepコンポーネントにオブジェクトをセット

(test03.gh) a=str(x)と記述してGUIDを出力
### Copyright (c) 2021 Syntegrate
###
### This software is released under the MIT License.
### https://opensource.org/licenses/MIT

import os
import rhinoinside #pip install rhinoinside
import clr         #pip install pythonnet

#Rhino.insideのimport
rhinoinside.load() #これがないとimport Rhinoができない → import Grasshopperができない
import Rhino  #Grasshopperを使うだけなら必要ないが、メッシュとかを扱うなら必要

#pythonnetを用いて、GlasshopperのDLLを読み込んでimport
dir_plugins = "C:\\Program Files\\Rhino 7\\Plug-ins\\Grasshopper"

path_dll_Grasshopper = os.path.join(dir_plugins, "Grasshopper.dll")
path_dll_GHIO        = os.path.join(dir_plugins, "GH_IO.dll")
path_dll_GHUtil      = os.path.join(dir_plugins, "GH_Util.dll")

clr.AddReference(path_dll_Grasshopper)
clr.AddReference(path_dll_GHIO)
clr.AddReference(path_dll_GHUtil)

#ここまでは同じコード

import Grasshopper

from Grasshopper.Kernel.Types  import GH_Brep #Numberを入れるクラス
from Grasshopper.Kernel.Data   import GH_Path
from Grasshopper.Kernel import GH_SolutionMode

path_gh = "○○\\△△\\test03.gh" #Grasshopperファイルへのパス

path_3dm = "○○\\△△\\tmp.3dm" #サーフェスが入っているパス

#--- Grasshopper.Kernel.Parameters(Numというやつ)にセット
def AssignKernelParameter_brep(gh_doc, nname_param, rh_brep):
    for obj in gh_doc.Objects:  #IGH_DocumentObject
        if obj.NickName != nname_param:
            continue
        obj.AddVolatileData(GH_Path(0), 0, GH_Brep(rh_brep)) #ValatileDataのセット
        return True
    return False

#--- Grasshopper.Kernel.Parametersにセットされている値を出力
def PrintParameterInput(gh_doc, nname_param):
    for obj in gh_doc.Objects:  #IGH_DocumentObject
        if obj.NickName != nname_param:
            continue
        #出力
        print("input : {}".format(nname_param))

        for branches in obj.VolatileData.Branches:
            for item in branches:
                print("        {}".format(item))
        return True
    return False

#---コンポーネントの出力結果を出力
def PrintComponentOutput(gh_doc, nname_cmp):
    for obj in gh_doc.Objects:  #IGH_DocumentObject
        if obj.NickName != nname_cmp:
            continue
        for outp in obj.Params.Output:
            print("output : {} ({})".format(nname_cmp, type(outp)))
            #謎の処理
            outp.CollectData()
            #計算(いらない?)
            outp.ComputeData()
            for branches in outp.VolatileData.Branches:
                for item in branches:
                    print("        {}".format(item.Value))
        return True
    return False

def main():

    #3dmファイルの読み込み
    rh_brep = None #セットするbrep

    f3dm = Rhino.FileIO.File3dm.Read(path_3dm)
    if f3dm is None:
        return

    for obj in f3dm.Objects:
        if obj.Geometry.ObjectType == 16: #Brepの番号
            rh_brep = obj.Geometry
            break
    if rh_brep is None:
        print("can not extract brep...")
        f3dm.Dispose()
        return


    #.ghファイル読み込み
    gh_doc_io = Grasshopper.Kernel.GH_DocumentIO()
    ret = gh_doc_io.Open(path_gh)
    if not ret:
        return

    #.ghの中身取得
    gh_doc = gh_doc_io.Document #GH_Document

    #パラメータコンポーネントxにセット
    ret_assign_Brep = AssignKernelParameter_brep(gh_doc, "Brep", rh_brep)

    #入力コンポーネントの値チェック
    ret_input_Brep = PrintParameterInput(gh_doc, "Brep")

    #計算
    gh_doc.NewSolution(True, GH_SolutionMode.Silent)

    #結果を出力
    ret_out_Py = PrintComponentOutput(gh_doc, "Python")


    #3dmを閉じる
    f3dm.Dispose()

if __name__ == '__main__':
    main()

出力結果です。Pythonコンポーネントの”a”からBrepオブジェクトのGUIDが出力されています。

input : Brep
        Untrimmed Surface
output : Python (<class 'Grasshopper.Kernel.Parameters.Param_String'>)
output : Python (<class 'Grasshopper.Kernel.Parameters.Param_GenericObject'>)
        8b2ffb7c-9357-46d2-9a15-c14d52885480

次回

今回は以上になります。前回とは扱うクラスが異なる上に、ソースコードが冗長になっています。また、CPythonでGrasshopperを扱うための情報が少ないので、動作はするが正しい方法なのか不明であることに注意です。より正しい方法があればコメントしていただけると助かります。

次回は実践編として、数式コンポーネントの利用や、前回と同様にサーフェスモデルにGrasshopperを適用して別のオブジェクトを作るということをしてみます。

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

jyun-ichi

シンテグレートのプログラミング担当。入社2年目。業務はPythonやUnity(C#)の開発。運動と車と読書の田舎暮らし。

シェアする