2008/10/25

Sukio Sukio Sukio Python - III

Python好きよ好きよ好きよな初心者のためのPython講座その3です。

今回で、恐らく脱落者続出だと思われますが、意味が全然分からなくても

「ああ、オレってだめぽ・・・。スクリプト向いてないぽ・・・。」

って、思わないで欲しいです。
一朝一夕で覚えられたら、この記事を読まなくてもPythonを使いこなせるはずです。
Pythonは、いつまでも貴方を待ち続けます。

さて、始めましょう。

今回は、def文です。
defは、他の言語の言う所の、functionに当たります。
同じスクリプトで、何度も同じ内容のものを繰り返すときや、処理を見やすくす
るとき、そして、Pythonでは、前に保存したスクリプトのdefを呼び出すことも、簡単に行えます。

まず、何度も使うといえば

Application.LogMessage()

この文は、何度もデバッグのためなどに多用することでしょう。
ですので、例えばログ文を少し豪華にしたいなどのときや、何回も使うものをまとめておきます。

def log(pMsg):
  Application.LogMessage(pMsg)
  return '+ + + ' + pMsg + ' + + +'

log('abc')
log(r'C:\Softimage\XSI_7.0\Application\bin\XSI.exe')
sReturn = log('I love XSI')
log(sReturn)

このようにして、LogMessageを簡略化出来ます。

Pythonの場合、defを変数にすることが出来、最良手はコレですが。
log = Application.LogMessage
log('abc')

logというファンクションを作り、受け取る変数pMsgをセットします。これは、
何個でも作成することが出来ます。

def log(pMsg,pType):

こんな感じです。

下の、logファンクションで、'abc'というストリングを第1引数に入れ実行する
と、上部のpMsgという変数に、'abc'が代入され、

Application.LogMessage('abc')が実行されるシンプルなファンクションです。

def文の中に、return文がありますが、コレは返ってくる値をセットすることが出来ます。
無くても動作します。
'I love XSI'がpMsgに代入されたとすると、返ってくるのは、'+ + + I love XSI + + +'になり、上記の例では、sReturnに代入されるという仕組みです。

r'C:\Softimage\XSI_7.0\Application\bin\XSI.exe'

この文で、スクリプトを書いたことがある方は、へぇ~と思いますが、Pythonには、Row Stringという型が用意されており、バックスラッシュ'\'を通常の文字として認識させることが可能です。
例えば、JScriptで書くには、このようにしなければ、'\'は認識できません。

'C:\\Softimage\\XSI_7.0\\Application\\bin\\XSI.exe'

バックスラッシュの嵐が訪れる事になります。

String型で'\'は、エスケープ文字と呼ばれ、改行やタブを表現することが出来ます。
これにはいろいろあります。

\\:バックスラッシュ (\)
\':一重引用符 (')
\":二重引用符 (")
\a:ASCII 端末ベル (BEL)
\b:ASCII バックスペース (BS)
\f:ASCII フォームフィード (FF)
\n:ASCII 行送り、改行 (LF)
\r:ASCII 復帰 (CR)
\t:ASCII 水平タブ (TAB)
\v:ASCII 水平タブ (VT)
\uxxxx:16-bit の 16 進数値 xxxx を持つ文字 (Unicode のみ)
\Uxxxxxxxx:32-bit の 16 進数値 xxxxxxxx を持つ文字 (Unicode のみ)
\ooo:8 進数値 ooo を持つ文字
\xhh:16 進数値 hh を持つ文字
\N{name}:Unicode データベース中で名前 name を持つ文字 (Unicode のみ)

