第3回 C言語によるグラフィックス1 (2024年10月7日)

今日の内容


前回の課題2について

前回の課題2

1000以下の奇数のうち素数で無いものを全て求め、 それらを画面に表示しかつそれらの和も画面に表示するプログラムを作成せよ。

解答例として、2種類のプログラムを以下に置いておきます。 ダウンロードして実際に動かしてみてください。 見た目はかなり異なるプログラムですが、当然同じ結果を出力します。 ここでは、2つのプログラムの違いについて簡単に見ていきましょう。

解答例1

解答例2

授業内でのヒントに従って作成した人は、解答例1に似たプログラムを作成したかと思います。 一方、解答例2は配列を使い素数を求めるアルゴリズムも異なるため、解答例1よりも複雑になっています 一見すると、解答例1の方がシンプルで分かりやすいので良いプログラムに思えるかもしれませんが、 必ずしもそうではありません。

一番大きな違いは、プログラムの実行速度です。 今回の問題だと、「1000以下」の場合であれば実行速度の違いはほとんど感じられませんが、 「100000以下」の場合には、明らかな違いが現れます。 その違いを実感してもらうために、先ほどコピペした書くプログラムの上部にある 1000100000 に書き換えて実行してみましょう。 解答例2の方が、明らかに早く最後の和を表示するはずです。

このことから、アルゴリズムの違いによって明らかな実効速度の違いが現れることを実感できたはずです。 プログラミングに慣れてきたら、どの様なアルゴリズムが効率的なのかを考えて問題に取り組むことにチャレンジしてみましょう。 もちろん、慣れないうちは効率が悪くても良いので、 シンプルで分かりやすいプログラムをキチンと書けるようにするのが、一番大切です。

実行速度測定用のプログラムも置いておきます。自分のPCでも試してみると面白いかもしれません。

解答例1

解答例2



GLSC3D利用の準備

2024年度は引き続き、GLSC3D を利用します。

今回から利用するGLSC3Dの準備を行います。 この準備ができていないと今回以降の演習ができませんので、本日の演習で確実に準備を行います。

GLSC3Dについては、 制作者の秋山先生(富山大学)のページインストーラーやマニュアルがあり、 GitHubのページもあります。
この授業では、配布されているインストール用ファイルを秋山先生の許可を得て、直接ダウンロードできるようにしています。

前回の授業での gnuplot 利用のための準備で GLSC3D 利用のための下準備はできています。
前回の準備が済んでいない場合には、第2回の資料を見て下準備する必要があります。 gnuplot が起動できれば前回までの準備としては問題がないはずです。

前回の授業で gnuplot が起動しなかった人は、Macにログインしているアカウントに問題がある可能性があります。
自分で作成したアカウントにログインしている場合には一度ログアウトして、 入学時に最初から存在したアカウント(おそらく sougousuuri という名前)でログインしてみてください。

もし上手くいかない場合は、小田切が対応するので説明後にすぐに声をかけてください。


GLSC3Dの導入

前回までの準備ができていたら、次に以下のファイルをダウンロードして下さい。

Script_on_mac.zip

ダウンロードしたzipファイルを解凍すると、フォルダ内に以下の4つのファイルがあります。 このフォルダは、自分のホームディレクトリ(ev24xxxx)に移動させておくと、後の作業が楽かもしれません。

次に、ダウンロードしたファイル(シェルスクリプト)を1番目から順に3番目まで実行していきます。
ターミナル上でダウンロードしたフォルダまで移動して以下のコマンドを実行して下さい。

./1_Install_macports_mac

全部間違いなく打つよりも、./1 まで打ってからtabキーを押せば残りの文字列が補完されるので、 そちらの方がはるかに簡単で間違いが少ないです。 以降のファイルの実行についても同様です。 処理に際し、パスワードの入力が求められますので、自分のMacのパスワードを入力してください。 必要なものがインストールされていない場合にはインストールが行われるため、処理にしばらく時間がかかります。

ちなみに、Finderでダウンロードしたファイルをダブルクリックすることでも実行することは可能ですが、 セキュリティの関係でダウンロードしてきたファイルは実行許可がないため、 「システム環境設定」->「セキュリティとプライバシー」->「一般」->「ダウンロードしたアプリケーションの実行許可」で パスワードを入力して「確認済みの開発元からのアプリケーションを許可」にチェックを変更しなければいけない可能性があります。 許可を与えれば、ターミナルが自動的に立ち上がり処理が開始します。 ただし、操作の手間としては上記のターミナル上で処理する方が圧倒的に楽なので、そちらをおすすめします。

処理が完了すると、ターミナル上に "...completed."の表示と「プロセスが完了しました」と表示されます。 この表示を確認したら、処理が行われていたターミナルを閉じて次の番号のファイルを実行してください。 もしエラーが出てしまい自分で対応できない場合は、教員が対応しますので、質問してください。


なお、2023年度以前入学の学生で MacOS のメジャーアップデートをおこなっている場合、 MacPorts を再インストールしなければいけない場合があります。 この場合、こちらのページから 現在の自分のMacOSのバージョンにあわせて MacPorts のインストーラーをダウンロードして、 インストールを行ってください(ダウンロードおよびインストールにかなりの時間がかかる場合があります)。 ただし、この作業については様々なエラーが出る可能性があるので、GLSC3D がとりあえず動いているのであれば、 この作業は行わないでください。


3番目のファイルはサンプルプログラムの実行確認になります。 サンプルプログラムのビルドが行われた後、

"Do you want to run the Sample Program? (YES=1, NO=0)"

とサンプル表示を確認されますので、1 を押してサンプルを実行してください。 すると、画面上にサンプル画面が現れます。確認したら、キーボード左上にあるescを押すと次のサンプルに移ります。 30個近くのサンプルを確認すると、再びターミナル上にサンプル表示の確認の文章が出ますので、 1を押して次のサンプルを確認してください。 全てのサンプルの確認が終わりましたら、次のGLSC3Dの最終チェックを行います。


GLSC3Dの最終チェック

GLSC3Dが正しくインストールされ使うことができるかを最終チェックします。 4番目のファイルを実行してください。下のwindowが表示されれば、最終チェック完了です。

上手くいかなければ教員またはTAが対応しますので、質問してください。


GLSCとは

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

この授業で学ぶGLSC3Dは、GLSCの欠点を補うべく秋山正和先生を中心に開発されたグラフィックライブラリです。 既にGLSCを利用していたユーザーにもなるべく同じように使えるように設計されています。 とは言え、GLSCで動いていたコードをそのまま動かせるわけではなく、GLSCからの変更点も存在しますので、 既にGLSCを学んだことがある人はこれらの点に注意してください。

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


もっとも単純なGLSC3Dのプログラム

次のプログラムは、画面上に白いウィンドウを表示するだけのプログラムです。 GLSC3Dでは、このウィンドウ内に絵を描くので、ウィンドウの表示は絶対に必要です。 表示されたウィンドウは、5秒経つと消えます。あるいは、表示されている間にescキーを押しても消えます。 以下のサンプルプログラムをエディタで打ち込み、~/Prog2/GLSC フォルダ内にファイル名を 03-01.c として保存してください (~ はホームフォルダの意味。~/Prog2 はホームフォルダにある Prog2 フォルダを指す)。 他のフォルダ内にファイルを保存する場合には、各自読み替えてください。 コンパイルの方法がこれまでと異なります。後にある説明を見てください。

 1: #include <glsc3d_3.h>
 2: #include <stdio.h>
 3: 
 4: int main()
 5: {
 6:     g_init("Test", 300, 300);
 7:     g_cls();
 8:     g_finish();
 9:     g_sleep(5);
10:
11:    return 0;
12: }

1行目: GLSC3Dを使う場合にはこの #include 文が必要、つまり glsc3d_3.h のインクルードが必要です

6行目: g_init 関数を用いて、絵を描くウィンドウの初期化を行います。 第一引数である "Test" は、ウィンドウの名前を指定します。ウィンドウの名前は、ウィンドウ上部に表示されます。 その後に続く2つの引数は、整数型で、ウィンドウのサイズを指定します。

