« 取得 その4。 | トップページ | 久しぶりの Beerware。 »

2010年10月29日 (金)

取得 その5。

Pass や Partition の取得を攻略しましょう。 Pass や Partition がらみのことって、反復操作が多いのでスクリプトの活躍どころです。その端緒をつかむために、簡単なサンプルを書いてみようと思います。


まず、シーンの中にある全ての Pass を取得する書き方です。

  var oAllPasses = ActiveProject.ActiveScene.Passes;

以上。

細かく言えば Application オブジェクトの ActiveProject プロパティを使って XSIProject オブジェクトを取得し、XSIProject オブジェクトの ActiveScene プロパティを使って Scene オブジェクトを取得し、Scene オブジェクトの Passes プロパティを使って PassCollection オブジェクトを取得している、という芋づるになっているのですが、まあこんな理屈は今は無視して、シーンの中にある全 Pass を取得するにはこう書くもんだと覚えましょう。

Return Value は PassCollection です。 Pass オブジェクトが入っている集合体(コレクション)です。ここが重要。 コレクションなので、中に入っている Pass ひとつひとつに対する操作は受け付けてくれません。 なのでループなどの方法で個別の Pass オブジェクトを取り出す必要があります。それさえできれば、Pass オブジェクトに対する色んなプロパティやメソッドが使えるということです。


Pass に対する操作でよく使いそうなのは、

  Pass.CreatePartition メソッド ( Partition を作る )
  Pass.Partitions プロパティ ( その Pass に存在するパーティションをコレクションとして取得 )

とかでしょうかね。

Pass.CreatePartition メソッドの RV( Return Value ) は Partition オブジェクトです。CreatePartition のヘルプページを見ると書いてあります。実行すると Partition が新規で作られるうえに、新規で作られたパーティションは Partition オブジェクトとして取得できちゃうということです。 なので以降この Partition オブジェクトに対する操作は全部自由です。

Pass.Partitions プロパティの RV は PartitionCollection オブジェクトです。Partitions のヘルプページを見ると書いてあります。その Pass に所属する全ての Partition をコレクションとして取得できるということです。取得できたコレクションから Partition オブジェクトをひとつひとつ取り出せば、それはもう Partition オブジェクトそのものを取得できたということです。なので同じく Partition オブジェクトに対する操作は全部自由です。



ってことでサンプルを書いてみます。
超基本から。

var oAllPasses = ActiveProject.ActiveScene.Passes;
for ( var i=0; i<oAllPasses.count; i++ );
{
    var oPass = oAllPasses(i);   
    var oPartitions = oPass.Partitions;

    {
        var oPartition = oPartitions(j);
        Logmessage( oPartition );
    }
}



これを実行すると、シーンに存在する全ての Partiton がスクリプトエディタ上に表示されます。

処理の順番としては、


1.全Pass取得
2.1で取得した全Pass をループし、ひとつの Pass を取り出す
3.その Pass に所属する全 Partitoin を取得
4.3で取得した全 Partition をループし、ひとつの Partition を取り出す
5.取り出したひとつの Partition をスクリプトエディタに表示する
6.3に戻って全部の Partition が終わるまで繰り返し
7.2に戻って全部の Pass が終わるまで繰り返し


こういう感じです。 もうこれで十分な説明になっている気もするのですが、同じスクリプトにいちおう冗長なコメントを入れてみます。


var oAllPasses = ActiveProject.ActiveScene.Passes;    //    全 Pass 取得(コレクションとして)
for ( var i=0; i<oAllPasses.count; i++ ) //    全Pass が入ったコレクションの中身をループ
{
    //    「コレクションの中の i 番目の人」を取得。 oPass の中身はもはやコレクションではなく、特定の Pass オブジェクト。
    var oPass = oAllPasses(i);

    //    特定の Pass オブジェクトにぶら下がる Partition を、コレクションとして取得
    var oPartitions = oPass.Partitions;

    for ( j=0; j<oPartitions.count; j++ )    //    取得できた全Partition が入っているコレクションをループ
    {
        //    「コレクションの中の j 番目の人」を取得。 oPartition の中身はもはやコレクションではなく、特定の Partition オブジェクト。
        var oPartition = oPartitions(j);

        //    スクリプトエディタ上に表示
        Logmessage( oPartition );
    }
}

こういうことになります。
取得したものを、その後どこで使っているのかわかり易いように、色分けもしてみました。 oAllPasses に全Pass をコレクションとして取得したのを皮切りに、次々と取得の連鎖です。芋づるです。



一歩進めて、全 Pass のパーティションに HagePartition というパーティションを作るサンプル。


var oAllPasses = ActiveProject.ActiveScene.Passes;

//    新規で作ったパーティションを入れておく箱=空っぽのコレクションをあらかじめ用意
var oNewPartitions = XSIFactory.CreateObject( "XSI.Collection" );


for ( var i=0; i<oAllPasses.count; i++ )
{
    var oPass = oAllPasses(i);
   
    //    oPass に対し、新規のパーティションを作る。 戻り値は Partition オブジェクトで、oPartition に格納。
    var oPartition = oPass.CreatePartition( "HagePartition", siObjectPartition );
   
    //    oPartition を oNewPartitions という箱に Add (追加) している
    oNewPartitions.Add( oPartition );
}
//    最後に箱ごと選択しておしまい
SelectObj( oNewPartitions );



このようにシーンにいくつか Pass がある状態で、
S51

上のスクリプトを実行すると、
S52
このように、全 Pass で HagePartition が作成され、選択状態になっているのがわかると思います。



では新たに出てきたものを説明します。


  var oNewPartitions = XSIFactory.CreateObject( "XSI.Collection" );

これは、空っぽの箱を作るということです。まだ何も格納されていないコレクションです。呪文のように覚えてしまいましょう。


    var oPartition = oPass.CreatePartition( "HagePartition", siObjectPartition );

これは、oPass に格納されているもの=つまり Pass オブジェクトに対して、CreatePartition メソッドを使って新しいパーティションを作成しています。 カッコの中で指定しているのはパーティションの名前とタイプです。 タイプというのは、オブジェクトパーティションなのかライトパーティションなのかということですね。 ま、CreatePartition のヘルプページを見れば載っています。

そしてこのメソッドの RV は Partition オブジェクトです。つまり新規で作られたパーティションをオブジェクトとして取得できたということです。 以降、Partition オブジェクトに対して何するにも自由です。この例では箱に入れる以外は特に何もやってないですが。


    oNewPartitions.Add( oPartition );

oNewPartitions は XSICollection オブジェクトであり、それに対して Add メソッドを使っているということです。最初に用意した空っぽの箱の中に、新規で作られたパーティションを格納しているということです。
 
なんでこんなことをするのか説明します。

このスクリプトは Pass を1つ1つループしています。 最初の Pass で HagePartition を作成して oPartition に格納し、次の Pass へループが進みます。 すると次の Pass でまた HagePartition を作って oPartition に格納してしまうので、最初の Pass で作られた方の HagePartition は2回目のループの中で上書きされてしまいます。別の言い方をすると、破棄されてしまい、スクリプトの中では永遠に失われます(もちろんシーンの中にできたパーティションが消えるという意味ではありません。スクリプトの中で、そのパーティションを指し示す変数の中身が上書きされるため、最初に格納されたものへの関連は失われるという意味です)。 

で、新規で作ったパーティションは最後に全部、選択状態にしたいじゃないですか。 でも選択したい Partition はループのたびに上書きされるため、ループが終了した時点では oPartition にはループの最後の回で格納された1個しか残っていません。 これはよろしくない。

だから1回ループするごとに、oPartition に格納された新規パーティションを oNewPartitions という箱の中にも格納しておくのです。待避させていると言ってもいいですね。 Add メソッドで 「追加」 しているので、前回のループで格納されたパーティションは上書きされません。どんどんケツに追記されていくことになります。 なのでループが全部終了した時点で、新規で作られた全ての Partition がスクリプト内で失われることなく、箱に入っている状態になります。 だから最後に SelectObj で箱ごと選択することができます。

これ、すんごくよく使うパターンなので、覚えておいて損は無いと思います。



あ、ここで勘違いしやすいので注意した方が良いと思われるのは、オブジェクトモデルのメソッドとしての CreatePartitoin と、コマンドの CreatePartition の混同です。

