CPythonからGrasshopperを制御する(実践編)

最終更新日

突然ですがGrasshopperを使用しますか? SyntegrateとViccでは使う機会がたくさんあります。前回の記事では、CPythonからGrasshopperを制御する練習として、入力用のコンポーネントに値を入れて、その値が正しく入力されているかを確認するところまで行いました。

今回は実践編として、数式やジオメトリ編集のコンポーネントを用いて計算結果を取得してみようと思います。

前回と同じモジュールを用います。Rhino 7とCPythonをインストールして、以下のCPythonのモジュールをインストールします。

RhinoInside

Pythonet

サーフェスを構築するGrasshopperの計算結果を取得する

最初にGrasshopperでサーフェスを構築して、その計算結果を出力します。

### 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
import System

# 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)

# Grasshopperのドキュメントは下のURL
# https://developer.rhino3d.com/api/grasshopper/html/723c01da-9986-4db2-8f53-6f3a7494df75.htm

import Grasshopper

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

# ここまでは同じコード

path_gh = "C:\\Users\\○○\\△△\\Piller.gh"

path_dst_3dm = "C:\\Users\\○○\\△△\\Piller.3dm"  # 出力先

# Grasshopper.Kernel.Parameters(int)にセット
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(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

# コンポーネントの結果を3dmで出力
def Export3dm_Extrusion(gh_doc, nname_cmp, path_dst_3dm, name_out="Extrusion"):

    List_geom = System.Collections.Generic.List[Rhino.Geometry.GeometryBase]()

    for obj in gh_doc.Objects:  # IGH_DocumentObject

        # 名前でExtrコンポーネントを探す
        if obj.NickName != nname_cmp:
            continue

        for outp in obj.Params.Output:

            if outp.Name != name_out:
                print("it is not {}...{}".format(name_out, outp.Name))
                continue

            # 計算
            outp.CollectData()
            outp.ComputeData()

            for branches in outp.VolatileData.Branches:
                for item in branches:
                    # Geometry化してリストに加える
                    List_geom.Add(item.Value)

    # GHの計算結果を3dmで保存
    Rhino.FileIO.File3dm.WriteMultipleObjects(path_dst_3dm, List_geom)

def main():

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

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

    # パラメータコンポーネント"Radius"にセット
    ret = AssignKernelParameter_Number(gh_doc, "Radius", 4.5)
    assert ret

    # パラメータコンポーネント"Count"にセット
    ret = AssignKernelParameter_Integer(gh_doc, "Count", 3)
    assert ret

    # パラメータコンポーネント"Radius"にセット
    ret = AssignKernelParameter_Integer(gh_doc, "Height", 7)
    assert ret

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

    # 計算結果を保存
    Export3dm_Extrusion(gh_doc, "Extr", path_dst_3dm)

if __name__ == '__main__':
    main()

出力結果です。サーフェスの3dmファイルができました。

既存のサーフェスを用いるGrasshopperの計算結果を取得する

先の計算結果を用いて、サーフェスにパネルをあてはめるGrasshopperを適用して、そのモデルを保存します。入力コンポーネントのSrfには先に保存した3dmファイルを使います。

### 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
import System

# 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)

# Grasshopperのドキュメントは下のURL
# https://developer.rhino3d.com/api/grasshopper/html/723c01da-9986-4db2-8f53-6f3a7494df75.htm

import Grasshopper

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

# ここまでは同じコード

# Grasshopperのドキュメントは下のURL
# https://developer.rhino3d.com/api/grasshopper/html/723c01da-9986-4db2-8f53-6f3a7494df75.htm

path_gh = "C:\\Users\\○○\\△△\\Panelization.gh"

# 入力元のモデル
path_src_3dm = "C:\\Users\\○○\\△△\\Piller.3dm"

# 出力先
path_dst_3dm = "C:\\Users\\○○\\△△\\Panelization.3dm"

# Grasshopper.Kernel.Parameters(Brep)にセット
def AssignKernelParameter_brep(gh_doc, nname_param, rh_brep):

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

        # ValatileDataのセット
        obj.AddVolatileData(GH_Path(0), 0, GH_Brep(rh_brep))

        return True
    return False

# 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(Number)にセット
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


#コンポーネントの結果を3dmで出力
def Export3dm_SBox(gh_doc, nname_cmp, path_dst_3dm, name_out="Twisted Box"):

    List_geom = System.Collections.Generic.List[Rhino.Geometry.GeometryBase]()

    for obj in gh_doc.Objects:  # IGH_DocumentObject

        # 名前でExtrコンポーネントを探す
        if obj.NickName != nname_cmp:
            continue

        for outp in obj.Params.Output:

            if outp.Name != name_out:
                print("it is not {}...{}".format(name_out, outp.Name))
                continue

            # 計算
            outp.CollectData()
            outp.ComputeData()

            for branches in outp.VolatileData.Branches:
                for item in branches:
                    # TwistedBoxからBrepに変換してリストに追加
                    List_geom.Add(item.ToBRep())

    # GHの計算結果を3dmで保存
    Rhino.FileIO.File3dm.WriteMultipleObjects(path_dst_3dm, List_geom)

def main():

    # 3dmファイルの読み込み(サーフェス一つだけ)
    rh_brep = None  # セットするメッシュ

    f3dm = Rhino.FileIO.File3dm.Read(path_src_3dm)
    if f3dm is None:
        assert False

    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()
        assert False

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

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

    # パラメータコンポーネント"Srf"にセット
    ret = AssignKernelParameter_brep(gh_doc, "Srf", rh_brep)
    assert ret

    # パラメータコンポーネント"U Count"にセット
    ret = AssignKernelParameter_Integer(gh_doc, "U Count", 10)
    assert ret

    # パラメータコンポーネント"V Count"にセット
    ret = AssignKernelParameter_Integer(gh_doc, "V Count", 10)
    assert ret

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

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

    # 計算結果を保存
    Export3dm_SBox(gh_doc, "SBox", path_dst_3dm)

    f3dm.Dispose()


if __name__ == '__main__':
    main()

出力結果です。パネルの3dmファイルができました。

複数のGHファイルを組み合わせる

先のサーフェスモデルを構築するGrasshopperと、そのサーフェスからパネルを構築するGrasshopperをひとつに組み込んでみます。

### 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を使うだけなら必要ないが、メッシュとかを扱うなら必要
import System

# 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)

# Grasshopperのドキュメントは下のURL
# https://developer.rhino3d.com/api/grasshopper/html/723c01da-9986-4db2-8f53-6f3a7494df75.htm

import Grasshopper

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

# ここまでは同じコード

# Grasshopperのドキュメントは下のURL
# https://developer.rhino3d.com/api/grasshopper/html/723c01da-9986-4db2-8f53-6f3a7494df75.htm

path_gh1 = "C:\\Users\\○○\\△△\\Piller.gh"

path_gh2 = "C:\\Users\\○○\\△△\\Panelization.gh"

# 出力先
path_dst_3dm = "C:\\Users\\○○\\△△\\All.3dm"

# Grasshopper.Kernel.Parameters(Brep)にセット
def AssignKernelParameter_brep(gh_doc, nname_param, rh_brep):

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

        # ValatileDataのセット
        obj.AddVolatileData(GH_Path(0), 0, GH_Brep(rh_brep))

        return True
    return False

# 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(Number)にセット
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

# ExtrusionコンポーネントからGH_Brepを取得
def GetBrep_Extrusion(gh_doc, nname_cmp, name_out="Extrusion"):

    for obj in gh_doc.Objects:  # IGH_DocumentObject

        # 名前でExtrコンポーネントを探す
        if obj.NickName != nname_cmp:
            continue

        for outp in obj.Params.Output:

            if outp.Name != name_out:
                print("it is not {}...{}".format(name_out, outp.Name))
                continue

            # 計算
            outp.CollectData()
            outp.ComputeData()

            for branches in outp.VolatileData.Branches:
                for item in branches:
                    # Geometry化してリストに加える
                    gh_brep = item.Value

                    return gh_brep

    assert False

# SBoxコンポーネントの結果を3dmで出力
def Export3dm_SBox(gh_doc, nname_cmp, path_dst_3dm, name_out="Twisted Box"):

    List_geom = System.Collections.Generic.List[Rhino.Geometry.GeometryBase]()

    for obj in gh_doc.Objects:  # IGH_DocumentObject

        # 名前でExtrコンポーネントを探す
        if obj.NickName != nname_cmp:
            continue

        for outp in obj.Params.Output:

            if outp.Name != name_out:
                print("it is not {}...{}".format(name_out, outp.Name))
                continue

            # 計算
            outp.CollectData()
            outp.ComputeData()

            for branches in outp.VolatileData.Branches:
                for item in branches:
                    #for f in dir(item):
                    #    print(f)

                    # TwistedBoxからBrepに変換してリストに追加
                    List_geom.Add(item.ToBRep())

    # GHの計算結果を3dmで保存
    Rhino.FileIO.File3dm.WriteMultipleObjects(path_dst_3dm, List_geom)

def main():

    # .ghファイル読み込み(その1)
    gh_doc_io1 = Grasshopper.Kernel.GH_DocumentIO()
    ret = gh_doc_io1.Open(path_gh1)
    assert ret

    # .ghの中身取得
    gh_doc1 = gh_doc_io1.Document  # GH_Document

    # パラメータコンポーネント"Radius"にセット
    ret = AssignKernelParameter_Number(gh_doc1, "Radius", 4.5)

    # パラメータコンポーネント"Count"にセット
    ret = AssignKernelParameter_Integer(gh_doc1, "Count", 3)

    # パラメータコンポーネント"Radius"にセット
    ret = AssignKernelParameter_Integer(gh_doc1, "Height", 7)

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

    # 計算結果をGH_Brep形式で取得
    rh_brep = GetBrep_Extrusion(gh_doc1, "Extr")


    # .ghファイル読み込み(その2)
    gh_doc_io2 = Grasshopper.Kernel.GH_DocumentIO()
    ret = gh_doc_io2.Open(path_gh2)
    assert ret

    # .ghの中身取得
    gh_doc2 = gh_doc_io2.Document  # GH_Document

    # パラメータコンポーネント"Srf"にセット
    ret = AssignKernelParameter_brep(gh_doc2, "Srf", rh_brep)
    assert ret

    # パラメータコンポーネント"U Count"にセット
    ret = AssignKernelParameter_Integer(gh_doc2, "U Count", 10)
    assert ret

    # パラメータコンポーネント"V Count"にセット
    ret = AssignKernelParameter_Integer(gh_doc2, "V Count", 10)
    assert ret

    # パラメータコンポーネント"Height"にセット
    ret = AssignKernelParameter_Number(gh_doc2, "Height", 0.108)
    assert ret

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

    # 計算結果を保存
    Export3dm_SBox(gh_doc2, "SBox", path_dst_3dm)

if __name__ == '__main__':
    main()

出力結果です。2つのGrasshopperを使って、先と同じ3dmファイルができました。

おわりに

以上になります。このようにGrasshopperをCPythonに組み込むことによって様々なことに活用できそうです。例えばサーフェスとパネルのGrasshopperを構築する役割を分担をして開発したり、DjangoやFlaskを使ってサーバーサイドのプログラムにGrasshopperを組み込むといったことができそうです。

最後に、これまで3回にわたってPythonからGrasshopperを扱いました。以前にもあったのですがCPythonからGrasshopperを扱うための情報は少ないので、動作はするが正しい方法なのかは不明であることに注意してください。より正しい方法があればコメントしていただけると助かります。

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

jyun-ichi

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

シェアする