7行目: g_cls 関数で、ウィンドウを背景色(デフォルトでは白)で塗りつぶします。 この行をコメントアウトするとウィンドウが背景色で塗りつぶされずに、ノイズがかかったような状態になります。

8行目: g_finish 関数で、この関数を呼ぶことでウィンドウ内の描画を行います。 この行をコメントアウトすると、背景色で塗りつぶされていないウィンドウが表示されます。

9行目: g_sleep 関数で、引数として指定した秒数(実数)だけ静止します。例えば g_sleep(1.2); であれば1.2秒停止します。


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

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

しかし、それをいちいち打ち込むのは面倒なので、 GLSC3Dプログラムを簡単にコンパイルするためのコマンド ccg が用意されています。 このコマンドを利用すると、長いコマンドを打ち込んだ結果と同様の結果が得られます。

(GLSC3Dやccgコマンドは、全てのMacに導入されている訳ではなく、 インストール作業を行った結果として導入されていることに注意してください。 自分で新たにMacを購入した場合には、導入作業が必要になります。)


ccg  03-01.c

上記の様に入力することで、実行可能なファイル 03-01 が生成されます。 プログラムはこれまで同様、

./03-01

で実行され、この場合、次のような真っ白な正方形ウィンドウが画面に表示されます。 ./a.outでの実行ではないので注意してください。

表示されたウィンドウは、5秒経つと消えます。あるいは、表示されている間にescキーを押しても消えます。

注意:今後、GLSC3Dを用いたプログラムで、実数型変数を使う場合には double 型を用いてください。


GLSC3Dのマニュアル

GLSC3Dのマニュアルは、こちらにあります。 関数の使い方が良く分からない場合は、マニュアルを参照してください。


g_init 関数

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

次の各場合について、ソースファイルを書き直した後、コンパイルし直してから実行してください。

g_init("Test2", 100, 200);

g_init("Test3", 300, 200);


標準座標系

GLSC3Dには、大きく分けて標準座標系と自由座標系という二つの座標系が存在します。ここではまず、標準座標系について説明します。

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

 

 

つまり、左上が座標 (0.0, 0.0) であり、右下が (200.0, 100.0) となるような座標系です。 数学で通常用いる2次元座標系とは、y軸(縦軸)の方向が逆になっていることに注意してください。


GLSC3Dによる文字列の描画

GLSC3Dでは g_text_standard 関数を用いて文字をウィンドウ内(標準座標系)に表示することができます。 (以前のGLSCでは漢字は表示できませんでしたが、GLSC3Dでは日本語も利用可能です。) 例えば、次のプログラムでは、画面ウィンドウ内に「こんにちは! 世界とGLSC」と表示します。

#include <glsc3d_3.h>
  #include <stdio.h>
  
  int main(){
    g_init("Test", 300, 300);
    g_cls();
  
    g_text_standard(10.0, 100.0, "こんにちは! 世界とGLSC");
  
    g_finish();
    g_sleep(10);
  
    return 0;
  }

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

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

#include <glsc3d_3.h>
  #include <stdio.h>
  
  int main(){
    g_init("Test", 300, 300);
    g_cls();
  
    g_text_standard(10.0, 100.0, "こんにちは!");
    g_text_standard(50.0, 150.0, "世界とGLSC");
  
    g_finish();
    g_sleep(10);
  
    return 0;
  }


g_text_color 関数を使って文字の色を変更したり、g_text_size 関数を使って文字の大きさを変更することもできます。

#include <glsc3d_3.h>
  #include <stdio.h>
  
  int main(){
    g_init("Test", 300, 300);
    g_cls();
  
    g_text_color(1.0, 0.0, 0.0, 1.0);
    g_text_size(40.0);
    g_text_standard(10.0, 100.0, "こんにちは!");
    g_text_standard(50.0, 150.0, "世界とGLSC");
     
    g_finish();
    g_sleep(10);
  
    return 0;
  }

g_text_color 関数や g_text_size 関数を使って、文字の色や大きさを変えると、 その後 g_text_standard 関数で表示する文字全てに適用されます。 例えば、"世界とGLSC" は黒く小さい文字で表示したい場合には、次の様に改めて黒とサイズを指定する必要があります。

#include <glsc3d_3.h>
  #include <stdio.h>
  
  int main() {
    g_init("Test", 300, 300);
    g_cls();
  
    g_text_color(1.0, 0.0, 0.0, 1.0);
    g_text_size(40.0);
    g_text_standard(10.0, 100.0, "こんにちは!");
     
    g_text_color(0.0, 0.0, 0.0, 1.0);
    g_text_size(20.0);
    g_text_standard(50.0, 150.0, "世界とGLSC");
     
    g_finish();
    g_sleep(10);
  
    return 0;
  }

 

g_text_color 関数の1番目から3番目の引数は、それぞれ光の三原色である、赤(R)・緑(G)・青(B)の強さを 0〜1 の間の実数値で指定します。 また、4番目の引数は不透明度を 0〜1 の間の実数値で指定します(完全に不透明なら 1.0)。 これらを混ぜ合わせた色が表示色となります。 例えば、こんにちは! という文字を、R:G:B=1:0.5:0.1 の比率で混ぜ合わせた色で表示します。 g_text_color 以外の色指定関数( g_line_color や g_area_color 等)にも同様にして使えます。 GLSC の文字は、黒色(R:G:B=0:0:0)で表示します。 なお、もっとも明るい白色は R:G:B=1.0:1.0:1.0 で指定します。

#include <glsc3d_3.h>
  #include <stdio.h>
  
  int main() {
    float  r, g, b;
   
    r = 1.0; 
    g = 0.5; 
    b = 0.1;
   
    g_init("Test", 300, 300);
    g_cls();
     
    g_text_color( r, g, b, 1.0 );
    g_text_standard(10.0, 100.0, "こんにちは!");
   
    g_text_color( 0.0, 0.0, 0.0, 1.0 );
    g_text_standard(50.0, 150.0, "世界とGLSC");
   
    g_finish();
    g_sleep(10);
  
    return 0;
  }
  

もちろん、色の強度を指定する引数に変数を用いることもできます。 以下では、表示位置を変化させると同時に文字の色も変化させています。

#include <glsc3d_3.h>
  #include <stdio.h>
  
  int main() {
    int i;
    float r, g, b;
     
    g_init("Test", 300, 300);
    g_cls();
     
    for(i = 0; i < 8; i++) {
      r = (float)(i / 4);
      g = (float)(i / 2) - 2*r;
      b = (float)(i % 2);
      g_text_color(r, g, b, 1.0);
      g_text_standard(10.0 + i*30, 50.0 + i*30 ,"Hello!");
    }
     
    g_finish();
    g_sleep(10);
  
    return 0;
  }


g_cls 関数

g_cls 関数を用いるとウィンドウを背景色で塗りつぶすため、ウィンドウ内の描画を消去することができます。 次のプログラムでは、カラフルに Hello! を表示した後に3秒ほど止まります(一つ目の g_sleep(3))。 その後 g_cls 関数が実行されることで文字が消去され、再び止まります(二つ目の g_sleep(5))。 もし、二つ目の g_sleep 関数がない場合には、すぐにウインドウが閉じてしまうことに注意してください。 また、二つ目の g_finish 関数がない場合には、文字が消去されないことにも注意してください。 以上のことを踏まえて、動作を確かめてください。

#include <glsc3d_3.h>
  #include <stdio.h>
  
  int main() {
    int i;
    float r, g, b;
     
    g_init("Test", 300, 300);
    g_cls();
     
    for(i = 0; i < 8; i++) {
      r = (float)(i / 4);
      g = (float)(i / 2) - 2*r;
      b = (float)(i % 2);
      g_text_color(r, g, b, 1.0);
      g_text_standard(10.0 + i*30, 50.0 + i*30 ,"Hello!");
    }
     
    g_finish();
    g_sleep(3);
  
    g_cls();
    g_finish();
    g_sleep(5);
  
    return 0;
  }

