POV-Rayでの曲線の描き方

# はじめに
これは [POV-Rayによる数学お絵かき入門 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/povray4math) の14日目の記事です.

今日の記事では次のような曲線の描き方について書きます.

![](https://i.imgur.com/eazsXJd.png)

公式ドキュメントでは以下が近いです.
http://www.povray.org/documentation/3.7.0/r3_4.html#r3_4_5_1_13

# 愚直な方法
この節では`macro`で定義した曲線を折れ線近似して曲線を描きます.

具体例として常螺旋$(\cos(t),\sin(t),t/5)$を次のように描きます.
ここで`a`, `b`はパラメータの動く範囲を表し, `N`は曲線の分割数を表しています.
`r`は曲線の半径です.

```Povray
#macro p(w)
    <cos(w),sin(w),w/5>
#end

#declare a=0;
#declare b=2*pi;
#declare N=12;
#declare r=0.03;

merge{
    #declare i=0;
    #while(i<N)
        #declare w=a+i*(b-a)/N;
        sphere{p(w),r}
        cylinder{p(w),p(w+(b-a)/N),r}
        #declare i=i+1;
    #end
    sphere{p(b),r}
    pigment{rgb<1,0,0>}
}
```

![](https://i.imgur.com/9OGnV53.png)

分割数`N`を十分に増やせば次のような滑らかな曲線が得られます.
ここでは$N=96$としました.

![](https://i.imgur.com/eazsXJd.png)



# linear_spline
実はPOV-Rayには`sphere_sweep`と呼ばれるオブジェクトが用意されており, 折れ線を描くには次のような構文になります.

```Povray
sphere_sweep {
	linear_spline
	NUM_OF_SPHERES,
	CENTER, RADIUS,
	CENTER, RADIUS,
	...
	CENTER, RADIUS
}
```

`RADIUS`の後のカンマ`,`はあっても無くても構いません.

## 具体例1
5つの点を繋ぐ折れ線は次のように書けます.
各点での半径をも指定する事が可能で, 折れ線の各線分では半径が線形補間されます.

```Povray
#declare a1=<-1,.3,-.4>;
#declare a2=<-1,0,.2>;
#declare a3=<0,-1,-.4>;
#declare a4=<1,0,1>;
#declare a5=<1,.3,.3>;
sphere{a1,0.2 pigment{rgbft<.5,.5,.5,.5,.5>}}
sphere{a2,0.2 pigment{rgbft<.5,.5,.5,.5,.5>}}
sphere{a3,0.2 pigment{rgbft<.5,.5,.5,.5,.5>}}
sphere{a4,0.2 pigment{rgbft<.5,.5,.5,.5,.5>}}
sphere{a5,0.2 pigment{rgbft<.5,.5,.5,.5,.5>}}

sphere_sweep {
    linear_spline
    5,
    a1,0.15
    a2,0.1
    a3,0.15
    a4,0.15
    a5,0.05
    pigment{rgb<0,1,0>}
}
```

![](https://i.imgur.com/NNcnwE1.png)


## 具体例2
これを使えば, 先程の愚直な方法をより簡潔に次のように書く事が出来ます.

```Povray
#macro p(w)
    <cos(w),sin(w),w/5>
#end

#declare a=0;
#declare b=2*pi;
#declare N=12;
#declare r=0.03;

sphere_sweep {
    linear_spline
    N+1,
    #declare i=0;
    #while(i<N+1)
        p(a+i*(b-a)/N),r
        #declare i=i+1;
    #end
    pigment{rgb<1,0,0>}
}
```

![](https://i.imgur.com/9OGnV53.png)

# b_spline
## Bスプライン曲線の解説
Bスプライン曲線とは, 簡単に述べれば「節点で出来る限り滑らかに繋げた区分多項式曲線」の事です.

一般のBスプライン曲線はde Boor Coxの漸化式によって定義されますが, POV-Rayでは一様な3次Bスプライン曲線のみが扱えます.

この一様3次Bスプライン曲線を定めるためには$n$個の制御点$\boldsymbol{a}_1,\dots,\boldsymbol{a}_n\in\mathbb{R}^d$を与える必要があります.
ここでは一般の定義には触れずに, 一様3次Bスプライン曲線の定義を述べましょう.

まず関数$f(t)$を次で定義します.

```math
\begin{align}
	f(t)
	=
	\begin{cases}
		\frac{1}{6}t^3 &(0\le t <1) \\
		-\frac{1}{2}t^3+2t^2-2t+\frac{2}{3} &(1\le t <2) \\
		\frac{1}{2}t^3-4t^2+10t-\frac{22}{3} &(2\le t <3) \\
		-\frac{1}{6}t^3+2t^2-8t+\frac{32}{3} &(3\le t <4) \\
		0 &\text{(otherwise)}
	\end{cases}
\end{align}
```

$f$は$C^2$級に多項式を繋いだ関数で, そのグラフは次の黒実線になります.

![](https://i.imgur.com/qlN9SAH.png)
https://www.desmos.com/calculator/gpqwa1ebap

関数$f$を元に基底関数と呼ばれる$n$個の関数を次で定義します.

```math
\begin{align}
	f_1(t)&=f(t) \\
	f_2(t)&=f(t-1) \\
	&\cdots \\
	f_n(t)&=f(t-n+1)
\end{align}
```

$n=5$では基底関数は次のようになります.

![](https://i.imgur.com/auAylsI.png)

これらの基底関数を$n$個の制御点を掛けて足しあげて得られる曲線がBスプライン曲線$\boldsymbol{p}(t)$です.

```math
\begin{align}
	\boldsymbol{p}(t)=\sum_{i=1}^n f_i(t)\boldsymbol{a}_i
\end{align}
```

これをDesmosで描いたものが次の曲線です.

![](https://i.imgur.com/2CKkyDj.gif)
https://www.desmos.com/calculator/1wdbuoy859

上gifを見れば分かるように, 曲線は制御点$\boldsymbol{a}_i$の平行移動で一緒に平行移動する部分(実線 $t\in [3,5]$ )と変形する部分(点線 $t\notin [3,5]$ )に分かれます.

これの違いは基底関数の和$\sum_{i=1}^n f_i(t)$を見れば明らかで, 制御点$\boldsymbol{a}_i$の平行移動で一緒に平行移動する部分ではこの和が$1$になっています.

![](https://i.imgur.com/1Ct1EEm.png)

## POV-RayでのBスプライン曲線の扱い
Bスプライン曲線を描くには`linear_spline`の代わりに`b_spline`を指定します.
POV-RayでBスプライン曲線を描くには折れ線の時と同様に次のようにします.

```Povray
sphere_sweep {
	b_spline
	NUM_OF_SPHERES,
	CENTER, RADIUS,
	CENTER, RADIUS,
	...
	CENTER, RADIUS
}
```

この場合は曲線だけでなく, 半径に対してもBスプライン補完が適用されます.
つまり, 半径も曲線に(正確には媒介変数に)沿って$C^2$級に変化します.

次に具体例を挙げます.

```Povray
sphere_sweep {
    b_spline
    5,
    a1,0.15
    a2,0.1
    a3,0.15
    a4,0.15
    a5,0.05
    pigment{rgb<0,1,0>}
}
```

![](https://i.imgur.com/yUfD7HV.png)


# cubic_spline
`b_spline`で指定したは制御点を通る曲線を通るとは限りませんでしたが, `cubic_spline`を指定すれば与えた点列を通る曲線を描画する事が出来ます.
少し調べましたが, 具体的な曲線描画アルゴリズムを筆者は良く分かっていません.
とにかく, 与えた点列を良しなに繋いでくれるオブジェクトとして`cubic_spline`は便利です.
こちらも同様に次のような構文です.

```Povray
sphere_sweep {
	cubic_spline
	NUM_OF_SPHERES,
	CENTER, RADIUS,
	CENTER, RADIUS,
	...
	CENTER, RADIUS
}
```

ただし, `b_spline`と同様に曲線を描くには最低4つの制御点が必要で, 例外的に両端点となる2点を曲線は通りません.
これらは端点での速度ベクトルを指定するために使われます.

## 具体例

```Povray
sphere_sweep {
    cubic_spline
    5,
    a1,0.15
    a2,0.1
    a3,0.15
    a4,0.15
    a5,0.05
    pigment{rgb<0,1,0>}
}
```

![](https://i.imgur.com/wLkMyIC.png)