アニメーションによる可視化


交差点トレースプログラム

交差点を順にマーカーが移動する、アニメーション・プログラムを作成して下さい。交差点順番の入力、及びアニメーションのためのプログラムの流れは次のようになります。

1. 隣接する交差点の設定(移動経路の設定)

交差点のID(数字)を配列 path[] に入れる。 例えば、経路上の交差点IDが: 1, 5, 3, 2, 7 だとすると、

path[0] = 1;
path[1] = 5;
path[2] = 3;
path[3] = 2;
path[4] = 7;
path[5] = -1;

とする。最後の -1 は、経路が終ったことを示すダミーのID。

2. グラフィックの初期化、地図の表示

ウィンドウを開き、地図全体を描きます。「グラフィックス(データの可視化)」で作成した プログラム map.c 中の関数等をそっくりそのまま 流用可能です。

3. マーカーの移動アニメーション表示

入力したIDの順に交差点間を移動するマーカーを アニメーション表示する。

例えば、以下のようなループで実現できます。OpenGLではループごとに少しずつ描画するものを変化させる ことでアニメーションを表示できます。そこで、main関数のfor文中の描画部分で、ループごとに異なる描画をするため の判定文を書くことにします。

最初のif文でパスが最後までいっていないことを確認し、2 つの交差点の位置をとりだします。とりだした交差点間で少 しずつ現在位置を移動させます。現在位置が次の交差点を過ぎたところまでいったら現在の交 差点を表すインデクスを進めます。最後に移動体を表示します。

/* 今の交差点と次の交差点のIDがどちらもダミーでない時 */
if (path[ /* パス上の現在の交差点を表すインデクス */ ] != -1 &&
    path[ /* パス上の次の交差点を表すインデクス */ ] != -1) {

    /* まだゴールに達していないので、移動体の位置を進める */
    /* 現在の交差点と次の交差点を (x0, y0),(x1, y1)とする */
    /* 交差点の間で現在の位置(vehicle_x, vehicle_y)を少しずつ進める */

    if(/* 現在の位置が次の交差点を過ぎていたら */ ){
        /* パス上の現在の交差点を表すインデクスを進める */
    }
}
/* 現在の位置に移動体を表示 */

まず、以下のヒントを良く読み、何をプログラミングすればいいのか、どこを 修正すればいいのか、全体の見通しを立てて下さい。時間が来たらプログラムの 例を示すまで十分に時間をとりますから、それまで自力でプログラムを完成させて下さい。


演習のヒント

この課題は、「グラフィックスプログラミング(データの可視化)」で作成した map.c に関数を幾つか追加し、main()関数を変更すると楽に作成できます。追加する関数は次のものです。

/* 配列 path[] に、マーカーを移動させる経路上の交差点IDを代入する。*/
void path_set(void)

関数の詳細なヒントを以下に示します。

/* 経路を通過する交差点IDの例として配列pathに設定する(この通り作れば動きます) */
void path_set(void)
{
    int i=0;
    path[i++] = 60;        /* 若林 */
    path[i++] = 85;        /* 蒲町 */
    path[i++] = 48;        /* 六丁の目 */
    path[i++] = 49;        /* 苦竹IC */
    path[i++] = 50;        /* 原の町駅前 */
    path[i++] = 21;        /* レジャーセンター前 */
    path[i++] = 22;        /* 勾当台公園 */
    path[i++] = 23;        /* 市民会館 */
    path[i++] = 16;        /* 大学病院前 */
    path[i++] = 17;        /* 厚生病院前 */
    path[i++] = -1;        /* -1 は経路の終りを表す */
}

これらの関数の他に、以下のマクロ定義、配列宣言が必要です。

マクロ定義を追加

#define PATH_SIZE      100 /* 経路上の最大の交差点数 */
#define MARKER_RADIUS  0.2 /* マーカーの半径 */

経路のための配列 path を宣言

