標準機能で直線上に整列はできますが、自由に描いたパス上に整列はできません。これをスクリプトで実現します。
[概要]
全選択レイヤーをパスに沿って配置するスクリプト。
[使い方]
- 平面レイヤーを作成しマスクでパスを描く
- 上記パスのレイヤーを含め配置したいレイヤーを全選択
- スクリプトを適用
[オプション]
- 続けて開く「進行方向へ自動回転」の有無をアラートで選択可能
- レイヤーの天井がパスの進行方向へ向くように角度を設定可能
[解説]
var sel=app.project.activeItem.selectedLayers;
function f_posPathAlign(){
////////////////////////pathL抽出
for(i=0;i<sel.length;i++){//パス感知
if( isSolidLayer(sel[i]) ){
var pathL=sel[i];
var posM=pathL.position.value;
sel.splice(i, 1);
break;
}
}
////////////////////////pathLマスクをモーションパスに
try {
var shape = pathL.mask(1).maskPath.value;
var verts = shape.vertices;//頂点[x,y],[x,y]配列
var outTan = shape.outTangents;
var inTan = shape.inTangents;
var numVerts = verts.length;//頂点数
if (shape.closed) {
verts.push(verts[0]);
inTan.push(inTan[0]);
outTan.push(outTan[0]);
numVerts++;
}
for(i=0;i<numVerts;i++){
inTan[i][2] = outTan[i][2] = 0;
verts[i][2]=0;
}
for(i=0;i<numVerts;i++){//キーフレーム準備
pathL.position.setValueAtTime(i, verts[i]);
}
for (var idx, i=0; i < numVerts; i++){
idx = pathL.position.nearestKeyIndex(i);
pathL.position.setSpatialTangentsAtKey(idx, inTan[i], outTan[i]);
}
for (var idx, i=0; i < numVerts-1; i++){
idx = pathL.position.nearestKeyIndex(i);
pathL.position.setRovingAtKey(idx, true);
}
//////////////////////モーションパス上に子アイテム並べ
var firstKeyTime=pathL.position.keyTime(1);
var lastKeyTime=pathL.position.keyTime(pathL.position.numKeys);
var keyDuration=lastKeyTime-firstKeyTime;
var qty=sel.length-1;
var newV=[],newVR=[];
for(i=0;i<sel.length;i++){
newV.push(pathL.position.valueAtTime( (keyDuration/(sel.length-1))*(i) +firstKeyTime,true));
newVR.push(pathL.position.valueAtTime( ((keyDuration/(sel.length-1))*(i) +firstKeyTime)+(1/(30)),true));
sel[i].position.setValue(newV[i]);
}
var autoAngle=confirm("進行方向へ自動回転");//自動角度
if(autoAngle==true){
for(i=0;i<sel.length-1;i++){
var target=newVR[i]-newV[i];
var angle=Math.atan2(target[1],target[0]);
sel[i].zRotation.setValue(angle * 180 / Math.PI+90);
}
}
sel[sel.length-1].zRotation.setValue(sel[sel.length-2].zRotation.value);
for(var i=pathL.position.numKeys;i>0;i--){
pathL.position.removeKey(i);
}
pathL.position.setValue(posM);
} catch(e) {
alert("マスクパスが見つかりません");
}
}//function posPathAlign
function isSolidLayer(theLayer){
if(theLayer instanceof AVLayer){
if(theLayer.adjustmentLayer == false && theLayer.nullLayer == false && theLayer.source.mainSource instanceof SolidSource){
return true;
}
}
return false;
}
app.beginUndoGroup("masktoPosPathAlign");
f_posPathAlign();
app.endUndoGroup();
内部処理としてはマスクを一旦位置のモーションパス(キーフレーム)に変換して、レイヤーの数で割った均等なタイミング(timeAtValue)時の位置に配置していきます。
必要なら進行方向に向かって自動回転させます。
自動回転は「次のレイヤー位置」だとパスよりデコボコするため
カーブに忠実になるよう「モーションパス時の1フレーム後」の方向へ回転させています。
※整列順序は「レイヤー選択順」のため、自身のレイヤーの重ね方を確認し、パスの方向へ沿わせたいレイヤーを順に選択するよう注意します。
※整列の基準となるパスは、平面レイヤーのマスクパスに限られます。また、コンポジションと同じサイズの平面レイヤーでないと、整列したレイヤーの位置ズレを起こしますので、コンポジションとサイズの異なる平面レイヤーをプロジェクトウィンドウから配置することは推奨しません。
整列したレイヤーを目的の位置までスライドさせれば済みますが、既存の平面レイヤーとサイズの異なるコンポジションでこのスクリプトを使用したい場合、マスクのためだけでも平面レイヤーを新規作成してスクリプト適用を推奨します。
この記事へのコメントはありません。