Part.14

GLSC入門

内容

  • macOSでGLSCを使う場合の注意点
  • 簡単なGLSCプログラム
  • GLSCプログラムのコンパイル方法
  • 標準座標系
  • 文字列の描画
  • 簡単なアニメーション
  • 文字列の描画(その2)
  • 仮想座標系
  • 仮想座標系の定義(g_def_scale 関数)
  • 円の描画(g_circle 関数)
  • 長方形の描画(g_box 関数)
  • 線の描画(g_move 関数と g_plot 関数)
  • 定数の使い方
  • 数学関数の使い方

macOSでGLSCを使う場合の注意点

macOSでGLSCを使う場合には、macOS下準備において、XQuartzのインストールとGLSCのインストールを終えておく必要があります。また、その手順でインストールした場合、GLSCを利用したC言語で書かれたプログラムのコンパイルには、そのソースプログラムが hello.c であったとすると、ターミナル内で、

~/glsc_mac_bin/bin/cglsc hello.c

とする必要があります。alias を使うか、パスを通すか色々な方法で楽ができますが、詳しくない人はそのまま上記の方法でコンパイルしましょう。わかる人は、楽にできるよう設定して下さい。

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

次のプログラムは、画面上にウィンドウを表示するだけのプログラムです.このウィンドウは、今後お絵かきをするための画用紙のようなものです.このウィンドウは、ウィンドウ内をマウスで左クリックすると消えます.以下のサンプルプログラムをエディターで打ち込み、ファイル名を Part14-1.c として保存してください.コンパイルの方法がこれまでと異なります.次の節の内容をよく読み、コンパイル、実行をしてください.

 1: #include <glsc.h>
 2: #include <stdio.h>
 3: 
 4: int main()
 5: {
 6:     g_init("GRAPH", 100.0, 100.0);
 7:     g_device(G_DISP);
 8:     g_sleep(G_STOP);
 9:     g_term();
10: }

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 Part12-1.c -o Part12-1 -L/usr/local/lib -lglscs -L/usr/X11R6/lib -lX11 -lm

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

~/glsc_mac_bin/bin/cglsc Part14-1.c

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

./Part14-1

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

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

GLSCのマニュアル

GLSCのマニュアルは、この演習のホームページのトップページからリンクされています.関数の使い方が良く分からない場合は、マニュアルを参照してください.(印刷するとかなりのページ数となりますから、印刷せずにブラウザで見てください.)

当面、html を直におけるサーバーをもっていないので、申し訳ないですが、次のリンクをご覧下さい。

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! と表示します.文字の表示位置の指定には標準座標系を用います.

#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();
}

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();
}

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();
}

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();
}

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

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

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();
}

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

g_cls 関数

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

#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();
}

簡単なアニメーション

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

#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();
}

途中の繰り返し文の中で、表示、停止、消去を繰り返しています.

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

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

課題1

先の "Hello!" という文字列が斜めに動くプログラムを変更して、自分の名前(ローマ字)がウィンドウの4辺で跳ね返るような動きをするアニメーションに変更せよ.

文字列の描画(その2)

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

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

int main()
{
   double a;
   char text[256];

   a = 3.14;

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

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

    g_sleep(G_STOP);
    g_term();
}

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

仮想座標系

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

標準座標系は、仮想座標系の定義と、文字の位置指定のみに用いる座標系です.お絵かきには、ここで紹介する仮想座標系を用います.

標準座標系は、例えば g_init("GRAPH", 200.0, 100.0) と初期化されたウィンドウでは次の図のようになりました.これはなんの指定もなく得られる座標系です.

つまり、左上が座標 (0.0, 0.0) であり、右下が (200.0, 100.0) となるような座標系です.先に見たように、文字列をGLSCウィンドウ内に描画する際、その位置指定には、この標準座標系を用いました.例えば、以下のプログラムは、文字列 "Hello!" を標準座標 (0.0, 10.0) に描きます.

#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();
}

さて、例えば、 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 );

はじめの引数は、これから定義する仮想座標系につける通し番号です.2,3,4,5番目の引数は、仮想座標系の左端、右端、下端、上端の値であり、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();
}

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

円の描画

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);

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

  g_sleep(G_STOP);
  g_term();
}

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

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

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>

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();
}

四角形の描画

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

直線の描画

g_move 関数g_plot 関数を用いると直線を描くことができます.g_move 関数で直線の始点を指定し、g_plot 関数で直線の終点を指定します.また、g_line_color 関数で、直線の色を指定でき、g_line_width 関数で直線の幅を指定できます(それぞれマニュアル参照).

#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_area_color(G_BLUE);
  g_line_color(G_BLUE);
  g_circle(0.0, 0.0, 0.5, G_YES, G_YES);

  g_line_color(G_YELLOW);
  g_line_width(3);
  g_move(-1.0, -1.0);
  g_plot(1.0, 1.0);

  g_line_color(G_BLACK);
  g_line_width(1);
  g_move(-1.0, 1.0);
  g_plot(1.0, -1.0);

  g_sleep(G_STOP);
  g_term();
}

g_plot を連続してプログラム中に書くと、連続した直線を描く事ができます.直前のプログラムの2つめの g_move を消去した次のプログラムでは、次のような結果となります(実際に2つめの g_move 関数を消去して試してみよ).

つまり、直前の g_plot 関数で指定された座標を始点とする直線が引かれます.

課題2

複数の仮想座標を定義し、それぞれに対し円や長方形を描画せよ.

課題3

円や長方形を組み合わせて、なにか面白い絵を描け.

課題4

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

おまけ

課題1の解答例です.名前ではなく文字 0 を描いています.

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

int main()
{
  int i;
  double x, y, dx, dy;

  /* 初期位置 */
  x = 10.0;
  y = 30.0;

  /* 初期移動量 */
  dx = 2.0;
  dy = 2.0;

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

  /* テキストの色を赤色に設定 */
  g_text_color(G_RED);
  for(i = 0; i < 200; i++)
  {
    /* 文字 0 を描画 */
    g_text(x, y, "0");

    /* 描画位置を変更 */
    x = x + dx;
    y = y + dy;

    /* 左右の壁で跳ね返る */
    if((x < 10.0) || (x > 90.0))
    {
      dx = -dx;
    }

    /* 上下の壁で跳ね返る */
    if((y < 10.0) || (y > 90.0))
    {
      dy = -dy;
    }

    g_sleep(0.02);
    g_cls();
  }

  g_sleep(G_STOP);

  g_term();
}

課題5

GLSCを用いて, y = sin(x) のグラフを描け.

ヒント: y = sin(x) のグラフは曲線であるが,それを折れ線で近似する事で描ける.g_move , g_plot を用いて折れ線を描く事で近似的に曲線を描けば良い.(次回の内容)