機械学習基礎理論独習

誤りがあればご指摘いただけると幸いです。数式が整うまで少し時間かかります。リンクフリーです。

勉強ログです。リンクフリーです
目次へ戻る

2次ベジェ曲線による円弧の近似

2次ベジェ曲線の制御点を求める

2次ベジェ曲線で円弧を近似します。
円弧は単位円の部分集合とし、円弧の角度を  \theta\in(0,\pi) とします。
両端点 {\bf P}_0,{\bf P}_2 は円弧と一致するものとします。
両端点の接線ベクトルは円弧の接線ベクトルと方向が同じものであるとします。
 {\bf P}_1{\bf P}_2 の接線ベクトル  (0,1) と同じ方向であることから {\bf P}_1=(1,\kappa) であり、
また、{\bf P}_2 の接線ベクトル  (\sin\theta,-\cos\theta) と同じ方向であることから {\bf P}_1=(\cos\theta+\kappa\sin\theta,\sin\theta-\kappa\cos\theta) となります。

\begin{eqnarray}
{\bf P}(t)=(1-t)^2{\bf P}_0+2(1-t)t{\bf P}_1+t^2{\bf P}_2\tag{1}
\end{eqnarray}

\begin{eqnarray}
\left\{
    \begin{array}{l}
     {\bf P}_0=(\cos\theta,\sin\theta)\\
     {\bf P}_1=(1, \tan\frac{\theta}{2})\\
     {\bf P}_2=(1,0)\\
    \end{array}
  \right.\tag{2}
\end{eqnarray}

{\bf P}_1x 成分を比較して、\kappa を求めます。

\begin{eqnarray}
&&1=\cos\theta+\kappa\sin\theta\\
&&\Rightarrow\kappa=\frac{1-\cos\theta}{\sin\theta}\\
&&\Rightarrow\kappa=\tan\frac{\theta}{2}\tag{3}
\end{eqnarray}

(3) により、\kappa\theta にのみ依存することが分かりました。
\kappa が求まったので、2次ベジェ曲線の制御点の座標が定まりました。

円弧の中心からの距離が最大となるパラメータの値

円弧の中心からの距離が最大となるパラメータ t の値を求めてみます。
円弧の中心は原点なので、距離の2乗は以下のようになります。

\begin{eqnarray}
L(t)={\bf P}_x(t)^2+{\bf P}_y(t)^2\tag{4}
\end{eqnarray}

(6)t微分して =0 と置きます。

\begin{eqnarray}
L'(t)=0\tag{5}
\end{eqnarray}

L'(t) はかなり複雑な式なります。(なので、記載しません(できません)。)
(5)t について解いた結果は、以下となります。(この解法については、本記事の下側に記載します。)

\begin{eqnarray}
t=0,\frac{1}{2},1\tag{6}
\end{eqnarray}

(6)t=0,1 は円弧上の点であり、円弧の中心からの距離は極小値(最小値) 1 を取ります。

\begin{eqnarray}
 \left|\left| {\bf P}(0) \right|\right|=\left|\left|  {\bf P}(1)\right|\right|=1\tag{7}
\end{eqnarray}

 t=\frac{1}{2} の時、円弧の中心からの距離は極大値(最大値)を取ります。

\begin{eqnarray}
 \left|\left| {\bf P}\left(\frac{1}{2}\right)\right|\right|=\sqrt{\left(\frac{\sin{\left(\theta \right)}}{4} + \frac{\tan{\left(\frac{\theta}{2} \right)}}{2}\right)^{2} + \left(\frac{\cos{\left(\theta \right)}}{4} + \frac{3}{4}\right)^{2}}\tag{8}
\end{eqnarray}

\theta=22.5,45,67.5,90,135 度の時の  \left|\left| {\bf P}(\frac{1}{2})\right|\right| を計算してみます。

角度 長さ
22.5度 1.00018821930577
45度 1.00313586640184
67.5度 1.01707969308632
90度 1.06066017177982
112.5度 1.17776133964622
135度 1.49790468105892

上の表より、2次ベジェ曲線で円弧を近似するときはが45度ぐらいで、区切るのが良さそうです。
グラフは以下になります。


式 (5) のSymPyによる解法

SymPyでは式 (5)\thetaのままだと t について解けないようです。(参考サイトによるとmaximaなら多分解けます。)
\theta に適当な値を代入すれば t について解くことができます。

import sympy

sympy.init_printing()

sympy.var("px py p0_x p0_y p1_x p1_y p2_x p2_y t theta kappa alpha")
px = (1-t)**2 * p0_x + 2 * (1-t) * t * p1_x + t ** 2 * p2_x
py = (1-t)**2 * p0_y + 2 * (1-t) * t * p1_y + t ** 2 * p2_y

len2 = px ** 2 + py ** 2

len2 = len2.subs([
    (p0_x, sympy.cos(theta)), (p0_y, sympy.sin(theta)),
    (p1_x, 1), (p1_y, sympy.tan(theta/2)),
    (p2_x, 1), (p2_y, 0),
    (theta, sympy.pi * 0.5) # change here
])

len2d = sympy.diff(len2, t)
eq = sympy.Eq(len2d, 0)
solve = sympy.solve(eq, t)

display(solve)

コードの実行結果

式 (8) のSymPyによる解法

import sympy

sympy.init_printing()

sympy.var("px py p0_x p0_y p1_x p1_y p2_x p2_y t theta kappa alpha")
px = (1-t)**2 * p0_x + 2 * (1-t) * t * p1_x + t ** 2 * p2_x
py = (1-t)**2 * p0_y + 2 * (1-t) * t * p1_y + t ** 2 * p2_y

len2 = px ** 2 + py ** 2

len2 = len2.subs([
    (p0_x, sympy.cos(theta)), (p0_y, sympy.sin(theta)),
    (p1_x, 1), (p1_y, sympy.tan(theta/2)),
    (p2_x, 1), (p2_y, 0),
    (t, sympy.Rational(1,2)),
    #(theta, sympy.pi * 0.5) # change here
])

len = sympy.sqrt(len2)

lensub = len.subs([
    (theta, sympy.pi * 0.125), # change here
])
print(lensub.evalf())
print("\\begin{eqnarray}")
print("&&" + sympy.latex(len2) + "\\\\")
print("\\end{eqnarray}")

コードの実行結果
1.00018821930577
\begin{eqnarray}
&&\left(\frac{\sin{\left(\theta \right)}}{4} + \frac{\tan{\left(\frac{\theta}{2} \right)}}{2}\right)^{2} + \left(\frac{\cos{\left(\theta \right)}}{4} + \frac{3}{4}\right)^{2}\\
\end{eqnarray}

目次へ戻る