Last active
October 3, 2017 21:47
-
-
Save shspage/0a800aea85ec45fcfe444797f1cd8761 to your computer and use it in GitHub Desktop.
Illustrator : 2点をつなぐ弧を描く
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
// 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