Skip to content

Instantly share code, notes, and snippets.

@ikekou
Last active December 17, 2015 05:38
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 ikekou/5559137 to your computer and use it in GitHub Desktop.
Save ikekou/5559137 to your computer and use it in GitHub Desktop.
[Math][JavaScript][CoffeeScript] 3次ベジェ曲線を2次ベジェ曲線に変換する(近似するだけ、間違ってたり無駄のある可能性は十分にあり) 参考:http://www.noids.tv/2007/02/bspline_10fd.html
window.onlaod=()->
a1x=100
a1y=100
c1x=200
c1y=200
c2x=300
c2y=200
a2x=400
a2y=100
result=[]
Vector2DUtil.cubicBezierToQuadraticBezier(a1x,a1y,c1x,c1y,c2x,c2y,a2x,a2y,10,result)
console.log(result)
class Vector2DUtil
#3次ベジェ曲線を2次ベジェ曲線に変換する
#精度は中間点の距離で測る、precisionで設定
#resultに結果が入るので空の配列渡す
#a1はアンカーポイント1
#c1はアンカーポイント1から出るコントロールポイント
#c2はアンカーポイント2から出るコントロールポイント
#a2はアンカーポイント2
@cubicBezierToQuadraticBezier:(a1x,a1y,c1x,c1y,c2x,c2y,a2x,a2y,precision,result)->
#元の3次ベジェの中間点を求める
#http://nutsu.com/blog/2007/120500_as_cbezier_cut1.html
[p1x,p1y]=@getCubicBezierPoint(a1x,a1y,c1x,c1y,c2x,c2y,a2x,a2y,0.5)
#3次ベジェの2つのコントロールポイントの交点を求める
[p2x,p2y]=@getCorssPoint(a1x,a1y,c1x,c1y,c2x,c2y,a2x,a2y)
#2次ベジェの中間点を求める
#http://geom.web.fc2.com/geometry/bezier/quadratic.html
[p3x,p3y]=@getQuadraticBezierPoint(a1x,a1y,p2x,p2y,a2x,a2y,0.5)
#3次ベジェと2次ベジェの中間点の距離を求める
distance=@getDistance(p1x,p1y,p3x,p3y)
#中間点の距離が指定された値以下なら精度を満たしているので完了
if distance<=precision
result.push [a1x,a1y,p2x,p2y,a2x,a2y]
#それ以外だと分割して再帰
else
#3次ベジェを分割する
splited=@split(a1x,a1y,c1x,c1y,c2x,c2y,a2x,a2y,0.5)
#2つの3次ベジェを再帰的に計算
@cubicBezierToQuadraticBezier(splited[0],splited[1],splited[2],splited[3],splited[4],splited[5],splited[6],splited[7],precision,result)
@cubicBezierToQuadraticBezier(splited[8],splited[9],splited[10],splited[11],splited[12],splited[13],splited[14],splited[15],precision,result)
#2次ベジェの中間点の座標を求める
#a1はアンカーポイント1
#c1はアンカーポイント1から出るコントロールポイント
#c2はアンカーポイント2から出るコントロールポイント
#a2はアンカーポイント2
#tは分割する割合、0~1の範囲
@getQuadraticBezierPoint:(a1x,a1y,cx,cy,a2x,a2y,t)->
tp = 1 - t
x = t*t*a2x + 2*t*tp*cx + tp*tp*a1x
y = t*t*a2y + 2*t*tp*cy + tp*tp*a1y
return [x,y]
#座標(p1x,p2y)と座標(p2x,p2y)を結ぶ線分をt:1-tで分割した座標を求める
@interpolate:(p1x,p1y,p2x,p2y,t)->
return [
p1x+(p2x-p1x)*t,
p1y+(p2y-p1y)*t
]
#3次ベジェをt:1-tで分割した点の座標を求める
#a1はアンカーポイント1
#c1はアンカーポイント1から出るコントロールポイント
#c2はアンカーポイント2から出るコントロールポイント
#a2はアンカーポイント2
#tは分割する割合、0~1の範囲
@getCubicBezierPoint:(a1x,a1y,c1x,c1y,c2x,c2y,a2x,a2y,t)->
tp = 1.0 - t;
return [
a1x*tp*tp*tp + 3*c1x*t*tp*tp + 3*c2x*t*t*tp + a2x*t*t*t,
a1y*tp*tp*tp + 3*c1y*t*tp*tp + 3*c2y*t*t*tp + a2y*t*t*t
]
#(x1,y1),(x2,y2)を通る直線と(x3,y3),(x4,y4)を通る直線の交点の座標を求める
#http://mf-atelier.sakura.ne.jp/mf-atelier/modules/tips/index.php/program/algorithm/a1.html
@getCorssPoint:(x1,y1,x2,y2,x3,y3,x4,y4)->
ksi = (y4 - y3) * (x4 - x1) - (x4 - x3) * (y4 - y1)
delta = (x2 - x1) * (y4 - y3) - (y2 - y1) * (x4 - x3)
ramda = ksi / delta
x = x1 + ramda * (x2 - x1)
y = y1 + ramda * (y2 - y1)
return [x,y]
#座標(p1x,p1y)と(p2x,p2y)の距離を求める
@getDistance:(p1x,p1y,p2x,p2y)->
dx=p1x-p2x
dy=p1y-p2y
Math.sqrt(dx*dx+dy*dy)
#3次ベジェをt:1-tで分割した点の座標とコントロールポイントを得る
#a1はアンカーポイント1
#c1はアンカーポイント1から出るコントロールポイント
#c2はアンカーポイント2から出るコントロールポイント
#a2はアンカーポイント2
@split:(a1x,a1y,c1x,c1y,c2x,c2y,a2x,a2y,t)->
[tpx,tpy] = @getCubicBezierPoint(a1x,a1y,c1x,c1y,c2x,c2y,a2x,a2y,t);
[mx,my] = @interpolate( c2x,c2y, c1x,c1y, t);
#コントロールポイント
[ac0x,ac0y] = @interpolate( c1x,c1y, a1x,a1y, t);
[ac1x,ac1y] = @interpolate( mx,my, ac0x,ac0y, t);
#コントロールポイント
[bc1x,bc1y] = @interpolate( a2x,a2y, c2x,c2y, t);
[bc0x,bc0y] = @interpolate( bc1x,bc1y, mx,my, t);
return [
a1x,a1y,ac0x,ac0y,ac1x,ac1y,tpx,tpy
tpx,tpy,bc0x,bc0y,bc1x,bc1y,a2x,a2y
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment