|
本書のシミュレーション・プログラムは,パソコン(MS-DOS)のみならず,UNIX系のワーク・ステーションでも動作するよう,互換性のあるプログラムとして,作成しました。
2つのヘッダファイルを用意します。 "compat.h" は、Turbo Cと,UNIXの間の互換性をとるためのものです。また,本書では,画面にシミュレーション結果をグラフで表示していますが,これを便利にかつ複数のグラフを表示できるように,
"window.h" というヘッダファイルを準備しました。
なお、""で囲まれたヘッダファイルは、ほかのプログラムとおなじディレクトリに置くこととなっていますが、DOSBoxのTurboCではうまく取り込んでくれず、そのためこの2つのヘッダファイルはINCLUDEディレクトリに移してください。
compati.h は,MS-DOSとUNIX系の互換性をもたせるヘッダファイルで,次のような構成になっています。
#ifdef __MSDOS__
/*Turbo C*/
#else
/*UNIX*/
#endif
Cには,プリプロセッサがあり,コンパイルの前に,プリプロッセサに対する指令を解析し,ソースファイルに修飾をおこないます。 Turbo C++の場合,
__MSDOS__ が定義ずみマクロとして,定義されているため,Turbo C++でコンパイルすると,プリプロセッサが条件付きのコンパイルをおこない上半分をコンパイルします。UNIXの場合, #else 以下が有効になり,下半分がコンパイルされます。
マクロと定数
RAND_MAX
rand() 関数による乱数の最大値。DOSBoxのTurboCではおかしな値が入っていたので、あらためて32767とします。
RAND()
2.2節で述べた通り, rand() 関数による,乱数はTurbo Cの場合,0から32767の範囲です。シミュレーションで使う0以上1未満の乱数を得るため, RAND() マクロを作ります。
#define RAND() ((double)rand() / (1.0 + 32767))
と定義します。
WIDTH
スクリーンの横の大きさ。639と定義します。
HIGHT
スクリーンの縦の大きさ。DOSBoxでは480なので、479と定義します。(2018.10変更)
SPACE
スクリーン上での間隔。20と定義します。
グラヒックの関数
パソコンとワーク・ステーションで互換性の上で,最も考慮しなければならないのは,グラフィック関係の関数です。Turbo Cのグラフィック関数はたくさんありますが,このうち以下のものに限定して,用いることとします。
cleardevice()
putpixel()
setcolor()
setlinestyle()
line()
outtxtxy()
closegraph()
これらのほかにグラフィックの初期化手続きが面倒なので, opengraph() と言う関数をつくります。
void opengraph(void);
グラフィックを使う前に,この関数を呼びグラフィックの初期化をします。 compati.h の行21以下のように,この関数はBGI(Borland Graphic Interface)とよばれる,グラフィックドライバの値を detectgraph() 関数で得て, initgraph() 関数で初期化します。 initgraph() の第3パラメータは,実行時のグラフィックドライバがあるパスを指定することになっています。 c:ドライブ直下にturbocをインストールした場合,
グラフィックドライバがc:\turboc3\bgi\ のパスにあることを確かめてください。
compati.h のUNIXの部分は行37から,始まります。必要なヘッダファイルは, <stdlib.h> , <string.h> , <X11/Xlib.h> , <X11/Xutil> です。
マクロと定数
RAND_MAX
2147483647に定義します。
RAND()
次のように定義します。
#define RAND() ((double)rand() / (1.0 + 2147483647))
WIDTH
スクリーンの横の大きさ。639と定義します。
HIGHT
スクリーンの縦の大きさ。479と定義します。
SPACE
スクリーン上での間隔。20と定義します。
これらは,値はTurbo Cと同じにしています。より大きなスクリーンの部分を使う場合は,この値を大きくしてください。
MAXCOLORS
カラーの最大値を16色にしてあります。
グラヒックの関数
UNIX用のグラフィックの部分について説明します。UNIXにおいては,Xウインドウシステムとよばれる,X11ライブラリが一般的によく使われます。Xlibの中身は非常に大きく,全部は使いこなせませんので,これも次の関数だけを,用います。
XAllocNameColor()
XCloseDisplay()
XChangeWindowAttributes()
XCrearWindow()
XCreateGC()
XCreateSimpleWindow()
XDefaultRootWindow()
XDefaultColormap()
XDrawLine()
XDrawPoint()
XDrawString()
XFlush()
XMapWindow()
XOpenDisplay()
XSetAttributes()
XSetForeground()
XStoreName()
これらとTurbo Cとの変換を行なうわけです。すなわち,Turbo Cで用いる次の関数を,Xlibの関数を用いてつくります。
opengraph()
cleardevice()
putpixel()
setcolor()
setlinestyle()
line()
outtxtxy()
closegraph()
getch()
gotoxy()
void opengraph(void);
グラフィックを使う前に,最初に使う関数。16色モードとなり,大きさ横 WIDTH ,縦 HIGHT ,バックグランドカラー黒,フォアグランドカラー白,名称 graphic というウインドウが生成されます。
void cleardevice(void);
ウインドウをバックグランドカラーでクリアします。
void putpixel (int x, int y, int color);
スクリーン座標 x , y に color の色のドットを描画します。
void setcolor(int color);
color で示される,色にフォアグランドカラーを,設定します。 color の値と,色の関係は次のとおりです。
0:黒 1:青 2:緑 3:水色
4:赤 5:紫 6:茶 7:明るい灰色
8:濃い灰色 9:明るい青 10:明るい緑 11:明るい水色
12:明るい赤 13:明るい紫 14:黄 15:白
void setlinestyle (int linestyle, unsigned upattern, int thickness);
line() 関数における,ラインスタイルを設定します。 linestyle が0の時,実線,それ以外の時,破線となります。 upattern は,無効(なにを指定しても,無関係)です。 thickness は,線幅を指定します。
void line(int x0, int y0, int x1, int y1);
スクリーン座標 x0 , y0 からスクリーン座標 x1 , y1 へ,直線を書きます。
void outtextxy(int x, int y, char textstring[]);
スクリーン座標 x , y に 文字列textstringを出力します。スクリーン座標 x , y は文字列の第1文字の左下です。
void closegraph(void);
opengraph() 実行前の状態に,もどります。
int getch(void);
プログラムとグラフィックの同期を取ったのち,キーボードからの入力待となります。キーボードから入力されればその文字を返します。
int gotoxy(int x, int y);
Turbo Cにある,カーソルを移動させる関数ですが,UNIXでは,使えませんので,無効(なにも起らない)にします。
compati.h Turbo C++とXwindowとの互換性をもたせるライブラリ |
/* compati.h
* Turbo C++とXwindowとの互換性をもたせるライブラリ
* (C) H.Ishikawa 1994 2018
*/
#include <stdio.h> /* printf() sprintf() */
#ifdef __MSDOS__ /* Turbo C++の場合__MSDOS__は1と定義されている */
/*Turbo Cの場合*/
#include <stdlib.h> /* rand() */
#include <graphics.h> /* cleardevice() putpixel() setcolor()
setlinestyle() line() outtextxy()
closegraph() detectgraph() initgraph() */
#include <conio.h> /* getch() gotoxy()*/
#define RAND() ((double)rand() / (1.0 + 32767))
#define WIDTH 639 /* 高さ480幅640の場合 */
#define HIGHT 479
#define SPACE 20
void opengraph(void);
void opengraph(void)
{
int gerr;
int Gd, Gm;
detectgraph(&Gd, &Gm);
initgraph(&Gd, &Gm, "c:\\turboc3\\bgi\\"); /* BGIの位置を設定のこと */
if ((gerr = graphresult()) != 0) {
printf("Error:%s\n",grapherrormsg(gerr));
exit(1);
}
cleardevice();
}
#else
/*X−Windowの場合 */
#include <stdlib.h> /* rand() */
#include <string.h> /* strlen() */
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#define RAND_MAX 2147483647
#define RAND() ((double)rand() / (1.0 + 2147483647))
#define WIDTH 639
#define HIGHT 479
#define SPACE 20
#define MAXCOLORS 16
/* turbo C との互換性のためのグラフィック関数 */
void opengraph(void);
void cleardevice(void);
void putpixel(int x, int y, int color);
void setcolor(int color);
void setlinestyle(int linestyle, unsigned upattern, int thickness);
void line(int x0, int y0, int x1, int y1);
void outtextxy(int x, int y, char textstring[]);
void closegraph(void);
int getch(void);
void gotoxy(int x, int y);
/* X11のための変数 */
Display *comp_d;
Window comp_rw, comp_w;
XSetWindowAttributes comp_att;
GC comp_gc;
Colormap comp_map;
XColor comp_c0, comp_c1;
static char comp_col[MAXCOLORS][16] = {"black", "blue", "green",
"sky blue", "red", "magenta", "brown",
"light gray", "dark gray",
"light sky blue", "light sea green",
"light cyan", "hot pink",
"light magenta", "yellow", "white"};
unsigned long comp_c[MAXCOLORS]; /* カラーピクセル値 */
/* 関数定義 */
void opengraph(void)
{
int comp_i;
comp_d = XOpenDisplay (NULL); /* サーバとの接続 */
comp_rw = XDefaultRootWindow (comp_d); /* ルートウィンドウのID取得 */
comp_map = XDefaultColormap(comp_d, 0); /* デフォルトのカラーマップID取得*/
for (comp_i = 0; comp_i < MAXCOLORS; comp_i++) {
XAllocNamedColor (comp_d, comp_map, comp_col[comp_i], &comp_c1, &comp_c0);
comp_c[comp_i] = comp_c1.pixel; /* 指定した色の割当 */
}
comp_w = XCreateSimpleWindow (comp_d, comp_rw, 0, 0,
WIDTH, HIGHT, 1, comp_c[15], comp_c[0]);
/* ウィンドウの生成 */
comp_att.backing_store = Always;
XChangeWindowAttributes (comp_d, comp_w, CWBackingStore, &comp_att);
/* バッキングストアにより常に表示 */
XMapWindow(comp_d,comp_w); /* ウィンドウのマップ */
comp_gc = XCreateGC (comp_d, comp_rw, 0, 0);
XStoreName(comp_d, comp_w, "graphic"); /* ウィンドウに名前 */
XFlush (comp_d);
usleep(100000); /* 2018/11/06 タイミングをとる */
}
void cleardevice(void)
{
XClearWindow(comp_d, comp_w);
XFlush(comp_d);
}
void putpixel(int x, int y, int color)
{
if (color < MAXCOLORS) { XSetForeground(comp_d, comp_gc, comp_c[color]); }
XDrawPoint (comp_d, comp_w, comp_gc, x, y);
}
void setcolor(int color)
{
if (color < MAXCOLORS) { XSetForeground(comp_d, comp_gc, comp_c[color]); }
}
void setlinestyle(int linestyle, unsigned upattern, int thickness)
{
int line_style;
if (linestyle == 0){
line_style = LineSolid; /* 実線 */
}else{
line_style = LineOnOffDash; /* 破線 */
}
XSetLineAttributes (comp_d, comp_gc,
thickness, line_style, CapButt, JoinMiter);
}
void line(int x1, int y1, int x2, int y2)
{
XDrawLine(comp_d, comp_w, comp_gc, x1, y1, x2, y2);
}
void outtextxy(int x, int y, char textstring[])
{
int i;
char letter[2];
for (i = 0; i < strlen(textstring); i ++) {
letter[0] = textstring[i];
XDrawString(comp_d, comp_w, comp_gc, x + 7*i, y + 11, letter, 1);
}
}
void closegraph(void)
{
XCloseDisplay(comp_d);
}
int getch(void)
{
XFlush (comp_d); /* はきだしてから */
return(getchar()); /* 入力待ち */
}
void gotoxy(int x, int y)
{
/* 何もしない */
}
#endif
|
画面上のスクリーンにシミュレーションの結果を,グラフにして出力すると,全体が把握できたいへんわかりやすい。ところが,コンピュータのディスプレイ
のスクリーンは,一般に左上が原点で,右へあるいは,下へ行くほど座標の値が増える方向にあります。いっぽう,物理,数学などで用いる,グラフは原点は左
下で,増加方向は,右あるいは,上が通常です。図付1のように,シミュレーションで得られた結果を,ここでは,論理座標とよぶこととし,スクリーン座標に変換することとします。また,あわせて,一つのスクリーンに,二つ以上のグラフを同時に表示できるように,工夫しました。
window.h に含まれる,関数群により,論理座標だけを意識して,グラフを書けるようにします。また,一つのスクリーンの中に,複数のウインドウを用意し,ウインドウごとに,論理座標を持てるようにします。
window.h の関数は,たとえば, Line() のように,頭文字が大文字で,スクリーン座標の関数 line() と区別することとします。
図付1 スクリーン座標と論理座標 |
|
行6の定義で同時に使えるウインドウの数 WINDOW を,5にしています。行8から行19までは,このヘッダファイルの関数群に共通の変換定数を,ウインドウの数だけ宣言しています。
次にそれぞれの,関数を説明します。
void SetWindow(int win, double wx1, double wy1, double wx2, double wy2,int
x1, int y1, int x2, int y2); ;
第一パラメータ win で,ウインドウ番号を0から WINDOW-1 の,いずれかを指定します。次に,対応する論理座標とスクリーン座標を,図付1の(wx1,wy1),(wx2,wy2)および(x1,y1),(x2,y2)をもちいて,指定します。このクラスを実行するとウインドウごとの,論理座標とスクリーン座標の対応関係の変換定数が設定され,後に述べるクラスでそれが有効に作用することになります。
double GetWx(int win, int screenx);
ウインドウ win において,スクリーン座標上の横軸値 screenx を与えると,論理座標上の横軸値の値を返します。
double GetWy(int win, int screeny);
ウインドウ win において,スクリーン座標上の縦軸値 screeny を与えると,論理座標上の縦軸値の値を返します。
iint GetSx(int win, double x);
ウインドウ win において,論理座標上の横軸値 x を与えると,スクリーン座標上の横軸値の値を返します。
int GetSy(int win, double y);
ウインドウ win において,論理座標上の縦軸値 y を与えると,スクリーン座標上の縦軸値の値を返します。
void Axis(int win,char x_axis[], double x_scale, char y_axis[], double
y_scale);
ウインドウ win に,lightGrayの座標を書くクラスです。座標は図付2のような形です。また,ウインドウのタイトルとして,ウインドウの番号
win ,横軸の名称 x_axis[] ,横軸の目盛り幅 x_scale ,縦軸の名称 y_axis[] ,縦軸の目盛り幅 y_scale を,ウインドウの下に出力します。
図付2 axis()により表示される座標 |
|
void Line(int win, double x0, double y0, double x1, double y1);
ウインドウ win の,論理座標(x0,y0)と,(x1,y1)の間に,直線を書きます。
void PutPixel(int win, double x, double y, int color);
ウインドウ win の,論理座標(x,y)に,色 color のドットを出力します。
void MoveTo(int win, double x, double y);
ウインドウ win の現在位置を, x , y に設定します。
void LineTo(int win, double x, double y);
ウインドウ win の現在位置から(x,y)までの間に直線を書きます。その後現在位置は(x,y)となります。
window.h スクリーン座標と論理座標を変換する |
/* window.h
* スクリーン座標と論理座標を変換するWINDOWシステム
* (C) H.Ishikawa 1994 2018
*/
#define WINDOW 5 /* Windowの数 0から最大4まで */
int win_sx1[WINDOW]; /* スクリーン座標上のx1 */
int win_sy1[WINDOW]; /* スクリーン座標上のy1 */
int win_sx2[WINDOW]; /* スクリーン座標上のx2 */
int win_sy2[WINDOW]; /* スクリーン座標上のy2 */
double win_wx1[WINDOW]; /* 論理座標上のx1 */
double win_wy1[WINDOW]; /* 論理座標上のy1 */
double win_wx2[WINDOW]; /* 論理座標上のx2 */
double win_wy2[WINDOW]; /* 論理座標上のy2 */
double win_sdx[WINDOW]; /* スクリーン座標と論理座標の比 */
double win_sdy[WINDOW]; /* スクリーン座標と論理座標の比 */
double win_cpx[WINDOW]; /* 現在位置x */
double win_cpy[WINDOW]; /* 現在位置y */
/* プロトタイプ */
void SetWindow(int win, double wx1, double wy1, double wx2, double wy2,
int x1, int y1, int x2, int y2);
double GetWx(int win, int screenx);
double GetWy(int win, int screeny);
int GetSx(int win, double x);
int GetSy(int win, double y);
void Axis(int win,
const char x_axis[], double x_scale, const char y_axis[], double y_scale);
void Line(int win, double x0, double y0, double x1, double y1);
void PutPixel(int win, double x, double y, int color);
void MoveTo(int win, double x, double y);
void LineTo(int win, double x, double y);
/* 関数定義 */
void SetWindow(int win, double wx1, double wy1, double wx2, double wy2,
int x1, int y1, int x2, int y2)
{
win_sx1[win] = x1; win_sy1[win] = y1;
win_sx2[win] = x2; win_sy2[win] = y2;
win_wx1[win] = wx1; win_wy1[win] = wy1;
win_wx2[win] = wx2; win_wy2[win] = wy2;
win_sdx[win] = (double)(x2-x1) / (wx2-wx1);
win_sdy[win] = (double)(y2-y1) / (wy2-wy1);
}
double GetWx(int win, int screenx)
{
return(win_wx1[win] + (double)((screenx - win_sx1[win]) / win_sdx[win]));
}
double GetWy(int win, int screeny)
{
return(win_wy1[win] + (double)((screeny - win_sy1[win]) / win_sdy[win]));
}
int GetSx(int win, double x)
{
return(win_sx1[win] + (int)((x - win_wx1[win]) * win_sdx[win]));
}
int GetSy(int win, double y)
{
return(win_sy1[win] + (int)((y - win_wy1[win]) * win_sdy[win]));
}
void Axis(int win,
const char x_axis[], double x_scale, const char y_axis[], double y_scale)
{
double x ;
double y ;
char scale[80];
setcolor(2); /* 緑 */
if (win_wy1[win] <= 0.0 && 0.0 <= win_wy2[win]) {
Line(win, win_wx1[win], 0.0, win_wx2[win], 0.0);
} /* 縦軸 */
if (win_wx1[win] <= 0.0 && 0.0 <= win_wx2[win]) {
Line(win, 0.0, win_wy1[win], 0.0, win_wy2[win]);
} /* 横軸 */
setlinestyle(1, 0, 1); /* 破線 */
x = x_scale;
/* 原点から右へ目盛りをかく */
while (x <= win_wx2[win]) {
if (win_wx1[win] <= x) {Line( win, x, win_wy1[win], x, win_wy2[win]);}
x = x + x_scale;
}
x = -x_scale;
/* 原点から左に目盛りをかく */
while (win_wx1[win] <= x) {
if (x <= win_wx2[win]) {Line( win, x, win_wy1[win], x, win_wy2[win]);}
x = x - x_scale;
}
y = y_scale;
/* 原点から上に目盛りをかく */
while (y <= win_wy2[win]) {
if (win_wy1[win] <= y) {Line( win, win_wx1[win], y, win_wx2[win], y);}
y = y + y_scale;
}
y = -y_scale;
/* 原点から下に目盛りをかく */
while (win_wy1[win] <= y) {
if (y <= win_wy2[win]) Line( win, win_wx1[win], y, win_wx2[win], y);
y = y - y_scale;
}
setcolor(15); /* 白 */
setlinestyle(0, 0, 1);
/* タイトルをかく */
sprintf(scale, "W%1d: hor:%s[%g/div] ver:%s[%g/div]",
win,x_axis, x_scale, y_axis, y_scale);
outtextxy(GetSx(win,win_wx1[win]), GetSy(win,win_wy1[win]), scale);
}
void Line(int win, double x0, double y0, double x1, double y1)
{
line(GetSx(win,x0), GetSy(win, y0), GetSx(win, x1), GetSy(win, y1));
}
void PutPixel(int win, double x, double y,int color)
{
putpixel(GetSx(win, x), GetSy(win, y),color);
}
void MoveTo(int win, double x, double y)
{
win_cpx[win] = x;
win_cpy[win] = y;
}
void LineTo(int win, double x, double y)
{
Line(win, win_cpx[win], win_cpy[win], x, y);
MoveTo(win, x, y);
}
|
|
|