楕円面上の測地線

こちらの記事が面白かったので
http://d.hatena.ne.jp/rikunora/20100905/p1
昔作った楕円面上の測地線を計算するコードを貼っておきます。コード中の計算式はA4一枚くらい使って手計算して結果だけ書いてあるんで今読むとどう導出したかよく分かりません^^;
筋道としては緯度と経度を時間の関数にしてある時間の間の経路長を積分で表してそれが極小になるようオイラーラグランジュ方程式を緯度と経度について出した後、出てきた二つの式を使って緯度を経度の関数と考えた場合の一つの微分方程式に直して解いています。

追記

上の計算はR^nからR^m (楕円面だと緯度経度の二次元面から三次元ユークリッド空間)へ解析関数でマップされる多様体の場合ですが、もっと一般的に三角メッシュで現される曲面について測地線を計算しました。データはフリーのものを拾って改造。これなら手計算不要。CPUパワーに任せて細かく面を分割すれば、どんな面でもOK. まあメッシュ生成自体、結構難しくて奥が深いんですが。アルゴリズムは単純に、三角形上で直線で進み、辺に来たら二つの三角を伸ばして平にして直線を進む。
データはこちらからダウンロードhttp://artist-3d.com/free_3d_models/dnm/model_disp.php?uid=592&count=count

以下楕円面の測地線コード

// Calc geodecics on an ellipsoid
// z len = 1
// x and y len = r0
#define r0 (4.0)

#include <stdio.h>
#include <stdlib.h>
#include <math.h>


int  main(int argc, char **argv)
{

    double th=0.0; //initial longitude 
    double f = 0.0; // initial latitude
    double dt = 0.00001;

    double vf = (double)atof(argv[1]); // slope of initial velocity= df/dth

    double R = r0*r0-1.0;

    int i=0;
    while(th<M_PI*2)
    {
	double s=sin(f);
	double c=cos(f);
	double d0 = (1+R)*c*c;
	double c0 = 1+R*s*s;

	double d1 = -2*c*s*(1+R);
	double c1 = R*2*c*s;

	double af = (d1-c1*vf*vf)/(2*c0) + vf*vf*d1/d0;
	// (D'-C'f'f')/2C + D'/D f'f'
	//  V2= C(phi)vphi^2+D(phi)vth^2

	f += vf*dt;
	vf += af*dt;
	th += dt;

	if(i%3000==0 || th >= M_PI*2)
        {
	    double x = cos(th)*r0 * cos(f);
	    double y = sin(th)*r0 * cos(f);
	    double z = sin(f);
	    printf("%f %f  %f %f %f\n",th, f, x,y,z);
        }
	i++;
    }
    printf("\n\n\n");
}

三角メッシュの場合

1:現在線がいる辺、辺上の位置、線の方向と辺のなす内積、線がすすむ三角形を記録。
2:次に三角形の面上にあって線の方向に垂直なベクトルを求める。法線と線のベクトルの外積で計算可能。現在いる辺の両端から三角形にそって線を延ばしてこのベクトルと直交する点を計算することで線と各辺の交点を計算、線を延長した場合どちらの辺とどこで交わるか計算する。線とこの辺との内積を計算、辺をいまいる三角形と共有している隣の三角を求める。これらを1:の値に代入し処理を繰り返す