AEスクリプトのプロパティは、追加、移動、削除、複製などをすると、事前に変数に入れてあったプロパティのリンクが外れてしまいます。つまり、その変数にプロパティが入っているつもりで作業するとエラーとなることがあるんですね。
そんなときの対処法の一つをご紹介します!
■mGetSelPptyAdrs
選択プロパティから、その元レイヤー~選択プロパティまでの各階層のプロパティ番号が入った配列を得る関数。
//選択プロパティから、選択レイヤー~選択プロパティのまでの階層別インデックス配列を得る。
//選択レイヤーについてのみ、番号や名前ではなくObjであることに注意。
function mGetSelPptyAdrs(aSp) {
var mSp = aSp;
var mAdrs = [];
//親プロパティ情報を上から入れていく。
for (var i = mSp.propertyDepth; i >= 1; i--) {
var mPropTmp = mSp.propertyGroup(i);
//一番手前のものはレイヤーなので、プロパティとしては呼べないのでレイヤーを直接入れる。
if( i === mSp.propertyDepth ){
mAdrs.push(mSp.propertyGroup(mSp.propertyDepth));
}else{
mAdrs.push(mPropTmp.propertyIndex);
}
}
//自身の情報を入れる。
mAdrs.push(mSp.propertyIndex);
return mAdrs;
}
■mGetSelPptyAdrsName
上記関数の名前版。各階層プロパティの番号ではなく名前を得る。
//選択プロパティから、選択レイヤー~選択プロパティのまでの階層別名前配列を得る。
//選択レイヤーについてのみ、番号や名前ではなくObjであることに注意。
function mGetSelPptyAdrsName(aSp) {
var mSp = aSp;
var mAdrs = [];
//親プロパティ情報を上から入れていく。
for (var i = mSp.propertyDepth; i >= 1; i--) {
var mPropTmp = mSp.propertyGroup(i);
//一番手前のものはレイヤーなので、プロパティとしては呼べないのでレイヤーを直接入れる。
if( i === mSp.propertyDepth ){
mAdrs.push(mSp.propertyGroup(mSp.propertyDepth));
}else{
mAdrs.push(mPropTmp.name);
}
}
//自身の情報を入れる。
mAdrs.push(mSp.name);
return mAdrs;
}
■mGetPptyFromAdrs
mGetSelPptyAdrsで得た配列から、選択プロパティを割り出す関数。
//階層別idx配列から、該当Pptyを得る(引数2は得る予定の最新深層Pptyからの上層数)。
//選択プロパティを得たいならゼロを、選択プロパティの親を得たいなら1を入れればよい。
//その他、処理途中でどこかの名前が変わったならばそれを明示的に入れ替えた配列を入れれば作動する。
function mGetPptyFromAdrs( aAdrs , aNum ){
var mAdrs = aAdrs;
//まずはレイヤーが直接入っている0番目を参照する。
var mSpfydPpty = aAdrs[ 0 ];
//階層を下って、選択pptyを変えていく。
for(var i = 1 ; i < mAdrs.length - aNum ; i++ ){
mSpfydPpty = mSpfydPpty( mAdrs[ i ] );
}
return mSpfydPpty;
}
*Adrsという変数名は『アドレス』と読むつもりで作ってあります。エクスプレッションと同じで、〇〇.××といった具合にドットでつないでいくとプロパティにたどり着くのが『住所みたいだから』くらいの意味です。が、C系の言語ですとアドレスはメモリ関連の用語らしいので、C系の言語に慣れていて紛らわしい方はAdrsを他の名前に変えて使って下さい。
使い方
スクリプトではたいがい、最初に
var mAi = app.project.activeItem;
var mSl = mAi.selectedLayers[0];
var mSp = mSl.selectedProperties[0];
などで使うプロパティを得ますが、そこに
var mAdrs = mGetSelPptyAdrs( mSp );
などと加えて、インデックスor名前配列を得ておいてください。
この場合はmSpがmSl.selectedProperties[0]ではなくundefinedになってしまう、ということですね。
mSp =mGetPptyFromAdrs( mAdrs , 0 );
とすれば、mAdrs配列に入っている番地(というか番号か名前)をたどって再び同じプロパティを定義することができるというわけです!
使いどころ
プロパティを扱うスクリプトを書いていると、最後の並び順整理や削除の段階でこのような「実は変数が使えなくなっていた」問題が起こりがちです。そのようなときにお使いください。
別の対処法として、最初に「明らかにユーザーが名付けないであろう名前を付けておき、それを探す」というやり方があります。しかしこれだと、途中でエラーが出た場合、その変な名前を元に戻す処理前に止まってしまうことがあるので、この『階層別インデックスor名前配列』を使うのもアリかと思います!
mGetPptyFromAdrsの引数2をうまく使えば、選択プロパティを得るだけでなく「パスを選択している状態で、その一個上のプロパティを得て、そこにパス関連の追加プロパティを入れる」などもできます。
*追記
プロパティの種類(propertyType)を調べるうち「名前を変えられるものは番号も変わり得る、名前を変えられないものは番号が変わらない」ということがわかりました。名前と番号、どちらを参照するか場合分けする意味がないですね…。
なので、インデックス参照版と名前参照版の2つに分けました。それぞれ一長一短ありまして、
名前参照だと「処理途中でプロパティの順番がかわってもリンクが外れない」ですが「同じ名前が同じ階層にあったとき、一番上のものを参照してしまう」という欠点があります。
インデックス参照だと「同じ名前が同じ階層にあったときでも正確に参照できる」が「処理途中でプロパティの順番が変わるとリンクが外れる」という欠点ですね。
ちょうど長所と短所が逆です。処理内容によってどちらかをお使いください。インデックス参照のほうが、途中の処理内容さえわかれば手動でインデックスの入れ替えをすればいいので確実です。
解説
原理は非常に簡単で、
mGetSelPptyAdrsについては
空の配列を作って、そこに手前から順に「レイヤー、その下のプロパティの番号、その下の…」を入れているだけです。
mGetPptyFromAdrsについては
〇〇(××)という形で「○○というプロパティの中の××というプロパティ」と表せるので、
作っておいた配列に入っている番号or名前(一番最初のみ、レイヤー名やレイヤー番号ではなくレイヤーそのもの)をたどって該当プロパティを得る、ということです。