Skip to content

Instantly share code, notes, and snippets.

@shspage
Last active October 3, 2017 21:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shspage/0a800aea85ec45fcfe444797f1cd8761 to your computer and use it in GitHub Desktop.
Save shspage/0a800aea85ec45fcfe444797f1cd8761 to your computer and use it in GitHub Desktop.
Illustrator : 2点をつなぐ弧を描く
// 3つのオブジェクト(前面からp1, p2, o)が選択されているとき、
// p1の中心からp2の中心へ反時計回りに弧を描く。
// o の中心を弧の中心とする。
// ----------------------------------------------
// 点
// x, y : float
var Point = function(x, y){
this.x = x;
this.y = y;
}
Point.prototype = {
add : function(p){ // p : Point
return new Point(this.x + p.x, this.y + p.y);
},
sub : function(p){ // p : Point
return new Point(this.x - p.x, this.y - p.y);
},
mul : function(m){ // m : float
return new Point(this.x * m, this.y * m);
},
dot : function(p){ // 内積. p : Point
return this.x * p.x + this.y * p.y;
},
cross : function(p){ // 外積. p : Point
return this.x * p.y - this.y * p.x;
},
// o から this へ引いた線の角度を返す
getAngle : function(o){ // o : Point
return Math.atan2(this.y - o.y, this.x - o.x);
},
byAngle : function(t){ // t : float (radian)
return new Point(Math.cos(t), Math.sin(t));
},
norm : function(){ // ノルム
return Math.sqrt(this.x * this.x + this.y * this.y);
},
normalize : function(){ // 正規化
var d = this.norm();
if(d == 0) return new Point(0, 0);
return new Point(this.x / d, this.y / d);
},
// o を中心に反時計回りに回転する
rot : function(t, o){ // t : float (radian), o : Point
var c = Math.cos(t);
var s = Math.sin(t);
var x = this.x - o.x;
var y = this.y - o.y;
return new Point(x * c - y * s,
x * s + y * c).add(o);
},
equals : function(p){ // p と位置が等しいとき true
return this.x == p.x && this.y == p.y;
},
toArray : function(){
return [this.x, this.y];
}
}
// ------------------------
// Point p1, p2 の間の距離を返す
function dist(p1, p2) {
return Math.sqrt(dist2(p1, p2));
}
// --------------------------------------
// Point p1, p2 の間の距離の2乗を返す
function dist2(p1, p2) {
var dx = p1.x - p2.x;
var dy = p1.y - p2.y;
return dx*dx + dy*dy;
}
// ------------------------
// Point p1, p2 の中点を返す
function getMidPoint(p1, p2){
return new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
}
// ------------------------
var HPI = Math.PI / 2;
var WPI = Math.PI * 2;
// ------------------------
// 弧の中心角に対して半径が1の場合のハンドル長さを返す
// t : float, radian
function getHandleLengthBase(t){
if(t > Math.PI){ // 4点で描画
t /= 3;
} else if(t > HPI){ // 3点で描画
t /= 2;
} // else : 2点で描画
return 4 * Math.tan(t / 4) / 3;
}
// ------------------------
// PathItem を生成して返す
function createAPath(){
var path = app.activeDocument.activeLayer.pathItems.add();
path.closed = false;
path.filled = false;
path.stroked = true;
path.strokeWidth = 2;
var gray = new GrayColor();
gray.gray = 100;
path.strokeColor = gray;
return path;
}
// ------------------------
// PathPoint の位置を設定する
// p : PathPoint
// anchor, rdir, ldir : Point
function setPathPoint(p, anchor, rdir, ldir){
p.anchor = anchor.toArray();
p.rightDirection = rdir.toArray();
p.leftDirection = ldir.toArray();
}
// ------------------------
// o を中心として p1 から反時計回りに p2 へ円弧を描く
// p1, p2, o : Point
function drawArc(p1, p2, o){
if(p1.equals(p2)) return;
// 1. 弧の中心角を求める
var v1 = p1.sub(o).normalize();
var v2 = p2.sub(o).normalize();
var t = Math.acos(v1.dot(v2));
if(v1.cross(v2) < 0) t = WPI - t;
// 2. 始点・終点のハンドルの位置を求める
var t1 = p1.getAngle(o);
var t2 = p2.getAngle(o);
var radius = dist(p1, o);
var han = radius * getHandleLengthBase(t); // ハンドル長
// ハンドルの位置
var h1 = new Point().byAngle(t1 + HPI).mul(han).add(p1);
var h2 = new Point().byAngle(t2 - HPI).mul(han).add(p2);
var p = createAPath().pathPoints;
// 3. 中間点のアンカーの位置を決める
if(t > Math.PI){
t /= 3;
setPathPoint(p.add(), p1, h1, p1);
setPathPoint(p.add(), p1.rot(t, o),
h1.rot(t, o),
h2.rot(-t * 2, o));
setPathPoint(p.add(), p2.rot(-t, o),
h1.rot(t * 2, o),
h2.rot(-t, o));
setPathPoint(p.add(), p2, p2, h2);
} else if(t > HPI){
t /= 2;
setPathPoint(p.add(), p1, h1, p1);
setPathPoint(p.add(), p1.rot(t, o),
h1.rot(t, o),
h2.rot(-t, o));
setPathPoint(p.add(), p2, p2, h2);
} else {
setPathPoint(p.add(), p1, h1, p1);
setPathPoint(p.add(), p2, p2, h2);
}
}
// --------------------------------------
// PageItem item の中心の座標を返す
function getCenter(item){
var gb = item.geometricBounds; // [left, top, right, bottom]
return new Point((gb[0] + gb[2]) / 2,
(gb[1] + gb[3]) / 2);
}
// --------------------------------------
// メイン処理
function main(){
var sel = activeDocument.selection;
var p1 = getCenter(sel[0]);
var p2 = getCenter(sel[1]);
var o = getCenter(sel[2]);
// p2 が円周上になるように補正
p2 = p2.sub(o).normalize().mul(
p1.sub(o).norm()).add(o);
drawArc(p1, p2, o);
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment