Part.15

GLSC実践

内容

  • y = sin(x) のグラフの描画
  • y = sin(x + t) のアニメーション

y = sin(x) のグラフの描画

これから、GLSCを使って y = sin(x) のグラフを描きます.実際には y = sin(x) のグラフは滑らかな曲線ですが、コンピュータではそのような滑らかな曲線を直接は扱えませんので折れ線で近似します.つまり、変数 x は、実際には連続的に変化するものですが、とびとびの値を使います.後の例で見るように、とびとびとはいえ、十分に細かく取れば、折れ線で曲線をうまく近似することができます.

図のように [0, L] 区間を N-1 等分して、その等分した小区間の幅を dx とします.N-1 等分すると図のように N 個の区切りが現れますが、それらに 0,1, ...., i, ...., N-1 と番号をつけることにします.また、 N を分割数、 dx を分割幅と呼びます.このとき、

Xi = i*dx

の部分についてそれぞれ y = sin(Xi) を計算し、それらを折れ線で結ぶことを考えます.C言語風に書けば

X[i] = dx*i;

Y[i] = sin(X[i]);

をそれぞれ求め、(X[0], Y[0]), (X[1],Y[1]), ...., (X[N-1], Y[N-1]) を結ぶ折れ線を描くことになります.これをプログラムにすると次のようになります.(上記の説明にあわせるためにかなり無駄なことをしています.)ファイル名を Part15-1.c として打ち込み、実行してみてください.

注意: 数学関数(sin, cos, tan 等)を使う場合には #include <math.h> が必要となります.

#include <glsc.h>
#include <stdio.h>
/* 次の文は数学関数を使う場合に必要 */
#include <math.h>

/* 定数の定義 */
#define N (5)
#define PI (3.1415926)
#define L (2*PI)

int main()
{
  int i;
  double X[N], Y[N], dx;

  /* dx を求める */
  dx = L/(N - 1);

  /* GLSCの初期化および仮想座標系の定義 */
  g_init("GRAPH", 200.0, 100.0);
  g_device(G_DISP);

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

  /* 外枠の描画 */
  g_sel_scale(1);
  g_area_color(G_WHITE);
  g_line_color(G_BLACK);
  g_line_width(2);
  g_box(0.0, L, -1.0, 1.0, G_YES, G_YES);
  g_move(0.0, 0.0);
  g_plot(L, 0.0);

  /* X[i] を求める */
  for(i = 0; i < N; i++)
  {
    X[i] = i*dx;
  }

  /* Y[i] を求める */
  for(i = 0; i < N; i++)
  {
    Y[i] = sin(X[i]);
  }

  /* 折れ線を描く */
  g_line_color(G_RED);
  g_line_type(G_LINE_SOLID);
  g_line_width(2);

  g_move(X[0], Y[0]);
  for(i = 1; i < N; i++)
  {
    g_plot(X[i], Y[i]);
  }

  g_sleep(G_STOP);
  g_term();
}

上記プログラムに、グラフの左端右端等の情報を書き入れたプログラムの出力を使って、N とグラフの関係を見てみましょう.青色の破線で書かれたグラフが描きたいグラフ (y = sin(x) )です(とはいっても、もちろんこれも N=100 の折れ線です).赤色の実線が描こうとしている折れ線です.

N=5 の場合は次のようになります(赤線).これでは、まったく y = sin(x) のグラフには見えません.

つづいて、N=10 の場合(赤線).曲線っぽくみえてきました.

N=50 の場合(赤線).見た目はほぼ y=sin(x) のグラフに見えます.

先のプログラム例では、 g_move および g_plot 関数を用いてグラフを描いていましたが、GLSCにはグラフを描く為のg_data_plot 関数が用意されているので通常それを用います.g_data_plot 関数を用いた同様のプログラムを次に示します.先のプログラムにはあった X[N] の配列は不要なのでなくしました(先のプログラムでも実際には X[N] の配列は不要でした).ファイル名を Part15-2.c として打ち込み、実行してみてください.

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

/* 定数の定義 */
#define N (5)
#define PI (3.1415926)
#define L (2*PI)

