MODOではじめるPython_09 選択したエッジの長さの総和を求める

mihi
こんにちは、mihiです。
今回は「選択中のエッジの長さの総和」を求めてみたいと思います。
自分は建築CGの制作過程の中で、
ときどき道路の長さ(距離)を知りたい時があります。
そんな時に、スクリプトを数行書くことで、簡単に選択しているエッジの長さの総和を求めることが可能です。

選択エッジの長さの総和

実は、MODOは選択しているエッジの長さの総和を表示する機能が、
デフォルトで用意されています。

手順は、

  1. エッジを選択
  2. 選択エッジ上にマウスカーソルを当てる
  3. 3Dトラッキングパネルで確認

以下のスクリーンショットはレイアウトをカスタマイズしすぎてて、
皆さんと同じ場所に「3Dトラッキング」のパネルがありませんが、
デフォルトでは、下の方に出ていると思います。

便利で良いのですが、
MODOのレイアウトの状態によっては隠れてしまって表示されなかったり、
「案外選択したエッジ上にマウスカーソルを合わす」
というのが煩わしく感じることが多かったりします。

ですので、スクリプトを使って求めてみましょう。

まずは、お約束のおまじないからです。

#python
import lx

次に、前回と同様に「アクティブなメッシュレイヤー」をクエリーします。

mesh = lx.eval('query layerservice layer.id ? active')

変数「mesh」を宣言し、「lx.eval()」関数で、アクティブレイヤーのIDを変数「mesh」へ代入します。

選択しているエッジを取得

スクリプトリファレンスのPDFによると、
選択しているエッジを取得するには、

‘query layerservice edges ? selected’

とすればよいことがわかります。
ですので、以下のように追記します。

edge = lx.eval('query layerservice edges ? selected')

変数「egde」を宣言し、「lx.eval()」関数で選択しているエッジの一覧を変数「edge」へ代入します。

適当にポリゴンを作成し、エッジを数本選択します。
スクリプトの最終行で「print()」関数の引数に変数「egde」を渡して、
スクリプトを実行してみましょう。

print(edge)

すると上記画像の通り、
選択されているエッジの一覧が配列(タプル型)で返ってきます。

MODOはエッジの定義を、エッジを構成する頂点のインデックスのペアで定義します。
ですので、1番目のエッジは
頂点のインデックスが(1, 3)で構成されたエッジということになります。

表示オプションでインデックスを表示してみると、
1番目のエッジが、頂点インデックス「1」と「3」で構成されていることがわかりますね。

lx.eval()の罠

ここで、「lx.eval()」のについてお話しておきます。

エッジが複数選択されている場合、

edge = lx.eval(‘query layerservice edges ? selected’)

として選択しているエッジの一覧を取得すると、
配列(タプル型)として返ってくると解説しました。

本当にタプル型なのかを調べてみましょう。

エッジを複数選択した状態で、
「print()」関数の引数を以下のように変更し、
スクリプトを実行します。

print(type(edge))

しかし、エッジを1本だけ選択してスクリプトを実行してみてください。

なんと、str型(文字列型)で返ってきています!
返す値が1つしかない場合、「lx.eval()」関数は文字列を返してくれるようです。

この後、「for文」による処理を考えているので、
なにがなんでも、配列で値を返してもらわないと困ります。

lx.evalN()

そこで、返す値が1つしかなくても、
キチンと配列で値を返す機能が、「lx.evalN()」関数です。

edge = lx.eval(‘query layerservice edges ? selected’)

の部分を以下のように書き換えて、スクリプトを実行してみましょう。

edge = lx.evalN('query layerservice edges ? selected')

上記画像の通り、エッジを1本のみ選択した状態でも、
配列(タプル型)で戻り値が返ってきていることが確認できました。

選択したエッジの長さの総和を求める

エッジの長さの総和を求める考え方はとても単純です。
選択されているエッジの長さをひとつづつ調べて、
足し合わせていくだけです。

まずは、変数「length」を宣言し、値として「0.0」を代入しておきます。

length = 0.0

そして「for文」によって、
選択されているエッジの長さを1本ずつ調べて。
変数「length」の値に足し合わせていくだけです。

「for文」で変数「i」を宣言し、配列の「egde」をセットします。

for i in edge:

続いて、エッジの長さを調べる方法です。
スクリプトリファレンスのPDFによると、

‘query layerservice edge.length ? first’

となっています。

「first」の部分を長さを知りたいエッジのインデックス(頂点インデックスのペア)に置き換えるだけですね。

ですので、
次行にインデントによる字下げを行い、
以下のように記述します。

for i in edge:
	
	length = length + lx.eval('query layerservice edge.length ? %s' % i)

詳しく見ていきましょう。

lx.eval(‘query layerservice edge.length ? %s’ % i)

このの部分でのポイントは、
もうおなじみの「%s演算子」による文字列への置き換えですね。
変数「i」へは、エッジを構成する頂点のインデックスのペアの文字列が、
「for文」でループ処理されるたびに、順次代入されてきます。

その結果、
1本目のエッジの長さ
2本目のエッジの長さ


最後のエッジの長さ

という具合にスクリプトは動きます。

そして、

length = length + ・・・

という記述ですが、
これは、変数「length」の値に新たにエッジの長さを加えて加算していく処理です。

たとえば、1+2+3 という処理を考えてみます。
まずは変数「i」を宣言し、値に「0」を代入します。

i = 0

そして以下のようにすると、

i = 0

i = i + 1
i = i + 2
i = i + 3

とすると、

i = 0

i = i + 1 # 変数「i」の値は 0 + 1 で 1
i = i + 2 # 変数「i」の値は 1 + 2 で 3
i = i + 3 # 変数「i」の値は 3 + 3 で 6

という具合になります。
変数の今の値に対して、さらに値を加えたい場合は、
このように処理します。

※今回のロジックを解説するために、上記の通りの解説をしていますが、
単純に1+2+3の答えを求める場合は、普通に「i = 1 + 2 + 3」 と書いてOKです。

ですので、「for文」によってエッジの長さを1本ずつ調べて、
変数「length」へどんどん加算していく事で、
長さの総和を求めることができます。

ちなみにですが、「python」では、以下のように書くことも可能です。

length += lx.eval('query layerservice edge.length ? %s' % i)

これは、

length = length + ~~

length +=

と書き換えたものです。
「+=」とすることで、length = length + ~~と等価な式になります。

最終行の行頭に

print (length)

とすることで、スクリプトを実行すると、
選択されているエッジの長さの総和が表示されるはずです。

MODOの「3Dトラッキング」に表示されている選択エッジの長さの総和と、
スクリプトで求めた値が一致していることを確認してみてください。
スクリプトで表示されている値の単位は、基本的に「メートル」です。
また、スクリプトで表示されている値のほうが小数点以下の数値が多く、
「3Dトラッキング」に表示されている値は四捨五入されている数値です。

スクリプトの全文

#python
import lx

mesh = lx.eval('query layerservice layer.id ? active')
edge = lx.evalN('query layerservice edges ? selected')

length = 0.0

for i in edge:
	
	length += lx.eval('query layerservice edge.length ? %s' % i)
	
print (length)

mihi
いかがでしょうか?
「for文」による繰り返し処理に慣れてきたでしょうか?
>Profile

Profile

■mihi■

愛猫家

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

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

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

CTR IMG