フラクタル図形を描こう(1)

目的

EGGXによるグラフィック機能を用いて、いろいろな図形を描くプログラムを作成する。特に、作図のための便利な関数を自分で作り、それを用いた再帰的呼び出しを学習する。

各自のMacBookでのXcode環境でのCコンパイラおよびEEGXグラフィックス環境の設定は「現象の数学」講義のMoodleの指示を参考とせよ。

我々の作図のための関数を作る

EGGXでの線の描画は、終点のウインドウ座標(x,y)を指定して行った。
今回行う図形の描画においては、線を引く方向の角度線の長さを指定する角座標の方が便利である。そこで、EGGXで準備されたライブラリーを組み合わせて、以下のようなオリジナルな作図関数を定義する。

グローバル変数

座標の規準点 ( LPX, LPY )、線引き規準角 ( ANGLE )

float LPX, LPY, ANGLE;
int w;

LPX,LPYはウインドウ座標系での現時点でのX, Y座標位置、ANGLEはどちらの方向(角度、ラジアンではなくディグリー)に向かって線を引こうとしているかを示す。

描画ウインドウのidである w もグローバル変数として定義しておいて、どの関数においても w で指定される描画ウインドウに描画できるようにしておく。

座標の規準点 ( LPX, LPY ) を (lpx, lpy) へ変更する関数


線引き規準角 ( ANGLE ) を ang へ変更する関数


線引き規準角 ( ANGLE ) をang だけ回転させる関数


360度単位で、小数点以下を加え直す

例: ANGLE=402.5度の場合
(int)ANGLE は 402となり、これを360 度で割った余りは、42となる。
ANGLE-(int)ANGLEは0.5であるので、これを加えてANGLEは42.5度となり、もとのANGLEの値を0.0から360.0の間の角度に直してくれる。

規準点( LPX, LPY ) から線引き規準角 ( ANGLE )の方向に長さleng の線を引く関数

線を引いた後は、終点を新たな基準点とする。 M_PIは標準数学関数ライブラリーで定義されている円周率である。


EGGXを用いた簡単な図形の描画

プログラム例1 lines.c

規準点 (LPX,LPY) を (200.0, 200.0) 、規準角 ANGLE を0.0 度に設定し、長さ 100.0 の線を引く。
その線の終わりから、規準角を45.0 度回転し、長さ50.0 の線を引く。
その線の終わりから、さらに規準角を45.0 度回転し、長さ50.0 の線を引く。

説明: ローカル変数とグローバル変数、プロトタイプ宣言など

演習1: 上のプログラムlines.c をmoodleの講義ページよりダウンロードして、
egg -o lines lines.c
によってコンパイルし、実行してみよ。

演習2

規準点を (200.0, 200.0)、規準角を 0.0 度に設定し、長さ 100.0 の線を引く。これを底辺とする正三角形を作図するプログラムをプログラム例1を変えて作成、コンパイル、実行せよ。

関数の再帰的呼び出し

プログラム例2: spiral.c




このプログラムでは、main から関数 spiral を呼んでいる。関数 spiral は、引き数 leng を受け取り、lengの絶対値が 1.0 以上であれば現在の規準点から長さleng の線を引く。そして、規準角を 45 度回転して、leng をSCALE 倍して再帰的(recursive)に関数spiral(自分自身)を呼んでいる。これで呼ばれた、関数spiralは、lengの値としては、前のlengのSCALE倍が代入される(ローカル変数)。線を引き、規準角を変え(グローバル変数)、再び関数spiralを再帰的に呼んでいる。この再帰的呼び出しは、lengの絶対値 が 1.0 以下に縮小されるまで繰り返される。leng の絶対値が 1.0 以下になったら、if 文で条件が偽となるため、なにも実行しないで一つ前の関数spiral に戻る。これで、この関数内の実行が終了し、どんどんと再帰的呼びだしをさかのぼって行き、最後は、main プログラムに戻って終わる。


演習3:上のプログラムspiral.cをファイルに作成し、コンパイル、実行せよ。ただし、上に示したプログラムの後ろに、lines.c にあるようにsetlp, setangle, turn, sen_hikiの関数を付け加える必要がある。先ほど作成したlines.cをコピーして、異なる部分を編集すると楽である。
プログラム中の SCALE の値を1以下で他の値に変えて、図形の変化を見よ。絶対値が1以下の負の値を入れると面白い。
また、SCALE の値が1より少し大きいの正の値の場合(例 1.1)や負の値の場合(例 -1.1)などの場合も試してみよ。

考察:上のプログラムにおいて、関数spiralでの引き数であるlengに関して考えてみる。この変数は関数でのローカル変数であるので、この関数の中でのみ値を参照でき、変更する事ができる。再帰的呼び出しにより、次々と同じ関数spiralが呼ばれていくので、その度にローカル変数のlengが作られていく。重要な事は、これらは名前は同じであるが、すべてメモリ上では異なる場所に保管されており、対応する関数のみからしかアクセス出来ない。このため、同じ関数が再帰的に呼び出されても、変数が混ざってしまうことは無いのである。変数lengをグローバル変数にした場合には、再帰的呼び出しを行ったときに、どのような事が生じるか考えてみよ。

関数の再帰的呼び出しが出来るかどうかで、プログラム言語のレベルの違いが明確になる。例えば、以前数値計算では独占的な位置を占めていた、FORTRANという言語では関数の再帰的呼び出しは出来ない。

インデックスへ戻る