/* データの宣言 */
Crossing cross[CROSSING_SIZE];
int path[PATH_SIZE + 1];  /* 経路: 通過する交差点IDを順番に格納したもの */

int vehicle_pathIterator = 0; /* パス上の現在の交差点を表すインデクス  */
int vehicle_stepOnEdge = 0;   /* 交差点の間を何ステップで移動するか  */
double vehicle_x = 0.0,
       vehicle_y = 0.0;  /* 移動体の座標 */

それぞれしかるべき位置に追加して下さい。

また、main関数にはマーカの移動アニメーション用のコードを追加します。


プログラム例

時間が来たら表示します。

ひな形を こちら に用意しましたので、適宜使用ください。

/* mobile.c --- 移動体の表示 *
 *
 * cc mobile.c -g -O2 -Wall -Wno-unused-result -o mobile -I/usr/include/freetype2 -lftgl -lglfw -lGLU -lGL -lX11 -lXrandr -lm 
 */

#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include <GL/glfw.h>
#include <FTGL/ftgl.h>

#define CROSSING_SIZE 100  /* 交差点数=100 */
#define MAX_NAME_SIZE  50  /* 最大文字数50文字(半角) */

/* 追加するマクロ定義 */
#define PATH_SIZE     100  /* 経路上の最大の交差点数 */
#define MARKER_RADIUS 0.2  /* マーカーの半径 */

/* 座標変換マクロの定義 */
#define ORIGIN_X      1.0
#define ORIGIN_Y     -1.0
#define REAL_SIZE_X  10.0
#define REAL_SIZE_Y   8.0

#ifndef FONT_FILENAME
/* 演習サーバに用意されているフォントのファイル名 */
#define FONT_FILENAME "/usr/share/fonts/truetype/takao-gothic/TakaoGothic.ttf"
#endif
static FTGLfont *font; /* 読み込んだフォントを差すポインタ */

/* データ構造の定義 */
typedef struct {
    double x, y;           /* 位置 x, y */
} Position;                /* 位置を表す構造体 */

typedef struct {
    int id;                /* 交差点番号 */
    Position pos;          /* 位置を表す構造体 */
    double wait;           /* 平均待ち時間 */
    char jname[MAX_NAME_SIZE];   /* 交差点名(日本語) */
    char ename[MAX_NAME_SIZE];   /* 交差点名(ローマ字) */
    int points;            /* 交差道路数 */
    int next[5];           /* 隣接する交差点番号 */
} Crossing;

/* データを格納する変数の定義 */
static Crossing cross[CROSSING_SIZE];
static int path[PATH_SIZE + 1];  /* 経路: 通過する交差点IDを順番に格納したもの */


/* 円を描画 */
void draw_circle(double x, double y, double r) {
    int const N = 12;             /* 円周を 12分割して線分で描画することにする */
    int i;

    glBegin(GL_LINE_LOOP);
    for (i = 0; i < N; i++)
        glVertex2d(x + cos(2 * M_PI * i / N) * r,
                   y + sin(2 * M_PI * i / N) * r);
    glEnd();
}

/* 文字列を描画 */
void draw_outtextxy(double x, double y, char const *text) {
    double const scale = 0.01;
    glPushMatrix();
    glTranslated(x, y, 0.0);
    glScaled(scale, scale, scale);
    ftglRenderFont(font, text, FTGL_RENDER_ALL);
    glPopMatrix();
}


/* 経路を通過する交差点IDの例として、配列pathに設定する */
void path_set(void) {

    /*
     * 上に書いた通り
     */

}


