計算数理A演習第2回

第2回


今週および来週の演習

今日はGLSCグラフィックライブラリの使い方について紹介します。
第2回と第3回で、GLSCの使い方をマスターしてもらいます。
進み方としては、

  • 今週:直線の描画(その2)まで
  • 来週:関数の描画とアニメーション

を予定しています。
早く進みたい人は、どんどん先に進んでかまいません。


GLSCとは

GLSCは(Graphic Library for Scientific Computing)の略で、C言語でかかれたグラフィックライブラリです。GLSCを用いれば、科学技術計算の結果をディ スプレイに表示したり、プリンターに出力したりすることができます。通常、コンピュータに絵を描かせる場合、その予備知識としてかなり多くのことを学ぶ必 要があります。GLSCは機能が限定されている分、使い方が簡単で、覚えることも最小限ですみます。より高度なグラフィックスを表示させたい人は、他のラ イブラリを用いることになるでしょうが、基本をGLSCで学んでおけば、すんなりと習得できるでしょう。

注意:GLSCという言語があるわけではありません。この演習では cglsc コマンドを用いてコンパイルするため、あたかもGLSCという言語があるかのように錯覚する人もいるようですが、この演習で扱うものは全てC言語のプログラムです。後の「GLSCを使ったプログラムのコンパイルの仕方」にあるように、cglsc コマンドは実際には cc コマンドをオプション付きでよびだしているにすぎません。


もっとも簡単なGLSCプログラム

次のプログラムは、画面上に白いウィンドウを表示するだけのプログラムです。GLSCでは、このウィンドウ内に絵を描くので、ウィンドウの表示は 絶対に必要です。表示されたウィンドウは、ウィンドウ内をマウスで左クリックすると消えます。以下のサンプルプログラムをエディターで打ち込み、ファイル 名を 0419-1.c として保存してください。コンパイルの方法がこれまでと異なるので、コンパイル、実行はまだしないで下さい(後ほど説明します)。

#include <glsc.h>
#include <stdio.h>

int main()
{
    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);
    g_sleep(G_STOP);
    g_term();

    return 0;
}

1行目: GLSCを使う場合には、この#include文が必要です

6行目: g_init関数を用いて、絵を描くウィンドウの初期化を行います。第一引数である "GRAPH"は、画面に表示した絵をファイルとして保存する場合に使いますが、当面はこのように "GRAPH"としてください。その後に続く2つの引数は、実数型で、ウィンドウのサイズを指定します。

7行目: g_device関数で、何に対して表示するかを指定します。画面に表示したいので、このように G_DISPを引数に指定します。

8行目: g_sleep関数でプログラムの一時停止を行っています。引数として正の実数値または G_STOPを受けます。引数が正の実数値である場合、その秒数停止します(例えば g_sleep(1.2); であれば1.2秒停止します)。引数が G_STOP である場合、ウィンドウの内部がマウスで左クリックされるまで停止します。

9行目: g_term関数にてGLSCの処理を終わります。GLSCプログラムの最後に呼ばれます。


GLSCを使ったプログラムのコンパイル方法

GLSCでは画面上に線を表示したりする必要がある為、様々な非標準ライブラリ(標準関数以外の関数の集まり)を使うようコンパイル時に指定する必要があります。その為には、例えば先のプログラムをコンパイルする場合、次のようなコマンドを打ち込む必要があります。

cc -I/usr/X11/include -I/usr/local/include 0419-1.c -o 0419-1 -L/usr/local/lib -lglscd -L/usr/X11R6/lib -lX11 -lm

しかし、これをいちいち打ち込むのは面倒なので、GLSCプログラムを簡単にコンパイルするためのコマンドをこちらで用意しました。そのコマンドを 利用すると、上記の長いコマンドを打ち込んだ結果と同様の結果が、次の短いコマンドで得られます(先ほど行ってもらったGLSC利用の為の準備は、このコ マンドを利用できるようにする為のものでした)。

cglsc  0419-1.c

これにより、実行可能なファイル 0419-1 が生成されます。プログラムはこれまで同様、

./0419-1

で実行され、この場合、次のような真っ白な正方形ウィンドウが画面に表示されます。

ウィンドウ内をマウスで左クリックすることで、ウィンドウが消えます。


GLSCのマニュアル

GLSCのマニュアルは、この演習のホームページのトップページからリンクされています。
関数の使い方が良く分からない場合は、マニュアルを参照してください。


g_init 関数

先のプログラム例の6行目で呼ばれている g_init関数では、第2、第3引数でウィンドウの大きさを指定することができると説明しました。実際に、その数値を変更してウィンドウのサイズがどうかわるか見てみましょう。

次の各場合についてコンパイル実行してみてください。

g_init("GRAPH", 100.0, 200.0);

g_init("GRAPH", 300.0, 200.0);


標準座標系

GLSCには、標準座標系と仮想座標系という二つの座標系が存在します。
ここではまず、標準座標系について説明します。

標準座標系は、例えば g_init("GRAPH", 200.0, 100.0);と初期化されたウィンドウでは次の図のようになります。

つまり、左上が座標 (0.0, 0.0) であり、右下が (200.0, 100.0) となるような座標系です。


GLSCによる文字列の描画

GLSCでは g_text関数にて文字をウィンドウ内に表示することができます(残念ながら漢字は表示できません)
例えば、次のプログラムでは、画面ウィンドウ内に Hello! と表示します。(0419-2.c)

#include <glsc.h>
#include <stdio.h>

int main(){
    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    /* 文字を描画 (横の座標, 縦の座標, 文字)*/
    g_text(0.0, 10.0, "Hello!");

    g_sleep(G_STOP);
    g_term();

    return 0;
}

g_text関数の第一、第二引数はウィンドウ上の標準座標系での座標です。
コンパイルし実行すると、次のようになります。

実行結果

次のプログラムでは Hello! と GLSC という文字列をそれぞれ異なる場所に表示します。

#include <glsc.h>
#include <stdio.h>

int main()
{
    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    /* 文字を描画 (横の座標, 縦の座標, 文字)*/
    g_text(0.0, 10.0, "Hello!");
    g_text(10.0, 20.0, "GLSC");

    g_sleep(G_STOP);
    g_term();

    return 0;
}

実行結果

g_text_color 関数を使って、文字に色をつけることもできます。

#include <glsc.h>
#include <stdio.h>

int main()
{
    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    /* 文字の色を指定してから描画 */
    g_text_color(G_RED);
    g_text(0.0, 10.0, "Hello!");
    g_text(10.0, 20.0, "GLSC");

    g_sleep(G_STOP);
    g_term();

    return 0;
}

実行結果

g_text_color 関数を使って、文字の色を変えると、その後 g_text 関数で表示する文字全てに適用されます。
例えば GLSC は黒で表示したい場合は次のよう改めて黒と指定する必要があります。

#include <glsc.h>
#include <stdio.h>

int main()
{
    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    /* 文字の色を指定してから描画 */
    g_text_color(G_RED);
    g_text(0.0, 10.0, "Hello!");

    /* 文字の色を指定してから描画 */
    g_text_color(G_BLACK);
    g_text(10.0, 20.0, "GLSC");

    g_sleep(G_STOP);
    g_term();

    return 0;
}

実行結果

g_text_color の引数には色を指定しますが、GLSCでは以下の8色を使うことができます。

color カラーディスプレイ
番号 マクロ
0 G_BLACK
1 G_RED
2 G_GREEN
3 G_BLUE
4 G_MAGENTA マゼンタ
5 G_YELLOW
6 G_CYAN シアン
7 G_WHITE

例えば、黄色で文字列を描画したい場合は、

g_text_color(G_YELLOW);

または

g_text_color(5);

とします(どちらの指定方法でもよい)。後者の指定方法は、例えば次のようなプログラムでは大変有用です。

#include <glsc.h>
#include <stdio.h>

int main()
{
    int i;

    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    for(i = 0; i < 8; i++)
    {
        /* 文字の色を数値で指定 */
        g_text_color(i);
        g_text(0.0 + i*10, 10.0 + i*10 ,"Hello!");
    }

    g_sleep(G_STOP);
    g_term();

    return 0;
}

実行結果

白色で描画された最後の Hello! は背景色と同じである為見えません。


g_cls 関数

g_cls 関数を用いると、ウィンドウ内を消去することができます。次のプログラムでは、カラフルに Hello! を表示した後とまります(一つ目の g_sleep(G_STOP))。マウスのクリックをすることで g_cls 関数が実行され、画面が消去され、再びとまります(二つ目の g_sleep(G_STOP))。動作を確かめてください。(0419-7.c)

#include <glsc.h>
#include <stdio.h>

int main()
{
    int i;

    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    for(i = 0; i < 8; i++)
    {
        g_text_color(i);
        g_text(0.0 + i*10, 10.0 + i*10, "Hello!");
    }

    g_sleep(G_STOP);

    /* ウィンドウ内を消去 */
    g_cls();

    g_sleep(G_STOP);

    g_term();

    return 0;
}

簡単なアニメーション

文字列をある位置に表示し、しばらくとまった後、画面を消去し、先ほどとは少し違った場所に再び文字列を表示するという一連の作業を繰り返すと、アニメーション効果を得ることができます。(0419-8.c)

#include <glsc.h>
#include <stdio.h>

int main()
{
    int i;

    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    for(i = 0; i < 100; i = i + 2)
    {
        /* 描写 */
        g_text_color(i%8);
        g_text(0.0 + i, 10.0 + i, "Hello!");

        /* 少々停止 */
        g_sleep(0.05);
        /* 消去 */
        g_cls();
    }

    g_sleep(G_STOP);

    g_term();

    return 0;
}

g_text_color の引数は 0~7 の整数であったので、 % 演算子を用いて、その範囲になるようにしてあります。

途中にある g_sleep(0.05); で約 0.05 秒間停止します。これが無いと早すぎてアニメーションに見えません。


文字列の描画(その2)

例えば、変数の内容を GLSC ウィンドウに表示したい場合は次のように文字列型(文字型の配列)を用いる必要があります。(0419-9.c)

#include <glsc.h>
#include <stdio.h>

int main()
{
    double a;
    char text[256]; // 256文字までの文字列の宣言

    a = 3.14;

    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    sprintf(text, "a = %f", a);
    g_text(0.0, 10.0, text);

    g_sleep(G_STOP);
    g_term();

    return 0;
}

文字型の配列、および sprintf 関数と g_text 関数を組み合わせて使うことで、色々な文字列を表示することができます。 sprintf 関数は、第一引数に文字型配列の名前(この例では text)を書く以外は、printf 関数と同様です(同じ変換文字列が使える)。

(おまけ)先のアニメーションの方法と組み合わせてカウントダウンとかさせられます。(0419-10.c)


仮想座標系

GLSCでは、先程でてきた標準座標系の他に仮想座標系を定義し利用することができます。しかも、仮想座標系はいくつでも(もちろん限界はありますが)定義することができます。仮想座標系を定義するには g_def_scale という関数を使います。

前回あつかった標準座標系は、例えば g_init(“GRAPH”, 200.0, 100.0) と初期化されたウィンドウでは次の図のようになりました。

 

 

つまり、左上が座標 (0.0, 0.0) であり、右下が (200.0, 100.0) となるような座標系です。例えば、 y = sin(x) のグラフを 0 < x < 4 の範囲で描きたい場合、標準座標系にそのようなグラフを描くには、座標変換の手続きが必要となります。それでは、いちいち紛らわしいので、この場合次のような座標系となっていれば便利です。

 

座標系がこのようになっていれば、y = sin(x) のグラフを描くのが簡単です。実は、GLSCでは、このような座標系を、先の GLSC ウィンドウ内にいくつでも作ることができます。そして、実際に GLSC で絵を描く場合、この座標系(仮想座標系と呼びます)を用います。先の標準座標系は、文字列の描画時と仮想座標系の定義時にのみ使います。(仮想座標系を GLSC ウィンドウ内に定義する場合には何らかの座標系が必要です。標準座標系はその為にあるものだと考えてください。)

例えば、先の標準座標系の中に、1つの仮想座標系を定義した場合のイメージを次に示します。

青色が、GLSCウィンドウ全体をあらわしています。その中に赤色の仮想座標系が定義されている様子を示しています。このような仮想座標系は次の g_def_scale 関数を用いて定義することができます。


仮想座標系の定義(g_def_scale 関数)

GLSC ウィンドウ内に長方形領域を定義して、その長方形内の座標系を定義する関数が g_def_scale 関数です。GLSC ウィンドウ内に長方形を定義するには長方形の位置大きさを指定する必要があります。また、新たに作成する仮想座標系の左端、右端、下端、上端の値を指定する必要があります。つまり、長方形の位置と大きさを指定するために4つの値が必要であり、新座標系を指定するために4つの値が必要(計8つ!)となるということを理解すれば、g_def_scale 関数に多くの引数の意味がおのずとわかります。また、仮想座標系はいくつでも定義できるので、どの仮想座標系の定義であるかを示す通し番号も必要となります。つまり、g_def_scale 関数には、全部で9つの引数があります。

GLSCマニュアルの g_def_scale 関数の説明部分は次のように記載されています。このように、GLSC の関数は引数が多いので、関数利用の際には各人マニュアルを参照してください。


g_def_scale 標準面上に仮想座標系を定義する

  • 書式 g_def_scale ( scale, x_left, x_right, y_bottom, y_top, x_left_std, y_top_std, x_wid_std, y_wid_std );
  • 入力パラメータ
    scale 整数 スケール番号。50個まで仮想座標系を 導入することができる。( 0 ≦ scale ≦ 49 )
    x_left 実数 長方形の左辺の仮想座標系におけるx座標
    x_right 実数 長方形の右辺の仮想座標系におけるx座標
    y_bottom 実数 長方形の下辺の仮想座標系におけるy座標
    y_top 実数 長方形の上辺の仮想座標系におけるy座標
    x_left_std 実数 長方形の左辺の標準x座標
    y_top_std 実数 長方形の上辺の標準y座標
    x_wid_std 実数 長方形の標準面上でのx軸方向の幅
    y_wid_std 実数 長方形の左辺の標準y軸方向の幅

     

  • 説明
    下図のように適当な長方形を考えて,上記描くパラメータを定めることにより仮想座標系を決める。

はじめの引数は、これから定義する仮想座標系につける通し番号です。2,3,4,5番目の引数は、仮想座標系の左端、右端、下端、上端の値であり、6,7,8,9番目の引数は、仮想座標系の位置と大きさを標準座標系で与えます。長方形の位置と大きさ(6,7,8,9番目の引数)は、4頂点の座標を与えるのではなく、長方形の左上の標準座標系での座標と、長方形の幅と高さを与えるというところに注意してください。

では、実際に g_def_scale 関数をつかったサンプルプログラムを見てみましょう。

#include <glsc.h>
#include <stdio.h>

int main()
{
    g_init("GRAPH", 200.0, 100.0);
    g_device(G_DISP);

    g_def_scale(1, 0.0, 4.0, -1.0, 1.0, 10.0, 10.0, 180.0, 80.0);

    g_sleep(G_STOP);
    g_term();

    return 0;
}

このプログラムでは、次のような仮想座標系を定義しています。


円の描画

GLSC では g_circle 関数で円を描画することができます。g_circle には5つの引数があり、はじめの2つで、円の中心位置を仮想座標系にて指定します。3つめの引数は、描く円の半径。4つめの引数は円のふちを描くかどうかの指定(G_YES または G_NO)で、5つめの引数は、円の内部を塗りつぶすかどうかの指定(G_YES または G_NO)を行います。

#include <glsc.h>
#include <stdio.h>

int main()
{
    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    /* 下図のような仮想座標系の定義 */
    g_def_scale(1, -1.0, 1.0, -1.0, 1.0, 10.0, 10.0, 80.0, 80.0);

    /* 仮想座標系「1」を選択 */
    g_sel_scale(1);
    /* 円の描写 (横座標, 縦座標, 半径, ふち描く?, 塗りつぶす?) */
    g_circle(0.0, 0.0, 0.5, G_YES, G_NO);

    g_sleep(G_STOP);
    g_term();

    return 0;
}