上の例で使ったのは、Pass オブジェクトに対するメソッドとしての CreatePartition です。 でも XSI にはコマンドの CreatePartition もあるんですよ。 わかりにくいですね。 区別できる名前を付けりゃいいのねえ。

オブジェクトモデルのメソッドは、 オブジェクト ドット メソッド という書き方になります。まずオブジェクトの部分が取得できていることが前提になります。 今回の場合は Pass.CreatePartition( うんたらかんたら ) であり、Pass オブジェクトが取得できていることが前提です。 これに対しコマンドの方は、Pass を必ずしもオブジェクトとして取得していなくても、名前さえわかれば実行できてしまうという性質のものです。 オブジェクトモデルとコマンドの違いを解説するのが主旨ではないので詳説はしませんが、今回この記事で扱っているのはオブジェクトモデルのメソッドである CreatePartition であり、「CreatePartition コマンド」とは全く別物だということだけ留意しておいて下さい。 特にヘルプページを見るときに注意です。 間違ってコマンドの CreatePartition の方を読んでいたりしたら、この記事に書いてあることが意味不明になり、デタラメを書くななどと junki にクレームを出し、俺は怒ってモニタをあなたの頭上めがけて遠投するような事態に容易になり得るからです。




さ、戻りましょう。
さらに一歩進めて、 「ユーザが選択しているオブジェクトを、全 Pass の中の特定のパーティションに入れる。 もしそのパーティションが無かったら、新規で作る」 というのを書きましょう。



// ぶち込みたいパーティションの名前。 ここを書き換えて下さい。
PartitionName = "HagePartition";

var oNewPartitions = XSIFactory.CreateObject( "XSI.Collection" );
var oAllPasses = ActiveProject.ActiveScene.Passes;

for ( var i=0; i<oAllPasses.count; i++ )
{
    var oPass = oAllPasses(i);
    var oPartitions = oPass.Partitions;

    //    oPartitions というコレクションに入っているパーティションのうち、
    //    PartitionName に入っている文字列と名前が一致するパーティションを、
    //    oTargetPartition に取得している

    var oTargetPartition = oPartitions( PartitionName );

    //    取得したものが null かどうかを調べている。 null だったら=空っぽ=存在しない=じゃあ新規で作りましょう
    if ( oTargetPartition == null )
    {
        var oTargetPartition = oPass.CreatePartition( PartitionName, siObjectPartition );
        Logmessage( "そんな Partiton はないから作ってやりますた。 --> " + oTargetPartition );
    }
   
    //    この時点で、上の if を通っているので、必ず PartitionName の名前の Partition が存在し、
    //    oTargetPartition に格納されている。 安心して Partiton にメンバーを追加できる。
    //    Partition オブジェクトのメソッドのひとつ、AddMember を使って選択中のオブジェクト(Selection)をぶち込んで終了。

    oTargetPartition.AddMember( Selection );
   
    oNewPartitions.Add( oTargetPartition );
}
SelectObj( oNewPartitions );



コード中のコメントで説明し切っちゃってるので冗長になりますが、いちおう説明します。

冒頭の PartitionName の中に入れる文字列が、パーティションの名前になります。ここを自由に書き換えてください。 ただの文字列でありオブジェクトではないので、変数名に o は付けていません。

その後しばらくは前のサンプルで出てきたので説明不要ですよね。 カラのコレクションを用意し、全 Pass をループしています。


次のここはポイントです。

    var oTargetPartition = oPartitions( PartitionName );

オレンジの oPartitions は、「その Pass に所属する全 Partition 」が入っています。コレクションです。 そして、そのコレクションの中から特定の名前を持つものだけを抽出しようとしています。 特定の名前とは、最初にユーザが自由に書き換えた文字列 PartitionName です。  このように、コレクション( 特定の文字列 ) という書きかたをすると、コレクションの中でその名前を持つものだけが取得できます。 今まで何度も コレクション( 数字 ) という書き方は出てきましたよね。 Selection(0) とか、oModels(1) とかです。これって、コレクションの中の「何番目」という言い方で、つまり格納順の番号(インデックス)で指定していたことになります。  今回は番号の代わりに、「番号は何番目でもいいから、この名前のやつを出せゴルァ」 と言っているわけです。 インデックスを使っても名前を使っても、いずれにせよ特定のひとりを指名したんだから、取得できたものはもはやコレクションではありません。 こうして特定の1つの Partition が oTargetPartition に取得できました。 しかし・・・・

