next up previous
Next: 5 レポート Up: 計算天文学 II 第2回 C++言語入門 Previous: 3 再帰

Subsections


4 お絵書き

プログラムを書いて実行しても、でるものが数字ばかりではつまらない。せっかく再帰 をやったので、再帰を使ってわりあい簡単なプログラムで複雑な図形を書いてみよう。

// tree.C
#include <iostream>
#include <math.h>
using namespace std;

#include "cpgplot.h"

#define PI  3.14159265358979

void tree(int n, double x, double y, double angle, double length)
{
    double x1, y1;
    if (n >0){
	x1 = x + length*cos(angle);
	y1 = y - length*sin(angle);
	cpgmove(x,y);
	cpgdraw(x1,y1);
	tree(n-1,x1,y1,angle + 0.25, length*0.7);
	tree(n-1,x1,y1,angle - 0.25, length*0.7);
    }

}

int main()
{
    int n;
    if(cpgopen("?") !=1 ) exit(-1);
    cpgask(0);
    cpgpage();
    cpgenv(0, 1, 0, 1,0,-2);
    cerr << "Level of the tree? :";
    cin >> n;
    tree(n, 0.5, 0.0, -PI/2, 0.25);
    cpgend();
}

これは、樹形図を書くプログラムである。原理は、指定した長さと角度で一本 線を引き、その先から角度をつけて2本線を引き、それぞれの端からまた角度 をつけて、、、というのを繰り返すだけである。

再帰を使うことで、角度、長さを変えて自分自身を呼び出すという形で簡潔に アルゴリズムが表現できている。

4.1 グラフィックライブラリ

ここでは、 PGPLOT の基本的な機能を使っ て画面に線を書いている。とりあえず、ここで使っている関数についてそれら が何をしているかを見ておこう。cpg で始まっている関数がすべて PGPLOT が 提供しているものである。

    if(cpgopen("?") !=1 ) exit(-1);
    cpgask(0);
    cpgpage();
この 3 行はグラフィックスを使うための準備である。大抵のプログラムでは ここはこのままでいいはずである。

    cpgenv(0, 1, 0, 1,0,-2);
これは、画面(ウインドゥ)内での座標系を決めている。最初の 4 つの引数 は x座標の最小値、x座標の最大値、y座標の最小値、y座標の最大値であり、 最後の2つは画面をいっぱいに使うか、それとも縦横のスケールを合わせるかなど の細かい指定である。

        cpgmove(x,y);
        cpgdraw(x1,y1);

cpgdraw は「現在位置」から指定した座標まで線を引く。その後では この指定された座標が「現在位置」になる。cpgmove のほうは、なにも しないでただ指定された座標が「現在位置」になる。従って、これらを上の順 で呼ぶことで指定した座標から指定した座標までの線分を引くことが出来る。

なお、cpgdraw を繰り返して呼ぶと折れ線が引ける。グラフを書いたり するにはこれが便利である。

    cpend();
これによりグラフィックの利用を終える。 PS ファイルなどに結果を書く時に は、これを呼ばないと正しいファイルが作られない。画面に出す分にはこれが なくても動くので注意すること。

同工異曲だが、こんなものも書ける。

// fract.C
#include <iostream>
#include <math.h>
using namespace std;

#include "cpgplot.h"

#define PI  3.14159265358979

void fractal(int n,
	     double x0,
	     double y0,
	     double x1,
	     double y1)
{
    double dx, dy, xa, ya; 
    if (n > 0){
	dx = (x1 - x0)/2;
	dy = (y1 - y0)/2;
	xa =  x0 + dx  - dy ;
	ya =  y0  + dx +dy ;
	fractal(n-1,x0, y0, xa, ya);
	fractal(n-1,xa, ya, x1, y1);
    }else{
	cpgmove(x0,y0);
	cpgdraw(x1,y1);
    }
}


int main()
{
    if(cpgopen("?") !=1 ) exit(-1);
    cpgask(0);
    cpgpage();
    cpgenv(0, 1, 0, 1,1,-2);
    int n;
    cerr << "Level of the tree? :";
    cin >> n;
    fractal(n, 0.6, 0.3, 0.6, 0.7);
    cpgend();
}

4.2 練習

  1. この木では枝が2本ずつ出ているが、もっとたくさん出すにはどうすればい いか?

  2. この木は枝分かれが完全に規則的で不自然である。自然にするにはどうすれ ばいいだろうか?

    (ヒント:drand48() という関数を呼ぶと、0 から x まで の範囲の乱数 -- デタラメな数 -- を返す。これを使って枝分かれの長さ、 角度を変えて見よう)

  3. 他のフラクタル曲線、たとえばコッホ曲線やペアノ曲線を書くプログラ ムを作ってみる。



Jun Makino
平成16年10月24日