月齢計算とRealBasicでの3D表示
RealBasic で作成中のエディタに仕込んだ検索ルーティンにバグが多くて正常動作せず、いまだ使い物になっていない。どうも日本語の検索でときおり失敗があるようなので、別の検索エンジンを組み込むことを検討しているが、これまたこれで長い道のりになりそうで… 。
▽あんまり苛々してもはじまらないので、以前 Objective C で作成した月齢計算機、moonphase を「移植」して憂さばらし(?)。
■月齢計算
「移植」といっても、基本的な部分は変わらない。月齢算出については、誤差±2日程度で計算可能な簡易数式を使う。
b=(西暦年 − 1740)×210/19−2+月+日+補正項
を30で除した余りが月齢の近似値になる(つまり b mod 30が近似値)。
補正項は、1月であれば「1」、2月であれば「2」、3月または5月であれば「−1」をとる。その他の月では「0」になる。古くから用いられている近似式である。
さて、MacOS X の cocoa を使ったときは、月齢計算結果を参照して、月のだいたいの見かけを2次元的に描画していたのだが、そのまま移植してもつまらない。今回は3D表示をしてみることにする。その点 RealBasic では、RB3DSpace というコントロールがある。3次元表示用のコントロールだ。デモを走らせてみると、動くものと動かないものとがある。多少バグいらしい。
■ RB3DSpace
この RB3DSpace、実は利用するのにちょっとした準備がいる。
フリーの3Dライブラリ、quesa が必要になる(http://www.quesa.org/)のだ。quesa は、Apple がそのむかし出していた、QuickDraw 3D互換の API を備えている(いまでも MacOS 9.x (クラシック環境)を起動するときにはアイコンパレードに並ぶみたいだけど)。quesa は Windows でも、linux でも動作する。さらに、Apple が QuickDraw 3D を凍結した今、MacOS X でも QuickDraw 3D の API を使うのに、この quesa が必要になる。
■月の描画
今回は開発を至極簡便にするために、のっぺらぼーの「月面」を描画する。要するに空間内に球体を浮かべ、太陽役のライトの位置を変更して月齢表示っぽくするのである。子供のころ、バスケットボールだかバレーボールだかをつかい、太陽光線の方向に対して位置を変えながらボールにできる陰を観察し、月齢に応じて月の満ち欠けが発生する仕組みを習ったのではないだろうか。あれを仮想的に実践するわけである。
月としての球体は、3DMF(3DMetaFile、なつかしいな)で記述する。3DMFについては、Apple のドキュメントが Web で参照できる。
球体はプリミティブの一つでしかない。ドキュメントにあるサンプルをちょいちょいといじって、球体を表現する3DMFが完成。
※その昔、計算で得た粒子軌跡を3D表示するのに、この3DMFを使ってみたが、まぁまぁ良好であったのを思い出した。3DMFには、テキストで表現できるモードがあるので、コードの吐き出しが大層簡単だったのだ。今回のもテキスト表現をつかう。
3DMetafile ( 1 5 Normal tableofcontents0> )
Container (
Ellipsoid (
3 0 0
0 3 0
0 0 3
0 0 0
0
1
0
1
)
Container (
AttributeSet ( )
DiffuseColor ( 1.0 1.0 0.7 )
)
)
本当の月は灰色っぽいはずなのだが、月らしいイメージを出すため、ちょっと黄みがかった白色にしてみた。このテキストを、moonshape.3dmf というファイル名でアプリケーションが出来る予定のフォルダにしまう。
このプリミティブを RD3DSpace に導入するには、
f =GetFolderItem("moonshape.3dmf")
obj = New Object3Dobj.AddShapeFromFile f
これだけ。
■ライティング
さてライティング。RD3DSpace には、環境光(アンビエントライト)と、フラッドライトとが最初から用意されている。今回は夜空に浮かぶ月に対して、この周囲を(相対的な意味で)回る「太陽」のライトを照射して満ち欠けを表現するつもり。つまり環境光や余計なフラッドライトは不要。ということでプロパティ設定ですっかりオフにしてしまいます。
つぎに、太陽役のライトを登場させる。ライトは Light3D という構造体になっている。ポジションと、ディレクション(方向)とを設定できるようだが、ディレクションを設定しなければ(NILにしておけば)全方位に広がる光源(点光源)として扱われるらしい。今回は太陽役だから、これ(点光源)で十分。と、いうことで、RD3DSpace にライトを足す。
th = 6.283 / 30 * p
pl = New Light3D
pl.Attenuation=0
pl.Brightness=80
pl.Position = New Vector3D
pl.Position.X=cLgtDistance * sin(th)
pl.Position.Y=0
pl.Position.Z=-cLgtDistance * cos(th)Phase3DView.AddLight pl
ここに、p はフェーズ、つまり月齢値を表わしており、th (theta のつもり)は、月を模した球体に対する角度位置を算出したもの。
カメラ(視点)をZ軸の正の方向のどこかに置く予定なので、満月のとき、このカメラと同じ方向から照射したい。で、こんな感じに。
明るさは適当。アッテネーション(距離による減衰)は面倒なので「ない」ことに。なお、最初は Light3D 構造体内のポジション変数は NIL になってい、ちゃんと Vector3D を初期化してやらないといけない。
■カメラ
最後にカメラを置く。これをしないと何も表示されないのだ。知らずに「表示されない」というQ(Question)がFA(Frequently Asked)であるとか。
v=New Vector3D
v.X=0
v.Y=0
v.Z=10
Phase3DView.Camera.Position= vPhase3DView.Refresh(true)
最後の一行は「オマジナイ。」きっと、なくても大丈夫でしょうが。
■デバッグ用項目
今回は、現在日時から演算した月齢のほか、月齢を直接指定してライティングの様子をためす機能を一応実装し、それでライティングがまぁまぁ精度よく機能する位置を調べた。一番時間がかかった(というか思わず楽しんでしまった)のは、この工程だったかも。まぁ、今回は大層簡単な物体相手だからよかったが、数値だけで3D空間を設定するのはやっぱり難しいので、複雑なもの相手の場合、別途ツールの開発が必要かも。
■スクリーンショット
最後に、できあがったアプリケーションのスクリーンショット。今日(8月7日)の月齢は「12」らしい。web で調べてみると、12.9 とあるので、まぁまぁ正しい?
ちなみに、こちらは ObjectiveC 版の画像
計算値がちょっとばかり違うようなのは、ご愛嬌(すこし数式をイジったんだと思うけど忘れてしまった)。
□ Amazon のサーバがちょーし悪いみたいなので、ご紹介したい本などは、後ほど追記させていただきます。
| 固定リンク







コメント