
B−スプライン関数
自由曲線を描きたいことは、コンピュータで図形やグラフを扱う場合に結構ある。
こんな中で、理解し易く扱い易いのがB−スプラインだ。
利用するだけなら、そのアルゴリズムは容易に理解できると思う。
B−スプラインは、幾つかの制御点を定義してやることで、滑らかな一本の曲線を
得ることができる。ただし、曲線は始点と終点以外の制御点は通らない。
また、制御点が影響する範囲が限定されているため制御点1点を動かした影響が
全体にまで及ぶ事はない。
補間曲線を得たい場合、連立方程式を解いて、通過点を制御点に変換すれば
B−スプラインによる補間も可能である。
ここでは、自分のプログラムにB−スプラインを組み込みたいが、
数式を見ても概念が分からないという人のために原理解説する。
プログラマーには、数式を見るよりプログラムのソースリストを読んだ方が
原理を理解するのが早いという人もあると思う。
そこで、構造が見易いシンプルな動作確認済みのC言語ソース・リストを掲載した。
尚、ここでは実用的な3次に限定して説明する。
命題
M個の制御点P0,P1,・・・,Pn (n=M−1とする)
が与えられた時、これらの制御点で作られる3次B−スプライン曲線を計算せよ。
尚、3次B−スプラインの性質から、媒介変数の範囲は、(−1≦t≦n+1)とする。
パラメトリック表現
一般的にX−Y平面にグラフを描く場合、y=f(x)という
関数を使ってグラフを作図することが多い。
f(x)は任意の関数で例えば、x*x(xの2乗)ならば
放物線を描く事が出来る。
しかし、関数は1つのxに対して複数のy(解)は持たないため
これでは円の様な図形は描けない。
そこで、パラメトリック表現という手法を使う。
xとyの直接的な関係式ではなく、別の変数(媒介変数)を
定義して、その変数とx、その変数とyの関係を独立して定義する。
例えば、円を描くとしたら、角度θを媒介変数として、θを0度から360度
(ラジアンでは0〜2π)まで変化させた時、x座標とy座表がそれぞれ
どう変化するかを個別の関数で定義してやる。
単位円を描くなら
x=cos(θ)
y=sin(θ)
とすればいい。
B−スプラインもこの表現で定義される。
3次B−スプライン関数の定義
数式で書くと以下の様になる。

ただし、P0とPnを必ず通る様にするため、
P−2=P0,P−1=P0,Pn+1=Pn,Pn+2=Pnと定義する。
2次元平面の座標値を得る関数は、これを2組用意して以下の様になる。

重み関数
B−スプライン曲線は、幾つかの制御点の座標値に重みづけの係数を
掛けてブレンドするという、補間に似た考え方で曲線を得ている。
この係数を重み関数で得る訳だが、この関数は次数が決まれば
一定のものである。3次では次の様になる。

グラフで描くと以下の様な滑らかな曲線となる。

媒介変数と制御点の関係
重み関数に与える関数の入力がt−jとなっているが、少し理解しづらい。
これは媒介変数と制御点(のインデックス)を同じ座標軸で扱う事を意味する。
制御点をある座標軸に整数間隔で並べて、媒介変数はその間の任意の値を取る。
下のグラフでは、tを移動させるとtを頂点とした重み関数の山が移動することを示す。

この山にかかった制御点に関して、その座標値にその制御点における重み係数を
掛けてその結果を加算したものがtにおけるB−スプライン関数で得られる座標値となる。
ある制御点における重み係数を得るには、そのインデックスであるjを
数値としてtから引いて重み関数に渡せばいい訳だ。
その他、3次B−スプライン関数の性質
重み関数の次数は、制御点の数とは関係ない。
パラメトリック表現で規定されているので、2次元平面を3次元空間に
拡張するのは容易で、x,yと同様にz(t)を定義してやればいい。
JAVAアプレット・ソース [BSpline.java]
以下の様な図形を描画する。(プログラムはラインのみ)

import java.applet.Applet;
import java.awt.*;
import java.lang.Math;
public class BSpline extends Applet {
static final int M = 5;
static int n = M - 1;
static double px[]={
10.0,
77.0,
21.0,
171.0,
153.0
};
static double py[]={
30.0,
49.0,
165.0,
43.0,
164.0
};
public void start() {
}
public void BSpline() {
repaint();
}
public void update(Graphics g) {
paint(g);
}
public boolean handleEvent(Event event) {
return true;
}
public void paint( Graphics g ) {
double t,step;
g.setColor(Color.white);
g.fillRect(0,0,200,200);
g.setColor(Color.blue);
// Spline Draw main
step = (n + 1) * 0.01;
for(t = -1.0 ; t <= n + 1 ; t += step) {
DrawSpline(g,t);
}
}
public double Coefficent(double t) {
double r,d;
if(t < 0.0) t = -t;
if(t < 1.0) {
r = (3.0 * t * t * t -6.0 * t * t + 4.0) / 6.0;
}
else if(t < 2.0) {
d = t - 2.0;
r = -d * d * d / 6.0;
}
else r = 0.0;
return r;
}
public void DrawSpline(Graphics g, double t) {
double cn,x,y;
int j,k;
int ix,iy;
x = 0.0;
y = 0.0;
for(j = -2 ; j <= n + 2 ; j++) {
k = j;
if(j < 0) k = 0;
if(j > n) k = n;
cn = Coefficent(t - j);
x += px[k] * cn;
y += py[k] * cn;
}
// Plot Pixel
ix=(int)x;
iy=(int)y;
g.drawLine(ix,iy,ix,iy);
}
}
参考文献
共立出版株式会社 グラフィックスの数理(情報数学講座13)
杉原厚吉著
ISBN4-320-02663-2 C3341 \3200E
フューチャー・ホームページへ戻る
(C)1999,2002 Future on netyou ALL RIGHTS RESERVED.