上のプログラムでは、次のような仮想座標系(赤色で示した)を1番(g_def_scale の第一引数)として定義しています。その仮想座標系に絵を描きたいので、それを選ぶために g_sel_scale 関数で1番の仮想座標系を選びます。その後、g_circle 関数で円を描きます。このように、仮想座標系がたった一つであっても、g_sel_scale で一度選ぶ必要があります。g_def_scale 関数と g_sel_scale 関数は GLSC プログラムには必ず出てくると思ってください。

プログラムの実行結果は次のようになります。

例えば g_def_scale 関数の2つ目以降の引数を変えてみたり、以下のようにg_def_scale 関数と g_sel_scale 関数をコメントアウトしたものと比べてみると、どういうことか分かりやすいと思います.

#include <glsc.h>
#include <stdio.h>

int main()
{
    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    /*
    g_def_scale(1, -1.0, 1.0, -1.0, 1.0, 10.0, 10.0, 80.0, 80.0);

    g_sel_scale(1);
    */
    g_circle(0.0, 0.0, 0.5, G_YES, G_NO);

    g_sleep(G_STOP);
    g_term();

    return 0;
}

2つの仮想座標系を定義して、使ってみましょう。次の例では、標準座標系上の全く同じ位置に同じ大きさの仮想座標系を作っています。ただし、座標のスケールが異なります。

#include <glsc.h>
#include <stdio.h>

int main()
{
    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    g_def_scale(1, -1.0, 1.0, -1.0, 1.0, 10.0, 10.0, 80.0, 80.0);
    g_def_scale(2, -2.0, 2.0, -2.0, 2.0, 10.0, 10.0, 80.0, 80.0);

    g_sel_scale(1);
    g_circle(0.0, 0.0, 0.5, G_YES, G_NO);

    g_sel_scale(2);
    g_circle(0.0, 0.0, 0.5, G_YES, G_NO);

    g_sleep(G_STOP);
    g_term();
}

実行結果は次のようになります。2つの g_circle は全く同じなのに(位置、半径が同じ)、2つの見た目違う半径の円が描かれました。異なる仮想座標系を用いて描いているからですが、その意味をよく理解してください。

円に色をつけることもできます。g_area_color 関数で、円内部の塗りつぶし色を指定し、 g_line_color 関数でふちの線色を指定します。2つめの g_circle では、内部塗りつぶしをしない(5つめの引数が G_NO である)ので先に描かれた赤色が残っています(5つめの引数を G_YES に変更してみよ)。

 

#include <glsc.h>
#include <stdio.h>

int main()
{
    g_init("GRAPH", 100.0, 100.0);
    g_device(G_DISP);

    g_def_scale(1, -1.0, 1.0, -1.0, 1.0, 10.0, 10.0, 80.0, 80.0);
    g_def_scale(2, -2.0, 2.0, -2.0, 2.0, 10.0, 10.0, 80.0, 80.0);

    g_sel_scale(1);
    g_area_color(G_RED);
    g_line_color(G_BLUE);
    g_circle(0.0, 0.0, 0.5, G_YES, G_YES);

    g_sel_scale(2);
    g_area_color(G_GREEN);
    g_line_color(G_GREEN);
    g_circle(0.0, 0.0, 0.5, G_YES, G_NO);

    g_sleep(G_STOP);
    g_term();

    return 0;
}


四角形の描画

g_box 関数により、四角形を描くことができます。g_box 関数の使い方はGLSCマニュアルを参考にしてください。


課題1

上記プログラムの g_def_scale(2, -2.0, 2.0, -2.0, 2.0, 10.0, 10.0, 80.0, 80.0)で2~9番目の変数をいろいろ変えて比較する事で, 仮想座標系の性質に慣れてください。


課題2

円が時間と共に移動するアニメーションプログラムを作成せよ。