Skip to content

Instantly share code, notes, and snippets.

@shspage
Created February 16, 2017 15:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shspage/c02e5422b53d6cc10cf1b0a0a3ce8e98 to your computer and use it in GitHub Desktop.
Save shspage/c02e5422b53d6cc10cf1b0a0a3ce8e98 to your computer and use it in GitHub Desktop.
flapClose.jsx日本語コメント+エラーメッセージ版
#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