/* ファイルの読み込み */
int map_read(char *filename) {
    FILE *fp;
    int i, j;
    int crossing_number;          /* 交差点数 */

    fp = fopen(filename, "r");
    if (fp == NULL) {
        perror(filename);
        return -1;
    }

    /* はじめに交差点数を読み込む */
    fscanf(fp, "%d", &crossing_number);

    for (i = 0; i < crossing_number; i++) {
        /* 関数fprintfを使って、構造体のデータを
             ファイルへ書き出すプログラムを記入しなさい */

        fscanf(fp, "%d,%lf,%lf,%lf,%[^,],%[^,],%d",
                     &(cross[i].id), &(cross[i].pos.x), &(cross[i].pos.y),
                     &(cross[i].wait), cross[i].jname,
                     cross[i].ename, &(cross[i].points));

        for(j=0; j<cross[i].points; j++) {
            fscanf(fp, ",%d", &(cross[i].next[j]));
        }

    }
    fclose(fp);

    /* ファイルから読み込んだ交差点数を返す */
    return crossing_number;
}


/* 道路網の表示 */
void map_show(int crossing_number) {
    int i, j;
    double x0, y0, x1, y1;

    for (i = 0; i < crossing_number; i++) {     /* 交差点毎のループ */
        x0 = cross[i].pos.x;
        y0 = cross[i].pos.y;

        /* 交差点を表す円を描く */
        glColor3d(1.0, 0.5, 0.5);
        draw_circle(x0, y0, 0.1);

        /* 交差点の名前を描く */
        glColor3d(1.0, 1.0, 0.0);
        draw_outtextxy(x0, y0, cross[i].jname);

        /* 交差点から伸びる道路を描く */
        glColor3d(1.0, 1.0, 1.0);
        glBegin(GL_LINES);
        for(j = 0; j < cross[i].points; j++) {
            x1 = cross[ cross[i].next[j] ].pos.x;
            y1 = cross[ cross[i].next[j] ].pos.y;
            glVertex2d(x0, y0);
            glVertex2d(x1, y1);
        }
        glEnd();
    }
}


int main(void) {
    int crossing_number;          /* 交差点数 */
    int vehicle_pathIterator = 0;     /* 移動体の経路上の位置 (何個目の道路か) */
    int vehicle_stepOnEdge = 0;       /* 移動体の道路上の位置 (何ステップ目か) */
    int width, height;
    double x0, y0, x1, y1;
    double vehicle_x = 0.0, vehicle_y = 0.0 ; /* 移動体の座標 */
    double distance;
    int steps;

    /* 中略: 経路の設定関数の呼出 */

    path_set()

    while (1) {

        /* Esc が押されるかウィンドウが閉じられたらおしまい */
        if (glfwGetKey(GLFW_KEY_ESC) || !glfwGetWindowParam(GLFW_OPENED))
            break;

        glfwGetWindowSize(&width, &height); /* 現在のウィンドウサイズを取得する */
        glViewport(0, 0, width, height); /* ウィンドウ全面をビューポートにする */

        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT); /* バックバッファを黒で塗り潰す */

        map_show(crossing_number);                /* 道路網の表示 */

        /* 移動体を進めて座標を計算する */
        if (path[vehicle_pathIterator + 0] != -1 &&
                path[vehicle_pathIterator + 1] != -1) {
           /* まだゴールに達していないので、移動体の位置を進める */
		x0=cross[path[vehicle_pathIterator + 0]].pos.x
                y0=cross[path[vehicle_pathIterator + 0]].pos.y
		x1=cross[path[vehicle_pathIterator + 1]].pos.x
                y1=???
		distance=??
		steps=??

            /* 道路上を進んで、座標を更新 */
		vehicle_stepOnEdge++;
		vehicle_x = x0 + (x1 - x0) / steps * vehicle_stepOnEdge; 
		vehicle_y=???

            /* 交差点に達したので次の道路へ入る */
            if (vehicle_stepOnEdge >= steps) {
		vehicle_pathIterator++;
		vehicle_stepOnEdge = ??;
            }
        }

        /* 移動体を表示 */
        glColor3d(1.0, 1.0, 1.0);
        draw_circle(vehicle_x, vehicle_y, MARKER_RADIUS);

        glfwSwapBuffers(); /* フロントバッファとバックバッファを入れ替える */
        usleep(50 * 1000); /* 50ミリ秒くらい待つ */
    }

    glfwTerminate();

    return 0;
}

戻る