MODOではじめるPython_08 同じ向き(法線)のポリゴンを選択する

mihi
こんにちは、mihiです。
今回はもっとスクリプトらしく、
1枚だけ選択したポリゴンと同じ向き(法線)のポリゴンを、
一括で選択するスクリプトを書いてみましょう。

同じ向き(法線)のポリゴンを選択するスクリプト

まずは、以下の図のようにボックスを9個程度同じメッシュレイヤーへ作成しておいてください。

続いて、前回同様におまじないを書きます。

#python
import lx

スクリプトの動作方法をまずまとめておきましょう。

  1. まず、基準となるポリゴンを1枚選択する。
  2. スクリプトを実行。
  3. 選択したポリゴンと同じ向きのポリゴンが選択される。

といった流れのスクリプトを作成することとします。

ポリゴンなどの操作をする際の作法

MODOのスクリプトで、ポリゴンの選択や作成・加工などといった「エレメント」を操作する際には、
まず、どのメッシュレイヤーの「エレメント」を操作するのか?というのを事前に明示しなくてはなりません。

※「エレメント」とは、「頂点」「エッジ」「ポリゴン」の事です。

ですので、次のように記述して、これから操作するメッシュレイヤーを明示します。

#python
import lx

mesh = lx.eval('query layerservice layer.index ? main')

まず、「mesh」という変数を宣言し、
「lx.eval()」関数で現在のアクティブになっているメッシュレイヤーのインデックスを取得しています。

'query layerservice layer.index ? main'」と引数を渡していますが、
これは、現在アクティブになっているレイヤーのインデックスを返すコマンドです。
こうすることで、これから操作するメッシュレイヤーをMODOへ明示することができます。

、どうやってこのコマンドの存在を見つけるか?ですが・・・
MODOのヘルプのPDFから探すことになります。

私はWindowsでMODO 14.2を使っているのですが、
以下の場所に「Scripting_and_Commands.pdf」というPDFがあります。

C:\Program Files\Foundry\Modo\14.2v2j\help\

print (mesh)

として、確認してみましょう。

現在のアクティブメッシュレイヤーのインデックスが「1」であるということがわかりますね。

選択されているポリゴンのインデックスを取得する

はじめに1枚選択したポリゴンのインデックスを取得します。

poly = lx.eval1('query layerservice polys ? selected')

と追記します。

#python
import lx

mesh = lx.eval('query layerservice layer.index ? main')

poly = lx.eval1('query layerservice polys ? selected')

「poly」という変数を宣言し、「lx.eval1()」関数によって選択されているポリゴンのインデックスを変数「poly」へ代入します。

「lx.eval1()」の引数の

'query layerservice polys ? selected'

も、やはり先ほどのPDFのリファレンスから探してきます。

例では、

「query layerservice polys ? all」

となっていますが、この場合はアクティブメッシュレイヤーのポリゴンすべてのインデックスが、
配列(タプル型)として返ってきます。

選択しているポリゴンのインデックスだけが欲しいので、
「all」を「selected」へ変更しています。

この「selected」へ変更した部分は全部で4種類から指定することができます。
意味は、上記画像の通りです。

この時に、「lx.eval()」ではなく「lx.eval1()」を使用していることに注目してください。
基本的に、「lx.eval(‘query layerservice polys ? selected’)」とした場合、
選択されているポリゴンのインデックスが配列で返ってきます。
たとえば、ポリゴンを3枚選択している場合は、3個の要素を持つ配列が返ってきます。

ですが、今回のスクリプトの仕様は、1枚だけ選択したポリゴンと同じ向きのポリゴンを選択する。という仕様なので、
戻り値は1個のほうが都合が良いです。

そんな時には、「lx.eval()」ではなく、「lx.eval1()」を使用すると、
複数ポリゴンを選択していたとしても、1番目に選択したポリゴンのインデックスのみが返ってきます。

選択したポリゴンの法線の情報を調べる

