タイムカウント


1秒ごとに変化させるプログラムを書いてみる。 まずは端末の標準出力から、タイムカウントを表示させてみる。

#include <gtk/gtk.h>

static gboolean ivent_loop(gpointer user_data)
{
  static gint count = 0;

  //g_printはprintfのgtk+版
  g_print("progress(sec.):%2d\n", count++);

  return (TRUE);
}

int main(int argc, char *argv[])
{
  gtk_init(&argc, &argv);

  //1000ミリ秒毎にivent_loop()関数を呼び出す。
  g_timeout_add(1000, (GSourceFunc)ivent_loop, NULL);

  gtk_main();
  return 0;
}

今さらながら、printfの代わりに
g_print
が有る事を知った。 g_timeout_addを使えば、1ミリ秒単位で関数を呼び出す事ができる。 となると、アニメーションも可能である。


GDKとCairo


GTK+マニュアルdevhelpの中に、GDKのCairoの説明が有った。 ウィンドウの中に描画し、形、色、サイズ、位置を指定できるらしい。 これは使ってみたいと思い、チャレンジしてみた。 まず、図形を描いてみる。

#include <gtk/gtk.h>
#include <gdk/gdk.h>

GtkWidget *window;

static gboolean cb_expose_event(GtkWidget *widget, cairo_t *cr, gpointer data)
{
  cr = gdk_cairo_create(gtk_widget_get_window(widget));
  //線の太さ
  cairo_set_line_width(cr, 5.0);
  //角を丸くする
  cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);

  //長方形の描画
  {
    //赤色の指定(外枠:Red,Green,Blue)
    cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
    //長方形(x,y,width,height)を描画
    cairo_rectangle(cr, 50.0, 50.0, 50.0, 50.0);
    //長方形の外枠を作る
    cairo_stroke_preserve(cr);
    //桃色の指定(塗りつぶし:Red,Green,Blue)
    cairo_set_source_rgb(cr, 1.0, 0.8, 0.8);
    //長方形を塗りつぶす
    cairo_fill(cr);
  }

  //グラデーション付き長方形
  {
    //外枠(青)を作る
    cairo_set_source_rgb(cr, 0.0, 0.0, 1.0);
    cairo_rectangle(cr, 150.0, 50.0, 50.0, 50.0);
    cairo_stroke_preserve(cr);
    //グラデーションを作る
    //どのようなグラデーションにするかをpattern変数で指定する
    cairo_pattern_t *pattern;
    pattern = cairo_pattern_create_linear(200.0, 60.0, 200.0, 100.0);
    //白(1.0, 1.0, 1.0)から青(0.0, 1.0, 0.0)にグラデーションする
    cairo_pattern_add_color_stop_rgb(pattern, 0.0, 1.0, 1.0, 1.0);
    cairo_pattern_add_color_stop_rgb(pattern, 1.0, 0.0, 0.0, 1.0);
    //上記のグラデーションを設定する
    cairo_set_source(cr, pattern);
    //上記のグラデーションで長方形を塗りつぶす
    cairo_rectangle(cr, 150.0, 50.0, 50.0, 50.0);
    cairo_fill(cr);
  }

  //円形(長方形とほぼ同じ)
  {
    //枠は緑色
    cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
    //円(x,y,半径,角度(start):0°,角度(end):360°)を描画
    cairo_arc(cr, 270.0, 75.0, 20.0, 0.0, 2.0*3.14);
    cairo_stroke_preserve(cr);
    //塗りつぶしは黄色
    cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
    cairo_fill(cr);
  }

  cairo_destroy(cr);
  return FALSE;
}

int main(int argc, char *argv[])
{
  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "draw");
  gtk_widget_set_size_request(window, 400,200);

  //ウインドウに図形を描けるように設定
  gtk_widget_set_app_paintable(window, TRUE);
  gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);

  //ウインドウが表示されたときにcb_expose_event()を呼び出す
  g_signal_connect(G_OBJECT(window), "draw", G_CALLBACK(cb_expose_event), NULL);
  g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);
  gtk_main();

  return 0;
}

長方形と円を、指定したサイズ、位置に表示する事ができた。 なお、GDKを使うには
gdk.gdk.h
ヘッダファイルをインクルードする。


図形の等速移動


いま作った黄色い円を、上下左右に等速運動させてみる。画面の端で、円を跳ね返す。

#include <gtk/gtk.h>
#include <gdk/gdk.h>

GtkWidget *window;
//ウインドウの横幅
#define W_WIDTH 350
//ウインドウの縦幅
#define W_HEIGHT 500

static gboolean cb_expose_event(GtkWidget *widget, cairo_t *cr, gpointer data)
{
  cr = gdk_cairo_create(gtk_widget_get_window(widget));

  //ボールに関する変数
  //ボールのx座標
  static gint x = W_WIDTH/2;
  //ボールのy座標
  static gint y = W_HEIGHT/2;
  //ボールの半径
  static gdouble radius = 20.0;
  //ボールの進む向きのx方向
  static gint vx = 1;
  //ボールの進む向きのy方向
  static gint vy = 1;
  //ボールの進む速さ
  static gint velocity = 5;

  //ウインドウの端でボールを跳ね返す
  if( x + radius > W_WIDTH || x - radius < 0 ) vx *= -1;
  if( y + radius > W_HEIGHT || y - radius < 0 ) vy *= -1;
  x += vx * velocity;
  y += vy * velocity;

  //図形の位置を指定する
  cairo_translate(cr, x, y);

  //ボールを描画する
  cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
  cairo_arc(cr, 0.0, 0.0, radius, 0.0, 2.0*3.14);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
  cairo_fill(cr);

  cairo_destroy(cr);
  return FALSE;
}

static gboolean ivent_loop(GtkWidget *widget)
{
  if (gtk_widget_get_window(widget) == NULL)
    {
      return FALSE;
    }

  gtk_widget_queue_draw(widget);

  return (TRUE);
}

int main(int argc, char *argv[])
{
  GtkWidget *window;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "move");
  gtk_widget_set_size_request(window, W_WIDTH, W_HEIGHT);
  gtk_widget_set_app_paintable(window, TRUE);

  gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);

  g_signal_connect(G_OBJECT(window), "draw", G_CALLBACK(cb_expose_event), NULL);
  g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

  g_timeout_add(50, (GSourceFunc)ivent_loop, window);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

赤い矢印の方向に、円が等速で移動する。


図形の加速移動


錆びついた物理知識を掘り起こし、空気抵抗無しのスーパーボールを作ってみる。 位置エネルギーと運動エネルギーのみを使い、自由落下から永久にバウンドさせ続ける。 バウンド時の跳ね返り係数は1とする。

#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <math.h>

GtkWidget *window;
//ウインドウの横幅
#define W_WIDTH 350
//ウインドウの縦幅
#define W_HEIGHT 500.0

static gboolean cb_expose_event(GtkWidget *widget, cairo_t *cr, gpointer data)
{
  cr = gdk_cairo_create(gtk_widget_get_window(widget));

  //ボールに関する変数
  //ボールの半径
  static gint radius = 20;
  //ボールのx座標
  static gint x = W_WIDTH/2;
  //ボールのy座標
  static gdouble y = 20;
  static gdouble g = 9.8;
  static gint t = 0;
  static gint h1;
  static gint h0;
  static gint h;
  static gdouble v;
  static gdouble v0;
  static gboolean f = TRUE;
  static gint y2;
  h1 = W_HEIGHT - radius;
  h0 = radius;
  h = h1 - h0;
  v0 = sqrt( 2 * g * h );

  ++t;
  if( f == TRUE ){
    y = g * pow( t, 2 ) / 2;
    v = g * t;
    if( y >= 480 || v >= v0 ){
      t = 0;
      y = h1;
      f = FALSE;
      v = v0;
    }
  }
  else{
    y = h1 - v0 * t + g * pow( t, 2 ) / 2;
    v = v0 - g * t;
    if( y <= 20 || v <= 0 ){
      t = 1;
      y = h0;
      f = TRUE;
      v = 0;
    }
  }
  y2 = y / 1;
  g_printf( "t:%d y:%d\n", t, y2 );

  //図形の位置を指定する
  cairo_translate(cr, x, y2);

  //ボールを描画する
  cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
  cairo_arc(cr, 0.0, 0.0, radius, 0.0, 2.0*3.14);
  cairo_stroke_preserve(cr);
  cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
  cairo_fill(cr);

  cairo_destroy(cr);
  return FALSE;
}

static gboolean ivent_loop(GtkWidget *widget)
{
  if (gtk_widget_get_window(widget) == NULL){
    return FALSE;
  }

  gtk_widget_queue_draw(widget);

  return (TRUE);
}

int main(int argc, char *argv[]) {   GtkWidget *window;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "super ball");
  gtk_widget_set_size_request(window, W_WIDTH, W_HEIGHT);
  gtk_widget_set_app_paintable(window, TRUE);

  gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);

  g_signal_connect(G_OBJECT(window), "draw", G_CALLBACK(cb_expose_event), NULL);
  g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

  g_timeout_add(50, (GSourceFunc)ivent_loop, window);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

べき乗のpowと平方根のsqrtを使うため、 ヘッダファイル
math.h
をインクルードしたので、 コンパイルには-lmオプションが必要である。
gcc window.c -o test $(pkg-config --cflags --libs gtk+-3.0) -lm
落下はy = ½ * g * t²、 跳ね返りはy = h₁ - ( v₀ * t - ½ * g * t²) を使ってy座標を計算した。 とりあえず、サンプルはスーパーボールのように弾んでくれた。 GDKに寄り道したが、次回はGTK+に戻ろうと思う。


前章  | 目次 |  次章



トップ



/