ちょっと待った。 本当に取得できたのでしょうか?

ユーザが指定した名前のパーティションがすでにシーンに存在していれば、当然取得できます。しかし存在してなかったら、取得できないですよね? できないんです。当たり前です。ありもしないものは取得できません。 なので、その名前のパーティションが存在するかどうかを調べているのが、次の if ブロックです。

    if ( oTargetPartition == null )
    {
        var oTargetPartition = oPass.CreatePartition( PartitionName, siObjectPartition );
        Logmessage( "そんな Partiton はないから作ってやりますた。 --> " + oTargetPartition );
    }



前の行では、その名前のパーティションがすでにあるかどうかに関係なく、強引に oTargetPartition に格納していました。 もしその名前のパーティションが無かった場合は、oTargetPartition には何も入りません。 何も入ってないという状態は、 「無」 が入っていると同じ意味になります。 なので JScript の場合は null が入っているかどうかで判断することができます。 この null とは、ビューポート上に現れるヌルのことではなく、プログラミング的に「無」とか「無効」とかを表す null です。 なので上のように、oTargetPartition の中身が null かどうかを if で判断させて、null だった場合はそんな名前のパーティションは存在しなかったということになるので、ならば新規で作りましょうということで、先ほど出てきた CreatePartition メソッドを使ってユーザが指定した名前のパーティションを作り、あらためて oTargetPartition に代入しています。

ちなみに、あるオブジェクトが存在するのかどうかを判定するには、今回使った if ( xxx == null ) という書き方以外にもいっぱい方法はあります。 今回はたまたまこれを使ったというだけです。 また、VBScript や Python ではこの辺の書き方が違ってくると思います。 この辺はいずれまた別記事で書くかもしれません。 書かないかもしれません。


ってことで、 既に存在していた or 無かったので作った というブロックを越えたので、この時点で確実にその名前のパーティションが存在していることになります。 そしてそのパーティションは oTargetPartition に格納されています。 なので、Partition オブジェクトに対するメソッドのひとつ、AddMember を使って、Selection を Partition にぶち込んでいるのが次の行です。

    oTargetPartition.AddMember( Selection );

Partition.AddMember は、ユーザが Partition に入ることの出来ない変なもの(例えば Curve )を選択していても特にエラーは出しません。無視してくれます。 なのでいちいちエラー処理を書いていません。 でも、ライトなのかそれ以外のオブジェクトなのかという問題がありますよね。 そこら辺の判定を入れたり、パーティションの名前はスクリプトの冒頭を書き換えるのではなく PPG を表示して入力を促すなどのブラッシュアップを行うと、より実用的なツールになると思います。  っていうかそういうスクリプト、もう何年も前に書いたものがあります。 そのうち整理して公開するかもしれません。しないかも知れません。

えーと、後は同じく箱に待避させて最後に箱ごと選択、で終了ですね。 はい終了。 一応実行してみます。


Pass が複数あり、cone と cube を選んでいる状態で実行します。 HagePartition はどの Pass にも存在しません。
S55

.スクリプトを実行すると、
S56
各 Pass に HagePartition が作られ、今まで Background_Partition に入っていた cube と cone は HagePartition に入れられ、そして全ての HagePartition が選択状態になっているのがわかると思います。 わかるでしょ。 わかれよ。




ついでにもう一個書いちまおう。先ほどのスクリプトをちょっと改造しただけです。

if ( Selection.count != 0 )    //    何か選んでないと名前をゲットできないからね。
{
    //    ユーザが最初に選んでいるものから名前をゲット
    PartitionName = Selection(0).name;

    var oAllPasses = ActiveProject.ActiveScene.Passes;
    var oTargetPartitions = XSIFactory.CreateObject( "XSI.Collection" );

    for ( var i=0; i<oAllPasses.count; i++ )
    {
        var oPass = oAllPasses(i);
        var oPartitions = oPass.Partitions;
       
        //    ユーザが選んでいたオブジェクトと同じ名前を持つパーティションをゲット
        var oTargetPartition = oPartitions( PartitionName );
       
        //    null じゃなかったら、存在しているということになるので、oTargetPartitionsコレクションに追加
        if ( oTargetPartition != null )
        {
            oTargetPartitions.Add( oTargetPartition );
        }
    }
    //    最後にコレクションをまるごと選択し、ついでに PPG を表示させておしまい。
    SelectObj( oTargetPartitions );
    InspectObj( oTargetPartitions );
}
else
{
    Logmessage( "何か選んどけやドルァ", siError );
}



