RINEARN > VCSSL > VCSSL実践講座 > 3Dグラフ描画ツールを作ろう! 〜 第5回 縮尺を自動調整する
このエントリーをはてなブックマークに追加
2013/09/02

3Dグラフ描画ツールを作ろう! 〜 第5回 縮尺を自動調整する


VCSSLで色々なものを作っていく「 VCSSL実践講座 」、3Dグラフ開発編の第5回です。

前回では、第 1 〜 3 回までの締めとして、後半での拡張がしやすいように、コードを整理しました。 という事で今回からは、また少しずつ機能を拡張していきましょう。 今回は、最大最小値に応じて、縮尺を自動調整する処理を実装します。

座標値の大きなファイルをプロットしてみると…

ここまでは、サンプルの座標値ファイルとして、X,Y,Z共に範囲が -1.0 〜 1.0 に収まるものをプロットしてきました。

なので、読み込んだ座標値そのものの位置に( 倍率 1 倍で )点をプロットするような処理でも、特に問題は無かったわけです。

しかし、もしも X, Y, Z 値の範囲がもっと大きいような座標値ファイルをプロットするとどうなるでしょうか ? おそらくそのまま大きなグラフとして、画面に描画される事になります。 画面をはみ出してしまう場合もあるでしょう。

サンプルとして、ローレンツアトラクタの座標値ファイルを書き出す

実際に、現状のコードのまま、範囲の大きな座標値ファイルをプロットしてみましょう。

サンプルの座標値ファイルは、RINEARN CODE にあるローレンツアトラクタのプログラム(詳細は下記)で書き出したものを使います。

このコードは以下のようなものです。

このコードを実行すると、ローレンツ方程式の解曲線をRK4法で計算し、結果を「 lorenz.dat3d 」というファイルに書き出してくれます。

面倒な方は、以下のリンクから座標値ファイルをダウンロードしてご使用下さい。

>> 座標値ファイルをダウロード

なお、上のコードを実行すると、VCSSL付属のグラフ機能(内部ではリニアングラフ3Dが使用されます)で、3Dグラフにプロットされます:

実行結果スクリーンショットの画像、ローレンツアトラクタがプロットされる。
実行結果
ローレンツ方程式の解曲線(ローレンツアトラクタ)がプロットされます。座標値ファイルは「 lorenz.dat3d 」に書き出されます。

今回は最終的に、これと同じ形のグラフがプロットできるようになれば目的達成です。

前回のコードのままプロットしてみると…

それでは、上の「 lorenz.dat 」を、前回 のコードのままプロットしてみましょう。

まず注意点ですが、今回プロットする「 lorenz.dat 」は、これまでのCSV(コンマ区切り)形式では無く、TSV(タブ区切り)形式で座標値が記載されています。実際にテキストエディタで開いてみると、中身は:

0.1000004978179402 0.10099595210105944 0.09995401578903723
0.10056912505277632 0.13387176984999052 0.09845423623579286
0.10218972262508343 0.16700338595543118 0.0969889083866805
0.10484208323562481 0.2007349444388992 0.0955585098187121
0.10851772077834687 0.23540473131358178 0.09416397051600152

と、確かに値が、コンマの代わりにタブ(≒空白)で区切られていますね。

この形式では、ファイルをTSV読み込みモード "rtsv" で開く必要がります。なので、前回のコードの先頭部分をこう書き換えます:

実際のコード全体は以下のようになります。

こんな感じです。このコードを実行してみると…

実行結果のスクリーンショット、原点から点が飛び飛びで、画面外へと飛んでいく図。
実行結果
大きすぎて画面に収まっていません。

めっちゃ飛んでいってます。やはり縮尺調整無しだと、大きすぎて画面に収まっていません。

恐らくこっちの方向にローレンツアトラクタがあるはずだ…とZ軸(青色の矢印)を画面奥の方向に向けてみると…

実行結果のスクリーンショット、遠くのほうにローレンツアトラクタが見えます。
実行結果
遠くのほうにローレンツアトラクタが見えます。

いました。遥か遠くに、衝突した渦巻き銀河のような超巨大ローレンツアトラクタが。

なんだか予想外にロマンチックな絵が見れましたが、グラフとしては使い物になりませんね。画面に収まるようにプロットの縮尺を調整しなければいけません。

縮尺の自動調整処理を実装する

という事で、プロット範囲が画面にちょうど良く収まるように、縮尺の自動調整処理を実装しましょう。

これは素直に考えると、画面の描画倍率を変えてしまえば簡単なような気がします。 しかしそうすると、XYZ方向の比率を独立に調整できません。また、座標軸の大きさをプロット範囲に合うように描画してやらないといけなかったり、何かと面倒です。

そこで、ファイルから読み込んだ座標にそのまま点をプロットするのでは無く、拡大縮小などの変換をかませた座標に点をプロットするようにします。

具体的な処理の流れ

具体的な処理の流れは、以下のようになります:

  • もとの座標値配列「 vector 」とは別に、拡大縮小変換した座標値を入れる配列「 transformedVector 」を用意する
  • vector のXYZ座標それぞれ、最大最小値を求める(拡大縮小に必要)
  • vector の座標値を、XYZ方向がそれぞれ-1.0〜1.0の立方体領域にちょうど収まるよう拡大縮小変換し、それを transformedVector に代入する
  • transformedVector の座標に点をプロットする (従来は vector の座標にそのままプロットしていた)

上記の通り実装

実際に上記の通りに処理を実装すると、以下のようなコードになりました。
※ getWholeRange 関数と transform 関数を新たに追加

実際に上のコードを実行すると、以下のような画面が表示されます。

実行結果のスクリーンショット、ローレンツアトラクタがちょうどいい大きさで表示されます。
実行結果
ローレンツアトラクタがちょうどいい大きさで表示されました。

この通り、今度はローレンツアトラクタがちょうどいい大きさで表示されました。具体的には、XYZ方向が-1.0〜1.0の立方体領域に、ちょうど収まるようにプロットされています。先ほどのリニアングラフ3Dによるプロット結果とも一致していますね。

それでは、拡張したコード各部を見ていきます。今回は内容の都合上、コードの順番が上下飛び飛びになります。

グローバル領域

まずグローバル領域では、拡大縮小変換した座標値を格納する座標値配列「 transformedVector 」と、プロット範囲の最大最小値をまとめた構造体「 Range 」を定義しています。

X,Y,Zの最大最小値は合計6つの値になりますが、引数や戻り値にほぼワンセットで受け渡しする事になるので、こうして構造体にまとめておくと楽になります。

getWholeRange 関数で最大最小値を求める

実際、今回新たに実装した getWholeRange 関数では、座標値配列 vector の最大最小値を求めて Range 構造体で返すようにしています:

ポイントは、暫定最小値の初期値に float 型の最大値 System.FLOAT_MAX を、暫定最大値の初期値に float 型の最小値 System.FLOAT_MIN をセットする点です。あべこべですね。

あとは、それらと vector 各点のXYZ値を比較しながら、暫定最大値より大きい値があれば暫定値更新、といった具合で、最終的に vector のXYZ最大最小値を求めています。

transform 関数で拡大縮小変換

続いて、これも今回新たに実装した transform 関数です。この関数は、座標値配列の拡大縮小変換を行います。

この関数の引数には、先の getWholeRange 関数で求めた、vector のXYZ最大最小値を受け取ります。

この最大最小値に基づいて、座標値配列 vector を、座標値の範囲がXYZ方向それぞれ -1.0〜1.0 の立方体領域にちょうど収まるよう拡大縮小変換して、その結果を配列 transformedVector に格納します。

変換の過程を詳しく追ってみましょう。まずは、vector → transformedVector と、そのまま座標値を移します。

続いて、XYZ方向それぞれ最小値だけ減算します。これにより、XYZ最小値の位置が原点 ( 0, 0, 0 ) に平行移動されます。

この状態では、座標値の領域は一辺の長さが(最大値-最小値)の直方体領域になっています。これを一辺の長さが 2 の立方体領域にしたいわけですから、 XYZ値それぞれに 2 / (最大値-最小値) を乗算します。

これで一辺の長さ 2 の立方体領域になりましたが、端(最小値)が原点のままなので、0.0〜2.0 の範囲になっています。 これを -1.0〜1.0 の範囲にしたいわけですから、XYZ値それぞれから 1 を減算します。

以上で、XYZ値がそれぞれ -1.0〜1.0 の立方体領域に収まるよう変換できました。

initialize 関数で、上の 2 つの関数を呼ぶように追記

続いて initialize 関数です。これは素直に、上の 2 つの関数を呼ぶように書き加えます。

まずファイルを読み込んで座標値配列 vector にセットされたところで、getWholeRange 関数で最大最小値を取得( range )しています。 そしてそれを引数にして transform 関数を呼び、vector を拡大縮小して transformedVector にセットさせています。

plotPoint関数で、transformedVector の座標をプロットするよう改修

最後に、点ポリゴンを配置してプロットする、plotPoint 関数です。

この関数は、前回 までは vector の座標に点をプロットしていましたが、それを transformedVector に代えただけです。

具体的には、拡大縮小変換された transformedVector 各座標の位置に、点ポリゴンを生成して配置します。

今回の内容はこんなところです。今回は少しヘビーでしたが、恐らく次回は軽い内容になります。次回は、グラフに目盛りを付けてみます。



スポンサーリンク


このエントリーをはてなブックマークに追加
RINEARN > VCSSL > VCSSL実践講座 > 3Dグラフ描画ツールを作ろう! 〜 第5回 縮尺を自動調整する
  スポンサー リンク

  インデックス
  サイトマップ
- その他のテクノロジ -
  スポンサー リンク

  おすすめ / 人気のコーナー
フリーソフト
RINEARN では、インストール不要の各種解析ソフトを無償公開しています。Windows 8.1 で動作確認済み !
ピックアップ
【VCSSL実践講座】 3Dグラフ描画ツールを作ろう!
C言語系の簡易プログラミング言語「 VCSSL 」で、色々なものを作っていく連載記事コーナーです。現在は3Dグラフ描画ツールを開発中 !
2013年08月29日
【RINEARN CODE】 凸レンズを通過する波のシミュレーション
凸レンズ形状の高密度媒質を通過する、波のシミュレーションです。まっすぐ入射した波がレンズ通過時に屈折し、焦点へ収束する様子がアニメーションで楽しめます。
2013年03月15日
  新着情報
RSSフィード
RINEARN の新着・更新情報をRSSフィードで配信しています !
新着リスト
台形近似による数値積分

積分の値を数値的に求めます。長方形近似よりも高精度な方法として、台形で近似した微小領域を足しあげる方法を使用します。
2015年06月25日
長方形近似による数値積分

積分の値を数値的に求めます。長方形で近似した微小領域を足しあげる、最も単純な方法を使用します。
2014年11月01日
小数(浮動小数点数)から分数へ近似的に変換する

小数(浮動小数点数)を、適当な誤差の範囲内で、近い分数に変換するプログラムです。
2014年08月10日
ベクターソフトニュース様でリニアングラフ3Dをご紹介頂きました!

オンラインソフトウェア流通サイトVector様の「ベクターソフトニュース」コーナーにて、リニアングラフ3Dを取り上げて頂きました。
2014年07月31日
ユーザーが入力した数式を2次元グラフにプロットする

実行時にユーザーが入力した数式の値を、2次元グラフにプロットするサンプルプログラムです。
2013年11月30日
配列を3次元グラフにプロットする

座標値配列の内容を、3次元グラフにプロットするサンプルプログラムです。
2013年11月28日
配列を2次元グラフにプロットする

座標値配列の内容を、2次元グラフにプロットするサンプルプログラムです。
2013年11月28日
ファイルを3次元グラフにプロットする

座標値ファイルの内容を、3次元グラフにプロットするサンプルプログラムです。
2013年11月27日
ファイルを2次元グラフにプロットする

座標値ファイルの内容を、2次元グラフにプロットするサンプルプログラムです。
2013年11月26日
2013年の不具合修正情報

2013年1月〜12月に実施された不具合修正に関する情報を掲載しています。
2013年11月01日
3Dグラフ描画ツールを作ろう!〜第5回縮尺を自動調整する

VCSSL実践講座、3Dグラフ開発編。第5回では、最大最小値に応じて、縮尺を自動調整する処理を実装します。
2013年09月02日
3Dグラフ描画ツールを作ろう!〜第5回縮尺を自動調整する

VCSSL実践講座、3Dグラフ開発編。第5回では、最大最小値に応じて、縮尺を自動調整する処理を実装します。
2013年09月02日
  Twitter