DrawPS2 Sample

このページは単振り子のシミュレーションをサンプルにDrawPS2の機能の紹介をします。今回は以下の機能を利用します。


  1. ウィンドウ作成
  2. ウィンドウ設定
  3. 初期化関数の登録
  4. 計算関数の登録
  5. 描画関数の登録
  6. グラフウィンドウの作成
  7. エネルギー保存の計算と描画関数の登録

1.ウィンドウの作成

まず真っ白な何もしないウィンドウの作成をします。DrawPS2を用いたシミュレーションは以下のような基本コードのファイルを作成します。
#include "drawps2.h"

DRAWPS2 drps2;

void psMain()
{
	drps2.psCreateWindow();
}
1行目は必要なヘッダーを読み込みのためのコードです。 3行目は親ウィンドウとなるオブジェクトdrps2の作成を行っています。drps2のオブジェクト名は固定で必須です。 それ以下のpsMain()関数はウィンドウの設定は記述しますが、ここではデフォルトの状態で作成してみます。 7行目のpsCreateWindow()でウィンドウ作成を行います。 これでコンパイルを行うと真っ白な何も動作しない実行ファイルが作成されます。

2.ウィンドウの設定

親ウィンドウの設定を行います。ウィンドウの大きさを640x480、表示位置を(50,100)とします。
#include "drawps2.h"

DRAWPS2 drps2;

void psMain()
{
	drps2.window.Size(640, 480);
	drps2.window.Position(50, 100);
	drps2.psCreateWindow();
}
ウィンドウの大きさはwindowクラスのSize()関数を、位置はPosition()関数で指定します。これらウィンドウの設定情報はpsCreateWindow()の前に宣言する必要があります。

3.初期化関数の登録

ウィンドウの作成時に一度だけ呼び出す初期化関数の設定を行います。必要な変数を宣言し、その初期化をInit()関数に作成します。今回は振り子の時間t、角度x、その時間微分をvとします。
#include "drawps2.h"

DRAWPS2 drps2;

// 変数
double t, x, v;

// プロトタイプ宣言
void Init();

void psMain()
{
	drps2.window.Size(640, 480);
	drps2.window.Position(50, 100);
	drps2.event.Init(Init);
	drps2.psCreateWindow();
}

void Init()
{
	t = 0.0;
	x = 1.0;
	v = 0.0;
}
イベントの登録はeventクラスの関数で行います。初期化はInit()関数なのでその引数に初期化を行う関数ポインタを指定します。関数ポインタは関数名になります。

4.計算関数の登録

内部処理を行う計算関数の設定を行います。今回はルッゲクッタ法を用います。
#include "drawps2.h"

DRAWPS2 drps2;

// 変数
double t, x, v;
const double g  = 9.8;
const double l  = 1.0;
const double dt = 0.01;

n// プロトタイプ宣言
void Init();
void Calculate();

// 連立微分方程式
double fx(double v){ return v;}
double fv(double x){ return -g/l*sin(x);}


void psMain()
{
	drps2.window.Size(640, 480);
	drps2.window.Position(50, 100);
	drps2.event.Init(Init);
	drps2.event.Calculate(Calculate);
	drps2.psCreateWindow();
}

void Init()
{
	t = 0.0;
	x = 1.0;
	v = 0.0;
}

void Calculate()
{
	double kx[4],kv[4];

	kx[0] = fx(v)*dt;
	kv[0] = fv(x)*dt;
	kx[1] = fx(v+kv[0]/2.0)*dt;
	kv[1] = fv(x+kx[0]/2.0)*dt;
	kx[2] = fx(v+kv[1]/2.0)*dt;
	kv[2] = fv(x+kx[1]/2.0)*dt;
	kx[3] = fx(v+kv[2])*dt;
	kv[3] = fv(x+kx[2])*dt;

	t += dt;
	x += (kx[0]+2.0*kx[1]+2.0*kx[2]+kx[3])/6.0;
	v += (kv[0]+2.0*kv[1]+2.0*kv[2]+kv[3])/6.0;
}

初期化と同様、eventクラスのCalculate関数に計算処理をする関数を登録します。

5.描画関数の登録

描画処理を行う描画関数の設定を行います。今回はDRAW_PSスタイルを使っているのでwin32API用の関数を用います。ここまででDRAW_PSと同一のプログラムとなります。
#include "drawps2.h"

DRAWPS2 drps2;

// 変数
double t, x, v;
const double g  = 9.8;
const double l  = 1.0;
const double dt = 0.01;

// プロトタイプ宣言
void Init();
void Calculate();
void Display();

