Created
February 16, 2017 15:43
-
-
Save shspage/c02e5422b53d6cc10cf1b0a0a3ce8e98 to your computer and use it in GitHub Desktop.
flapClose.jsx日本語コメント+エラーメッセージ版
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#target "illustrator" | |
// 端点を一致させる | |
// 選択されているオープンパスについて、 | |
// 選択アンカーから始点・終点までの部分を | |
// 始点・終点が一致するように回転させます。 | |
// 回転させる部分の形状は変わりません。 | |
// このため、回転させても端点を一致させることが | |
// できない場合は、その旨のメッセージを表示します。 | |
// 設定 | |
// ・以下の config の箇所で close_path を true に | |
// すると、端点を一致させたパスをクローズパスに | |
// します。 | |
// 補足 | |
// ・始点のアンカーが選択されているときは、始点から | |
// 2つ目のアンカーが選択されているものとします。 | |
// 終点が選択されている場合も同様です。 | |
// ・選択範囲中で、アンカー数が4つ以下のパスは | |
// 無視されます。 | |
function main(){ | |
// 設定 | |
var config = { | |
// close_path: trueの場合、端点を一致させたあと | |
// クローズパスにします。 | |
close_path: false, | |
}; | |
var alert_title = "実行不可\r"; | |
// オープンパスを選択(アンカー数4以上のもの) | |
var paths = []; | |
extractOpenPaths(activeDocument.selection, paths, 4); | |
if(paths.length < 1){ | |
alert(alert_title + "オープンパスが選択されていません。"); | |
return; | |
} | |
for(var si = 0; si < paths.length; si++){ | |
var path = paths[si]; | |
var pp = path.pathPoints; | |
// 選択されているアンカーポイントのインデックスを取得 | |
var z = pp.length - 1; | |
var idx1 = -1; | |
var idx2 = -1; | |
var r1 = 0; | |
var r2 = 0; | |
for(var i = 0; i < z; i++){ | |
if(isSelected(pp[i])){ | |
if(i == 0) i = 1; | |
idx1 = i; | |
r1 = dist(pp[i].anchor, pp[0].anchor); | |
break; | |
} | |
} | |
for(var i = z; i > 0; i--){ | |
if(isSelected(pp[i])){ | |
if(i == z) i = z - 1; | |
idx2 = i; | |
r2 = dist(pp[i].anchor, pp[z].anchor); | |
break; | |
} | |
} | |
// 検証 | |
if(verify1(idx1, idx2, alert_title)) return; | |
var d = dist(pp[idx1].anchor, pp[idx2].anchor); | |
if(verify2(r1, r2, d, alert_title)) return; | |
// 回転角度の計算 | |
var calcErr = false; | |
var v1, v2; | |
var t3, t4, t5; | |
var t1, t2; | |
t3 = lawOfCosines(r2, r1, d); | |
if(isNaN(t3)) calcErr = true; | |
if(!calcErr){ | |
v1 = subCoord(pp[0].anchor, pp[idx1].anchor); | |
v2 = subCoord(pp[idx2].anchor, pp[idx1].anchor); | |
t4 = innerProduct(v1, v2, r1, d); | |
if(isNaN(t4)) calcErr = true; | |
} | |
if(calcErr){ | |
alert(alert_title + "角度を算出できませんでした(始点側)。"); | |
return; | |
} | |
t5 = crossProduct(v1, v2); | |
t1 = t5 > 0 ? t4 - t3 : t3 - t4; | |
t3 = lawOfCosines(r1, r2, d); | |
if(isNaN(t3)) calcErr = true; | |
if(!calcErr){ | |
v1 = subCoord(pp[z].anchor, pp[idx2].anchor); | |
v2 = subCoord(pp[idx1].anchor, pp[idx2].anchor); | |
t4 = innerProduct(v1, v2, r2, d); | |
if(isNaN(t4)) calcErr = true; | |
} | |
if(calcErr){ | |
alert(alert_title + "角度を算出できませんでした(終点側)。"); | |
return; | |
} | |
t5 = crossProduct(v1, v2); | |
t2 = t5 > 0 ? t4 - t3 : t3 - t4; | |
// パスの操作 | |
rotPathPoints(pp, 0, idx1-1, pp[idx1].anchor, t1); | |
if(!isEqualCoord(pp[idx1].leftDirection, pp[idx1].anchor)){ | |
pp[idx1].leftDirection = rotPoint(pp[idx1].leftDirection, pp[idx1].anchor, t1); | |
} | |
rotPathPoints(pp, idx2+1, z, pp[idx2].anchor, t2); | |
if(!isEqualCoord(pp[idx2].rightDirection, pp[idx2].anchor)){ | |
pp[idx2].rightDirection = rotPoint(pp[idx2].rightDirection, pp[idx2].anchor, t2); | |
} | |
if(config.close_path){ | |
closeProcessedPath(path, pp); | |
} | |
} | |
} | |
// ---------------------------------------------- | |
function closeProcessedPath(path, pp){ // PathItem, PathPoints | |
pp[0].leftDirection = pp[pp.length - 1].leftDirection; | |
pp[pp.length - 1].remove(); | |
path.closed = true; | |
} | |
// ---------------------------------------------- | |
// 検証その1 | |
function verify1(idx1, idx2, alert_title){ | |
if(idx1 < 0 && idx2 < 0){ | |
alert(alert_title + "アンカーポイントが選択されていません。"); | |
return true; | |
} else if(idx1 < 0 || idx2 < 0 || idx1 == idx2){ | |
alert(alert_title + "アンカーが1つしか選択されていません。"); | |
return true; | |
} | |
return false; | |
} | |
// ---------------------------------------------- | |
// 検証その2 | |
function verify2(r1, r2, d, alert_title){ | |
if(d == 0){ | |
alert(alert_title + "選択アンカー同士の距離が0の場合には対応していません。"); | |
return true; | |
} if(r1 + r2 < d){ | |
alert(alert_title + "端点が一致する点はありません(パスが短いため)。"); | |
return true; | |
} else if(d + r1 < r2 || d + r2 < r1){ | |
alert(alert_title + "端点が一致する点はありません(パスが長いため)。"); | |
return true; | |
} | |
return false; | |
} | |
// ---------------------------------------------- | |
// 座標p1, p2が等しい場合trueを返す | |
function isEqualCoord(p1, p2){ | |
return p1[0] == p2[0] && p1[1] == p1[1]; | |
} | |
// ---------------------------------------------- | |
// pathPoints pp のインデックスidxStartからidxEndまでの | |
// pathPointを、点oを中心に角度tだけ回転する | |
function rotPathPoints(pp, idxStart, idxEnd, o, t){ | |
for(var i = idxStart; i <= idxEnd; i++){ | |
pp[i].anchor = rotPoint(pp[i].anchor, o, t); | |
pp[i].rightDirection = rotPoint(pp[i].rightDirection, o, t); | |
pp[i].leftDirection = rotPoint(pp[i].leftDirection, o, t); | |
} | |
} | |
// ---------------------------------------------- | |
// 点pを点oを中心に角度tだけ回転する | |
function rotPoint(p, o, t){ | |
var c = Math.cos(t); | |
var s = Math.sin(t); | |
var dx = p[0] - o[0]; | |
var dy = p[1] - o[1]; | |
return [c * dx - s * dy + o[0], | |
s * dx + c * dy + o[1]]; | |
} | |
// ---------------------------------------------- | |
// 余弦定理。a,b,cは三角形の各辺の長さ。 | |
// 辺b,cに挟まれた角の角度を返す | |
function lawOfCosines(a, b, c){ | |
// return angle A | |
var d = 2 * b * c; | |
if(d == 0){ | |
//alert("lawOfCosines:d == 0"); | |
return NaN; | |
} | |
return Math.acos((b*b + c*c - a*a) / d); | |
} | |
// ---------------------------------------------- | |
// ベクトルv1, v2の外積を返す | |
function crossProduct(v1, v2){ | |
return v1[0] * v2[1] - v2[0] * v1[1]; | |
} | |
// ---------------------------------------------- | |
// ベクトルv1, v2の内積を返す。 | |
// n1, n2はそれぞれのノルム。undefinedの場合は計算される | |
function innerProduct(v1, v2, n1, n2){ | |
if(n1 == undefined) n1 = norm(v1); | |
if(n2 == undefined) n2 = norm(v2); | |
var n = n1 * n2; | |
if(n == 0){ | |
//alert("innerProduct:n == 0"); | |
return NaN; | |
} | |
return Math.acos((v1[0] * v2[0] + v1[1] * v2[1]) / n); | |
} | |
// ---------------------------------------------- | |
// p2を原点とした場合のp1の座標を返す | |
function subCoord(p1, p2){ | |
return [p1[0] - p2[0], p1[1] - p2[1]]; | |
} | |
// ---------------------------------------------- | |
// pathPoint ppt が選択されていればtrueを返す | |
function isSelected(ppt){ | |
return ppt.selected == PathPointSelection.ANCHORPOINT; | |
} | |
// ---------------------------------------------- | |
// ベクトルvのノルムを返す | |
function norm(v){ | |
return Math.sqrt(v[0]*v[0] + v[1]*v[1]); | |
} | |
// ---------------------------------------------- | |
// 点p, qの間の距離を返す | |
function dist(p, q){ | |
var dx = p[0] - q[0]; | |
var dy = p[1] - q[1]; | |
return Math.sqrt(dx*dx + dy*dy); | |
} | |
// ---------------------------------------------- | |
// pageItem の配列 sel (selectionなど) に含まれる | |
// pathItem (オープンパス)を、配列 paths に追加する。 | |
// min_pp_length が指定されている場合、pathPoint の | |
// 数が min_pp_length 未満のものは除外する。 | |
function extractOpenPaths(sel, paths, min_pp_length){ | |
if(!min_pp_length) min_pp_length = 1; | |
for(var i = 0; i < sel.length; i++){ | |
if(sel[i].typename == "PathItem"){ | |
if(sel[i].pathPoints.length >= min_pp_length | |
&& sel[i].closed == false){ | |
paths.push(sel[i]); | |
} | |
} else if(sel[i].typename == "GroupItem"){ | |
// search for PathItems in GroupItem, recursively | |
extractPaths(sel[i].pageItems, paths, min_pp_length); | |
} else if(sel[i].typename == "CompoundPathItem"){ | |
// searches for pathitems in CompoundPathItem, recursively | |
// ( ### Grouped PathItems in CompoundPathItem are ignored ### ) | |
extractPaths(sel[i].pathItems, paths, min_pp_length); | |
} | |
} | |
} | |
// ---------------------------------------------- | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment