« 友愛その7。 | トップページ | マスターコース。 »

2010年7月15日 (木)

友愛その8。

まだ続くの?


今回は新しい話はあんまり出てきません。
Logic の部分の応用例とでも言うか。
応用ってほどでもないですね。
まあいいや。


var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";

oProp.AddParameter2 ( "XRes",  siInt4,  1280 );
oProp.AddParameter2 ( "YRes",  siInt4,  720 );
oProp.AddParameter2 ( "Aspect",  siDouble,  1280/720);

var oLayout, oItem;
oLayout = oProp.PPGLayout;

oItem = oLayout.AddItem ( "XRes", "X" );
oItem = oLayout.AddItem ( "YRes", "Y" );
oItem = oLayout.AddItem ( "Aspect", "Aspect Ratio" );

//    Logic
oLayout.Language = "JScript";
oLayout.Logic =    XRes_OnChanged.toString( )+
                     YRes_OnChanged.toString( )+
                     Aspect_OnChanged.toString( );

function XRes_OnChanged( )
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

function YRes_OnChanged( )
{
    PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
}

function Aspect_OnChanged( )
{
    XRes_OnChanged( );
}

InspectObj ( oProp );


よくある、解像度の X と Y と Aspect の自動アップデートですね。

これを実行すると
Ui_81
こうなります。 
X や Y の値をテキトーに変えてみて下さいまし。 X の値が変更されると Y が自動で更新され、その逆も同じく、という挙動になっています。あまり説明するまでもない話ではありますが一応説明すると、


function XRes_OnChanged( ) //   X の値が変更されたら Y を変更
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

function YRes_OnChanged( ) //   Y の値が変更されたら X を変更
{
    PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
}

function Aspect_OnChanged( )  //  Aspect の値が変更されたら、Xが変更された時と同じ挙動
{
    XRes_OnChanged( );
}


こういうことなんですがね。コメント付けただけですが。
さらに冗長な説明になりますが、画像のアスペクト比というのはヨコ÷タテで出てくる比率ですので、基本的に、

 Aspect =  X / Y

ですわね。 なので、

 Y = X / Apect
 X = Y * Aspect

という式が成り立ちますね。その計算を Logic の function の中でやっているだけです。 Aspect が変更された時は X を基準にして Y を変えるという仕様にしていますが、まあこの辺の仕様はツールの目的によりけりでしょう。 で、その仕様に従えば、Aspect が変更された時の挙動は X が変更された時の挙動と全く同じわけです。なので、Aspect_OnChanged( ) の中から XRes_OnChanged( ) を呼び出しています。このように、ある function から他所の function を呼び出すことももちろん可能で、何度も同じ処理を記述して冗長なコードになるのを避けるためにこうしたりもします。

※でもちょっとしたことなら、敢えて他所の function を呼び出さずに冗長でもいいから同じ処理をそこに書いちゃった方が、後からコードを見直した時に処理が把握しやすかったりするんですよね。






次。 現在の仕様だと X や Y を変えた場合は強制的に他方の値が更新されてしまいます。 X と Y の値はもう決まっているがアスペクトは判っていない、という場合には不便です(ま、X と Y が決まっていればアスペクトは自動的に X / Y なんですが)。 Y を自由に入力したいのに X を入れた瞬間に Y の値は更新されてしまい、あわてて Y の値を入れ直すと X が変わってしまう、という無限ループになるわけですからね。 アスペクトを計算してあらかじめ入力しておかなければいけないことになり、不便ですね。

なので、アスペクトのロックをオンオフする機能を付けます。 

 アスペクトが現在の比率でロックされている場合 → これまでと同じ挙動
 アスペクトのロックが解除されている場合 →  X や Y の値の変更によって Aspect が変わる

という仕様にしてみます。


var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";

oProp.AddParameter2 ( "XRes",  siInt4,  1280 );
oProp.AddParameter2 ( "YRes",  siInt4,  720 );
oProp.AddParameter2 ( "Aspect",  siDouble,  1280/720);
oProp.AddParameter2 ( "LockAspect",  siBool,  true);