// 連立微分方程式
double fx(double v){ return v;}
double fv(double x){ return -g/l*sin(x);}

void psMain()
{
	drps2.window.Size(640, 480);
	drps2.window.Position(50, 100);
	drps2.event.Init(Init);
	drps2.event.Calculate(Calculate);
	drps2.event.Display(Display);
	drps2.psCreateWindow();
}

void Init()
{
	t = 0.0;
	x = 1.0;
	v = 0.0;
}

void Calculate()
{
	double kx[4],kv[4];

	kx[0] = fx(v)*dt;
	kv[0] = fv(x)*dt;
	kx[1] = fx(v+kv[0]/2.0)*dt;
	kv[1] = fv(x+kx[0]/2.0)*dt;
	kx[2] = fx(v+kv[1]/2.0)*dt;
	kv[2] = fv(x+kx[1]/2.0)*dt;
	kx[3] = fx(v+kv[2])*dt;
	kv[3] = fv(x+kx[2])*dt;

	t += dt;
	x += (kx[0]+2.0*kx[1]+2.0*kx[2]+kx[3])/6.0;
	v += (kv[0]+2.0*kv[1]+2.0*kv[2]+kv[3])/6.0;
}

void Display()
{
	drps2.EraseOrbit();
	drps2.SetPen(PS_SOLID, 1, RGB(0, 0, 0));
	drps2.DrawLine(
		320.0,
		100.0,
		320.0+l*sin(x)*200.0,
		100.0+l*cos(x)*200.0
	);
	drps2.DrawEllipse(
		320.0+l*sin(x)*200.0-10.0,
		100.0+l*cos(x)*200.0-10.0,
		320.0+l*sin(x)*200.0+10.0,
		100.0+l*cos(x)*200.0+10.0
	);
}

6.グラフウィンドウの作成

エネルギー保存が成り立っているか確認するため、エネルギーのグラフを他ウィンドウに図示してみます。4行目のDRAWPS2クラスに新たなオブジェクトgraphを宣言します。 新しいウィンドウのサイズは320x240、位置を(680,100)とします。
#include "drawps2.h"

DRAWPS2 drps2, graph;

// 変数
double t, x, v;
const double g  = 9.8;
const double l  = 1.0;
const double dt = 0.01;

// プロトタイプ宣言
void Init();
void Calculate();
void Display();

// 連立微分方程式
double fx(double v){ return v;}
double fv(double x){ return -g/l*sin(x);}

void psMain()
{
	drps2.window.Size(640, 480);
	drps2.window.Position(50, 100);
	drps2.event.Init(Init);
	drps2.event.Calculate(Calculate);
	drps2.event.Display(Display);
	drps2.psCreateWindow();

	graph.window.Size(320, 240);
	graph.window.Position(680, 100);
	graph.psCreateWindow();
}

void Init()
{
	t = 0.0;
	x = 1.0;
	v = 0.0;
}

void Calculate()
{
	double kx[4],kv[4];

	kx[0] = fx(v)*dt;
	kv[0] = fv(x)*dt;
	kx[1] = fx(v+kv[0]/2.0)*dt;
	kv[1] = fv(x+kx[0]/2.0)*dt;
	kx[2] = fx(v+kv[1]/2.0)*dt;
	kv[2] = fv(x+kx[1]/2.0)*dt;
	kx[3] = fx(v+kv[2])*dt;
	kv[3] = fv(x+kx[2])*dt;

	t += dt;
	x += (kx[0]+2.0*kx[1]+2.0*kx[2]+kx[3])/6.0;
	v += (kv[0]+2.0*kv[1]+2.0*kv[2]+kv[3])/6.0;
}

void Display()
{
	drps2.EraseOrbit();
	drps2.SetPen(PS_SOLID, 1, RGB(0, 0, 0));
	drps2.DrawLine(
		320.0,
		100.0,
		320.0+l*sin(x)*200.0,
		100.0+l*cos(x)*200.0
	);
	drps2.DrawEllipse(
		320.0+l*sin(x)*200.0-10.0,
		100.0+l*cos(x)*200.0-10.0,
		320.0+l*sin(x)*200.0+10.0,
		100.0+l*cos(x)*200.0+10.0
	);
}

7.エネルギー保存の計算と描画関数の登録

エネルギー保存の計算をCalculate()に行い、グラフを図示するための描画関数をgraphオブジェクトに登録します。また必要な変数も宣言します。 運動エネルギーの数値をgraphT[]に、ポテンシャルエネルギーの数値をgraphU[]にそれぞれ保持させます。
#include "drawps2.h"

#define GN 160

DRAWPS2 drps2, graph;

// 変数
double t, x, v;
const double g  = 9.8;
const double l  = 1.0;
const double dt = 0.01;
const double m  = 1.0;
double T, U, E;
double graphT[GN], graphU[GN];

// プロトタイプ宣言
void Init();
void Calculate();
void Display();
void subDisplay();

// 連立微分方程式
double fx(double v){ return v;}
double fv(double x){ return -g/l*sin(x);}

void psMain()
{
	drps2.window.Size(640, 480);
	drps2.window.Position(50, 100);
	drps2.event.Init(Init);
	drps2.event.Calculate(Calculate);
	drps2.event.Display(Display);
	drps2.psCreateWindow();

	graph.window.Size(320, 240);
	graph.window.Position(680, 100);
	graph.event.Display(subDisplay);
	graph.psCreateWindow();
}

void Init()
{
	int i;

	t = 0.0;
	x = 1.0;
	v = 0.0;

	T = 0.0;
	U = 0.0;
	E = 0.0;

	for(i=0;i<GN;i++){
		graphT[i] = 0.0;
		graphU[i] = 0.0;
	}
}

void Calculate()
{
	int i;
	double kx[4],kv[4];

	kx[0] = fx(v)*dt;
	kv[0] = fv(x)*dt;
	kx[1] = fx(v+kv[0]/2.0)*dt;
	kv[1] = fv(x+kx[0]/2.0)*dt;
	kx[2] = fx(v+kv[1]/2.0)*dt;
	kv[2] = fv(x+kx[1]/2.0)*dt;
	kx[3] = fx(v+kv[2])*dt;
	kv[3] = fv(x+kx[2])*dt;

	t += dt;
	x += (kx[0]+2.0*kx[1]+2.0*kx[2]+kx[3])/6.0;
	v += (kv[0]+2.0*kv[1]+2.0*kv[2]+kv[3])/6.0;

	T = m*pow((l*v),2.0)/2.0;
	U = g/l*(1.0-cos(x));
	E = T + U;

	for(i=0;i<GN-1;i++){
		graphT[i] = graphT[i+1];
		graphU[i] = graphU[i+1];
	}
	graphT[GN-1] = T;
	graphU[GN-1] = U;
}

void Display()
{
	drps2.EraseOrbit();
	drps2.SetPen(PS_SOLID, 1, RGB(0, 0, 0));
	drps2.DrawLine(
		320.0,
		100.0,
		320.0+l*sin(x)*200.0,
		100.0+l*cos(x)*200.0
	);
	drps2.DrawEllipse(
		320.0+l*sin(x)*200.0-10.0,
		100.0+l*cos(x)*200.0-10.0,
		320.0+l*sin(x)*200.0+10.0,
		100.0+l*cos(x)*200.0+10.0
	);
}

void subDisplay()
{
	int i;
	char str[64];

	graph.EraseOrbit();

	sprintf(str, "x : %f", (l*sin(x)));
	graph.DrawText(2, 2, str, RGB(0, 0, 0));
	sprintf(str, "y : %f", (l*cos(x)));
	graph.DrawText(2, 22, str, RGB(0, 0, 0));

	sprintf(str, "T : %f", T);
	graph.DrawText(120, 2, str, RGB(0, 0, 255));
	sprintf(str, "U : %f", U);
	graph.DrawText(120, 22, str, RGB(0, 255, 0));
	sprintf(str, "E : %f", E);
	graph.DrawText(120, 42, str, RGB(255, 0, 0));

	graph.SetPen(PS_SOLID, 1, RGB(0, 0, 255));
	for(i=0;i<GN-1;i++){
		graph.DrawLine(
			(double)(i*2),
			200.0-graphT[i]*30.0,
			(double)((i+1)*2),
			200.0-graphT[i+1]*30.0
		);
	}
	graph.SetPen(PS_SOLID, 1, RGB(0, 255, 0));
	for(i=0;i<GN-1;i++){
		graph.DrawLine(
			(double)(i*2),
			200.0-graphU[i]*30.0,
			(double)((i+1)*2),
			200.0-graphU[i+1]*30.0
		);
	}
	graph.SetPen(PS_SOLID, 1, RGB(255, 0, 0));
	for(i=0;i<GN-1;i++){
		graph.DrawLine(
			(double)(i*2),
			200.0-(graphT[i]+graphU[i])*30.0,
			(double)((i+1)*2),
			200.0-(graphT[i]+graphU[i])*30.0
		);
	}
}

作成日:2009年2月25日
更新日:2009年3月31日