int main()
{
  int i;
  double Y[N], dx;

  /* dx を求める */
  dx = L/(N - 1);

  /* GLSCの初期化および仮想座標系の定義 */
  g_init("GRAPH", 200.0, 100.0);
  g_device(G_DISP);

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

  /* 外枠の描画 */
  g_sel_scale(1);
  g_area_color(G_WHITE);
  g_line_color(G_BLACK);
  g_line_width(2);
  g_box(0.0, L, -1.0, 1.0, G_YES, G_YES);
  g_move(0.0, 0.0);
  g_plot(L, 0.0);

  /* Y[i] を求める */
  for(i = 0; i < N; i++)
  {
    Y[i] = sin(i*dx);
  }

  /* 折れ線を描く (g_data_plot)*/
  g_line_color(G_RED);
  g_line_type(G_LINE_SOLID);
  g_line_width(2);

  g_data_plot(0.0, L, Y, N);

  g_sleep(G_STOP);
  g_term();
}

g_data_plot 関数には4つの引数があります.1番目、2番目の引数は仮想座標系における左端および右端の値、3つめはこれから描くグラフの y 座標が格納されている配列名(今の場合、配列を Y[N] で定義しているので、 Y が配列名となる).4つめは配列のサイズ(整数)となります.詳しくはマニュアルを参照してください.

課題1

上のサンプルプログラム(Part15-2.c)を表示例のように表示するプログラムに変更せよ.(グラフの左端等の表示とN=100のグラフを重ねて表示するように変更)

ヒント: Y[N] とは別に Y0[100] という配列を作って、それぞれを異なる線種で g_data_plot を用いて描く.また、文字列の描画(その2)で説明した手法を用いてタイトル部分を書くと良い.

課題2

仮想座標系を2つ用意し、下の図のように、y = sin(x) のグラフと y = cos(x) のグラフをそれぞれの仮想座標系に対して描け.(必ず仮想座標系を2つ用意すること.)

y = sin(x + t) のグラフのアニメーション

すでに、アニメーションの作り方を知っているので簡単です.ただし、いま t は時間として扱いますが、当然連続量として扱うことができないので、x 同様とびとびの値として扱います.先ほどの x と同様に考え、t の範囲を [0, T] として、それを K-1 等分します.その小区間の幅を dt として(つまり、 dt = T/(K-1))、

Tk = dt*k

とすることにより、 Tk を止めるごとに Y[i] = sin(Xi + Tk) のグラフを描くことを繰り返せば、アニメーションとなります.次のサンプルプログラムを Part15-3.c として打ち込み実行してみてください.これまでのアニメーションプログラムと仕組みが同じであることに気づくことが重要です.

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

/* 定数の定義 */
#define N (50)
#define K (100)
#define PI (3.1415926)
#define L (2*PI)
#define T (10.0)

int main()
{
  int i, k;
  double Y[N], dx; 
  double dt;

  /* dx, dt を求める */
  dx = L/(N - 1);
  dt = T/(K - 1);

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

  /* 仮想座標系の定義 */
  g_def_scale(1, 0.0, L, -1.0, 1.0, 10.0, 10.0, 180.0, 80.0);

  g_sel_scale(1);

  for(k = 0; k < K; k++)
  {
    /* 外枠の描画 (グラフの消去) */
    g_area_color(G_WHITE);
    g_line_color(G_BLACK);
    g_line_width(2);
    g_box(0.0, L, -1.0, 1.0, G_YES, G_YES);
    g_move(0.0, 0.0);
    g_plot(L, 0.0);

    /* Y[i] を求める */
    for(i = 0; i < N; i++)
    {
      Y[i] = sin(i*dx + k*dt);
    }

    /* 折れ線を描く (g_data_plot)*/
    g_line_color(G_RED);
    g_line_type(G_LINE_SOLID);
    g_line_width(1);

    g_data_plot(0.0, L, Y, N);

    g_sleep(0.05);
  }

  g_sleep(G_STOP);
  g_term();
}

課題3

上のサンプルプログラム(Part15-3.c)で N を 5 にするとどうなるか?やってみよ.

課題4

上のサンプルプログラム(Part15-3.c)にグラフの左端等の情報を表示する部分を追加せよ.

課題5

次の図を描くプログラムを作成せよ.

ここで学んだ事

  • 曲線を折れ線で近似し描画することを学びました
  • 数学関数を用いた簡単なアニメーションを描きました