この記事は rhino3dm を触ってみる記事の後編です。
前編はこちら↓
blog.vicc.jp
前編では、属性情報を入れたオブジェクトを作成し、ファイルに保存するということをやってみました。今回は、前回記事で作成したファイルの中のオブジェクトの属性情報について操作してみようと思います。
作例
前編で作成したファイルを利用するので、実際に試したい人は合わせて確認ください。
前編と同じくすべて MIT ライセンスとします。
ファイルを開いて属性情報などを確認する
まずは読み取りだけ。前回作成の box_v2.3dm を利用します。
# -*- coding: utf-8 -*- import rhino3dm def read_object_info(file_path): # ファイルを開く my_file = rhino3dm.File3dm.Read(file_path) # print(my_file) # オブジェクトを取得 objects = my_file.Objects print("=== .3dm ファイル内のオブジェクト情報 ===\n") for obj in objects: geometry = obj.Geometry # ジオメトリ情報 attributes = obj.Attributes # 属性情報 # オブジェクトの基本情報 print("\n-----------------------------") print("Object ID : {}".format(attributes.Id)) print("Object Type : {}".format(type(geometry).__name__)) print("Object Layer : {}".format(my_file.Layers[attributes.LayerIndex].Name)) # 追加の属性情報 print("Object Name : {}".format(attributes.Name if attributes.Name else 'N/A')) print("Object Visible : {}".format('True' if attributes.Visible else 'False')) print("Object UserString : {}".format(attributes.GetUserStrings())) print("\n=== 解析完了 ===") # ファイル名 target_file = "C:\\Users\\naoki yoshioka\\Desktop\\box_v2.3dm" read_object_info(target_file)
実行するとこんな感じ。


狙った通り、ライノのファイルの情報を正しく取得できています。
ファイルを開いて、属性情報を追加し、レイヤーを変更する
次に、属性情報を追加し、レイヤーを変更する操作を試します。
上の情報の読み取りのプログラムでは、ループ処理を入れていますが、こちらのサンプルはオブジェクトが1つしか入っていない前提で決め打ちで書きます。データは同じく box_v2.3dm を利用します。
# -*- coding: utf-8 -*- import rhino3dm def read_and_edit_object_info(file_path): # ファイルを開く my_file = rhino3dm.File3dm.Read(file_path) # print(my_file) # オブジェクトを取得 objects = my_file.Objects # オブジェクトが一個しか入っていないので決め打ちです obj = objects[0] geometry = obj.Geometry # ジオメトリ情報 attributes = obj.Attributes # 属性情報 # 属性情報の追加 attributes.SetUserString("UserSrting_Test", "Hello Rhino3dm") # レイヤーを変更 # ここでは 0 = "New Layer 1", 1 = "New Layer 2" attributes.LayerIndex = 0 # ファイルの上書き保存 my_file.Write(file_path, 7) print(f"*** File Saved (Overwrite) : {file_path}\n") print("=== .3dm ファイル内のオブジェクト情報 ===\n") # オブジェクトの基本情報 print("\n-----------------------------") print("Object ID : {}".format(attributes.Id)) print("Object Type : {}".format(type(geometry).__name__)) print("Object Layer : {}".format(my_file.Layers[attributes.LayerIndex].Name)) # 追加の属性情報 print("Object Name : {}".format(attributes.Name if attributes.Name else 'N/A')) print("Object Visible : {}".format('True' if attributes.Visible else 'False')) print("Object UserString : {}".format(attributes.GetUserStrings())) print("\n=== 解析完了 ===") # ファイル名 target_file = "C:\\Users\\naoki yoshioka\\Desktop\\box_v2.3dm" read_and_edit_object_info(target_file)
Powershell のスクショは割愛しますが、問題無く実行されると、ファイルが更新されます。


ファイルを開いて特定のオブジェクトの属性情報を変更する
最後に前回作成した box_v3.3dm を用いて、複数のオブジェクトが入ったファイルで特定の要素の属性情報を書き換えてみます。
それぞれの部材を 未解決 または 解決済み として属性情報で管理して、最初はすべて未解決、解決したら 解決済み を変更するような流れを想定してプログラムを書いてみます。実際の属性情報としては、キーに isSolved 、バリューに True or False とします。
まず一律ですべてのオブジェクトを isSolved を False と設定します。
# -*- coding: utf-8 -*- import rhino3dm def initialize_is_solved(file_path): # ファイルを開く my_file = rhino3dm.File3dm.Read(file_path) # print(my_file) # オブジェクトを取得 objects = my_file.Objects for obj in objects: geometry = obj.Geometry # ジオメトリ情報 attributes = obj.Attributes # 属性情報 # 属性情報の追加(一度全部 False に設定) attributes.SetUserString("isSolved", "False") # ファイルの上書き保存 my_file.Write(file_path, 7) print(f"*** File Saved (Overwrite) : {file_path}\n") target_file = "C:\\Users\\naoki yoshioka\\Desktop\\box_v3.3dm" initialize_is_solved(target_file)
ライノで開いてみるとこんな感じ。

次に、Python の input() 関数を利用して、解決済みにしたいオブジェクトを入力して、属性情報を更新してみます。
ちなみに、前回作成した box_v3.3dm には Index という属性情報を用意して、原点に近いものから、0, 1, 3,......9 と通し番号が降られています。今回はこのインデックスを数字で指定しようと思います。
# -*- coding: utf-8 -*- import rhino3dm def edit_userstring_with_input(file_path, input_value): # ファイルを開く my_file = rhino3dm.File3dm.Read(file_path) # print(my_file) # オブジェクトを取得 objects = my_file.Objects # 処理を分けるためのフラグ flag_exist = 0 for obj in objects: attributes = obj.Attributes # 属性情報 # インデックスを取得 userstring_value = attributes.GetUserString("index") # print(userstring_value) if str(userstring_value) == input_value: # 属性情報の追加(解決済みに変更) attributes.SetUserString("isSolved", "True") flag_exist = 1 # 存在しない部材の属性情報を更新しようとすると、無いよとメッセージを出す if flag_exist == 0: print("インデックスが {} の部材は存在しません".format(input_value)) # 部材が存在していたら属性情報を更新する else: print("インデックスが {} の部材を解決済みにしました".format(input_value)) # ファイルの上書き保存 my_file.Write(file_path, 7) print(f"*** File Saved (Overwrite) : {file_path}\n") target_file = "C:\\Users\\naoki yoshioka\\Desktop\\box_v3.3dm" input_value = input("解決済みに変更する部材のインデックス番号を入力してください : ") edit_userstring_with_input(target_file, input_value)
実行するとこんな感じです。

コンソール上でインデックス番号を 3 として、その部材の属性情報を更新しました。


ここでは input() 関数を利用しているため、単純で味気ないように見えますが、この部分をほかのファイルやソフトウェアと連携させたり、今どきの手法として AI を活用した対話型の入力インターフェースに置き換えたりすることで、属性情報の運用がさらに便利で簡単になる様子が読者の皆さまにも容易に想像できるのではないでしょうか。
まとめ
ここまでいくつか小さなプログラムを書いてみました。ライノと Grasshopper でやれば簡単にできる作業ですが、Python + rhino3dm ライブラリを利用することでライノのアプリ無しでも 3dm ファイルの操作が可能です。前編で太字にしていた 通常は 3D CAD のみで操作できるネイティブファイルを、3D CAD 関与なしの自前のプログラムから簡単に触ることができる というおもしろさが実感できましたでしょうか。
Python と関連ライブラリさえ動けば属性情報の更新ができるということは、ライセンスの購入やライノそのものが動くハードウェアが必要でなくなるので、Linux 等のマシン、さらにはクラウド環境での動作や、他のプログラムへの組み込みなども可能でしょう。
ライノでできることはライノを開いてやれば良いじゃんということも言えますが、このライブラリには上で挙げたように明確にメリットがあると思います。(業務の中でやりたいことについて業務でのコアを避けつつうまい説明ができず申し訳ないです。)
隙が出来たらもう少し掘り下げてみます。今回の rhino3dm を触ってみたシリーズはここで終わります。
最後になりますが、留意点を書いておきます。
一般的な BIM ソフトに関しては、ベンダーが専用のクラウドストレージとそこで動作する適切な API を用意していることが多く、とても便利です(特定のクラウドでの開発やほかソフトとの連携や認証まわりなど難易度は様々だと思います)。一方、ライノに関してはそのような環境が用意されていないため、今回紹介したような手法が選択肢に入ってくるはずです。
また、IFC といった仕様が公開され、テキストファイルとして保存される BIM データの汎用フォーマットも存在します。IFC に関しても、さまざまな実行環境から属性情報を操作することは技術的に可能です(が、IFC 周りで気が利くサードパーティのライブラリの情報はあまり見当たりませんでした)。 その点、ライノでは公式がライブラリを公開してくれているので、開発をはじめるのがとても簡単です。
このように技術の選定には個別の背景があるはずなので、「Rhino が最強!」「この手法がベスト!」というわけではありません。補足として、ここに記しておきます。
(終わり)