20010. 5.25.
石立 喬
Visual C++ 2010 Express の易しい使い方(6)
――― Graphicsを用いて、画面上にグラフを描く ―――
ここでは、フォーム上にグラフを描く方法を紹介する。いままで、パソコンを用いて計算をし、計算結果を数値でフォーム上に出力する方法を述べてきたが、やはり、結果をグラフで画面に表示しないと実感が湧かない。すでに紹介した「易しい使い方」で説明した部分は省略されている場合があるので注意して欲しい。
英語版Beta2では、コードエディタで、プログラムの定数(数字)が赤茶色で表示されていた("
"で囲まれた文字列と同様に)のに、それが無くなり、2008版と同様になったのは残念である。
概要
電気技術者にとって興味のあるのは、各種の波形を画面上に表示することである。電気技術者が波形と言えば交流(正弦波)を思い浮かべるが、単なる交流波形では面白くないので、振幅変調波形、周波数変調波形を表示してみることにした。これにより変調の仕組みが理解でき、色々なパラメータを変化させて、どう変わるかを確認することができる。
変調に使用する波形の式

ただし、AC は搬送波(carrier)の振幅(amplitude)、AS は信号波(signal)の振幅、ωC は搬送波の角周波数(angular frequency)、ωS は信号波の角周波数、MAM は振幅変調(amplitude modulation)の変調度(modulation factor)、MFM は周波数変調(frequency modulation)の変調度である。
グラフを描くためのGraphicsの使い方
Graphicsクラスには、画面上に描画するのに便利なメソッドが多数ある。ここでは、グラフを描くのに必要なメソッドを紹介する。
◎グラフの背景
グラフの背景などに色を付けるには、矩形範囲を塗りつぶす必要がある。これには、
g->FillRectangle(Pens::Black,x,y,width,height);
などを使用する。塗りつぶす色は、Pens::Black などのペンで指定し、x,yは矩形範囲の左上の座標である。
◎直線を引く
グラフの座標軸や目盛には、直線を引く必要がある。これには、
g->DrawLine(Pens::Gray,x1,y1,x2,y1);
などを使用する。x1,y1は直線を引き始める座標、x2,y1は直線を引き終わる座標である。Y座標は同じなので、これは横(水平)線を引くことになる。
線幅が2ピクセル以上の線を引く場合には、
Pen^ pen1=gcnew Pen(Color::Blue,2);
などでペンを指定する。
◎点と点を結ぶ
これも、結局は短い直線を引くことになるので、一般的には、二点をP1(x1,y1)とP2(x2,y2)として、
g->DrawLine(Pens::Black,x1,y1,x2,y2);
でよい。
しかし、グラフを描く場合のように、点を順次結んでゆく場合には、それらの点を、x[0]、y[0]、… …、x[i]、y[i]、… … として、
1)線の開始点では、
old_x=x[0];
old_y=y[0];
を用いて位置の設定のみを行い、
2)二回目からは、
g->DrawLine(pen1,old_x,old_y,x[i],y[i]);
old_x=x[i];
old_y=y[i];
を繰り返して用いると良い。
フォームの設定
すでに示した方法に従って、
1)タイトルバーに「振幅変調と周波数変調」のキャプションを入れる。
2)フォームの背景色を「Window」にする。
3)使用するフォントを「MS ゴシック,9.75pt」に設定する(MSゴシックで10ポイントを指定しても、なぜか9.75ポイントになる)。
4)プログラム実行時に開くウインドウのサイズを決める
「Form1」の「プロパティ」ウインドウの「配置」欄で、「Size」の「300,300」を「528,444」に書き換える。このように設定すると、ユーザが使用できる領域(Client
Size)は、横方向に左右4ピクセルずつ合計8ピクセル引き、縦方向に上30ピクセル、下4ピクセル引いた、「520,410」になる。横幅は、グラフの幅が500ピクセルなので、両側に10ピクセル余白を取ってある。縦方向は、グラフの高さが90ピクセルあり、それが4個で90
x 4=360ピクセルと、上下の余白を含めて10 x 5=50ピクセルから成るので、合計410ピクセルを確保する。なお、グラフのサイズや配置を決めて実際に実行させてみて、最終的に設定すると良い。
プログラムの構成
プログラムは、Form1_Paint()メソッドに記述する。
主な内容は以下の通りである。
1)4種類のグラフを表示するために、4個の黒い背景(矩形)と、その中心に4本の明るいグレイの基準線を引く。
2) 時間を t=0 から t=499 まで変化させ、搬送波、変調信号などをグラフで描画する。
3) 各グラフの右下に、説明の文字列を表示する。
グラフの描き方
すでに説明した方法によれば、以下に示す「最も素直な方法」が一番分かり易いが、if文を500回も繰り返す無駄がある。t=0の場合のみをforループの外に出したのが次の「最初のt=0だけ別に計算する方法」であり、if文の繰り返しはなくなったが、同じoutの計算を外と中でやるのはスマートでない。最後の「t=0の時の値をold_yに直接入れる方法」は、t=0の時のoutの値を別途計算して、直接old_yに入れる方法で、outの計算が簡単な場合には好ましい(可読性はやや悪くなるが…)。
◎最も素直な方法
//搬送波
for(int t=1;t<500;t++){
out=AMP_C*Math::Sin(OMEGA_C*t);
y=Y0-(int)out;
if(t==0)
old_y=y;
else{
g->DrawLine(Pens::LightGreen,X0+(t-1),old_y,X0+t,y);
old_y=y;
}
}
◎最初のt=0だけ別に計算する方法
//搬送波
int t=0;
old=AMP_C*Math::Sin(OMEGA_C*t);
old_y=Y0-(int)out;
for( t=1;t<500;t++){
out=AMP_C*Math::Sin(OMEGA_C*t);
y=Y0-(int)out;
g->DrawLine(Pens::LightGreen,X0+(t-1),old_y,X0+t,y);
old_y=y;
}
◎t=0の時の値をold_yに直接入れる方法
//搬送波
old_y=Y0; //t=0でout=0が分かっているので
for(int t=1;t<500;t++){
out=AMP_C*Math::Sin(OMEGA_C*t);
y=Y0-(int)out;
g->DrawLine(Pens::LightGreen,X0+(t-1),old_y,X0+t,y);
old_y=y+
}
プログラムの実際
private: System::Void Form1_Paint(System::Object^ sender,
System::Windows::Forms::PaintEventArgs^
e) {
Graphics^ g=e->Graphics;
int X0=10;
int Y0=55,Y1=Y0+100,Y2=Y0+200,Y3=Y0+300;
double AMP_C=20.0; //搬送波(被変調波)振幅
double FREQ_C=1/12.5; //搬送波周波数(時間500に40サイクル)
double OMEGA_C=2*Math::PI*FREQ_C; //搬送波角周波数
double AMP_S=1.0; //信号波(変調波)振幅
double FREQ_S=1/250.0; //信号波周波数(時間500に2サイクル)
double OMEGA_S=2*Math::PI*FREQ_S; //信号波角周波数
double MOD_A=0.5; //振幅変調変調度
double MOD_F=0.025; //周波数変調変調度
double out;
int y,old_y;
for(int i=0;i<4;i++){
//黒で背景を描く
g->FillRectangle(Brushes::Black,Rectangle(X0,Y0-45+100*i,500,90));
//明るいグレイで横線を引く
g->DrawLine(Pens::LightGray,X0,Y0+100*i,X0+500,Y0+100*i);
}
//搬送波
old_y=Y0;
for(int t=1;t<500;t++){
out=AMP_C*Math::Sin(OMEGA_C*t);
y=Y0-(int)out;
g->DrawLine(Pens::LightGreen,X0+(t-1),old_y,X0+t,y);
old_y=y;
}
g->DrawString("搬送波(被変調波)",Font,Brushes::White,380,Y0+27);
//変調信号
old_y=Y1;
for(int t=1;t<500;t++){
out=20.0*AMP_S*Math::Sin(OMEGA_S*t); //表示のために20倍に拡大
y=Y1-(int)out;
g->DrawLine(Pens::LightGreen,X0+(t-1),old_y,X0+t,y);
old_y=y;
}
g->DrawString("信号波(変調波)",Font,Brushes::White,380,Y1+27);
//振幅変調
old_y=Y2;
for(int t=1;t<500;t++){
out=AMP_C*(1+MOD_A*AMP_S*Math::Sin(OMEGA_S*t))*Math::Sin(OMEGA_C*t);
y=Y2-(int)out;
g->DrawLine(Pens::Blue,X0+(t-1),old_y,X0+t,y);
old_y=y;
}
g->DrawString("振幅変調波",Font,Brushes::White,380,Y2+27);
//周波数変調
old_y=Y3;
for(int t=1;t<500;t++){
out=AMP_C*Math::Sin(OMEGA_C*(1+MOD_F*AMP_S*Math::Sin(OMEGA_S*t))*t);
y=Y3-(int)out;
g->DrawLine(Pens::Red,X0+(t-1),old_y,X0+t,y);
old_y=y;
}
g->DrawString("周波数変調波",Font,Brushes::White,380,Y3+27);
}
得られた画面
得られた結果を下図に示す。
図 得られた結果
「Visual C++ の勉強部屋」(目次)へ