いろいろありますが、何のことかサッパリ。って感じでしょうか。
ここで、使うだろうなっていうのは、バックスラッシュ(\)、ダブルクォーテーション(")、改行、タブくらいでしょうか。
それだけ覚えておけば、豪華なLogMessageや、ログとしてテキスト吐き出しをするときなど、応用が可能です。

さてそれでは、defを絡めつつ、PickSessionを使用して、動作するScriptを書いて見ましょう。

1. Pickする度に、原点に移動する。
2. 終了するには、右クリックする。

という内容にしましょう。

まず、たくさんのNullを出してバラバラに配置しておきます。
Iで作ったScriptを実行してもOKです。
もちろん、自分で作成されるのがベストです。


def MatchTransformationPickElement():
  oBtn = 1
  while(oBtn != 0):
    oBtn,oMdf,oElm = app.PickElement()
    if oBtn==0: log('Done'); break
    app.ResetTransform(oElm)
    log(oElm)

app = Application
log = app.LogMessage
MatchTransformationPickElement()


こんな感じでしょうか。内容を見ていきましょう。
プログラム的に言えば、上から順に実行されて行きますが、defに関しては

一度メモリに溜め込んで、他の行で実行されて初めて効力を発揮する

というニュアンスです。
このコードは

app = Applicationが一番最初に実行されます。
次に、log = app.LogMessage
次に、作ったファンクションである

MatchTransformationPickElement()

が実行されます。
oBtn = 1 や whileなどがありますが、まず、注目していただきたいのは、PickElement()です。
実は、PickElement()もファンクションなのです。
PickElement()を実行すると、Picking Modeになります。
そして、ユーザーがマウスボタンを押すと3つの戻り値、Return Valueを得ます。

ButtonPressed, ModifierPressed, PickedElement

ButtonPressedは、なんのボタンを押したか。
1:左クリック
2:中クリック
0:右クリック
このような数字が返ってきます。

ModifierPressedは、装飾キーは押しているか。
0:なし
1:+Shiftクリック
2:+Ctrlクリック
3:+Shift+Ctrlクリック
今回は、使いません。

最後のPickedElementは、XSIのオブジェクトを返します。
詳しくはPickElementをハイライトして、マニュアルを見てみましょう。

oBtn, oMdf, oElm = app.PickElement()

変数を並べておけば、それぞれに代入されます。
左クリックでNullを選んだとすると、oBtnに1が、oMdfには0、oElmには、Nullが入る事になります。
次の文に進みます。

if oBtn==0: log('Done'); break
app.ResetTransform(oElm)

前回やった、if文ですが、このようにセミコロン(;)で改行を代用しこのように書くことが出来ます。
つまり、oBtnが0のとき、これは、右クリックしたときに、

log('Done')
break

を実行することを意味します。
終了したいので'Done'とログし、break文で、whileを抜けます。
この場合、defは、もう何もないので、ココで終わります。
breakを実行すると、forwhiledefの途中で終了出来るのです。

左クリックをした場合、ifは、スルーされ

app.ResetTransform(oElm)

が実行され、Nullが原点に戻ります。
実は、ここでスクリプトは、終わりません。それはwhile文というものがあるからです。
while文は、条件式がTrueであれば、ずーっといつまでも実行し続けるというものです。
if文のループ版みたいな感じでしょうか。かなり強力です。
しかし、while文を多用するのはあまりオススメしません。
一歩間違うと、いつまでたっても終わらないスクリプトが完成してしまいます。
ちょっと恐ろしい文です。

while(oBtn != 0):

は、oBtnが、0じゃないとき。つまり、右クリックじゃないときにずーっと実行しなさいという意味になります。
def文のはじめに

oBtn = 1

を定義しているのは、whileに一番最初に入ったとき、oBtnという変数は存在しませんので、エラーになるのを回避するためです。

さて、こんな感じですが、お分かりになりましたでしょうか?
defは結構つかみにくいものであったと記憶してますので、無理に使わなくてもスクリプトを書くことは出来ます。
ですが、構造が理解できると、必ず使うようになります。
少しずつ覚えて行くと良いでしょう。

今回も物凄く長くなってしまいました・・・。
スクリプトは、細かい定義がたくさんあって、説明するのが大変です。
もうちょっとシンプルに行きたいものです。

次回は、GUIの作成をしてみたいと思います。
カスタムパラメータを作り、ボタンをプチっとしたら、何かが起こるというものです。
これが出来れば、結構面白い事が出来るでしょう。
楽しみの幅が広がりますね。

ではでは、またの機会に・・・。

2008/10/18

Sukio Sukio Sukio Python - II

Python好きよ好きよ好きよな初心者のためのPython講座その2です。

今回は、if文をご紹介。
ついでに、for文のもうひとつの使い方をご紹介します。
今回は、Selectionをからめた使い方です。
選択したものに対してのみ、実行するスクリプトを作ってみましょう。

前回のIで作ったNullを作るスクリプトで、10個の並んだNullを作りましょう。

スクリプトを使って、ある特定のオブジェクトを取得するというのは
常に苦労するところです。
コーディングをしていて、1/3くらいはそこに注力します。
ですので、最重要であるといっても過言ではありません。
気を遣って充分考えて取得しましょう。

選択したものを変数に入れるには次の命令文を書きます。

oSel = Application.GetValue("SelectionList")

または

oSel = Application.Selection

です。
上記はCMを使い、下記はOMを使用しています。
変数に代入されるのは、内部的には決定的に違いますが、結果はどちらも大して差はありません。
10個のNullを選択して、実行してログを見てみましょう。
ログを見るには、この命令文です。

Application.LogMessage(oSel)

VBやJなら、両方とも選択したオブジェクト名がずらーっと表示されますが、Pythonの場合、ちょっとだけ違う結果になってしまいます。
ここで変数のデータの型という定義が存在するのですが、進めながら少しずつお話しましょう。

さて、選択された10個のNullを取得することが出来るようになりました。
次に、その10個に対して、Sizeを少しずつ大きくしていく事にします。

NullのSizeを変えるコマンドを知りたいので、ためしにひとつ選んでSizeを変えてみます。

Application.SetValue("null.null.size", 2, "")

このようなログが出てきました。
SetValueの第1引数は、ターゲット。第2引数はその値です。

先ほど選択したものの変数oSelと前回のforを使い順々に処理していきます。


i = 1
app = Application
oSel = app.GetValue("SelectionList")
for oObj in oSel:
  app.SetValue(oObj.Name+".null.size", i, "")
  i += 1


ここでのポイントは、forに対し、rangeではなくoSelという変数を入れています。
oSelのデータ型は、XSICollectionです。
[null,null1,null2,null3,null4,null5,null6,null7,null8,null9]
内部的には、このような格納されています。
変わってrange(10)のデータ型は、リストです。
[0,1,2,3,4,5,6,7,8,9]
内部的には、こんな感じ。
Pythonでの配列(Array)は、リスト、タプル、ディクショナリーと種類が存在しますがXSICollectionは、リストにとても近いです。
ですので、oSelの左から順にoObjという変数に格納して、forのインデント内を実行しなさい。
となります。
1順目は、null(OM)がoObjに格納されて、処理されるという仕組みです。

次に、SetValueの第1引数ですが、ターゲットを順に変えていく必要があります。

app.SetValue(oObj.Name+".null.size", i, "")

oObjには"null"が入っています。oObjは、オブジェクトモデルなので、Nameというパラメーターを取得出来ます。
そして、iには、1が入っています。

なので内部的には、こう解釈されます。

app.SetValue("null.null.size", 1, "")

最後の

i += 1

で、iに1が足されますので、結果2になりました。
これで、1順目が終了です。

次に2順目に行きます。
仕組みは分かっていますので、次の命令文が実行される事になります。

app.SetValue("null1.null.size", 2, "")

こんな感じで、次々に処理され、NullのSizeがどんどん大きくなるというスクリプトが出来ました。

それでは、次のステップに進みましょう。

並べられた、Nullですが、全てに処理を適用するのではなく、ひとつ飛ばしに実行するには、どうしたらいいでしょう?
ここで、if文が登場です。
iの値が、偶数のときのみNullのSizeを変えるというのを実行することにしてみます。


i = 1
app = Application
oSel = app.GetValue("SelectionList")
for oObj in oSel:
  if i % 2 == 0:
    app.SetValue(oObj.Name+".null.size", i, "")
  else:
    app.SetValue(oObj.Name+".null.size", 1, "")
  i += 1


if文は、後に条件式が来ます。演算子(ここでは "==" お互いに等価であることを意味します)を境に、True(真)なのかFalse(偽)なのかを判断するだけです。
ここでの条件式は

i % 2 == 0

%は、剰余を表します。剰余とは、割り算をしたときの余りのことです。
つまり、2で割ったときに余りが0になるのは偶数なので、割り切れるときにTrueとなり、if文の中身が実行される仕組みです。

else文は、ifFalseのときに、実行されます。
iが奇数のときは、Sizeを1にという命令文が実行されます。

それでは、その上、4番目のNullにだけは、Sizeを0.1にするということをするには、どうしたらいいでしょうか?

4番目のNullだけに適用したい場合、iの値が4では、偶数なので

i % 2 == 0

この式に、さらに、4以外という条件を付与しなければなりません。
その値以外という演算子は!=を使います。

i != 4

ですので、偶数だけど、4以外という条件式は

if i % 2 == 0 and i != 4:

とやり、andを入れます。

そして4になるときの処理は、if文がFalseになり、elseにたどり着く前にif文を定義しなくてはなりません。
そのときは、elif文です。else ifを省略した形ですね。

elif i == 4:

この文を使って、こんな感じになりました。


i = 1
app = Application
oSel = app.GetValue("SelectionList")
for oObj in oSel:
  if i % 2 == 0 and i != 4:
    app.SetValue(oObj.Name+".null.size", i, "")
  elif i == 4:
    app.SetValue(oObj.Name+".null.size", 0.1, "")
  else:
    app.SetValue(oObj.Name+".null.size", 1, "")
  i += 1



4のときだけ、elif文が適用されるようになりました。
これで、4番目のNullにだけ、Size 0.1が適用されます。

if文は、こんな感じで、とてもシンプルです。
複雑になるのは、条件式の部分。ここには、結構毎回悩まされますが、ロジックを探すのもスクリプトの面白さと言えるでしょう。

今回も、かなり長くなってしまいましたが、コレでほぼPythonは手中に入れたようなものです。
ほとんどのスクリプトは、for文とif文で成り立っています。
物を自動的に判別するというところが、時間が一番かかるところ。
処理するものは、Nullなのか、PolygonMeshなのか、Lightなのか、Passなのか。
XSIには、とても多くのオブジェクトタイプが存在しますが、それを上回る判別方法がたくさん存在します。
それをひとつひとつ見つけて行くことが進歩の秘訣です。

それでは今回はこの辺で。
次は、defでしょうか。これも大切なものでファンクションと呼ばれるものです。
オブジェクトをPickして、実行するタイプのものを作りましょう。

では、次回まで・・・。

オタノシミニ☆

2008/10/14

Sukio Sukio Sukio Python - I

スクリプトの羅列とかいって、全然書いてなかったので、ちくちく書いてみる事にします。

ですが、VBは良く分からんですし、いまさらJScriptをやるのはなんなので
Pythonにスポットを当ててみようと思います。

なんで、いちいち標準で入っている、VBやJを使わないん?

って思いますが、Pythonを触り始めると、元に戻るのがイヤんなります。

だまされたと思って、門をくぐってみるとたちまち便利な事に気づくでしょう。
ただ、これを感じるのは、他の言語をやった人のみですが・・・。

しかしながら、初心者の方もPythonからハジメテミルのはとっても良いと思います。

なので、早速ゆーっくり初めてみましょう。

------------------------------------------------------------------

まずはじめに

初心者の方には全く分からないと思いますが、XSIの命令文とPythonの命令文があります。
XSIの命令文というのは、

GetPrim()

とか

SetValue()

とかXSIのマニュアルに書いてある命令文たちです。

変わって、Pythonの命令文は

for

とか

if

とかが存在します。
こちらは、XSIマニュアルには書いてません。
webなどを参考にするしか手はありません。

最初は、全然分からないと思いますが、見分けられるようになると、次のステップに上がったのかなと思ってもらってOKです。
ちなみに、Pythonの命令文を覚えておきさえすれば、他のソフトでも同じやり方で通用するということになります。

このサイトでは、Pythonの命令文XSIの命令文と、このように表記しますね。

それでは、第一回目。
当然、forを取り上げたいと思います。

for i in range(10):
  Application.LogMessage( i )

2行のこのスクリプトでは、0~9を表示せよ。
という内容です。

コピペして、スクリプトエディターで実行してログを見てみましょう。
数字の羅列が表示されたと思います。

それでは、2行目に注目してください。
Pythonでは、インデントというものが、存在します。
インデントというものは、forの下の行のスペース、段落を意味します。
これは、スペースでも、tabでもOKです。

for文は、インデント内を繰り返す命令文です。
他の言語では、インデントを付けるのは習慣で、無くても動作するんですが、Pythonでは、無くては動作しません。
他の制作者が作っても必ずこの形になるので、コードが見やすくなるという特徴もあります。

range文は、数字の羅列を生成します。
結果を見ても分かりますが、0~9を生成します。
10は、生成しません。
コンピューターは、0が一番最初の数字と認識します。
そして、10未満までというニュアンスでOKです。

in文もありますが、簡単に言うと

0~9を i という変数に順々に代入して、インデント内の処理をしなさい。

という意味になります。

ほとんどのスクリプトは、このような、ループ処理を多用しています。

では、次に、nullを10個出してみましょう。

app = Application
for i in range(10):
  app.GetPrim("Null")

for文の処理の中身だけを取り替えました。
そして、行が増えましたが、少し簡略化してみました。
Applicationと書くのは、少々手間ですので、appという変数にしてみました。
このように、"."(ドット)の前は他の変数にする事が出来ます。

そして、書き方には、もう一通りの書き方があります。

app = Application
for i in range(10):
  app.ActiveSceneRoot.AddNull()

このようなものです。
上記は、コマンドと呼びます(以下CM)
XSIは、ボタンを押すとコマンドを呼び出して、結果を表示します。
下記は、オブジェクトモデルです(以下OM)
メニューやボタンからでは、呼び出すことが出来ない直接的なやり方です。
計算を内部的に行い、スクリプトが終わったと同時に結果のみを表示します。
スピードは、こちらのほうが、断然早いです。
100個作成する。といった、スクリプトで試してみると一目瞭然だと思います。
ただし、いきなりOMから入ると本当に訳が分からなくなるので、最初からはあまりオススメしません。

次に進みます。
ただ出すだけではつまらないので、作る度に1グリッド移動して配置するというものを作ってみましょう。

一度、nullを作成して、xに1移動します。

Application.GetPrim("Null", "", "", "")
Application.Translate("", 1, 0, 0, "siAbsolute", "siPivot", "siObj", "siX", "", "", "siXYZ", "", "", "", "", "", "", 0, "")

これをコピペして、また同じように、for文の中身だけ取り替えてみましょう。
ですが、ここで、ちょっとしたポイントがあります。

app = Application
for i in range(10):
  app.GetPrim("Null", "", "", "")
  app.Translate("", i, 0, 0, "siAbsolute", "siPivot", "siObj", "siX", "", "", "siXYZ", "", "", "", "", "", "", 0, "")

1のところをiにちょこっとだけ、変えています。
スクリプトエディターで、Translateというところをハイライトして、?ボタンを押してみてください。
マニュアルが表示されました。
カッコ"()"の中身にいろいろ書いてありますが、iの部分の第2引数("ひきすう"といいます)は、X座標を指定してあげればいいことが分かります。
ですので、rangeで生成したiの変数を使用して、X座標を入れてあげます。

nullを作ってXの0~9の座標に移動しなさい。

といった、スクリプトが出来ました。

XではなくYに移動させたい場合は第3引数のYのところをiにすればいいので

app = Application
for i in range(10):
  app.GetPrim("Null", "", "", "")
  app.Translate("", 0, i, 0, "siAbsolute", "siPivot", "siObj", "siY", "", "", "siXYZ", "", "", "", "", "", "", 0, "")

このように書けばOKです。
第8引数のsiXが実は曲者ですが、フィルターをsiYにしましょう。

そして、ちょっとダイエットするとコレだけで動作します。

app = Application
for i in range(10):
  app.GetPrim("Null")
  app.Translate("", i)

実は、マニュアルの[]でくくられた引数は、省略することが出来ます。
省略したらどうなるのかというと、デフォルト値がセットされることになっています。

書き方はいろいろありますが、簡単な方法でやります。
一番スマートな書き方は、このようになります。

app = Application
oRoot = Application.ActiveSceneRoot
for i in range(10):
  oNull = oRoot.AddNull()
  oNull.posx.Value = i

OMを使用し、作ったNullを変数にして、posxパラメータをダイレクトに入力しています。
何度も書きますが、スピードは、歴然です。

for文の中にfor文を書くことも可能です。

app = Application
oRoot = Application.ActiveSceneRoot
for ix in range(10):
  for iy in range(10):
    oNull = oRoot.AddNull()
    oNull.posx.Value = ix
    oNull.posy.Value = iy

100個のNullが碁盤の目に並びましたね。

と、このあたりで、訳が分からなくなる人続出でしょうから、for文は、このくらいにします。
次回は、ifですね。

オタノシミニ!

2008/10/07

画角と解像度の緻密な関係

今回はメモ的なログで。

元の画像をキープしたまま、画像の見える範囲を広げたい。
と思ったことはありませんか?

ズームアウトで外側の枠だけを広げて2倍の解像度にしたい場合
カメラの画角をどうやって設定したらいいか分かりますか?



『画角を2倍、解像度2倍でいいんじゃね?』

最初は誰もがそう考えるはずです。
ワタクシも疑いませんでした。
だけど、実際は違うのです。

角度を2倍にしたら、見える範囲は2倍になるのは正しいのですが・・・。
なんでかは、あんまり証明できないけれど、実際は違うみたいです。

Mayaには、カメラスケールというものが存在していて、簡単に結果を出すことが出来たりしますが
残念ながら、XSIには、標準でそんなものは用意されていません。

なので、ピッタリ来る計算式を出してExpressionを組んで見ましょう。

中学校でおなじみの懐かしの三角定理を使います。

下図は、トップビューからの図です。



緑の角度 x は、三角定理を使うので元の画角の1/2を示しています。
求めたい赤の角度は x' として、これまた1/2とします。

aは、仮想のプレーンを想像して、そこまでの距離の長さ
bは、解像度の1/2の長さと仮定します。

最終目的はbがn倍になったときに、x' がどのように変化するかです。

まずタンジェントを使います。

tan(x)=b/a ...①

が成り立ちます。

xを出すには、arctanを使います。
アークタンジェントとは、タンジェントの逆関数で、角度とか座標を求めるときに良く使ったりします。かなり便利なので、覚えて置くと◎です。

tan(x) = y のときに、arctan(y) = xになります。

これを利用して

arctan(tan(x))=x

①の式を使って

arctan(b/a)=x

です。
bをn倍したら x' になるので

arctan(nb/a)=x'

になります。なので、また式を戻してみます。

arctan(n・tan(x))=x'

になります。これで、角度の半分が求まります。

結果が求まりました!
XSIのExpressionは、こんな感じでしょうか。

atan(n*tan(Camera.camera.fov/2))*2

ちなみにですが、ソフトによって、tan()は、radianしか入らなかったりするんですが
XSIは、degreeで入ってくれますね。
pythonでコレをやる場合は、radianをdegreeに変換して使いましょう。

import math as m
x=40
n=1.1
m.degrees(m.atan(n*m.tan(m.radians(x/2))))*2

こんな感じですね。
話がそれましたが・・・。

数学って、こんな感じで使うんですよねー。
美しいものです。

こうやって、数学を覚えたりしたら、もうちょっと勉強したんだけどなぁ。
数学って楽しいものなんですよね。

2008/10/02

XSI7 - Solve a freezing probrem

XSI7、入れた当初は、不安定すぎ・・・。
これじゃ、全然使えなさ過ぎ・・・。
win2000互換じゃないとsample sceneが、開けなかったり、viewerが、shadedモードで
サブディビかけただけで、青画面になったり・・・。

などなど、不安だらけでしたが、青画面のログを見てみると、display driverなんちゃらかんちゃら、って書いてあったので、思い切って最新のドライバーに更新してみました。

したら、全部解消されました♪

win2000互換じゃなくても、sample落ちません♪

サブディビも落ちなくなりました♪

そのほかは知りませんが、ちょっとは前進したかも。

みなさんも、最新のドライバー試して見るといいかもです。

またエラーがあったら、報告しますね。

Perforce: 複数のワークスペースを更新するバッチ

batを叩けば全部更新。 @echo off set P4PORT=x.x.x.x:xxxx set P4USER=user set P4PASSWD=password echo %P4PORT% echo %P4USER% echo %P4PASSWD% echo %P4PAS...