何かパーティションをひとつ選んで実行すると、全 Pass の中で同じ名前を持つパーティションを探し、選択状態にして、かつ PPG を表示するというスクリプトです。 すでに Pass がいっぱい存在していて、同じ名前のパーティションがいっぱいあり、そいつらをイッキにハイドにしたいだとかなんとか、そういう時に使えるスクリプトじゃないですかね。


ポイントになる部分だけ説明すると、

  if ( Selection.count != 0 ) 

まずユーザが何も選んでいない状態で実行すると、探すべき名前がゲットできないので、エラーを出して終了にしています。 何か選ばれているかどうかは Selection の Count の数字を調べればわかります。 Selection というコレクションに入っているアイテムの数、つまり count が 0 以外だったら何かが選ばれているということだから次のブロックを実行しろ(正常実行)、  さもなくば( 0 だったら)何も選ばれていないということなので、else 以下を実行しろ(異常終了) という書き方です。 A != B という書き方は、JScript では 「 A と B が一致していない 」 という意味です。 びっくりマークは「否定」を表すんです。 びっくりイコールと書いているので、イコールを否定している、つまりイコールじゃない、という意味になります。



  if ( oTargetPartition != null )

ユーザが選んでいたオブジェクトの名前と一致するパーティションを oTargetPartition に格納したので、その中身が null かどうか、つまりそんな名前のパーティションが存在するのかしないのかを調べています。 ここでもびっくりイコールです。 null じゃなかったら、つまり存在していたということになるので、次の行以降でコレクションに格納しておきます。



    InspectObj( oTargetPartitions );

最後に InspectObj コマンドで PPG を表示させています。 1つのオブジェクトを InspectObj コマンドに食わせた場合は当然そのオブジェクトの PPG が開きますが、今回はコレクションを食わせているので、コレクションの中身全員に対するマルチモードの PPG が自動で立ち上がります。便利です。

一応やってみましょう。
以下のように、複数の Pass がある状態で、Background_Partition をひとつ選択しておきます。
S510

この状態でスクリプトを実行すると、
S511
はい、全ての Pass 以下にある Background_Partition が選択され、かつマルチな PPG も立ち上がっています。 なかなか使えるじゃないか。



ちなみにですが、ユーザが選んでいたものがパーティションかどうかのチェックはしていません。 つまり、Group だろうが Kinematics だろうが、何か1つ以上選んでさえいれば、このスクリプトは走ります。 選んでいたオブジェクトの種類は問わずに名前をゲットして、それに一致する名前のパーティションを探しているということです。 もしユーザが最初に選んでいるオブジェクトをパーティションのみに制限したいのであれば、Selection(0) の type をチェックして Partition じゃなければエラー出して終了するような書き方にすれば良いでしょう。





ふーー。 ひとまずネタが尽きた。今回の攻略は終了します。全然攻略してないけど。 表面だけの気がするけど。 知りません。





あ、もうひとつ注意しなければいけないのは、この記事で書いた Partition がらみのことって、XSI 7 の新機能なんですよね。 XSI 6 の頃は Partition という名前のオブジェクトは存在しておらず、したがって Pass.CreatePartition メソッドも、Pass.Partitions プロパティも、Partition.AddMember メソッドも存在していませんでした。 なので上記のスクリプトは XSI の七ちゃん以降でしか動かないはずです。 昔はしかたなく、コマンドの CreatePartition や MoveToPartition を使っていました。 今はすげえラクになっています。






うむ、しんどい。
ごきげんよう。




.

|

« 取得 その4。 | トップページ | 久しぶりの Beerware。 »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック


この記事へのトラックバック一覧です: 取得 その5。:

« 取得 その4。 | トップページ | 久しぶりの Beerware。 »