DTPab

印刷やデザイン、アドビ製アプリやスクリプトなど、雑多な技術ブログ

Illustratorのスクリプト勉強中②

前回までのあらすじ

ちゃんと続きましたw
選択しているオブジェクトがどんな順番でDocument.selectionプロパティに格納されるのかを調べました。
結果的には、レイヤーパネルに表示されている上のオブジェクトから順番にselection[0]selection[1]…に格納されるようです。

前回のコード

var myDoc = app.activeDocument; //最前面のドキュメント
var mySels = myDoc.selection; //ここらへんはInDesignと同じ
var getColorValue = function (color){
    return [color.cyan, color.magenta, color.yellow, color.black];
}
$.writeln(getColorValue (mySels[0].fillColor));

冒頭の2行めから、また新たに処理を加えていきます。

今日の目標

この前の3つの長方形を、どこか一辺に揃えて整列させてみようと思います。方針は2つです。

  1. 整列パネルの機能で整列
  2. 整列したい座標値を決め、オブジェクトの座標にそれを代入して移動させて整列

ですが、10分ほど1について調べたもののやり方が見つかりませんでした。仕方なく、地道に2で実装しようと思います。こういうことがあるのですよ、Illustratorスクリプトには! アプリケーションの標準機能がスクリプトから(そのままでは)叩けない。いや、僕が見つけられなかっただけかもしれないので、ご存知の方はぜひ教えてください…。

座標を取得する

さて、選択しているオブジェクトそれぞれの座標を取得していきます(というか、していきたいのです)。InDesignスクリプトと同じであれば、Object.geometricBoundsやObject.visibleBoundsが使えるはずです。
でもせっかくなので、ここで一旦スクリプティングリファレンス*1を見てみます。う〜ん、PageItemの項でいいのかな?(Rectangleの項はなかった)
PageItemの項、PageItem propertiesから、座標を表していそうなものをピックアップします。

  • controlBounds
  • geometricBounds
  • visibleBounds

というわけで3つのBoundsたちが見つかりました。説明を読む限り、geometricBoundsはおそらくInDesignと同じで線幅を含まない座標値っぽい。一方のcontrolBoundsは線幅を含んだ座標値。そしてvisibleBoundsは「including stroke width of any objects in the illustration」だそうで、要するに線幅だけでなくその他何でも全部入りの座標値って感じですかね。
おっと、これ3つとも「Read-only」プロパティですね。取得はできるけど変更はできないプロパティだ。
ということは、オブジェクトを移動するためには別のメソッド(例えばObject.move()とか?)があるのかな。それはまたあとで調べることにします。
で、せっかくなので3つのBoundsの違いを明確にしておきたいので寄り道します。なんたってIllustratorスクリプト勉強中って謳ってるので^^;;

3つのBounds

比較検証のために雑ながらオブジェクト(アピアランスで変形かけたオブジェクトをグループ化したもの)を用意しました。これでうまく違いが説明できるといいな…w

f:id:uske_S:20180509003154p:plain

そしてそれぞれのBoundsを取得するコードがこちら。

var myDoc = app.activeDocument;
var mySel = myDoc.selection[0];
$.writeln("g: \r"+mySel.geometricBounds.join("\r"));
$.writeln("---\rc: \r"+mySel.controlBounds.join("\r"));
$.writeln("---\rv: \r"+mySel.visibleBounds.join("\r"));

で、違いがあるか見てみます。コンソールの結果はこちら。

f:id:uske_S:20180509003249p:plain

あれ?
パネルに表示されている数字がぜんぜん違う…。もしや単位が違う?(普段はmmで作業している)ということでpointにしてみた。

f:id:uske_S:20180509003335p:plain

なんだよ〜座標値はpointなのか〜!
InDesignには単位系をコントロールする魔法があるけど、Illustratorはどうなんだろう。とりあえず特に困るわけではないのでこのまま行きますか…。

さぁ気を取り直して数値をよく見てみると、geometricBoundsは明らかにほか2つと違いますが、controlとvisibleは違いが微妙ですね。違いを分かりやすくするために視覚化してみます。

f:id:uske_S:20180509003344p:plain

なるほど、こういうことか。
geometricBoundsは想像通り、まず線幅を含まない。そしてアピアランスなどに惑わされず、そのオブジェクト本来の座標を取得する。
controlBoundsは線幅を含む。さらにオブジェクト本来の座標を取得し、なおかつアピアランスなどの効果も含んだ座標を取得する。
visibleBoundsがいちばん分かりやすく、アピアランスなどの効果で結果的に見えているものの座標を取得する。
さっきvisibleBoundsが全部入りって書いたけど、見えないものは除外してくれている分、全部入りはどちらかと言うとcontrolBoundsだった。
というわけで3つのBoundsはすっきり理解できたぞ!

オブジェクトを整列しよう

さて本題に戻って、座標の取得にはとりあえずvisibleBoundsを使うことにします。

  1. オブジェクトの座標を取得
  2. 0番目のオブジェクトに合わせて天に整列

というスクリプトを作っていきますが、座標の取得はできました。次は整列=移動ですね。またスクリプトリファレンスに戻ってみますか。今度はpageItemの項のmethodsを参照します。

  • bringInPerspective
  • resize
  • rotate
  • transform
  • translate
  • zOrder

あれ………なんかmoveとかそんなメソッドがあると期待してたんだけど、ないのか…。「Reposition art object(s).」ってあるので、移動はtranslateメソッドで合ってるのかな?
物は試しで、とりあえずやってみますか!
引数を見るとdeltaX, deltaYとかあるので、どうやら移動量を指定するみたい。

var myDoc = app.activeDocument;
var mySel = myDoc.selection[0];
mySel.translate(10,10);

f:id:uske_S:20180509003906g:plain

おお!動いたぞ!
右上に動いたということは、右がX方向+で、上がY方向+っぽい。

うーん、でももうちょっと楽して動かしたいな…(移動距離じゃなくて、座標を渡したらそこにピタッと移動みたいな)。引数を見てると「transformObjects」って第三引数があるけど、オプティマス・プライムとかになったり? 個人的にはバンブルビーが好きなんだけど。
それはさておき、さすがにスクリプトリファレンスでは情報がなさすぎるのでオブジェクトモデルビューアを見ます。

f:id:uske_S:20180509003922p:plain

なーるほど、これは本当に「transform=変形」を表していたのですね。

f:id:uske_S:20180509003938p:plain

PageItem.translateメソッドの第三引数「transformObject」は、この移動メニューの中の「オブジェクトの変形」をオンにするかオフにするか、という意味のようですね。同様に第四引数は「塗りのパターン」、第五引数が「塗りのグラデーションパターン」、第六引数が「線のパターン」ということか。
ということは基本的に全部オンでもいいような気がする。シチュエーションというかケースバイケースでしょうけど。

そしてここで気づく。
オブジェクトモデルビューアで「PageItem.move」メソッドを検索してみたら、ちゃんとあった………。悔しいからスクリプトリファレンスで「move」を検索してみたけど、PageItem.moveメソッドがない。まぁtranslateメソッドとかその他もろもろ勉強になったから良しとしよう。

で、結局どうやって移動するの

ちょっと懲りました。なので、プロパティやメソッドを探すときは、一旦丁寧に探す必要がありそうです。探す手段としてたぶん以下の通り。

さて、まずは改めましてオブジェクトモデルビューアでmoveメソッドの引数を見てみます。

f:id:uske_S:20180509004011p:plain

えぇ…全然わかんないんだけど…。
戻り値で移動後のPageItemを再取得できるのはわかるんだけど、relativeObjectとinsertionLocation? これ説明省きすぎでしょ。ElementPlacementオブジェクトってなんだよ…。クリックしても参照先出てこないし。
しかたないのでググる。「illustrator script pageitem.move how to use」で検索。

で見つけたのがこのスレッド。こういうときはUSフォーラムに限る。

forums.adobe.com

なんと、メソッドではなく、PageItem.positionプロパティに2要素の配列を入れるだけなのか! geometricBoundsとかをRead-onlyにしておきながら、positionとかいうプロパティを用意しておく感覚がまったく共感できないInDesignスクリプト民です。InDesignではgeometricBoundsやvisibleBoundsに4要素の配列を渡すことで、オブジェクトの位置を移動(したように見せることが)できるのです。

散々調べまわって、どうやらこのpositionというプロパティに値を代入するのが早いみたいですね。おとなしくこれを利用することにします。

f:id:uske_S:20180509004216g:plain

これよ。この感じよ。待ってたのはこの手軽さですよ。オブジェクトの左上の座標を渡せばいいってことね。

0番目のオブジェクトにあわせて整列する

やっとひと通りのやり方がわかりました。座標の取得にはvisibleBoundsを使い、移動にはpositionを使います。

var myDoc = app.activeDocument;
var mySels = myDoc.selection; //ここまでは前回と同じ

var targetY = mySels[0].visibleBounds[1]; //0番目のオブジェクトの天のY座標

for (var i=1; i<mySels.length; i++){
    mySels[i].position = [mySels[i].visibleBounds[0], targetY];
}

これで選択範囲の0番目のオブジェクトの天に合わせてオブジェクトを整列することができました。

あーーーーーーしんどかった!
続きます!

*1:ここにある中から、「Adobe Illustrator CC 2017 Reference: JavaScript」を参照しました。