var oLayout, oItem;
oLayout = oProp.PPGLayout;

oLayout.AddRow( );
    oItem = oLayout.AddItem ( "XRes", "X" );
    oItem = oLayout.AddItem ( "YRes", "Y" );
oLayout.EndRow( );

oLayout.AddRow( );
    oItem = oLayout.AddItem ( "Aspect", "Aspect Ratio" );
    oItem = oLayout.AddItem ( "LockAspect", "Lock Aspect Ratio" );
oLayout.EndRow( );


//    Logic
oLayout.Language = "JScript";
oLayout.Logic =    XRes_OnChanged.toString( )+
                     YRes_OnChanged.toString( )+
                     Aspect_OnChanged.toString( );

function XRes_OnChanged( )
{
    if  ( PPG.LockAspect.value )
    {
        PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
    }
    else
    {
        PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
    }

}

function YRes_OnChanged( )
{
    if  ( PPG.LockAspect.value )
    {
        PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
    }
    else
    {
        PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
    }

}

function Aspect_OnChanged( )
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

InspectObj ( oProp );



赤字が変更もしくは追加部分です。 ついでに AddRow も追加してますが、これはまあ UI の見た目の話なのでどうでもいいです。

実行すると、
Ui_82
こうなります。
Lock Aspect Ratioオンの時は X が変われば Y も変わりその逆も同じく、というこれまでと全く同じ挙動になると思います。

一方、Lock Aspect Ratio がオフの時は、X や Y を変更すると、他方が変わるのではなく、Aspect が変更されるという挙動になっています。 前のバージョンでは Aspect ありきだったのが、このバージョンではロックを解除したときに限り、X と Y の値ありきで Aspect がそれに従うという状態になりました。

さらにさらに冗長な説明になりますが、 何が起こっているのかを X の値が変わった時を例に説明すると、

function XRes_OnChanged( )
{
    if  ( PPG.LockAspect.value )
    {
        PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
    }
    else
    {
        PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
    }

}



これを日本語で言うと、


X の値が変わったときは以下を実行しろ
{
  もしも  LockAspectパラメータがオンなら、
  {
     Y の値は、 X / Aspect にしろ
  }
  さもなくば(つまりオフなら)
  {
    Aspect の値は、X / Y にしろ
  }

}

こういうことになりますね。


全くの蛇足ですが、if の行の

  if  ( PPG.LockAspect.value )

は、

  if  ( PPG.LockAspect.value == true )

と同じ意味になります。 true かどうかを調べるだけであれば、こういう省略した書き方ができるみたいです。 と言うよりも、 == true などの条件を省略したときは、常に結果が true かどうかを判別するという挙動になる、と言った方がいいかもしれません。 よくわからんまま、いつもこうしています。


これまた蛇足的になりますが、上のコードをよく見てみると、X が変更された時、Y が変更された時の両方で、else 以降は全く同じコードが書かれています。 同じコードなのであれば、1つの function にまとめてもいいわけで、

//    Logic
oLayout.Language = "JScript";
oLayout.Logic =    XRes_OnChanged.toString( )+
                     YRes_OnChanged.toString( )+
                     Aspect_OnChanged.toString( )+
                     AspectUpdate.toString( );

function XRes_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
    }
    else
    {
       AspectUpdate( );
    }
}

function YRes_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}

function Aspect_OnChanged( )
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

function AspectUpdate( )
{
    PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
}

InspectObj ( oProp );


このようにするのもアリかと思います。 AspectUpdate という function を新設して、else だった場合は両方ともこの AspectUpdate に飛ばしています。
ただ、前述のように、たとえ内容がダブっていても他の function に敢えて飛ばさない方が分かり易い場合も多く、微妙なところです。





次。
XSI の Scene Render Options を真似して、Lock Aspect Ratio がオンの時は Aspect の値はいじれないようにする、という仕様に変更してみます。

