vicc blog

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

複数のRhinoファイルに複数のGrasshopperファイルを実行する方法③_レシピを作りこんで楽をしよう

はじめに

複雑な外装の生産設計では、大量のデータを扱う場面が多々あります。その際、Pythonを活用したバッチ処理が非常に大きな力を発揮します。 以前の記事で、複数のファイルに対して複数のGHを実行していく方法をご紹介しました。

blog.vicc.jp

blog.vicc.jp

しかし、実際に大規模プロジェクトをやっていく中で課題もいくつか見えてきました。

今回は、以前から数回に渡りご紹介している、スクリプトを使用した大規模プロジェクトにおけるバッチ処理のすすめ、 その更新回です。

背景と課題

まず、簡単に今回の方法を開発した背景を話しておきます。

前回の方法は 実行したいフォルダとGHをそれぞれフォルダに分けて実行するというものでした。 この方法は、フォルダ内のファイルにざくっとバッチ処理をかけたい時には便利ですが、 ものによっては、フォルダの中の一部のファイルにだけバッチ処理をかけたいときがあります。 そういう時、毎回バッチ処理したいものだけをファイルに分けるのは面倒です。

今回は、jsonに実行したいGHと実行先のファイルを両方書くことで、フォルダ内の特定のファイルに狙いを定めることができるようになりました。

前回までのフローチャート
今回のフローチャート

さて、前置きが長くなりましたが、具体的な方法の紹介です。

サンプルプロジェクト

今回説明用に、下のようなデータを作ります。(図1) はじめに、適当に10個の点を並べて一つずつ別のファイルにBakeします。 それを以下のような順番でGHを用いて、複製してソリッドを作成しようと思います。(図2)

図1: 完成イメージ
図2: Step0 点それぞれを別ファイルに分ける(バラファイル化)

  • step1_duplicate_points.gh : 点をy軸方向に10個複製(図3)
  • step2_make_curve.gh : 点を基点に断面線を作成(図4)
  • step3_make_solid.gh : 断面線を押し出しソリッドにする(図5)

図3
図4
図5

さらに、10個のファイルを2つのグループに分け、 グループAは円柱に、グループBは立方体に しようとおもいます。

そこで、以下のようにghを分岐させます。

グループA

  • step1_duplicate_points.gh : 点をy軸方向に10個複製
  • step2-a_make_curve_circle.gh : 点を基点に円形の断面線を作成
  • step3_make_solid.gh : 断面線を押し出しソリッドにする

グループB

  • step1_duplicate_points.gh : 点をy軸方向に10個複製
  • step2-b_make_curve_rectangle.gh : 点を基点に正方形の断面線を作成
  • step3_make_solid.gh : 断面線を押し出しソリッドにする

このとき、step1とstep3のghは同じものを使用します。

スクリプト

今回のスクリプトはこちらです。

前回紹介したスクリプトに加えて、 Jsonから実行先のファイル名を取得するようにしています。

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


# 指定されたディレクトリから.3dmファイルを検索し、パスのリストを返す
def get_rhino_files(_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)
                pathlist.sort()
    return pathlist


# 指定されたディレクトリから.ghファイルを検索し、パスのリストを返す
def get_gh_files(_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)
                pathlist.sort()
    return pathlist


# Grasshopperファイルを実行する
def run_gh(gh_file):
    gh.LoadEditor()
    gh.CloseAllDocuments()
    gh.ShowEditor()
    gh.OpenDocument(gh_file)
    gh.AssignDataToParameter("Bool_Bake", True)
    gh.RunSolver(True)


# 指定されたGrasshopperファイルを順番に実行する
def loop_gh(gh_files, target_ghs):
    for target_gh in target_ghs:
        for gh_file in gh_files:
            if target_gh in gh_file:
                run_gh(gh_file)
    gh.CloseAllDocuments()
    gh.HideEditor()


# Rhinoファイルを開き、指定されたGrasshopperファイルを実行して保存する
def open_close(rhino_file, gh_files, target_ghs):
    rhino_file = '"' + rhino_file + '"'
    rs.Command('_-open {}'.format(rhino_file))
    loop_gh(gh_files, target_ghs)
    print("open_close : {}".format(rhino_file))
    rs.Command("_Save")



# Grasshopperプラグインを取得
gh = Rhino.RhinoApp.GetPlugInObject("Grasshopper")
# アクティブなRhinoドキュメントを取得
sc.doc = Rhino.RhinoDoc.ActiveDoc

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

# INPUT
json_path = rs.OpenFileName("Sel Recipe_(Json)")

# レシピファイル(JSON)を開いて辞書型データとして読み込む
with open(json_path) as file:
    dict_data = json.load(file)


target_files = dict_data["MUL_ID"]
target_ghs = dict_data["GH_Files"]

print("target_files : {} , target_GH : {}".format(target_files, target_ghs))

# Rhinoファイルが格納されているフォルダを選択し、ファイルのリストを取得
rhino_files = get_rhino_files(rs.BrowseForFolder(None, "Sel Folder_(Rhino)"))
# Grasshopperファイルが格納されているフォルダを選択し、ファイルのリストを取得
gh_files = get_gh_files(rs.BrowseForFolder(None, "Sel Folder_(GH)"))

print("rhino_files : {}".format(rhino_files))
print("GH_files : {}".format(gh_files))

# Check GH list
for target_gh in target_ghs:
    exit_flag = False
    for gh_file in gh_files:
        if target_gh in gh_file:
            exit_flag = True
    if not exit_flag:
        print("Error : {} does not exit in gh_folder".format(target_gh))
        exit()

# Check rhino list
for rhino_file in rhino_files:
    filename = rhino_file.split('\\')
    filename = filename[-1]
    flag = False
    for target_file in target_files:
        if target_file in filename:
            flag = True
    print("{} : {}".format(filename, flag))

# Open_Close only Target_Files
for rhino_file in rhino_files:
    file_name = rhino_file.split('\\')
    file_name = file_name[-1]
    flag = False
    for target_file in target_files:
        if target_file in rhino_file:
            flag = True
    if flag:
        open_close(rhino_file, gh_files, target_ghs)

#バッチ処理が終わったら空のファイルを開く
rs.Command('-New "Large Objects - Millimeters.3dm"')

レシピ

Jsonファイルで、実行したいghの名前と実行先のファイル名を列挙します。 今回はグループを2つに分けたので、レシピもグループAとBで2つ作ります。

{
  "MUL_ID": [
    "point-1",
    "point-4",
    "point-5",
    "point-8",
    "point-10"
  ],

  "GH_Files": [
    "step1_duplicate_points.gh",
    "step2-a_make_curve_circle.gh",
    "step3_make_solid.gh"
  ]
}

グループごとにレシピを作成

やってみよう

さて、データの準備ができたので、スクリプトを実行してみましょう。 実行すると、レシピ→Rhinoフォルダ→GHフォルダの順に場所を聞いてくるので 使用するレシピ(Json)、実行先のファイルが入ったフォルダ、実行したいGHの入ったフォルダを それぞれ選択してください。

処理が終わったら、バラファイルを統合してみましょう。 うまくいったら、Jsonに書いたレシピに合わせて、円柱と立方体の列が作成されているはずです。

完成イメージ

まとめ

今回は、レシピの中に実行先のファイル名を書いておくことで、フォルダ内の一部のファイルに複数のGHファイルを実行する方法をご紹介しました。

サンプルプロジェクトがごく簡単なものなので、恩恵が伝わりづらいかもしれませんが、 バラファイルが数百以上あるプロジェクトだと、毎回バッチ処理をかける度にフォルダを移動させるのは地味に面倒になるため、Jsonに書いておくだけでよいのは楽に感じられるのでは・・・と思っています。

また、複数のチームメンバーで作業する際、レシピさえあれば、データの中身が分かっていないメンバーにもバッチ処理をお願いすることができるのも利点かもしれません。

反面、ファイル数が増えてくると、レシピに書きこむ内容も多くなり書き漏れの危険が出てくること、 データの中身が分かってないとバッチ処理が適正にできているか判断できない、 そもそもレシピファイルの管理が大変等、データ管理・運用に課題も残っています。

それらはバッチ処理のシステムだけでなく、 プロジェクトのデータ管理・運用法を用いて改善できることを期待しています。

今回のサンプルプロジェクトは以下のリンクにアップしています。 よろしければご活用ください。

https://github.com/viccBlog/250124_open_close_recipe_sample.git

おわり