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