Logic 以降の部分に、以下のように追加します。

//    Logic
oLayout.Language = "JScript";
oLayout.Logic =    XRes_OnChanged.toString( )+
                     YRes_OnChanged.toString( )+
                     Aspect_OnChanged.toString( )+
                     AspectUpdate.toString( )+
                     LockAspect_OnChanged.toString( );

function XRes_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}
function YRes_OnChanged( )
{
    if ( PPG.LockAspect.value  )
    {
        PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}

function Aspect_OnChanged( )
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

function AspectUpdate( )
{
    PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
}

function LockAspect_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.Aspect.ReadOnly = true;
    }
    else
    {
        PPG.Aspect.ReadOnly = false;
    }
}


InspectObj ( oProp );



LockAspect_OnChanged( ) が追加されました。チェックボックスのオンオフ変更を検知して、オンの時は Aspect パラメータの ReadOnly 属性をオンにする = つまりリードオンリーにする = つまり値を変更できなくする、 オフだった場合は ReadOnly 属性をオフにする = つまり値を変更できるようにする という挙動になります。

こいつを実行して、チェックボックスを数回オンオフ繰り返してみて下さい。
Ui_83
このように、 Lock Aspect Ratio のチェックがオンの時は Aspect がグレー表示になり、値が変更できなくなったのがわかると思います。


しかし。
現在のコードでは、オンオフ変更をした後は正しくグレー表示になるものの、PPG が立ち上がった時点ではチェックがオンであるにも関わらず Aspect はグレーになっていないという、本来の仕様に反した状態になってしまいます。

↓この状態ね。 PPG起動直後です。
Ui_82

これは、ReadOnly 属性をいじるための function LockAspect_OnChanged( ) という function が、LockAspect のチェックオンオフが変更された時のみ呼び出される、という書き方になっているからです。 PPG を起動した時点では値はまだ変更されていないため、この function は呼び出されず、結果、 Aspect の ReadOnly 属性はいじられないまま、という状態になっているのが原因だということです。


これを解消するにはいくつか方法があると思うのですが、ここでは、PPG が起動されたらまずは真っ先に Lock Aspect Ratio の状態を調べて Aspect の ReadOnly 属性を更新する、という方法を採ります。


//    Logic
oLayout.Language = "JScript";
oLayout.Logic =    XRes_OnChanged.toString( )+
                     YRes_OnChanged.toString( )+
                     Aspect_OnChanged.toString( )+
                     AspectUpdate.toString( )+
                     LockAspect_OnChanged.toString( )+
                     OnInit.toString( );

function XRes_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}

function YRes_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}

function Aspect_OnChanged( )
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

function AspectUpdate( )
{
    PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
}

function LockAspect_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.Aspect.ReadOnly = true;
    }
    else
    {
        PPG.Aspect.ReadOnly = false;
    }
}

function OnInit( )
{
    LockAspect_OnChanged( );
}


InspectObj ( oProp );



OnInit ( ) という function が追加されました。
この OnInit というのは特別な function で、PPG が起動されたとき(=イニシャライズされた時)に呼び出されるものです。これはもう、こういうものだと覚えてしまうしかない。
で、その中身は LockAspect_OnChanged( ) と書いてあるので、実際にはユーザが LockAspect の値を変更していないにも関わらず LockAspect_OnChanged( ) が呼び出され、ReadOnly 状態を更新しています。

つまり、
PPGの起動を検出 → OnInit に飛ばされ、→ OnInit から LockAspect_OnChanged( ) に飛ばされる

という順番で処理されています。これでめでたく、起動直後から Aspect のグレー表示が正しい状態になったはずです。





ごきげんよう。




.

|

« 友愛その7。 | トップページ | マスターコース。 »

コメント

コメントを書く



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




トラックバック


この記事へのトラックバック一覧です: 友愛その8。:

« 友愛その7。 | トップページ | マスターコース。 »