Windows + Adobe CC illustrator / CEP 5 でイラレのカスタム UI を作ってみる (7) 簡単な図形を生成する

## Introduction
Adobe illustrator (イラレ) の仕事では線幅やカラーが厳格に指定されている図形を多用する場合があります。(例えば地図作成や誌面デザイン、手芸の手順書 etc.)
illustrator では既定で直線や円などの描画モードがワンボタンで選べるので、同様に所定の図形をワンボタン化できないか調べてまとめました。

この記事は CC 版 illustrator / Windows 環境が対象です。この章では、ボタンを押すと所定の図形 (楕円) が生成される機能を実装します。

## 図形 (パス) の生成の流れ
前回に引き続き、地図作りを例に考えてみましょう。地図には形もプロパティも固定の地図記号がたくさん登場するので、これもスクリプトでカバーできるとよさそうです。例えば学校マークをいちいち書いたりコピペしたりしなくてもワンボタンで追加出来たら便利です。

なお、既存の図形を触っていたこれまでの処理と異なり、新たに図形を生成するため対象レイヤーの意識が必要となってきます。このため、まずは楕円の生成を体験してみます。

例によって、まずは index.html にボタンを作り、(CSS でスタイルを指定し) main.js で呼び出す関数を書き、それから hostscript.jsx スクリプトを書いていきましょう。

##ボタンを作る
前回までとは別の機能になるのでボタンから作っていきます。
これまで作ってきた SetProperty01 の下に作りました。ボタン名は `myAddPath01` です。

```html:index.html
    <div id="content">        
        
        <div>                
            <button id="btn_test" class="topcoat-button--large hostFontSize">Call ExtendScript</button>                
            <!-- プロパティ設定用ボタン -->
            <button id="btn_mySetProperty01" class="topcoat-button--large hostFontSize">mySetProperty01</button>            
            <!-- 図形追加用ボタン -->
            <button id="btn_myAddPath01" class="topcoat-button--large hostFontSize">myAddPath01</button>                
        </div>   

    </div>
```

CSS も追加しておきます。

```css:style.css
#btn_myAddPath01{
    width: 100%;
}

```
できました。
![SS33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1558908/925861d5-d477-7d04-ab0d-f7d67f1a4edb.png)

##スクリプトのガワを準備する
####main.js
呼び出し関数は `addPath01()` としました。デバッグに使うかもしれないので、例によってコールバック関数で返り値をコンソールに吐くようにしておきます。

```javascript:main.js
        $("#btn_myAddPath01").click(function (){
            //パス追加のボタン押したときに呼ぶ関数
            csInterface.evalScript('addPath01()',function(x){console.log(x)});
        });

    }
```

####hostscript.jsx

```javascript:hostscript.jsx
function addPath01(){
    return null;
}
```

## スクリプトの中身を書いていく
####試しに楕円を追加してみる
まずは簡単な図形ということで、円の追加を調べてみましょう。リファレンスを "circle" で調べると…出てきません。より一般に丸っこい図形をかけるよう、"ellipse" (楕円) が用意されています。(以下 p135)
https://www.adobe.com/content/dam/acom/en/devnet/illustrator/pdf/Illustrator_JavaScript_Scripting_Reference_2017.pdf


リファレンスを見てみると、`app.activeDocument` にレイヤーを追加し、さらに `layer.pathItems.ellipse` として呼んでいるようです。なるほど、パスを追加するときは追加先のレイヤーの作成または指定が必要なんですね。

試しに、シンプルに以下のように書いてみましょう。なお ellipse() の引数は top, left, width, height, reversed (bool), inscribed (bool) です。top, left は配置の座標、width, height は横縦のサイズ、reversed は反転状態にするか、inscribed は内接するかです。サイズ把握がわかりやすいので内接をオンにしておきます。

```javascript:hostscript.jsx
function addPath01(){
    app.activeDocument.pathItems.ellipse(-10,20,30,40,false,true);

    return null;
}
```
以下のように各サイズが反映されます。top と left で規定された余白線に内接する形で、所定の width と height の楕円ができるイメージです。(width と height は直径にあたる)
![SS34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1558908/c781fe5c-f164-31ce-0f0d-fb6231e22089.png)

####レイヤーの考慮
ところで、このように生成した楕円は作業レイヤーによらず、一番上のレイヤーに追加されてしまいます。やはりリファレンスに書いてある通り、本来はレイヤーを指定するべきですね。

