Rhinoceors 3D: Python script で「表示中のすべてのオブジェクトそれぞれに対してコマンドを実行」する

環境

Rhino 6 on Windows 10 Pro/Enterprise. だけど PythonScript は Mac でも動くらしい(RhinoScript は Win でしか動かないらしい)。

解きたい問題

現在画面に表示させているすべてのオブジェクト(今の場合は球だった)の体積中心に点を打ちたい、という状況があった。マウスクリックで球を選択してから VolumeCentroid を実行し、次の球を選択したら space キー(直前のコマンドの redo)、というのを延々と繰り返せばいいのだが、600個くらいあってちょっと手作業が嫌になった。こんな簡単な作業、スクリプトでできないはずがない、と思い、初めてスクリプトを使ってみようと思い立った。

参考リンク

主に以下を参考にした:

Developer とあるのでちょっと勘違いしていたが、スクリプトを使いたいユーザ向けでもあるよなこれ…

手順

まず Rhino のメニューから Tools > PythonScript > Edit... を選択しエディタを起動する。そこにスクリプトを書いて、エディタのツールバーにある緑の三角で実行。

スクリプト

import rhinoscriptsyntax as rs

rs.CurrentLayer("centroids")

obj = rs.FirstObject()
while obj:

    massprop= rs.SurfaceVolumeCentroid(obj)
    if massprop: rs.AddPoint( massprop[0] )
    
    obj = rs.NextObject(obj)

これにより、あるレイヤ(centroids という名前)をカレントレイヤに設定し、そこに VolumeCentroid*1の結果としての points を出力する。もちろん、VolumeCentroid のところを他のコマンドに変えれば色々応用できると思う。

おそらくこれくらいの簡単な作業なら旧来の RhinoScript でも問題なくできそうだが、どうも Python がこれからはスタンダードになっていくようなので Python にした。初めて書いたばかりなので syntax とかよくわかってない。公式によると

Rhino uses Python version 2.7 ( What is Rhino.Python? with Python )

ということでちょっとがっくり来たが、これはこの機能が実装され始めた時代的にしょうがなかったんだろうかな…

改良版(ロックされたレイヤを無視する)

毎回「見えてるレイヤだけ」処理するのが面倒で、「ロックされたレイヤは飛ばせないかなぁ」と思っていたが、下記でできた。

import rhinoscriptsyntax as rs

# This is optional. You can pre-select a layer and comment out this line instead.
rs.CurrentLayer("centroids")

obj = rs.FirstObject()
while obj:
    
    if rs.IsLayerLocked( rs.ObjectLayer(obj) ):
        pass
    else:
        massprop= rs.CurveAreaCentroid(obj)
        if massprop: rs.AddPoint( massprop[0] )
    
    obj = rs.NextObject(obj)

rs.ObjectLayer(obj) で「obj というIDのオブジェクトが属するレイヤ」のIDを返すっぽい。で、その結果を使って、rs.IsLayerLocked でロックされてるかどうかを調べる、ということ。

*1:なぜかスクリプトではコマンドと違って名前が SurfaceVolumeCentroid だが。