Skip to content

Instantly share code, notes, and snippets.

@shspage
Last active October 18, 2016 09:30
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save shspage/a35cbceffe723bdc984b5d0fd1d847c1 to your computer and use it in GitHub Desktop.
Save shspage/a35cbceffe723bdc984b5d0fd1d847c1 to your computer and use it in GitHub Desktop.
Adobe Illustrator script that draws voronoi diagrams - example (commented in Japanese)
//@include "~/ailib/rhill-voronoi-core.js"
// Adobe Illustrator script
// 選択した各オブジェクトの中心の座標を元にボロノイ図を描画します。
// 描画範囲は選択オブジェクト中で面積が最大のものを使用します。
// (このため、外枠となるオブジェクトも選択範囲に含めてください。
// 外枠はボロノイ計算の対象外です。)
// 描画した線はグループ化されています。
// 塗りのある面を描画しない場合は、スクリプト冒頭の
// DRAW_POLYGONS を false にしてください。
// (面はグループ化され、アクティブレイヤーの最背面に置かれます。)
// *** もうすこし詳しい説明 --> http://d.hatena.ne.jp/shspage/20160515
// テスト環境: Illustrator CS3
// 作成: Hiroyuki Sato http://github.com/shspage
// ----
// 'rhill-voronoi-core.js' を include しています。
// Copyright (c) 2010-2013 Raymond Hill https://github.com/gorhill/Javascript-Voronoi
// ----
(function(){
// 塗りのある面を描く場合は true、描かない場合は false にしてください。
var DRAW_POLYGONS = true;
// pageItem p の中心の座標 {x, y} を返します。
var getCenter = function(p){
var gb = p.geometricBounds; // left, top, right, bottom
return { x:(gb[0] + gb[2]) / 2, y:(gb[1] + gb[3]) / 2 };
}
// GrayColor の黒を返します。(線の色)
var getBlack = function(){
var black = new GrayColor();
black.gray = 100;
return black;
}
// GrayColor の白を返します。(面の色)
var getWhite = function(){
var white = new GrayColor();
white.gray = 0;
return white;
}
// pathPoint が2つある黒い線の pathItem を生成して返します。
var createAPath = function(){
var p = app.activeDocument.activeLayer.pathItems.add();
p.setEntirePath([[0,0], [0,0]]);
p.closed = false;
p.filled = false;
p.stroked = true;
p.strokeWidth = 1; // 描画する線の太さ(単位=ポイント)
p.strokeCap == StrokeCap.ROUNDENDCAP;
p.strokeColor = getBlack();
return p;
}
// 塗り用の pathItem を生成して返します。
var createAFilledPath = function(){
var p = app.activeDocument.activeLayer.pathItems.add();
p.closed = true;
p.filled = true;
p.stroked = false;
p.fillColor = getWhite();
return p;
}
// pathItem line の pathPoints[idx] のアンカー(とハンドル)の
// 座標を、Voronoi.vertex vtx の位置に設定します。
var setAnchor = function(line, idx, vtx){
var pt = line.pathPoints[idx];
pt.anchor = [vtx.x, vtx.y];
pt.rightDirection = pt.anchor;
pt.leftDirection = pt.anchor;
}
// 実行できない状態の場合 true を返します。
var isBadCondition = function(){
if(app.documents.length < 1){
return true;
}
var adoc = app.activeDocument;
if(adoc.selection.length < 3){
// 選択オブジェクト全体がグループ化されていると1つのオブジェクトとして
// 扱われてしまいます。一方で一つ一つのオブジェクトがグループ化されて
// いる場合もありえます。これを考慮して、3つ以上のオブジェクトが検出された
// 場合のみ実行する制限を設けています。
alert("3つ以上のオブジェクトを選択してください。");
return true;
}
if(adoc.activeLayer.locked){
alert("アクティブレイヤーのロックを解除してください。");
return true;
}
return false;
}
// 処理対象(選択範囲)から、面積が最大のオブジェクトを除いた配列を返します。
// また、rect に、除いたオブジェクトの大きさを設定します。
var excludeLargestObjectFromSelection = function(sel, rect){
var nsel = [];
var area = sel[0].area;
var idx = 0;
for(var i = 1, iEnd = sel.length; i < iEnd; i++){
if(sel[i].area > area){
idx = i;
area = sel[i].area;
}
}
for(var i = 0, iEnd = sel.length; i < iEnd; i++){
if(i == idx){
var gb = sel[i].geometricBounds; // left, top, right, bottom
rect.left = gb[0];
rect.top = gb[1];
rect.right = gb[2];
rect.bottom = gb[3];
} else {
nsel.push(sel[i]);
}
}
return nsel;
}
// 面の描画
// 面はグループ化され、アクティブレイヤーの最背面に置かれます。
var drawPolygons = function(adoc, diagram){
var grf = adoc.activeLayer.groupItems.add();
grf.move(adoc.activeLayer, ElementPlacement.PLACEATEND);
var pf = createAFilledPath();
pf.move(grf, ElementPlacement.PLACEATEND);
for(var i = 0, iEnd = diagram.cells.length; i < iEnd; i++){
var cell = diagram.cells[i];
var v = cell.halfedges[0].getStartpoint();
var points = [[v.x, v.y]];
for(var hi = 0, hiEnd = cell.halfedges.length - 1; hi < hiEnd; hi++){
v = cell.halfedges[hi].getEndpoint();
points.push([v.x, v.y]);
}
var polygon = pf.duplicate();
polygon.setEntirePath(points);
}
pf.remove();
}
// 線の描画
// 描画した線はグループ化されています。
var drawLines = function(adoc, diagram){
var gr = adoc.activeLayer.groupItems.add();
var p = createAPath();
p.move(gr, ElementPlacement.PLACEATEND);
for(var i = 0, iEnd = diagram.edges.length; i < iEnd; i++){
var line = p.duplicate();
var edge = diagram.edges[i];
setAnchor(line, 0, edge.va);
setAnchor(line, 1, edge.vb);
}
p.remove();
}
// メイン処理
var main = function(){
if(isBadCondition()) return;
var adoc = app.activeDocument;
var sel = adoc.selection;
var voronoi = new Voronoi();
// 外枠は選択オブジェクト中で面積が最大のものを用います。
// 見にくいですが、枠線も描画されます。
var rect = {left:0, top:0, right:0, bottom:0};
sel = excludeLargestObjectFromSelection(sel, rect);
var bbox = {xl:rect.left, yt:rect.bottom, xr:rect.right, yb:rect.top};
var sites = []; // 元にする点 { x, y } の配列
for(var i = 0; i < sel.length; i++){
sites.push(getCenter(sel[i]));
}
var diagram = voronoi.compute(sites, bbox);
// アートボードへの描画
// 面
if(DRAW_POLYGONS){
drawPolygons(adoc, diagram);
}
// 線
drawLines(adoc, diagram);
}
main();
})();
// 2016.05.15 一番上のレイヤーがロックされている場合の不具合を修正
// 2016.05.15 面を描画する機能を追加
// 2016.05.18 最大面積判定の処理を修正
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment