CSVファイルとグラフの描画

※ ここで扱う機能は、Vnanoでは未サポートで、VCSSLでのみ使用可能です。

今回は最終回として、少し応用的な内容を扱ってみます。 具体的には、CSVファイルに数値データを読み書きして、それをグラフソフトウェアに読み込ませ、2次元/3次元のグラフを描画してみます。

- 目次 -

CSVファイル

プログラムは、別のソフトなどと組み合わせて使う事も多い

これまでは、式や変数をはじめ、条件分岐や繰り返し、ファイルの読み書きなど、 プログラムにおける基本的な処理について説明してきました。 でも、前回までで基本はだいたい出そろいました。 これまでの内容を組み合わせれば、もう色々な事ができるはずです。 そこで最終回となる今回は、より実践的な内容に挑戦してみましょう。

これまでに扱ってきたのは、いわば「 プログラムの書き方を学ぶためのプログラム 」でした。 画面に表示する内容や、ファイルに書き込む内容なども、プログラムの動作を確認するためのものばかりでした。 変数の値や入力値などについてもそうで、いわば「 とりあえずの値 」を入れていましたね。

でも、実際のプログラムは、もっと具体的な目的があって使われるものです。 読み込んだり書き込んだりする内容も、ちゃんと意味や使い道があるものになるでしょう。 たとえば、なにか別のソフトで使うためのデータを作成したり、 逆に別のソフトで作ったデータを読み込んで処理したり、といったパターンはよくあります。

別のソフトやプログラムとデータをやり取りする方法はいくつかありますが、 単純にファイルの読み書きで行うのも、一般によく使われる方法です。 たとえばプログラムからデータをファイルに書き込んで、そのファイルを別のソフトで開くわけです。 人間同士が、手紙やメールでやり取りする感じに似ていますね。

ファイルの「 書き方 」によく使われるCSV形式

このようにファイル経由で別のソフトとやり取りする場合、 「 ファイル内に、データをどういう書き方で書くか 」というルール を決めておく必要があります。 これが書く側と読む側でちぐはぐだと、データを正しく伝えられません。

さて、「 ファイルの読み書き 」の回 では、 以下の2通りの書き方を使いました:

(1) は究極的にシンプルですが、値の個数が多い場合にファイルだらけになって管理が大変です。 その点で (2) のほうが実用的でしょう。でも、もう一歩すすんで、1行に複数の値を書けると、さらに便利です。 そこで実際には、

のような書き方がよく使われます。 こうすると、たとえば以下のような「 表 」の形のデータを書くのに便利です:

表計算ソフトでの、表の形のデータの図

このような表の形のデータは、よく表計算ソフトなどで登場します。 実際に表計算ソフトで上の図のデータを打ち込み、 「 CSV 」という形式で保存(「 エクスポート 」メニューなどから )すると、以下のようなファイルが作られます:

[ 表計算ソフトで保存したファイル「 hyou.csv( ひょう.csv )」の中身 ]

0,0
0.1,0.01
0.2,0.04
0.3,0.09
0.4,0.16
0.5,0.25
0.6,0.36
0.7,0.49
0.8,0.64
0.9,0.81
1,1

( ※ CSVファイルをふつうにダブルクリックすると、表計算ソフトで開かれてしまう事が多いでしょう。 その場合はファイルを右クリックし、「 プログラムから開く 」メニューからテキストエディタ( メモ帳など )を選択して開いてください。 )

上のように、表の行( よこ方向の並び )はそのままファイル内の行に対応しています。 そして、表の列( たて方向の並び )は、ファイル内で「 , 」記号で区切って表されています。 表の左側の列にある値は「 , 」記号の左に、表の右側の列にある値は「 , 」記号の右に、という具合です。

「 , 」記号はコンマやカンマと呼ぶので、このようなファイルの書き方は、「 カンマ区切りの値 」を意味する英語 「 Comma Separated Values 」を略して「 CSV( シーエスブイ ) 」形式と呼ばれています。 そしてCSV形式で書かれたファイルはCSVファイルと呼ばれます。 CSVファイルは、いろいろなソフトで幅広くサポートされているため、プログラムでも読み書きする場面がよくあります。 よって、今回もCSV形式を用いる事にしましょう。

グラフソフト

CSVファイルに描かれたデータから、グラフを描くソフト

さて、CSVファイルを使って何をするかですが、今回は絵的にわかりやすい 「 グラフを描く 」という事を行ってみましょう。 グラフを描くソフトは、CSVファイルを使う典型的なソフトの一つです。 高機能なものもありますが、今回はシンプルな「 1 行ごとに 1 つの点を描いてくれるタイプのグラフソフト 」を用意します。 たとえば:

などがあります。これらはPC用の各種OSで動作し、無料で使用できます。

試しにリニアングラフ2D( ※ 3Dではない )を使って、 先ほどのCSVファイルからグラフを描いてみましょう。 まず上のリンクからソフトをダウンロード・展開し、 中の「 RinearnGraph2D.jar(JARファイル)」をダブルクリックして起動してください。

そしてメニューバーの「 File(ファイル)」メニューから「 Open File(ファイルを開く)」を選び、 先ほど表計算ソフトで保存したCSVファイル「 hyou.csv 」を読み込んでプロットしてください。

( ※ 表計算ソフトが無い場合などは、上で掲載してある「 hyou.csv 」の内容をテキストエディタ( メモ帳など )で書いて保存してください。 保存の際は、「 ファイルの種類 」の項目を「 すべてのファイル 」にしてください。)

実際にプロットした様子は下図の通りです:

CSVファイル「 hyou.csv 」をリニアングラフ2Dで読み込んでプロットした図

このように画面上に、いくつかの点が線でつながれたグラフが表示されます。 この中の 1 つの点が、ファイル内の 1 行に対応していて、左の列(「 , 」記号の左側 )の値を x 座標、右の列の値を y 座標とみなした位置に描かれています。 実は、さっきのCSVファイルにおいて、右の列の値は、左の列の値を 2 乗したものになっていました。 よってこのグラフは、中学校で習う「 y = x2 」のグラフになっています。 きれいな形ですね。

プログラムからグラフソフトを起動させる

ところで、上で使ってみたリニアングラフは、実はVCSSLと同じ開発元のソフトです。 なので、VCSSLには標準で、プログラム内からグラフソフト( ※ リニアングラフが使われます )を起動させたり、操作したりする機能がサポートされています:

// グラフソフトを操作するために必要な行
import tool.Graph2D ;

// グラフソフトを起動し、ID番号を変数に代入
int graphID = newGraph2D () ;

// グラフソフトにCSVファイルを読み込ませる
setGraph2DFile ( graphID, "hyou.csv" ) ;

最初の行「 import 〜 」は、グラフソフトを操作するために必要な「 ライブラリ 」というものを読み込んでいる行です。 ライブラリとは、プログラム内で使う機能を増やすためのものです。 この行により、グラフ関連の関数が使えるようになります。

続いて使っている 「 newGraph2D (ニューグラフ・ツーディー)」関数は、 グラフソフトを起動する関数です。その際、グラフが複数あっても区別できるよう、ID番号が割りふられます。それを「 = 」の左の変数「 graphID 」に代入しています。

最後の「 setGraph2DFile(セットグラフ・ツーディー・ファイル)」関数は、 グラフソフトにCSVファイルなどを読み込ませてくれる関数です。 カッコ内の最初にグラフソフトのID番号を指定し、その次に、読み込ませるファイルの名前を書きます。

このプログラムを、先ほどのCSVファイル「 hyou.csv 」と同じフォルダ内に置いて実行すると、グラフが表示されます:

プログラムを実行すると表示される2Dグラフの図

このように、結果は手動でグラフソフトを使った場合と同じです。 ある意味、手動でプロットしたほうが「 プログラムを別のソフトと連携させてる感 」が出るかもしれないですが、 せっかくなので、今回は上のようにプログラム内からグラフソフトを起動し、CSVファイルを読ませる事にしましょう。 もちろん、なにか別のグラフソフトを使いたい場合など、あえて手動でグラフ表示を行っても OK です。

プログラムでのCSVファイルの読み書きとグラフ描画

プログラムでCSVを書き、グラフソフトに描画してもらう

さて、CSVファイルとグラフソフトがどんなものか大体わかったところで、本題です。 プログラムでデータをCSVファイルに書きだして、そのデータをグラフソフトに描画してもらいましょう。 これは、自作のプログラムを、別のソフトやプログラムと組み合わせて使う、いい例になります。

CSVファイルを読み書きするには、まず書き込む内容に「 , 」を付けたり、 読み込んだ行を「 , 」で区切ったりする処理が必要になりますね。 また、実際のCSVファイルにはいろいろな書き方があり、 値が「 " 」で囲まれていたりして、もっと複雑な処理が必要になる事もあります。

もちろん、そういった処理を自力でプログラムに書く事もできるのですが、 VCSSLにはCSVファイルを手軽に読み書きするための機能が、標準で用意されています。 今回はそれを使いましょう。 一番単純なのは、ファイルを開く open 関数で、CSV読み書き用のモードを使う事です。 たとえば書き込みなら、以下のようにします:

// グラフソフトを操作するために必要な行
import tool.Graph2D ;


// ファイルをCSV書き込みモードで開き、IDを変数に代入
int fileID = open ( "file.csv", WRITE_CSV ) ;

// くりかえし処理で、1行ごとに x, y の値を書き込む
for ( int i=0; i<=10; i++ ) {

    // ※ カウンタ変数 i は毎回 1 ずつ増える

    // x と y の値を変数に用意
    float x = i * 0.1 ;  // x は 0.1 ずつ増える
    float y = x * x ;    // y は x の2乗

    // x と y の値をファイルに「 , 」区切りで書き込む
    writeln ( fileID, x, y ) ;
}

// ファイルを閉じる
close ( fileID ) ;


// グラフソフトを起動し、CSVファイルを読み込ませて描画
int graphID = newGraph2D ( ) ;
setGraph2DFile ( graphID, "file.csv" ) ;

このように、open 関数の中で「 WRITE_CSV(ライト・シーエスブイ) 」と書いていますが、 これはファイルにCSV形式でデータを書き込むためのモードです( 実はここは "wcsv" と書いても同様に動きます )。 このモードを使った場合、上のように write 関数のカッコ内に複数の値を書くと、それらは「 , 」記号で区切ってファイルに書き込まれる ようになります。

上のプログラムでは、for 文を使う事で、ファイルの各行を自動で書き込むようにしています。 for 文の詳細は「 くりかえし処理 」の回を読みかえしてみてください。 ここでは、カウンタ変数「 i 」の値が 0 からスタートし、1 ずつ増えながら、10 までの間ずっと { ... } の中の処理がくりかえされます。 その中で毎回 x と y の値を求め、writeln 関数でファイルに 1 行書き込んでいます。

このプログラムを実行すると、 「 file.csv 」という名前のCSVファイルが作られ、その中に x と y の値が書き込まれます。 そして、それがグラフソフトに読み込まれて、以下のようなグラフが表示されます:

プログラムを実行すると表示される2Dグラフの図

また先ほどと同じグラフですね。 それもそのはず、上のプログラムで求めている x は、 for 文のカウンタ変数 i に 0.1 をかけたものなので、 0 から 1 まで 0.1 ずつ変化します。 y はその2乗としています。 これらは先ほど表計算ソフトで作った「 hyou.csv 」と同じなので、同じグラフになったわけです。

実際にプログラムで書き込んだCSVファイル「 file.csv 」の中身を見てみましょう:

0.0,0.0
0.1,0.010000000000000002
0.2,0.04000000000000001
0.30000000000000004,0.09000000000000002
0.4,0.16000000000000003
0.5,0.25
0.6000000000000001,0.3600000000000001
0.7000000000000001,0.4900000000000001
0.8,0.6400000000000001
0.9,0.81
1.0,1.0

このように、たしかに x の値( 「 , 」の左の列 )は 0.1 ずつ増えていて、y の値( 左の列 )は x の 2乗になっています。 ちゃんと計算して書き込めているようです。

でも上の内容には、たとえば「 0.010000000000000002 」のように、 いくつかの値に小さなゴミのようなものが付いています。 これは 「 式と計算 」の回 で説明した、小数( 浮動小数点数 )の誤差によるものです。 この誤差は非常に小さいので、ふつうはほとんど問題にならないですが、このようにファイルに書き出すとじゃまになる事もあります。 その場合、書き込み前に round 関数で適当な桁数に丸めれば、きれいになります :

    ...
    // x と y の値を変数に用意
    float x = i * 0.1 ;  // x は 0.1 ずつ増える
    float y = x * x ;    // y は x の2乗

    // 誤差を丸めるため、小数点以下5桁までに四捨五入
     x = round ( x, 5, HALF_UP ) ;
     y = round ( y, 5, HALF_UP ) ;


    // x と y の値をファイルに「 , 」区切りで書き込む
    writeln ( fileID, x, y ) ;
    ...

この例では x と y の値を、小数点以下が5ケタまでになるよう四捨五入しています。 「 HALF_UP(ハーフアップ) 」のかわりに「 HALF_UP_SIGNIF(ハーフアップ・シグニフィカンド) 」と書けば、 有効数字のケタ数指定もできます。 さて結果は:

0.0,0.0
0.1,0.01
0.2,0.04
0.3,0.09
0.4,0.16
0.5,0.25
0.6,0.36
0.7,0.49
0.8,0.64
0.9,0.81
1.0,1.0

誤差が丸め込まれて、きれいになりましたね。 「 00...0 」と続くケタは省略されてしまっていますが、 ちゃんと内部で小数点以下5ケタまでに丸められています。

CSVファイルに書かれたデータを、配列として読み込む

続いて、CSVファイルからデータを読み込んでみましょう。 これも、別のソフトで作ったデータを、プログラムで加工したい場合などによくあるパターンです。 ファイルを開く open 関数には、CSV形式の読み込みのためのモードが用意されています:

// ファイルをCSV読み込みモードで開き、IDを変数に代入
int fileID = open ( "file.csv", READ_CSV ) ;

// ファイルの行数を数えて、変数に代入
int n = countln ( fileID ) ;

// x と y の値を格納する配列を宣言( 要素数は行数 )
float x [ n ] ;
float y [ n ] ;

// くりかえし 1 行ずつ読んでいく
for ( int i=0; i<n; i++ ) {

    // 1 行の各列の値を格納する配列を宣言
    float line [ 2 ] ;      // 2 列のデータなので [ 2 ]

    // 1 行読んだ内容を、「 , 」区切りで配列に代入
    line = readln ( fileID ) ;

    // 左の列の値は x に、右の列の値は y に代入
    x [ i ] = line [ 0 ] ;    // [ 0 ] 番目は左の列
    y [ i ] = line [ 1 ] ;    // [ 1 ] 番目は右の列
}

// ファイルを閉じる
close ( fileID ) ;


// 読み込んだ内容を表示
for ( int i=0; i<n; i++ ) {
    println ( x [ i ], y [ i ] ) ;
}

基本的には、「 ファイルの読み書き 」の回で説明した、ループを使った読み込み と同様です。 ファイルを開いた後に countln 関数で行数を数えて、 for 文 でくりかえし1行ずつ、readln 関数を使って読み込んでいます。

違う点は、まず open 関数のカッコ内で 「 READ_CSV ( リード・シーエスブイ ) 」 と書いている事です。 これはCSV形式のファイルを読み込むためのモードで、書き込みで使っていた「 WRITE_CSV 」の逆バージョンです。

このモードでは、readln 関数が、読み込んだ行の内容を「 , 」記号で区切ってくれるようになります。 その結果は、「 = 」記号の左にある配列( ここでは「 line 」 )に代入されます。 その配列の [ 0 ] 番には一番左の列の値が入り、[ 1 ] 番にはその右の列、[ 2 ] 番には( あれば )さらにその右の列 ... という順番で値が入ります。

読み込んだ値をその場で println 関数で表示してもよいのですが、それでは実用的な例になりませんね。 実際には、読み込んだデータにいろいろな処理を加える事が多いでしょう。 そこで、ここではあらかじめ宣言しておいた x と y の配列に、各行ごとの値を入れておく処理にしています。 それを、あとでまとめてで表示しています。

実行結果は以下の通りです:

0.0      0.0
0.1      0.01
0.2      0.04
0.3      0.09
0.4      0.16
0.5      0.25
0.6      0.36
0.7      0.49
0.8      0.64
0.9      0.81
1.0      1.0

この通り、ちゃんとCSVファイルの値を正しく読み込めている事がわかりますね。

( ※ println 関数のカッコ内に複数の値を書いた場合、このように空白( タブ )で区切って表示されます。 )

3Dグラフの描画

3Dの線グラフを描画する

プログラム内でのCSVファイルとグラフソフトの扱いにもなれてきたところで、 一歩進んで、3Dのグラフを描いてみましょう。 まずは、これまでの2Dグラフの延長線上で簡単に描ける、「 線グラフ 」からです。 この場合、CSVファイルを 3 列にして、各行に「 x,y,z 」のように点の座標を書けば OK です:

// グラフソフトを操作するために必要な行
import tool.Graph3D ;


// ファイルをCSV書き込みモードで開き、IDを変数に代入
int fileID = open ( "file.csv", WRITE_CSV ) ;

// くりかえし処理で、1行ごとに x, y, z の値を書き込む
for ( int i=0; i<=10; i++ ) {

    // ※ カウンタ変数 i は毎回 1 ずつ増える

    // x, y, z の値を変数に用意
    float x = i * 0.1 ;      // x は 0.1 ずつ増える
    float y = x * x ;        // y は x の2乗
    float z = x * x * x ;  // z は x の3乗

    // 誤差を丸めるため、小数点以下5桁までに四捨五入
    x = round ( x, 5, HALF_UP ) ;
    y = round ( y, 5, HALF_UP ) ;
    z = round ( z, 5, HALF_UP ) ;

    // x, y, z の値をファイルに「 , 」区切りで書き込む
    writeln ( fileID, x, y, z ) ;

}

// ファイルを閉じる
close ( fileID ) ;


// グラフソフトを起動し、CSVファイルを読み込ませて描画
int graphID = newGraph3D ( ) ;
setGraph3DFile ( graphID, "file.csv" ) ;

// 点を線でつなぐオプションを有効化
setGraph3DOption ( graphID, "WITH_LINES", true ) ;

ちょっと長いですが、基本的には先ほどの2Dグラフの場合とほぼ同じです。 違うのは、ところどころ「 Graph2D 」のかわりに「 Graph3D 」になっている事と、 z の値の処理が追加されている事くらいです。 z の値は x の3乗にして、writeln 関数で y の右の列に書き込むようにしています。 このプログラムを実行すると:

0.0,0.0,0.0
0.1,0.01,0.001
0.2,0.04,0.008
0.3,0.09,0.027
0.4,0.16,0.064
0.5,0.25,0.125
0.6,0.36,0.216
0.7,0.49,0.343
0.8,0.64,0.512
0.9,0.81,0.729
1.0,1.0,1.0

このように、CSVファイル「 file.csv 」に「 , 」区切りで3列のデータが書き込まれます。 一番右の列に z の値が書かれています。たしかに x の値( 一番左の列 )の3乗ですね。 続いて、以下のような3Dグラフが描画されます。ちゃんと立体的に曲がった曲線になっていますね。

プログラムを実行すると表示される3Dグラフの図

3Dのメッシュグラフや曲面グラフを描画する

さて、最後です。少しむずかしくなってしまいますが、3Dのメッシュグラフを描いてみましょう。 メッシュグラフというのは、SF映画などであみの目のように立体形状が描かれている、あれです。 3Dグラフと言えば、やはりあれでしょう!

メッシュグラフを描くためには、CSVファイルに、独特の書き方でデータを書き込む必要があります。 1 行につき 1 点の座標を「 x,y,z 」と書く事は同じですが、その点を書いていく順番が決まっています。 たとえば、メッシュ上の点( 格子点 )が、X-Y 平面において下図のように並んでいるものとしましょう:

メッシュデータにおける格子点の番号の図

( ※ もちろん、それぞれの点は z の値も持っていますが、それはこの図では画面と垂直な方向です。)

さて、この図のような場合、まず y は端( はし )の値に固定して、x 軸の方向に点の座標を書いていきます。 この図では P1,1 から P5,1 までです。 そして x 軸の端まできたら、空( から )の行を 1 行だけ書き込みます。 続いて、y 軸方向に 1 個だけ進んで、また x 軸の方向に点の座標を書いていきます。 この図では P1,2 から P5,2 までです。 その後はまた空の行を 1 行だけはさんで、P1.3 から P5.3 まで書きます。 これを繰り返して、最終的に P5,5 まで書き込めば終わりです。

メッシュデータの座標値をファイルに書き込む順番の図

なんだかややこしい書き方ですね。でも実は、プログラムだと意外と簡単なのです:

// グラフソフトを操作するために必要な行
import tool.Graph3D ;


// ファイルをCSV書き込みモードで開き、IDを変数に代入
int fileID = open ( "file.csv", WRITE_CSV ) ;

// y 方向のくりかえし
for ( int j=0; j<=10; j++ ) {


    // x 方向のくりかえし
    for ( int i=0; i<=10; i++ ) {

        // ※ y 方向(外側)のくりかえしで j が 1 ずつ増加
        // ※ x 方向 (内側) のくりかえしで i が 1 ずつ増加


        // x, y, z の値を変数に用意
        float x = i * 0.1 ;        // x は i に伴い増加
        float y = j * 0.1 ;        // y は j に伴い増加
        float z = y*y - x*x + x ;   // z はちょっと複雑に

        // 小数点以下5桁までに四捨五入( 誤差の丸め )
        x = round ( x, 5, HALF_UP ) ;
        y = round ( y, 5, HALF_UP ) ;
        z = round ( z, 5, HALF_UP ) ;

        // x, y, z の値を「 , 」区切りで書き込む
        writeln ( fileID, x, y, z ) ;
    }

    // x 方向 (内側) のくりかえしが終わると空行をはさむ
    writeln ( fileID, "" ) ;
}


// ファイルを閉じる
close ( fileID ) ;


// グラフソフトを起動し、CSVファイルを読み込ませて描画
int graphID = newGraph3D ( ) ;
setGraph3DFile ( graphID, "file.csv" ) ;

// 点をメッシュでつなぐオプションを有効化
setGraph3DOption ( graphID, "WITH_MESHES", true ) ;

このように、基本的には for 文が2重になった事と、y の値の計算式が少し違うだけですね。 内側にある( 上から 2 つめの )for 文は、これまでと同様に、x の値を 0.1 ずつ増やすくりかえしです。 いまの場合は、これは先ほどの例の P1,1 から P5,1 までのように、 書き込む点をx 方向に動かしていく事に対応しています。 x が端( はし )にくると、この for 文は一旦終わりです。

すると、外側にある( 上から 1 つめの )for 文が 1 周回りますね。 これによって、カウンタ変数 j の値に 1 が足されて、y の値が 0.1 だけ増えます。 そして再び内側の for 文がくりかえされ、先ほどの例の P1,2 から P5,2 までのように、 書き込む点が x 方向に動いていきます。後も同様です。確かに先ほどの例の通りになりますね。

さて、上のプログラムでは、z については少し複雑に 「 z = y2 - x2 + x 」 という値にしています。 グラフがどんな形になるのか、ぱっとイメージするのはちょっと難しいですね。 でも、実はそれが狙いです。 結果が簡単にイメージできないからこそ、プログラムを書いてコンピューターに計算させる意味があると思いませんか?

それでは実行してみましょう。実行すると、まず以下の内容がCSVファイル「 file.csv 」に書き込まれます:

0.0,0.0,0.0
0.1,0,0.09
0.2,0,0.16
0.3,0,0.21
0.4,0,0.24
0.5,0,0.25
0.6,0,0.24
0.7,0,0.21
0.8,0,0.16
0.9,0,0.09
1.0,0.0,0.0

0.0,0.1,0.01
0.1,0.1,0.1
0.2,0.1,0.17
0.3,0.1,0.22
0.4,0.1,0.25
0.5,0.1,0.26
0.6,0.1,0.25
0.7,0.1,0.22
0.8,0.1,0.17
0.9,0.1,0.1
1.0,0.1,0.01

0.0,0.2,0.04
...

ちゃんと想定通りの内容ですね。そして、以下のようなグラフが表示されます:

プログラムを実行すると表示される3Dグラフ(メッシュプロット)の図

この通りです。思いもよらない形だったのではないでしょうか? という事は、 いま私たちはコンピューターに「 答えを教えてもらった 」わけです。 このコーナーでのこれまでのプログラムは、結果がわかりきったものばかりでしたが、今回は違います。 「 難しいグラフの形を求める 」という、れっきとした問題を、コンピューターとプログラミングによって解いたわけです!

最後に

さて、もしかすると上のグラフの形が、予想どおりだったという人もいるかもしれません。 そこで最後におまけとして、もっと複雑なグラフを描いておしまいにしましょう。 内容が数学的で少し難しくなってしまうため、詳しい説明は省略し、プログラムとグラフだけ掲載します:

// グラフソフトを操作するために必要な行
import tool.Graph3D ;

// 数学関数を使うために必要な行
import Math ;



// ファイルをCSV書き込みモードで開き、IDを変数に代入
int fileID = open ( "file.csv", WRITE_CSV ) ;

for ( int j=-50; j<=50; j++ ) {
    for ( int i=-50; i<=50; i++ ) {

        // ※ 外側のくりかえしで j が 1 ずつ増える
        // ※ 内側のくりかえしで i が 1 ずつ増える

        // x, y の値を変数に用意
        float x = i * 0.01 ; // x は i に伴い増加
        float y = j * 0.01 ; // y は j に伴い増加

        // X-Y平面での点と原点との距離 r を求める
        float r = sqrt ( x * x + y * y ) ;

        // 点が X 軸となす角度θを求める
        // ※ -π/2 〜 3π/2 の範囲で求める方が、atan 関数の連続領域を使って
        // 単純に求まるため、まずはその範囲で求め、最後に 0 〜 2π に直す
        float theta = 0.0;
        if ( x == 0.0 ) {
            if ( y > 0 ) {
                theta = PI / 2.0;
            } else {
                theta = -PI / 2.0;
            }
        } else if ( x > 0.0 ) {
            theta = atan ( y / x );
        } else if ( x < 0.0 ) {
            theta = atan( y / x ) + PI;
        }
        if (theta < 0.0) { // -π/2 〜 3π/2 から 0 〜 2π に直す
            theta += 2.0 * PI;
        }

        // θ と r の値を使って、z の値を計算する
        float z = sin(5.0*theta+10.0*r) * sin(6.0*r) ;


        // 小数点以下5桁までに四捨五入( 誤差の丸め )
        x = round ( x, 5, HALF_UP ) ;
        y = round ( y, 5, HALF_UP ) ;
        z = round ( z, 5, HALF_UP ) ;

        // x, y, z の値を「 , 」区切りで書き込む
        writeln ( fileID, x, y, z ) ;
    }

    // 内側のくりかえしの最後に、空の行をはさむ
    writeln ( fileID, "" ) ;
}

// ファイルを閉じる
close ( fileID ) ;


// グラフソフトを起動し、CSVファイルを読み込ませて描画
int graphID = newGraph3D ( ) ;
setGraph3DFile ( graphID, "file.csv" ) ;

// 点を描かずに、曲面を描くようにオプションを設定
setGraph3DOption ( graphID, "WITH_POINTS", false ) ;
setGraph3DOption ( graphID, "WITH_MEMBRANES", true ) ;
※ θ の基準がX軸ではなくY軸に 90 度ずれてしまっていたため、修正いたしました。» 詳細

このプログラムで描かれるのは、 座標の原点との距離を r 、X軸とのなす角度を θ として、「 z = sin(5θ+10r) sin(6r) 」のグラフです。 ちなみに、このような r と θ による表し方を「 極座標 」といいます。さあ、実行してみましょう:

実行結果の3Dグラフの図 ( ※ グラフの「 Option(オプション)」メニューから「 Tesselation(曲面高画質化)」を選択すると綺麗になります。)

三角関数が入り混じった複雑な式からすると、意外なほどきれいなグラフですね!   さらに、z の値を計算している行を、以下のように 1 行だけ書き換えると、もっと複雑なグラフになります:

// θ と r の値を使って、z の値を計算する
float z = sin(5.0*theta + 10.0*sin(14.0*r)) * sin(6.0*r) ;

実行結果は以下の通りです。 このグラフをすぐに、そして正確に手で描けるという人は、ほぼいないのではないでしょうか。

実行結果の3Dグラフの図

コンピューターは、言われた事を忠実に行うだけのやや不器用な存在ですが、 そのかわり処理の正確さとスピードは、人間よりもはるかに優れています。 そんなコンピューターの長所を理解して活用すると、上のグラフのように、人間だけでは見えなかった新しい世界を見せてくれます。 それでは、楽しいプログラミング生活を!

( VCSSLスタートアップガイドは、この回で最終回です。 )