以下のように追記します。

normal = lx.eval('query layerservice poly.normal ? %s' % poly)

変数「normal」を宣言し、「lx.eval()」関数の引数へ

「'query layerservice poly.normal ? %s' % poly」

とします。

これもやはり、PDFのリファレンスからポリゴンの法線を調べるコマンドを探してきます。

このコマンドについて、少し詳しく解説します。

「query layerservice poly.normal ? first」

というコマンドですが、
この場合はアクティブメッシュレイヤーのポリゴンのインデックスが0番のポリゴンの法線情報が返ってきます。
「?」の後の「first」が初めのポリゴンインデックスという意味合いだからです。

今回は「選択したポリゴンの法線情報」が欲しいので、
「first」の部分を先ほど取得した「選択したポリゴンのインデックス」に置き換える必要があります。

「選択したポリゴンのインデックス」は、先ほどの変数「poly」に代入されていますので、
その情報を使うわけですが・・・

「lx.eval(‘query layerservice poly.normal ? poly’)」

としてもうまくいかないことは、前回の記事で解説していますので、参考にしてみてください。

ですので「%s演算子」を使用して、変数の値を文字列に置き換えています。

「’query layerservice poly.normal ? %s’ % poly」

とすることで、「%s」の部分に変数「poly」の値が文字列として置き換えられます。

