曲線の長さを求めたい。 しかし、根号の中が複雑で、数学的には難易度が高そうだ。
ベジエ曲線の長さを求める
曲線の長さを求めるには、積分すればいいが、不定積分ができないことが多く、ベジエ曲線もできなさそうである。
よって、数値計算によって地道に求める必要がある。
定積分のための公式として、台形公式やシンプソンの公式がある。
台形公式
定積分するということは、積分したい関数のグラフ上で、積分区間の部分の面積を求めることを意味する。
台形公式では、積分区間を刻み数に分け、分割された区間の面積を、台形の面積として近似する。
一つ目の区間が、上図の水色の部分の場合、その面積は(f0+f1)h/2 となる。これを繰り返して合計の面積を求める。
シンプソンの公式
台形公式では、分割した部分のグラフを直線として台形の公式で計算したが、シンプソンの公式では、分割した部分を二次曲線として近似する。
3点を通る曲線で近似するので、刻み数は偶数である必要がある。
この図の場合、水色の部分の面積は、(h/3)(f0 + 4f1 + f2) であり、繰り返して全体の面積を求める。
関数f の、st~ed の間を、刻み幅h以内で積分する。 hは、刻み数が奇数になってしまった場合には変化する。
class MyMath{
//積分する
//シンプソンの公式で積分する
public delegate double simpsonFunc(double d);
static public double simpson(double st, double ed, double h, simpsonFunc f)
{
//刻み数を決める
int nh = (int)(Math.Abs((ed - st) / h) + 1.0);
if (nh % 2 != 07) nh++;
h = (ed - st) / (nh - 1);
int cnt;
double total = 0;
for (cnt = 0; cnt < nh; cnt++) {
double tmpadd = f(h * cnt);
if (cnt == nh - 1) { //最後:そのまま
} else if (cnt == 0) { //最初:そのまま
} else if (cnt % 2 != 0) { //奇数
tmpadd *= 4;
} else { //偶数
tmpadd *= 2;
}
total += tmpadd;
}
return h / 3.0 * total;
}
}
class Bezier{
// 略・・・
// 積分したい関数 tを与えると、一番上にある図のルートの部分を計算して返す。
// d_val は曲線を1回微分した関数
public double lenfunc(double t) {
return Math.Sqrt(d_val(t).x * d_val(t).x + d_val(t).y * d_val(t).y);
}
// 最大刻み幅hでstからedの長さを求める。積分する関数として lenfunc を与える。
public double t2l(double st, double ed, double h) {
return MyMath.simpson(st, ed, h, lenfunc);
}
//呼び出し元
public void moto() {
t2l(0, 1, 0.00001);
}
// ・・・略
}