簡単なアニメーション

文字列をある位置に表示し、しばらくとまった後、画面を消去し、先ほどとは少し違った場所に再び文字列を表示するという一連の作業を繰り返すと、 アニメーション効果を得ることができます。 ぱらぱらアニメの要領です。 ゲーム等で実現されている美しいアニメーションも、原理的にはぱらぱらアニメと何ら変わりません。

#include <glsc3d_3.h>
  #include <stdio.h>
  
  int main() {
    int i, j;
    float r, g, b;
   
    g_init("Test", 300, 300);
   
    for(i = 0; i < 300; i++) {
      // 画面の消去
      g_cls();
  
      j = i % 8;
      r = (float)(j / 4);
      g = (float)(j / 2) - 2*r;
      b = (float)(j % 2);
      g_text_color(r, g, b, 1.0);
      // 文字列の表示
      g_text_standard(10.0 + i, 10.0 + i, "Hello!");
   
      // 描画と停止
      g_finish();
      g_sleep(0.02);
    }
   
    g_sleep(10);
  
    return 0;
  }

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

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


文字列の描画(その2)

例えば、変数に納められた数値を GLSC3D ウィンドウに表示したい場合は、次のように文字列型(文字型の配列)を用いる必要があります。 これは一つのパターンで、今後よく使う事になります。 ここにその情報があることはよく覚えておいてください。

#include <glsc3d_3.h>
  #include <stdio.h>
  
  int main() {
    double   a;
    char   text[256];
  
    a = 3.14;
    g_init("Test", 300, 300);
    g_cls();
   
    sprintf(text, "a = %lf", a); 
  
    g_text_standard(10.0, 100.0, text);
  
    g_finish();
    g_sleep(10);
  
    return 0;
  }

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


課題の提出

簡単なアニメーションにあるプログラムを改変し、scanf関数を用いて自分で入力した文字列が動き回る アニメーションプログラムを作成してください。 ファイル名は、03-rep.c としてください。 その際、文字列の動き方を自分で工夫して変更すること。

例えば、文字列が色を変えながら左右に往復する、三角や四角(あるいは多角形状)に動く、円周上を動く、壁に跳ね返る、 色がランダムに変わる、数字が動きとともに増えていく等々。 動きの向きだけを変えた等、少しの改変しかしていないものは不可。

ヒント

ヒントについては、授業当日に公開します。

自分で入力した文字を動かすためには、scanf関数で入力した文字列をそのままg_text_standard 関数の引数として与えればよい。

具体的には、

char text[256];

printf("文字を入力\n");
scanf("%s", text);

として文字を取得し、変数 text をそのまま g_text_standard 関数の引数にする。

g_text_standard(10.0 + i, 10.0 + i, text);

文字列の変換指定子が%s、textはchar型の配列なので &text とはしないことに注意。 (上の例の座標は例で示したものなので、自分の考える動きに合わせて変えること)

(ただし、入力した文字列の先頭に "j" の文字があると強制終了してしまうバグあり)

ヒント2

「壁に跳ね返る」動きを実装したい場合には、壁の位置まで来たら動きを反転させる必要がある。
やり方の一つとしては、動く方向の速度ベクトル(vx,vy)を考えた上で、

・左右の壁に当たったら、左右の向きが反転するので vx の符号を反転
・上下の壁に当たったら、上下の向きが反転するので vy の符号を反転

させれば良い。 速度の符号の反転は、vx *= -1.0 (vx = vx * (-1.0) と同じ)などとすればOK。

壁の位置については if 文で判定する。
位置(x,y)については、1ステップ後の位置は現在の位置に速度ベクトルを足したもの、 つまり x += vx (x = x + vx と同じ) 等となることを考えれば良い。

注意

ただし、コメント文として

学生番号、名前、どんなアニメーションかの説明

を書くこと。

また、提出したファイルが正しいものであるかどうかは、毎回確認するよう習慣づけてください。

提出締め切り: 2024年10月7日19時


戻る