作業ドキュメントが activeDocument なので、作業レイヤーは activeLayer かな、とあたりをつけてリファレンスを見てみるとあっていそうです。こんな感じに activeLayer を対象とするように変更します。

```javascript:hostscript.jsx
function addPath01(){
    app.activeDocument.activeLayer.pathItems.ellipse(-10,20,30,40,false,true);

    return null;
}
```

これで作業レイヤーへ楕円が生成されます。が、もしアクティブレイヤーがロックされていたり非表示だったりすると当然生成は失敗し、コンソールには以下のエラーが出ます。

```
Error 9024: Cannot modify a layer that is locked
Line: 11
->      app.activeDocument.activeLayer.pathItems.ellipse(-10,20,30,40,false,true);
```

イラレの画面上は特に出ないのでこのままでもいいですが、条件分岐させるのであれば以下のようになります。(レイヤーのロック状態や非表示状態取得は以下 p92 参照)
https://www.adobe.com/content/dam/acom/en/devnet/illustrator/pdf/Illustrator_JavaScript_Scripting_Reference_2017.pdf

```javascript:hostscript.jsx
function addPath01(){
    
    //レイヤーがロックされている場合はアラートを出す
    if (app.activeDocument.activeLayer.locked == true){
        alert("The active layer is locked.");
    }
    //レイヤーが見えない場合もアラート
    else if (app.activeDocument.activeLayer.visible == false){
        alert("The active layer is invisible.");
    }
    //そうでなければ楕円を生成
    else {
        app.activeDocument.activeLayer.pathItems.ellipse(-10,20,30,40,false,true);
    }
    return null;
}

```
以上で楕円生成ボタンができました。同じノリで長方形の生成も可能です。
例えば円の集合や格子模様など簡単な図形の繰り返しであればスクリプトで作ってしまうのが楽ですね。

#### 作業中の画面中央に生成する
地図を描くような場合、ボタンを押すととりあえずスタンプのように図形をポンと置いて、あとは場所を手動調整していくような使い方もあるでしょう。この場合は「今見えているキャンパスの中央付近」に図形が出てくると便利です。

見えているキャンパスの範囲は app.activeDocument.activeView.bounds に格納されています。ややこしいですが第0、第2 要素が left (左端基準の x 座標) 、第1、第3 要素が top (上端基準の y 座標) にあたります。この中央を取るためには、第0、第2 要素の平均と第1、第3 要素の平均をとります。
以下のように centertop、centerleft を計算しておき ellipse の生成位置に利用すればいいわけですね。

```javascript
    var centertop =  (app.activeDocument.activeView.bounds[1] + app.activeDocument.activeView.bounds[3])/2 ;
    var centerleft = (app.activeDocument.activeView.bounds[0] + app.activeDocument.activeView.bounds[2])/2 ;
```

なお、例えば画面をずらさずボタンを 2 回クリックした場合には全く同じ場所に同じ図形が生成されるためわかりにくくなります。筆者は生成ごとにランダムに場所をすこしずつずらすようにし、以下のような生成プログラムにして使っています。
`Math.random()` は [0,1) の区間で一様分布するランダムな数値を出すので、この書き方だと -25 ~ 25 の数値が得られます。

```javascript:hostscript.jsx
function addPath01(){
    //レイヤーがロックされている場合はアラートを出す
    if (app.activeDocument.activeLayer.locked == true){
        alert("The active layer is locked.");
    }
    //レイヤーが見えない場合もアラート
    else if (app.activeDocument.activeLayer.visible == false){
        alert("The active layer is invisible.");
    }
    //そうでなければ楕円を生成
    else {
        //画面中央の座標取得
        var centertop  = (app.activeDocument.activeView.bounds[1] + app.activeDocument.activeView.bounds[3])/2 + (Math.random()-0.5)*50;
        var centerleft = (app.activeDocument.activeView.bounds[0] + app.activeDocument.activeView.bounds[2])/2 + (Math.random()-0.5)*50;
        //楕円生成
        app.activeDocument.activeLayer.pathItems.ellipse(centertop,centerleft,30,40,false,true);
    }
    return null;
}

```





## まとめ
楕円についてはプリセットの関数で生成できるものの、レイヤーの考慮が必要なことがわかりました。ではプリセットにない図形はどうしたらいいのか？を次回検討していきます。