DTPab

DTPにまつわるあれこれ

オブジェクトの色をランダムに塗るIllustratorのスクリプトを作ってみよう

久しぶりにIllustratorのスクリプト記事です。今回は標題のスクリプトを作る過程から解説しようと思います。

はじめに

どんなスクリプトを作るの?

f:id:uske_S:20201111012253g:plain
実行イメージ

こんな感じのスクリプトを作ります。

開発環境

  • macOS Mojave(10.14.6)
  • Illustrator 2020(24.3)
  • Visual Studio Code(1.51.0)

この記事で扱わないこと

  • VSCodeのデバッグ設定など
  • IllustratorのスクリプトAPI(DOM)の体系的な説明(部分的な説明にとどめています)
  • JavaScript(ExtendScript)の基本構文などの体系的な説明(部分的な説明にとどめています)

開発しよう

フローチャート

下図を目標にスクリプトを組み立てていきます*1

https://mermaid.ink/svg/eyJjb2RlIjoiZ3JhcGggVERcbiAgQVvjg4njgq3jg6Xjg6Hjg7Pjg4jjgYzplovjgYvjgozjgabjgYTjgovjgYtdIC0tPnxOb3wgWCjntYLkuoYpXG4gIEEgLS0-IHxZZXN8IEIo44Kq44OW44K444Kn44Kv44OI44KS6YG45oqe44GX44Gm44GE44KL44GLKVxuICBCIC0tPiB8Tm98IFhcbiAgQiAtLT4gfFllc3wgQyjoibLjgpLjg6njg7Pjg4Djg6DjgavnlJ_miJApXG4gIEMgLS0-IEQo5aGX44KK44Gr6Imy44KS6YGp55SoKVxuICBEIC0tPiB85qyh44Gu44Kq44OW44K444Kn44Kv44OI44GM44GC44KLfCBDXG4gIEQgLS0-IHzmrKHjga7jgqrjg5bjgrjjgqfjgq_jg4jjgYzjgarjgYR8IFgiLCJtZXJtYWlkIjp7InRoZW1lIjoiZm9yZXN0IiwidGhlbWVWYXJpYWJsZXMiOnsiYmFja2dyb3VuZCI6IndoaXRlIiwicHJpbWFyeUNvbG9yIjoiI2NkZTQ5OCIsInNlY29uZGFyeUNvbG9yIjoiI2NkZmZiMiIsIm1haW5Ca2ciOiIjY2RlNDk4Iiwic2Vjb25kQmtnIjoiI2NkZmZiMiIsImxpbmVDb2xvciI6IiMwMDAwMDAiLCJib3JkZXIxIjoiIzEzNTQwYyIsImJvcmRlcjIiOiIjNmVhYTQ5IiwiYXJyb3doZWFkQ29sb3IiOiJncmVlbiIsImZvbnRGYW1pbHkiOiJcInRyZWJ1Y2hldCBtc1wiLCB2ZXJkYW5hLCBhcmlhbCIsImZvbnRTaXplIjoiMTZweCIsInRlcnRpYXJ5Q29sb3IiOiJoc2woNzguMTU3ODk0NzM2OCwgNTguNDYxNTM4NDYxNSUsIDg0LjUwOTgwMzkyMTYlKSIsInByaW1hcnlCb3JkZXJDb2xvciI6ImhzbCg3OC4xNTc4OTQ3MzY4LCAxOC40NjE1Mzg0NjE1JSwgNjQuNTA5ODAzOTIxNiUpIiwic2Vjb25kYXJ5Qm9yZGVyQ29sb3IiOiJoc2woOTguOTYxMDM4OTYxLCA2MCUsIDc0LjkwMTk2MDc4NDMlKSIsInRlcnRpYXJ5Qm9yZGVyQ29sb3IiOiJoc2woNzguMTU3ODk0NzM2OCwgMTguNDYxNTM4NDYxNSUsIDc0LjUwOTgwMzkyMTYlKSIsInByaW1hcnlUZXh0Q29sb3IiOiIjMzIxYjY3Iiwic2Vjb25kYXJ5VGV4dENvbG9yIjoiIzMyMDA0ZCIsInRlcnRpYXJ5VGV4dENvbG9yIjoiIzMyMWI2NyIsInRleHRDb2xvciI6IiMwMDAwMDAiLCJub2RlQmtnIjoiI2NkZTQ5OCIsIm5vZGVCb3JkZXIiOiIjMTM1NDBjIiwiY2x1c3RlckJrZyI6IiNjZGZmYjIiLCJjbHVzdGVyQm9yZGVyIjoiIzZlYWE0OSIsImRlZmF1bHRMaW5rQ29sb3IiOiIjMDAwMDAwIiwidGl0bGVDb2xvciI6IiMzMzMiLCJlZGdlTGFiZWxCYWNrZ3JvdW5kIjoiI2U4ZThlOCIsImFjdG9yQm9yZGVyIjoiaHNsKDc4LjE1Nzg5NDczNjgsIDU4LjQ2MTUzODQ2MTUlLCA1NC41MDk4MDM5MjE2JSkiLCJhY3RvckJrZyI6IiNjZGU0OTgiLCJhY3RvclRleHRDb2xvciI6ImJsYWNrIiwiYWN0b3JMaW5lQ29sb3IiOiJncmV5Iiwic2lnbmFsQ29sb3IiOiIjMzMzIiwic2lnbmFsVGV4dENvbG9yIjoiIzMzMyIsImxhYmVsQm94QmtnQ29sb3IiOiIjY2RlNDk4IiwibGFiZWxCb3hCb3JkZXJDb2xvciI6IiMzMjY5MzIiLCJsYWJlbFRleHRDb2xvciI6ImJsYWNrIiwibG9vcFRleHRDb2xvciI6ImJsYWNrIiwibm90ZUJvcmRlckNvbG9yIjoiIzZlYWE0OSIsIm5vdGVCa2dDb2xvciI6IiNmZmY1YWQiLCJub3RlVGV4dENvbG9yIjoiYmxhY2siLCJhY3RpdmF0aW9uQm9yZGVyQ29sb3IiOiIjNjY2IiwiYWN0aXZhdGlvbkJrZ0NvbG9yIjoiI2Y0ZjRmNCIsInNlcXVlbmNlTnVtYmVyQ29sb3IiOiJ3aGl0ZSIsInNlY3Rpb25Ca2dDb2xvciI6IiM2ZWFhNDkiLCJhbHRTZWN0aW9uQmtnQ29sb3IiOiJ3aGl0ZSIsInNlY3Rpb25Ca2dDb2xvcjIiOiIjNmVhYTQ5IiwidGFza0JvcmRlckNvbG9yIjoiIzEzNTQwYyIsInRhc2tCa2dDb2xvciI6IiM0ODdlM2EiLCJ0YXNrVGV4dExpZ2h0Q29sb3IiOiJ3aGl0ZSIsInRhc2tUZXh0Q29sb3IiOiJ3aGl0ZSIsInRhc2tUZXh0RGFya0NvbG9yIjoiYmxhY2siLCJ0YXNrVGV4dE91dHNpZGVDb2xvciI6ImJsYWNrIiwidGFza1RleHRDbGlja2FibGVDb2xvciI6IiMwMDMxNjMiLCJhY3RpdmVUYXNrQm9yZGVyQ29sb3IiOiIjMTM1NDBjIiwiYWN0aXZlVGFza0JrZ0NvbG9yIjoiI2NkZTQ5OCIsImdyaWRDb2xvciI6ImxpZ2h0Z3JleSIsImRvbmVUYXNrQmtnQ29sb3IiOiJsaWdodGdyZXkiLCJkb25lVGFza0JvcmRlckNvbG9yIjoiZ3JleSIsImNyaXRCb3JkZXJDb2xvciI6IiNmZjg4ODgiLCJjcml0QmtnQ29sb3IiOiJyZWQiLCJ0b2RheUxpbmVDb2xvciI6InJlZCIsImxhYmVsQ29sb3IiOiJibGFjayIsImVycm9yQmtnQ29sb3IiOiIjNTUyMjIyIiwiZXJyb3JUZXh0Q29sb3IiOiIjNTUyMjIyIiwiY2xhc3NUZXh0IjoiIzMyMWI2NyIsImZpbGxUeXBlMCI6IiNjZGU0OTgiLCJmaWxsVHlwZTEiOiIjY2RmZmIyIiwiZmlsbFR5cGUyIjoiaHNsKDE0Mi4xNTc4OTQ3MzY4LCA1OC40NjE1Mzg0NjE1JSwgNzQuNTA5ODAzOTIxNiUpIiwiZmlsbFR5cGUzIjoiaHNsKDE2Mi45NjEwMzg5NjEsIDEwMCUsIDg0LjkwMTk2MDc4NDMlKSIsImZpbGxUeXBlNCI6ImhzbCgxNC4xNTc4OTQ3MzY4LCA1OC40NjE1Mzg0NjE1JSwgNzQuNTA5ODAzOTIxNiUpIiwiZmlsbFR5cGU1IjoiaHNsKDM0Ljk2MTAzODk2MSwgMTAwJSwgODQuOTAxOTYwNzg0MyUpIiwiZmlsbFR5cGU2IjoiaHNsKDIwNi4xNTc4OTQ3MzY4LCA1OC40NjE1Mzg0NjE1JSwgNzQuNTA5ODAzOTIxNiUpIiwiZmlsbFR5cGU3IjoiaHNsKDIyNi45NjEwMzg5NjEsIDEwMCUsIDg0LjkwMTk2MDc4NDMlKSJ9fX0

それぞれを小さな関数にまとめよう

スクリプトを開発していると、{}ブロックが何層も入れ子になりがちです。適宜関数に分解して書くことを心がけます。このとき、一つの機能を一つの関数にする、ということだけ気をつけてください。

ドキュメントを開いているかどうか

ここから実際にコードを書いていきますが、Illustratorのスクリプトは匿名関数*2でラップして実行するのがオススメです。というのも、スクリプトを終了する、という命令がないのでコードが複雑な入れ子になりやすいのです。関数内であればreturn文を使ってスクリプトを終了させることができるので、これを利用します。
匿名関数は下記のような書き方が一般的です。

(function() {
    // 処理を書く……
})();

関数そのものを()で囲み、末尾の()で実行させるという手法です。ただ()の入れ子や引数の与え方がわかりにくいかもしれません。
もうひとつの方法として、頭に否定演算子!や減算演算子-を付けてもいいっちゃいいです。

!function() {
    // 処理を書く……
}();

こうする方法もありますが、あまり見慣れないと思うので前者の方法で書いていきます。

ドキュメントが開かれているかどうかは、ApplicationDocumentsとプロパティをたどりましょう。

if (app.documents.length === 0) {
    // ドキュメントが開かれていない状態なので
    return; // 終了する
}

lengthプロパティでDocumentオブジェクトがいくつ開かれているかを調べることができます。これがゼロ=== 0であればスクリプトを終了します。
これを前述の匿名関数内に記述するとこうなります。

(function () {
    if (app.documents.length === 0) {
        return;
    }
})();

オブジェクトを選択しているかどうか

続いて、オブジェクトを選択しているかどうかを判定です。先ほどのlengthを、Application.selectionというオブジェクトの選択範囲(選択しているオブジェクト群)に対して用いることで同様に「選択しているオブジェクトの数」を得ることができます*3

if (app.selection.length === 0) {
    // オブジェクトを選択していない状態なので
    return; // 終了する
}

これを先ほどのコードに追記しましょう。2つのif文を設けるより、どちらか一方でも満たせばスクリプトを終了してしまっていいので、OR演算子||で条件式をつなげるとスマートです。

(function () {
    if (app.documents.length === 0 || app.selection.length === 0) {
        return;
    }
})();

オブジェクトにランダムに色を塗る

Illustratorのスクリプトでオブジェクトの塗り色に色を設定するにはどうすればいいでしょうか。
この部分をスクリプト的に分解すると下記の手順になります。

  1. 新しい色オブジェクト(CMYKColorオブジェクトなど)を生成する
  2. オブジェクトの塗りにその色オブジェクトを設定する

それぞれ見ていきましょう。

新しい色オブジェクト(CMYKColorオブジェクトなど)を生成する

今回はCMYKカラースペースでコードを組み立てます(RGBの場合は適宜読み替えてください)。CMYKの色オブジェクトはCMYKColorコンストラクタをnew演算子を付けて呼び出すことで生成することができます。そのオブジェクトの各色成分名のプロパティに値をセットします。

var myCMYK = new CMYKColor();
myCMYK.cyan = 20;
myCMYK.magenta = 30;
myCMYK.yellow = 40;
myCMYK.black = 0;

こうするとmyCMYKはC20、M30、Y40という色成分値を持つCMYKカラーオブジェクトとなります。

オブジェクトの塗りにその色オブジェクトを設定する

これをオブジェクトの塗り色に設定するにはObject.fillColorプロパティにこのオブジェクトを代入すればOKです。

試しにドキュメント上になにかオブジェクトを描画して下記のスクリプトを実行してみましょう。

var myCMYK = new CMYKColor();
myCMYK.cyan = 20;
myCMYK.magenta = 30;
myCMYK.yellow = 40;
myCMYK.black = 0;

app.activeDocument.selection[0].fillColor = myCMYK;

オブジェクトが意図した色に塗られたでしょうか?

ランダムな色を生成する

色の作り方と塗り方が分かったところで、今度はランダムに色成分を作ってみましょう。
ランダムな値を得る一般的なやり方は、JavaScriptが持っているMathオブジェクトの静的メソッドMath.random()を利用することでしょう。このメソッドは戻り値として0〜1までのランダムな値を返します*4