上の例では、上向き(Y軸に正方向のポリゴンを選択しているので、
法線の向きを表す(0.0, 1.0, 0.0)が返ってきます。
それぞれ、「X」「Y」「Z」の成分の組み合わせです。

同じ向きのポリゴンを選択する

選択したポリゴンの法線情報を取得できたので、
同じ向きの法線を選択する処理を書いていきましょう。

まずは、アクティブメッシュレイヤーのすべてのポリゴンのインデックスを取得します。

以下のように追記します。

polys = lx.eval('query layerservice polys ? all')

先ほどと同じコマンドで、「selected」の部分を「all」へ変更します。
また、今回は戻り値が1つだけでは困るので、「lx.eval()」を使用します。

次に、1枚だけ選択したポリゴンの選択を解除します。

lx.eval('select.drop polygon')

このコマンドは。MODOの「コマンド履歴」でポリゴンの選択を解除すれば出てくるコマンドです。
そのコマンドをそのままコピペで「lx.eval()」関数で実行しているだけです。

・選択したポリゴンの法線の情報
・アクティブメッシュレイヤーのすべてのポリゴンのインデックス情報

欲しい情報は揃ったので、
アクティブメッシュレイヤーのすべてのポリゴンの法線情報を調べて、
選択したポリゴンの法線情報と一致したポリゴンだけ選択する処理を考えましょう。

「for文」で全てのポリゴンについて調べる

全てのポリゴンのインデックスは、変数「polys」に配列として代入されています。
ですので、「for文」によってすべてのポリゴンの法線情報を調べることができます。

以下のように追記します。

for i in polys:
	
	n = lx.eval('query layerservice poly.normal ? %s' % i)
	
	print (n)

「for文」で変数「i」を宣言し、変数「polys」をセットします。
変数「polys」に代入されているポリゴンのインデックスが、
1つずつ変数「i」へ代入されてループ処理が繰り返されます。

続いてインデントによる字下げをした後に、
変数「n」を宣言し、先ほどの法線情報を調べた方法と同じ方法で、
変数「i」に代入されたインデックスのポリゴンの法線情報を変数「n」へ代入します。

print(n)として、スクリプトを実行してみましょう。

初めに選択したポリゴンの法線情報と同じポリゴンだけを選択する

全てのポリゴンの法線情報を調べることができたので、
初めに選択したポリゴンの法線情報を一致するポリゴンのみ選択する処理を考えます。

初めに選択したポリゴンの法線情報は変数「normal」に代入されています。
そして「for文」によってアクティブメッシュレイヤーの全てポリゴンの法線情報が変数「n」へ
ループ処理を繰り返すたびに更新・代入されます。

なので、変数「normal」と変数「n」が一致したインデックスのポリゴンだけを選択します。

そこで「if文」を使用し、条件分けを行います。
以下のように追記します。

	if n == normal:
	
		lx.eval('select.element %s 1 1 %s' %(mesh, i))

まず

「if n == normal:」

として、変数「n」と変数「normal」が等しい(真)か?を判定します。
そして真の場合の処理として、

「lx.eval(‘select.element %s 1 1 %s’ %(mesh, i))」

とします。
この時も、真の場合の処理のブロックを定義するために、字下げを忘れないよう注意しましょう。

ポリゴンを選択するコマンドは、MODOのコマンド一覧で調べることができます。
コマンドは「select.element」となっています。

「select.element」のコマンドには引数が必要で、以下のようになっています。

「select.element メッシュレイヤーのインデックス 選択するエレメントのタイプ 選択モード 選択するエレメントのインデックス」

今回のようにポリゴンを複数枚選択する場合は、

「select.element メッシュレイヤーのインデックス 1 1 選択するエレメントのインデックス」

となります。

選択するタイプや選択モードは数値で指定できます。
選択モードの「set」と「add」の違いですが、
「set」はポリゴンが選択されるたびに他の選択されていたポリゴンは選択が解除されます。
「add」は選択したポリゴンを追加で選択状態にしていきます。

今回は同じ法線情報のポリゴンを判定して追加選択していきたいので、
選択モードが「add」(1)を設定します。

もう一度、「select.element」の引数を確認してみましょう。

「select.element メッシュレイヤーのインデックス 1 1 選択するエレメントのインデックス」

・メッシュレイヤーのインデックス
・選択するエレメントのインデックス

どちらの値も、変数に代入されています。
ですので「%s演算子」を使用する必要があります。

lx.eval('select.element %s 1 1 %s' %(mesh, i))

・メッシュレイヤーのインデックス
・選択するエレメントのインデックス

の部分をとりあえず、「%s」に置き換えます。
今回のように「%s演算子」による置き換えが2個以上ある場合、
後ろに記述する変数をカンマで区切りで列挙することで、
以下のようなイメージで対応する順番に変数の値を文字列に置き換えてくれます

ですので、変数を列挙する順番を間違えないように注意してください。

初めにポリゴンを選択していない場合や、
そもそもアクティブメッシュレイヤーが無い状態などのエラー処理は何もしていないので、
完全ではありませんが、とりあえず、以上で完成です!

ポリゴンを1枚選択してみて、同じ向きのポリゴンが全て選択されるか確認してみましょう。

以上のように同じ向きのポリゴンが全選択されていれば成功です!

以下スクリプト全文

#python
import lx

mesh = lx.eval('query layerservice layer.index ? main')

poly = lx.eval1('query layerservice polys ? selected')

normal = lx.eval('query layerservice poly.normal ? %s' % poly)

polys = lx.eval('query layerservice polys ? all')

for i in polys:
	
	n = lx.eval('query layerservice poly.normal ? %s' % i)
	
	if n == normal:
	
		lx.eval('select.element %s 1 1 %s' %(mesh, i))

mihi
いかがだったでしょうか。
ほんの数行で、このようなことができてしまうスクリプト。
便利ではないでしょうか?基本的には
・「for文」による繰り返し処理
・「if文」による条件分け
ができれば、かなり色々な事ができるのではないかと思います。PDFのリファレンスから実現したい機能の有無や記述方法などを探すのが少し手間ですが、
割と丁寧に書かれていますので、いろいろ見てみてください!
>Profile

Profile

■mihi■

愛猫家

フリーランスとして建築CGを制作しています。

「MODO」と「3dsMax」を使用した
建築CGのテクニックや
日々の雑記を発信していきます。

よろしくお願いいたします。

CTR IMG