Java 3D による三体問題のシミュレーション

[Home] [Research]

はじめに

2000年夏より中野先生、中村先生の Java3D 勉強会に参加させて戴くことになり、 そこで勉強した成果などを少しここで紹介したいと思います。

三体問題のシミュレーション

まず最初に Java3D を使ったシミュレーションプログラムのデモを 見せることからはじめたいと思います。

[ソース] [アプレット(plugin版)]

このプログラムは中心力で引き合う3つの質点による系の運動をシミュレートす るプログラムです。万有引力定数は1としています。質点は大きさを考えていな いので、衝突も考えていません。 三体問題は惑星の運動の研究から始まり、求積法によって一般解を求めることは 不可能であることが知られています。しかし、特殊解や定性的性質については昔 から多くの研究がなされています。

プログラムは3つの質点の質量、初期条件(位置、運動量)を与えて[START]ボタン を押せば逐次数値計算をしてその結果に応じて物体を動かしていきます。 [STATUS] 欄をチェックすると、数値計算の結果が逐次更新されます(ただしかな り遅くなります)。質点の軌跡を質点と同じ色の曲線で表示するようにしていま す。

数値計算するクラスの作成例

この三体問題の数値計算をするアルゴリズムにはシンプレクティック法と呼ばれ るハミルトン系にのみ適用できる数値計算法を使いました。ルンゲ=クッタ法等 に比べて、エネルギーの発散がおきないこと、計算量のわりには誤差が少ないこ となどが利点です。

この数値計算部分は再利用を考えて、Javaのオブジェクト指向を使ってカプセル 化してクラスにしました。おそらくクラスにする方法はいくつもあると思います が、ここではその一つとして私が行った方法を紹介します。

数値計算をカプセル化することにおいては、数値計算のアルゴリズムと、 方程式の両方をいろいろ組み合わせて使えるように、

と言うことを考えました。ハミルトン系の方程式のクラスについては、 ようにしました。数値解法のクラスについては、 ようにしました。実際の方程式や数値解法が全てこの仕様を持つように まず次の二つの抽象クラスを作りました。 実際のハミルトン系はこのクラス Hamilton を拡張して実装します。 三体問題の方程式 を与える実装は次のようになります。 [ThreeBody3dHam]

実際のシンプレクティック法を行うクラスはこのクラス HamiltonSolver を拡張して作ります。 たとえば、1次のシンプレクティック法による実装は次のようになります。 [Symplectic1st]

実際にはプログラムの中では


       try{
            ham = new ThreeBody3dHam(
                   Double.valueOf(tf_weight[0].getText()).doubleValue(),
                   Double.valueOf(tf_weight[1].getText()).doubleValue(),
                   Double.valueOf(tf_weight[2].getText()).doubleValue());
            for(int i=0;i<9;i++){
                ham.setPosition(i,
                         Double.valueOf(tf_q[i].getText()).doubleValue());
                ham.setMomentum(i,
                         Double.valueOf(tf_p[i].getText()).doubleValue());
            }
        }catch(NumberFormatException exc){
            ham = new ThreeBody3dHam(initData[0]);
        }
        symp = new Symplectic2nd(ham,0.01);

の様にして、ハミルトン系のクラスと数値計算のクラスのインスタンスを生成し (逐次計算は0.01刻み)、アニメーションを行うメソッドの中で、


            symp.next();
            // 球を動かす
            t3d.setTranslation(new Vector3d
                (symp.getQValue(0),
                 symp.getQValue(1),
                 symp.getQValue(2)));
            ptr[0].setTransform(t3d);

などとして逐次計算を行っています。

Java3Dにおける物体の座標系の仕組み(Transform3Dについて)

Java3D では、物体を表示するのに木構造を使っています。 (本当は Java3D には物体側の木構造とビュー側の木構造の2つの木構造があるの ですが、ここでは物体側の方のみ触れます) 物体側の木構造の根元(ルート)に当たる部分は BranchGroup と言うクラスで、 普通物体を表示するには物体の座標系を表す TransformGroup クラスを BranchGroup の枝としてつけて、その TransformGroup に物体を表すクラス (Shape3Dなど)をつけることによって行われます。
BranchGroup -- TransformGroup -- Shape3D
物体を動かすには、木構造において物体の 上にある TransformGroup を変化させればできます。実際の TransformGroup に おける座標系の設定は Transform3D と言うクラスによってなされています。

Transform3D では座標系の回転と平行移動など ( 剛体変換およびアフィン変換) を扱うことができます。 実際にはその内部で 4x4 の行列で表されています。 Transform3D には add() や mul() と言うメソッドが用意されていますが、 これらは全てこの 4x4 の行列に関する演算です。したがって合成変換を計算す るにはかけ算のメソッド mul() を使わなくてはなりません(平行移動の変換の合 成だと、ベクトルの足し算のように思われますが、add() を使ってはいけません)。

物体をまとめて回転させたりするときには、Java3D の物体の木構造において、 TransformGroup の枝にさらに TransformGroup をつけることがあります。 scene を BranchGroup、tr1 と tr2 を TransformGroup 、t1 と t2 を それぞれ TransformGroup の中で使われている Transform3D、shape を 物体を表す Shape3D としましょう。

scene -- tr1(t1) -- tr2(t2) -- shape
このような木構造になっているとき、scene からみて、shape は t1 x t2 の行列によって変換がされています。t2 は shape の t1 における相対 座標だと考えられます。

もちろん Java3D ではこれらの計算は自動的に行われるので、 プログラマは木構造さえ正確に作れば細かいことは気にする必要はありません。 しかし自分で物体の動きを制御しようとするときには注意が必要です。 たとえば、

scene -- tr(t) -+- tr1(t1) -- shape1
                +- tr2(t2) -- shape2
                +- tr3(t3) -- shape3
                +- tr4(t4) -- shape4
と言う木構造になっていて、shape1 と shape2 だけを90度回転させたいとしま す。回転を表す Transform3D をあらかじめ作っておいて、rot とします。 このとき、 この2通りは明らかに異なる動きをします。これを ルービックキューブのプログラムで見てみましょう。 前者のように右から回転行列をかけると、物体はその位置で回転してしまいます (自転) [なんちゃってルービックキューブ]。 後者のように左から回転行列をかけると、その座標に関して回転します(公 転) [本物のルービックキューブ]。
Copyright (C) 2001 TOKUNAGA Ken-ichi
tkenichi@nucba.ac.jp 徳永 健一名古屋商科大学