Pasorica - パソリカ - : mlibの使い方

mlibの使い方

概要

mlibとは

mlib(エムリブ)は数値シミュレータ製作に特化した、グラフィック描画クラスライブラリです。 X Window Systemのプリミティブな機能のみを使用するため、ソースコードの可搬性が高く、LinuxやWindows、MacOSなどで使用できます。 mlibを用いることで様々なOSに対応したグラフィカルなソフトウェア(シミュレータ)を簡単に制作することができます。
mlibには必要最低限の機能がコンパクトにまとめられており、高い汎用性を持ちます。
mlibはC++で記述されていますので、使用するにはC++の知識が必要になります。 ですが、サンプルを参考にすればC言語の知識だけでもmlibを使用することができるでしょう。 逆に、最低限のC言語の知識は必須です。

以下にCWindow, CPixmap, CGC, CDisplayの説明があります。
CWindow, CPixmapの説明
CGCの説明
CDisplayの説明
これら以外のクラスについては実際の使い方がsample.cppやlorenz.cppの中で示されています。そちらを参考にしてください。

mlibの構成

mlibを構成するのは主に以下のクラスです。
CWindow ウィンドウの表示、管理、操作
CPixmap ピックスマップ(画面に表示されないウィンドウ、バックバッファ)の管理、操作
CDisplay ディスプレイの管理
CGC グラフィックコンテキスト(線や円を描くときの色やスタイルの情報)の管理、操作
CActPrm パラメータの表示や変更などユーザインタフェイスの一部
CLinearTrans3D 3次元アフィン変換の実行
vector2d, vector3d 2次元、3次元ベクトル
これらのクラスを組み合わせることで、ウィンドウの表示やグラフの描画を行います。

準備

mlibに必要なライブラリのインストール

mlibを使うための準備をします。
まず、mlibを使うために必要なライブラリをインストールします。
Windowsの場合、「Pasoricaの使い方」「Cygwinのインストール」に従っていれば、必要なライブラリはインストールされています。
Linuxの場合はお使いのディストリビューションに合わせて、xorg-develをaptやyumなどでインストールしてください。
MacOS X v10.5 Leopardの場合は最初から相当するライブラリがインストールされています。

Sample.cppのコンパイルと実行

サンプルプログラムを実行してみます。ここではコンパイラにg++を用います。またmakeも使用します。Cygwinでg++やmakeを使うためには、Cygwinのインストールの際に「X11」以外に「Devel」も「Default」から「Install」に変更してインストールを行います。setup.exeは一度インストール作業を行った後でも、パッケージの追加削除に使えますので、Cygwinのインストールの作業を繰り返せば、makeを追加できます。

mlibを使った描画のサンプルがSampleディレクトリに入っています。Sampleディレクトリを適当な場所にコピーし、コンソールでSampleディレクトリ内に移動します。
以下のように入力してコンパイルします。
	make
コンパイルが終わったら
	./sample
で起動します。Windowsの場合は「.exe」を追加して
	./sample.exe
としてください。以下「.exe」は省略します。


sampleの実行画面

左上に表示されている
	mlib1.2
	a=50.0
	v=1
	MouseY=0
	MouseX=0
のうち、vは青い円が移動する速度になっています。キーボードの下キーで「v=1」を選択し(赤文字が選択されている項目です)、左右のキーで値を変えてみてください。vの値に応じて青い円の速度が変化します。なお、「a=50.0」となっていますが、このプログラムではaの値は動作に特に影響を与えません。

次はウィンドウ上でマウスを、左ボタンを押しながら移動させてください。マウスポインタの位置に破線で描かれた円が表示されます。

Sample.cppの解説


#include "mlib.h"

// ウィンドウサイズを設定する
const int W=320;
const int H=240;

int main(int argc,char* argv[]){
	singleton pdis;	// Display
	CWindow win(W,H);			// Window
	CPixmap pix(win,W,H);		// Pixmap
	XEvent ev;					// Event
	CGC gck("black");			// GraphicContext 黒のペン
	CGC gcw("white");			// GraphicContext 白のペン
	CGC gcr("red");				// GraphicContext 赤のペン
	CGC gcb("blue");			// GraphicContext 青のペン
	CGC gcc(127,127,255);		// GraphicContext 青っぽい色のペン
	CActPrm prm;				// Param

mlib.hのインクルードと、各種クラスの宣言部です。
mlibを使うためには、メインとなるソースコードと同じディレクトリに「mlib」ディレクトリと「mlib.h」を置き、ソースコード内で「mlib.h」をインクルードします。Sample.cppもソースコードの先頭でmlib.hをインクルードしています。
	singleton pdis;
はディスプレイを管理するクラスです。マウスやキーボードからの入力もCDisplayから受け取ります。singletonについては、今は気にする必要はありません。pdisはCDisplayへのポインタとして振舞います。
	CWindow win(W,H);
はウィンドウを管理するクラスです。CWindowを宣言すると、自動的にウィンドウが生成され表示されます。宣言時にウィンドウの縦横のサイズをピクセル単位で指定します。
	CPixmap pix(win,W,H);
はピックスマップを管理するクラスです。CPixmapを宣言すると、自動的にピックスマップが生成されます。宣言時に元になるCWindowと、ピックスマップの縦横のサイズをピクセル単位で指定します。ピックスマップは画面に表れないウィンドウです。なぜピックスマップが必要かは、後述します。
	XEvent ev;
はマウスやキーボードへの入力(イベントといいます)の情報を格納する構造体です。CDisplayはXEvent構造体にイベントの内容を格納してくれます
	CGC gck("black");
	CGC gcw("white");
	CGC gcr("red");
	CGC gcb("blue");
	CGC gcc(127,127,255);
これらはグラフィックコンテキストと言います。ウィンドウやピックスマップの描画命令(線を引け、長方形を描けなどです)を実行するときに必ず必要になります。どんな色でどんなタイプの線を引くのかなどはこのCGCに設定します。正確な言い方ではありませんが「ペン」だと思えばいいでしょう。宣言時に色を、"red"や"yellow"のように名前で指定するか、255,255,0のようにRGBの値0〜255で指定してください。
	CActPrm prm;
はパラメータの管理をします。Sampleを実行したときに左上に出ていたパラメータ表示部はCActPrmによるものです。


	bool endflag=false;

	/* 変数a,bはサンプルです */
	double a=50;

	/* vは青円が移動する速度です */
	int v=1;

	/* 青円を描く位置と、移動方向 */
	int x=0,y=0,addx=1,addy=1;

	/* マウスのX座標,Y座標 */
	int mousex=0,mousey=0;

変数を宣言しています。
	bool endflag=false;
はプログラムの終了を決めるフラグです。endflagがtrueになるとプログラムが終了します。
	double a=50;
	int v=1;
	int x=0,y=0,addx=1,addy=1;
	int mousex=0,mousey=0;
aはCActPrmのデモンストレーションのための変数で特に役割はありません。
そのほかの変数もコメント通りです。


	/* ペンの設定 */
	/* gck(黒のペン)は鎖線 */
	gck.SetLineStyle(LineOnOffDash);
	/* 線の太さが3 */
	gck.SetLineWidth(3);
	/* 線の終端が半円 */
	gck.SetCapStyle(CapRound);

	/* gcr(赤のペン)は線の太さが5 */
	gcr.SetLineWidth(5);

	gck.SetLineStyle(LineOnOffDash);
はgckの線を破線に設定しています。LineOnOffDashのかわりにLineSolidを指定すると普通の線になります。初期状態ではLineSolidに設定されています。
	gck.SetLineWidth(3);
はgckの線の太さを3に設定しています。
	gck.SetCapStyle(CapRound);
はgckの線の終端を半円に設定しています。CapButtなら四角形になります。初期状態ではCapButtです。
	gcr.SetLineWidth(5);
はgcrの線の太さを5に設定しています。


	/*--------------------------------------------*/
	/*ここで表示するパラメータを設定してください。*/
	/*--------------------------------------------*/

	/* テキストデータを表示。プログラム実行中の編集は不可能。 */
	/* 表示したい文字列を指定するだけです。 */
	prm.Add(new CPrmText("mlib1.2"));

	/* double型のデータを表示。プログラム実行中に編集可能。 */
	/* 次の項目を指定してください。(表示の仕方(printfと同じ文法),表示する値へのポインタ,変化させる幅,変化の下限,変化の上限) */
	prm.Add(new CPrmT("a=%1.2lf",&a, 0.1,0,100));

	/* int型のデータを表示。プログラム実行中に編集可能。 */
	/* 指定する項目はdoubleの場合と同じです。 */
	prm.Add(new CPrmT("v=%d", &v, 1,0,10));

	/* int型のデータを表示。プログラム実行中の編集は不可能。 */
	prm.Add(new CPrmT("MouseX=%d", &mousex, CONST));
	prm.Add(new CPrmT("MouseY=%d", &mousey, CONST));

ここではCActPrmに対して、どんな文字を表示させ、どんなパラメータをユーザに変更させるかなどを指定しています。 詳細はコメントのとおりです。


	win.SelectInput(ExposureMask|ButtonPressMask|KeyReleaseMask|ButtonMotionMask|ButtonReleaseMask);

	while(!endflag){
		pix.FillRectangle(gcw,0,0,W,H);
		if(pdis->CheckEvent(&ev)){	// Check Event

ここでは、ウィンドウが受け取るイベントの種類の設定、ピックスマップの塗りつぶし、イベントのチェックを行っています。
	win.SelectInput(ExposureMask|ButtonPressMask|KeyReleaseMask|ButtonMotionMask|ButtonReleaseMask);
はウィンドウが受け取るイベントの種類を設定しています。深く考えずに、このまま使っていただいてかまいません。
	while(!endflag){
ここから先はプログラムが起動している間ずっとループしています。ループの中でちょっとずつずらして図形を描画することでアニメーションを実現できます。
		pix.FillRectangle(gcw,0,0,W,H);
はピックスマップを白色で塗りつぶしています。FillRectangleは長方形で塗りつぶす命令で書式はFillRectangle([ペン],[長方形左上のX座標],[長方形左上のY座標],[長方形の幅],[長方形の高さ])です。
もし、ウィンドウを白で塗り潰して、次々に図形を書き込んでいくと、消した瞬間、1つ目の図形を描画した瞬間、2つ目の図形を描画した瞬間…、すべてが画面に表示されてしまいチラつく原因となります。それを防ぐためにいったんピックスマップに描画し、全ての描画を終えてからまとめてウィンドウに転送します。
		if(pdis->CheckEvent(&ev)){	// Check Event
はマウスやキーボードからの入力をチェックしています。CheckEvent関数はイベントが発生していなければ0を、発生していれば非0を返します。また、イベントが発生していればXEvent構造体(ev)にイベントの詳細を格納します。


			switch(ev.type){
			case ButtonPress:		// If mouse button clicked
				switch(ev.xbutton.button){
				case 1:
					/*----------------------------------------------------*/
					/*ここに左クリックされたときの動作を記述してください。*/
					/*----------------------------------------------------*/
					break;
				case 2:
					/*----------------------------------------------------------*/
					/*ここにホイールクリックされたときの動作を記述してください。*/
					/*----------------------------------------------------------*/
					break;
				case 3:
					/*----------------------------------------------------*/
					/*ここに右クリックされたときの動作を記述してください。*/
					/*----------------------------------------------------*/
					endflag=true;	// 右クリックで終了
					break;
				case 4:
					/*--------------------------------------------------------*/
					/*ここにホイールが上に回された時の動作を記述してください。*/
					/*--------------------------------------------------------*/
					break;
				case 5:
					/*--------------------------------------------------------*/
					/*ここにホイールが下に回された時の動作を記述してください。*/
					/*--------------------------------------------------------*/
					break;
				}
				break;
			case KeyRelease:
				prm.Control(pdis->LookupString(&ev));
				break;
			case MotionNotify:
				/*--------------------------------------------------------*/
				/*ここにマウスがドラッグされた時の動作を記述してください。*/
				/*--------------------------------------------------------*/
				mousex=ev.xbutton.x;
				mousey=ev.xbutton.y;
				/* gck(黒のペン)でマウスの位置に直径30の円を描画します */
				pix.DrawCircle(gck,mousex-15,mousey-15,30);
				break;
			}
		}

ここででは、各イベントに対する動作を記述しています。
コメントにあるとおり、各所にイベントに対する動作を記述すればOKです。


		usleep(1);


		/*---------------------------------------------*/
		/*ここにpixに対する描画命令を記述してください。*/
		/*---------------------------------------------*/

		/* gck(黒のペン)で(0,0)から(W,H)まで線を描画します */
		pix.DrawLine(gck,100,100,W,H);

		/* gcr(赤のペン)で(200,0)に幅50,高さ70の長方形を描画します */
		pix.DrawRectangle(gcr,200,0,50,70);

		/* gcb(青のペン)で(220,30)に幅150,高さ90の長方形を描画し、塗りつぶします */
		pix.FillRectangle(gcb,220,30,150,90);

		/* gcb(青のペン)で(0,0)に幅200,高さ200の長方形を描画し、塗りつぶします */
		pix.DrawRectangle(gcb,0,0,200,200);

		/* gcc(青っぽいペン)で(x,y)に直径20の円を描画し、塗りつぶします */
		pix.FillCircle(gcc,x,y,20);

		/* 円の描画位置を更新 */
		x+=addx*v;y+=addy*v;
		if(x>W-20 || x<0)addx=-addx;
		if(y>H-20 || y<0)addy=-addy;

ここでは、短時間のスリープといくつかの描画命令を実行しています。 usleepは指定されたマイクロ秒間処理を停止する命令です。
その他の描画命令はコメントにあるとおりです。


		prm.Show(pix);
		pix.CopyArea(win, gcw, 0, 0, W, H, 0, 0 );
	}
	return(0);
}

ここでは、パラメータ表示部の描画とピックスマップからウィンドウへの転送をおこなっています。
		pix.CopyArea(win, gcw, 0, 0, W, H, 0, 0 );
pixのデータをwinに転送しています。 CopyArea([転送先のウィンドウorピックスマップ],[ペン],[転送元X座標],[転送元Y座標],[転送する幅],[転送するt高さ],[転送先X座標],[転送先Y座標] )のように指定します。

Sample.cppの中身をいろいろと変更してみるといいでしょう。色を変えたり、表示する位置を変えたり、ウィンドウのサイズを変えたり…。 いろいろ試しているうちに、使いこなせるようになります。

Lorenzディレクトリ内にはより実践的なサンプルがあります。lorenz.cppはローレンツモデルのシミュレーションをし、立体的に表示します。

Templateディレクトリ内には、mlibを用いたソフトウェアのひな形があります。そのままでは何もしないプログラムですが、ウィンドウやピックスマップなどは一通り宣言されています。描画命令を追加するだけで、すぐにmlibを用いたプログラムを作成できます。

top