しかし、欲しいのはそんな小さな数字ではないのですよね。0〜100までの間で欲しいわけです。Math.random()から得られる値をxと置くと、得られる戻り値は下記のように表現できます。

0 <= x < 1

全体を100倍すると

0 <= x < 100

となります。つまりMath.random() * 100とすれば、0〜100までのランダムな値を得ることができるわけですね。
ただこれで得られる値は浮動小数点ですごく細かい値です。できれば整数で割り切った値が欲しい。また、このままだと100も得られません。これを処理するために、得られた値を四捨五入しましょう。四捨五入はMath.round()メソッドを使います。

var myCMYK = new CMYKColor();
myCMYK.cyan = Math.round(Math.random() * 100);
myCMYK.magenta = Math.round(Math.random() * 100);
myCMYK.yellow = Math.round(Math.random() * 100);
myCMYK.black = 0;

app.activeDocument.selection[0].fillColor = myCMYK;

これで0〜100までのランダムな色でオブジェクトを塗ることができました。これを先ほどのコードに追記しましょう。全体はこうなります。

(function () {
    if (app.documents.length === 0 || app.selection.length === 0) {
        return;
    }
    
    var myCMYK = new CMYKColor();
    myCMYK.cyan = Math.round(Math.random() * 100);
    myCMYK.magenta = Math.round(Math.random() * 100);
    myCMYK.yellow = Math.round(Math.random() * 100);
    myCMYK.black = 0;

    app.activeDocument.selection[0].fillColor = myCMYK;
})();

次のオブジェクトがあるかどうか

これだと選択している一つのオブジェクトにしか適用されません。勘がいい方は気づいたでしょう、app.activeDocument.selection[0]0が選択しているオブジェクト群のインデックスになるので、ここを0、1、2、と選択しているオブジェクトの数だけ順番に対処すればいいわけですね。今回はfor文で実装してみましょう。

(function () {
    if (app.documents.length === 0 || app.selection.length === 0) {
        return;
    }

    var sel = app.activeDocument.selection;
    for (var i=0; i<sel.length; i++) { // for文を追加
        var myCMYK = new CMYKColor();
        myCMYK.cyan = Math.round(Math.random() * 100);
        myCMYK.magenta = Math.round(Math.random() * 100);
        myCMYK.yellow = Math.round(Math.random() * 100);
        myCMYK.black = 0;

        app.activeDocument.selection[i].fillColor = myCMYK; // indexを i に変更
    } // for文の受け
})();

こうすると選択しているオブジェクトをまとめてランダムに塗り分けることができます。

関数にしてみよう

スクリプトの基本はこれだけです。意外とかんたんでしたか? 聞き慣れない言葉ばかりで難しかったでしょうか?
発展編として、ランダムに色を生成する部分を関数化してみます。

for文の中のCMYKColorオブジェクトを生成する部分を関数として末尾に宣言してみるとこうなります。

(function () {
    if (app.documents.length === 0 || app.selection.length === 0) {
        return;
    }

    var sel = app.activeDocument.selection;
    for (var i=0; i<sel.length; i++) {
        var myCMYK = getRandomCMYKColor();
        app.activeDocument.selection[i].fillColor = myCMYK;
    }
})();

function getRandomCMYKColor () {
    var cmyk = new CMYKColor();
    cmyk.cyan = Math.round(Math.random() * 100);
    cmyk.magenta = Math.round(Math.random() * 100);
    cmyk.yellow = Math.round(Math.random() * 100);
    cmyk.black = 0;
    return cmyk;
}

関数化にあたり、変数の名前をちょっと変えました*5
この程度のスクリプトだとあまり関数化するメリットがないのですが、次回はこのスクリプトを少しカスタマイズしていきますのでその布石です。
今回の記事は以上です。

To Be Continued...

次回はもうちょっと応用的な感じにカスタマイズしようと思います。

例えば、ペールトーンでまとめるとか。

f:id:uske_S:20201111012914g:plain
ペールトーンでランダムなトーンイントーン配色

例えば、さらに色相を統一してみるとか。

f:id:uske_S:20201111014044g:plain
ペールブルー系でランダムなドミナント・カラー配色

お楽しみに!

応用編はこちら。

uske-s.hatenablog.com

*1:フローチャート図はMermaid.jsのライブエディタを利用しました

*2:詳細は割愛しますが、関数名を定義せず即時的に関数を実行する方法です

*3:Documentオブジェクトもselectionプロパティを持っているので、app.documents[0].selection.length や app.activeDocument.selection.length としても可です

*4:厳密には「0を含み、1を含まない0〜1まで」の浮動小数点を返します

*5:本当は変えなくても動作しますが、異なるスコープなのでいわゆる変数のシャドウイングをしないためです