曲線の長さを求めたい。 しかし、根号の中が複雑で、数学的には難易度が高そうだ。
ベジエ曲線の長さを求める
曲線の長さを求めるには、積分すればいいが、不定積分ができないことが多く、ベジエ曲線もできなさそうである。
よって、数値計算によって地道に求める必要がある。
定積分のための公式として、台形公式やシンプソンの公式がある。
台形公式
定積分するということは、積分したい関数のグラフ上で、積分区間の部分の面積を求めることを意味する。
台形公式では、積分区間を刻み数に分け、分割された区間の面積を、台形の面積として近似する。
一つ目の区間が、上図の水色の部分の場合、その面積は(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); } // ・・・略 }