illustrator
Swift
UIBezierPath

Swift : IllustratorCC で描いたオブジェクトを UIBezierPathに書き出す

More than 1 year has passed since last update.

ちょっとしたものなら画像よりはコードで書きたいもの.
でも,いい感じの図形をUIBezierPathだけで書くのは大変ですよね.

そこで使えるのがIllustratorの拡張機能「DrawScript」.
オブジェクトを様々なコードに変換してくる便利機能.
現在(2015/12/02)の対応は以下の9種類.

  • OBJ-C
  • C++
  • JAVASCRIPT
  • CREATEJS/EASELJS
  • PROCESSING
  • ACTIONSCRIPT 3
  • JSON
  • RAW BEZIER
  • POINTS

残念ながら,書き出されるコードはすべて整数型...orz
若干くずれちゃう図形.

どうしてもパスで書きたかった私は自分で作ることを決意!

Illustrator拡張機能の作成

使用するツールはAdobeの「Adobe Extension Builder 2.1」
http://www.adobe.com/jp/products/adobe-extension-builder.html
Eclipseベースの開発ツールで,HTML/CSSとJSで作ることができるため,Web屋はとっつきやすいかも.

参考:http://guchitaka.com/htmljs%E3%81%A7%E4%BD%9C%E3%82%8B-photoshopextension-%E5%89%8D%E7%B7%A8/
   http://aphall.com/2013/08/html5-extensions-for-cc-jp/

今回は,すぐに使いたかったってこともあったので,Swiftに変換することにした.

表の部分はシンプルに変換ボタンと変換したコードを表示するフィールドのみ.

Illustratorのオブジェクトへのアクセスは,*.jsx って拡張子のファイルで行う.

illustrator.jsx
"use strict";

createCode={
  run : function() {
    var digit = 1000;

    var doc;
    try {
      // AIファイルの読み込み
      doc = app.activeDocument;
    } catch(e){
      return "ファイルがありません.";
    }

    var pathItems = doc.selection;  // 選択状態のオブジェクト群
    var itemsLen = pathItems.length;

    if(itemsLen === 0) return "オブジェクトを選択してください";

    // 表示するコード
    var dispText = "";

    dispText += 'var path = UIBezierPath()'+'\n';

    // オブジェクトごとにUIBezierPathコードを生成
    for(var i = 0 ; i < itemsLen ; i++) {
      var pathItem = pathItems[i];
      var isClosed = pathItem.closed;
      var pathPoints = pathItem.pathPoints;
      var pathLen = pathPoints.length;
      var endPoint;
      var prePoint;

      if(pathLen === 1) return "最低2つのポイントを含む必要があります.";

      for(var j=0; j < pathLen; j++) {
        var ap = roundPoint(convertDToA(pathPoints[j].anchor), digit);
        var ld = roundPoint(convertDToA(pathPoints[j].leftDirection), digit);
        var c = (j != 0) ? j - 1 : pathLen - 1;
        var rd = roundPoint(convertDToA(pathPoints[c].rightDirection), digit);
        prePoint = roundPoint(convertDToA(pathPoints[c].anchor), digit);

        if (j == 0) {
          dispText += makeMoveToPoint(ap)+"\n";
          if(!isClosed) { continue; }

          if(rd.toString() === prePoint.toString() && ld.toString() === ap.toString()) {
            // 直線
            endPoint = makeAddLine(ap);
          } else {
            // 曲線
            endPoint = makeAddCurve(ap, ld, rd);
          }
        } 
        else {
          if(rd.toString() === prePoint.toString() && ld.toString() === ap.toString()) {
            // 直線
            dispText += makeAddLine(ap)+"\n";
          } 
          else {
            // 曲線
            dispText += makeAddCurve(ap, ld, rd)+"\n";
          }
        }
      }

      if (endPoint) {
        dispText += endPoint+"\n";
      }
    }

    dispText += "path.stroke()"+"\n";

    return dispText;
  }
};

function makeMoveToPoint(anchor) {
  return 'path.moveToPoint(' + makeCGPointMake(anchor) + ')';
}

function makeAddLine(anchor) {
  return 'path.addLineToPoint(' + makeCGPointMake(anchor) + ')';
}

function makeAddCurve(anchor, leftDirection, rightDirection) {
  return 'path.addCurveToPoint(' + makeCGPointMake(anchor) +
            ', controlPoint1:' + makeCGPointMake(rightDirection) +
            ', controlPoint2:' + makeCGPointMake(leftDirection) +
            ')';
}

function makeCGPointMake(point) {
  return 'CGPointMake(' + point[0] + ', ' + point[1] + ')';
}

function roundPoint(point, places) {
  var p = [];
  var len = point.length;
  for(var i=0; i < len; i++) {
    p.push(Math.round(point[i] * places)/places);
  }

  return p;
}

function convertDToA(point) {
  var doc = app.activeDocument;
  var pos = doc.convertCoordinate (
                     point, 
                     CoordinateSystem.DOCUMENTCOORDINATESYSTEM,
                     CoordinateSystem.ARTBOARDCOORDINATESYSTEM
                     );
  pos[1] *= -1;

  return pos;
}

ボタンクリック時の挙動のJS

main.js
$(function() {
    var a = new CSInterface();

    $("#createCodeBtn").click(function(){
        a.evalScript("createCode.run()", function(text) {
            // 表示
            var $codeArea = $("#dispCode");
            $codeArea.text(text);
        });
    });
});

CSInterface()は,Illustratorのインターフェースなんかをまとめたものらしい.
a.evalScript()で,*.jsxの処理を実行できる.

導入方法

これまたAdobeの Adobe Extension Manager にて導入
http://www.adobe.com/jp/exchange/em_download/

実行結果

いい感じにパスを出力できました.

ちなみに,複合パス,クリッピングマスクが施されたものに対しては未対応なので,エラー出ます.笑


これを用いてUIBezierPathアイコンをまとめたプロジェクト
https://github.com/krimpedance/KRIcon
色とかいろいろ変更可能
まだ数は少ないですが.笑
コードが汚くなってしまうのをどうにかしたい感(だれか指南を...!)

最後に

今後余裕がある時に今回作成した拡張機能の体裁を整えて後悔しようかなと思います.
言語の追加,複雑なオブジェクトへの対応などなど