1)プログラムの保存
個別に作成したプログラムはフォルダ毎に保存すると管理がしやすいので,
プログラム毎にフォルダを作成して,その中に.frmファイルと.vbpファイルを確実に保存しておきましょう..
なお保存時に十分にファイル名まで確認しておかないと以前のフォルダに保存されてしまうことが
あるので十分に注意しましょう.
(保存するファイル名のボックスに以前のディレクトリ名を含めて表示される場合があります.
その場合には一度表示されているプロジェクト名を削除してから,再度名前を入力してください.
そうしないと作成中の課題まで消されてしまうという事態が生じる可能性があります.)2)ヘルプファイルの活用
なお,プログラムを作成していく場合に,その文法を確認するためにヘルプを効果的に使いましょう.
ヘルプファイルのダウンロードは,
http://www.asia.microsoft.com/japan/developer/vbasic/download/dlindex.htm
からすることができます.3)プログラムのインデント(字下げ)にはTabキーを使うこと.
プログラムのインデント(字下げ)は複雑なプログラムを書く場合に,Forに対応するNextがあるか,Ifに対応するEnd Ifがあるかなどを確認する場合に便利で,またわかりやすくなります.
Tabキーで適当にインデントしておきましょう.複数行のインデントは範囲を選択してTab,またインデントを解除するには,Shiftを押しながらTabキーを押します.
Tabによる空白の数はツール>>オプション>>編集で設定可能です.
4)保存時のファイル名は半角英字及び半角数字で..
日本語のファイル名を使っているとVBが起動しなくなるおそれがあるので,注意してください.
起動できなくなったら下記アドレスにアクセスして情報を得てください.
http://www.asia.microsoft.com/japan/developer/vbasic/download/recents.htm
これからは,コンピュータ上において自分自身が定義した図形を描画する手法について学ぶ.すなわち,これはコンピュータ上に自らが定義する仮想空間実現のための第一歩である.
コンピュータグラフィックスの最も基本となる技術は,これまで画像処理において学んできたように,画面上を構成するひとつひとつのピクセルに対して,発色するか否か,また発色する場合にはRGBをどのような割合で発色させるか..ということをコンピュータに命令するということである.しかし,コンピュータ上により容易に絵を描画しようという場合に,1つ1つのピクセルに対する操作によって行なえる操作には限界がある.
紙上に鉛筆で絵を描く場合を考えてみれば,それは点ではなく,線により構成される.線はいくら細分化しても線であり,それは必ずしも点の集合ではない.しかし,コンピュータ上での表現はディスプレイに制約される点,すなわちピクセルの集合である.すなわち,コンピュータ上で線を描くためには,ベクトルとして表現される線を,ディスプレイ上の画素の点情報に変換するという必要がある.
そこで,まずディスプレイ上で線をいかに表現するかということから学んでいこう.
例えばコンピュータディスプレイ上に一本の水平線を描画しようと考える.
ディスプレイはピクセルによって構成されるから,水平線に対応するディスプレイ上のピクセルに対して発色するか否かについて指示を与えればよい.
この仕組みをVBを用いて理解しておこう.準備
貼り付けられたピクチャコントロールのウィンドウ上の座標は,以下のようになる.
- ピクチャコントロールボックスをForm1上に貼り付ける
- 貼り付けたピクチャコントロールを選択し,ScaleModeプロパティを3-ピクセルに変更する.
- (これはコード中でも変更は可能である..)
- ピクチャコントロールのScaleWidthとScaleHeightがそれぞれ300以上であることを確認しておく.
(足りない場合には,ピクチャコントロールのサイズを大きくすること)
![]()
図-1 Picture1の座標系
ここで,座標(100,100)から(200,100)の直線を描くことを考えよう.
コンピュータ上で描かれる線は点の連続であるから,つまり,(100,100)のピクセルから(200,100)までのピクセルまでを連続的に発色させる必要がある.つまり,
(100,100),(101,100),(102,100),(103,100),.......,(199,100),(200,100)
のそれぞれのピクセルにPsetメソッドで点の発色をさせればよいということである.つまり,
Picture1.Pset(100,100), RGB(255,0,0) '赤の線を描く (&HFF)
Picture1.Pset(101,100), RGB(255,0,0)
Picture1.Pset(102,100), RGB(255,0,0)
Picture1.Pset(103,100), RGB(255,0,0)
:
Picture1.Pset(199,100), RGB(255,0,0)
Picture1.Pset(200,100), RGB(255,0,0)
という命令を打ち込めばよい.しかし,これをいちいち打ち込むのは大変な苦労なので,ここでFor..Nextループを使う.
Dim X As Integer 'Xを整数として定義する
For X = 100 To 200
Picture1.Pset (X, 100), RGB(255,0,0)
Nextこれで,(100,100)から(200,100)までの直線が描かれる.
実際のプログラムは以下の通りである.
Option Explicit
Private Sub Picture1_Click() 'ピクチャボックスをクリックすると描画される.
Dim X As Integer
For X = 100 To 200
Picture1.PSet (X, 100), RGB(255, 0, 0) '赤で点を描く
Next
End Sub上記プログラムを実行すればピクチャボックスをクリックすれば赤の線が描画されるはずである.
さて水平線はこれで描けるが,斜めの線はどのように描けばよいのだろうか.
斜めの線を描く場合には,その直線の方程式を求めて,それに応じたピクセルの発色が必要である.
ここで,ピクチャボックスのウィンドウ座標に対して,(100,100)から(200,300)の直線を描くことを考えよう.
(100,100)と(200,300)を通過する直線の方程式は,Y = 2X-100 であるから,Xの増分に対するYの値を求めて,点を描画していけば直線として表現されるか??Option Explicit
Private Sub Picture1_Click() 'ピクチャボックスをクリックすると描画される.
Dim X As Integer
For X = 100 To 200
Picture1.PSet (X, X*2-100), RGB(255, 0, 0) '赤で点を描く
Next
End Sub上記のプログラムを実行してみるとわかるのであるが,これで描かれるのは点線である.つまり,Xに比べYの増分が多いことから,直線として描画されていないということになる.
そこで,増分の多い方から少ない方のピクセル値を計算して描画しなければならない.すなわち,XからYを求める方程式は X=1/2*Y+50 と表現されるから,プログラムは以下のように修正されるべきである.Option Explicit
Private Sub Picture1_Click() 'ピクチャボックスをクリックすると描画される.
Dim X As Integer, Y As Integer
For Y = 100 To 300
Picture1.PSet (Y/2+50, Y), RGB(255, 0, 0) '赤で点を描く
Next
End Sub
図-2 上記のプログラムで描かれた線 なお,上の図に示すように,描かれた直線は細かくみればギザギザ(CG用語でジャギーと言う)が生じるが,一応,斜めの線のように見える.
>>ジャギーを少なく見せる方法としてアンチエイリアシングという手法がある.これについては
後の講義にて解説する予定.初期のCGでは,これらのピクセルの発色のON,OFFをすべてプログラムしてやる必要があった.しかしながら,これらをCAD(Computer Aided Design)等のソフトウェア開発において,これらをいちいちプログラミングするということは非常に非効率である.
そのため,現在では多くの言語が直線等の基本図形を描画するためのコマンド(メソッド)を持っている.
図形描画の基本となるのは,直線の描画であり,これによりスクリーン座標系,あるいはユーザーが任意に設定した座標系の中でのベクトルによるグラフィックスが可能となるのである.
上述したようにベクタグラフィックスの基本は直線の描画である.
(一般的に始点と終点の座標を指定することにより描画される)VBでは直線は以下のように定義する. [ ]省略可能
直線を描画するコマンド
object.Line [Step](x1, y1)- [Step](x2, y2), [color], [B][F]
(x1,x1) は始点の座標,(x2,y2)は終点の座標
color は色(省略時はForeColorプロパティの色で塗りつぶされる)
[B]はそれを対角線とする四角形を描画する.
[F]は塗りつぶし.
[Step]は直前の座標に対する相対座標により指示する場合
例えば
Picture1.Line (10,10)-(100,100)
Picture1.Line -Step (100,100)
Picture1.Line (10,10)-(100,100), RGB(255,255,0), B
Picture1.Line (10,10)-(100,100), RGB(255,255,0), BFコンピュータ上での基本的なグラフィックスは,このように直線を描画することから始まる.
演習
- Picture1のピクセル座標上(20,50)と(250, 300)を結ぶ直線を描きなさい.
- 1.で描いた直線をを対角線とする四角形を描画しなさい.またその四角形を緑色で塗りつぶしなさい.
- ピクセル座標上の点A(100,100)から相対的に(20,50)移動した点Bを,さらに点Bから相対的に(-40, 20)移動した点を点Cとしたとき,点A,B,Cを LineメソッドのSTEPオプションを利用して描きなさい.
それでは曲線を描くためにはどうしたらよいだろうか.
円を描画するということから考えてみよう.原点(0,0)を中心とする半径rの円の方程式は
x2+y2 = r2 で与えられる.
これをどのように描画すればよいだろうか.
ここで y を x を変数とする方程式に置きかえれば,y = ±sqr(r2 -x2) 注)sqrは平方根を求める関数(VB)
これを第一象限の1/4円のみについて考えれば,yは常に正の値をとるから,
y = sqr(r2 -x2)
半径は自ら設定すべき値であるから,仮に半径を100として考えれば,
y = sqr(1002 - x2)
となる.
つまり上の式に対し,xの値を0から100(つまり半径)の範囲で変えることにより,円の描画が可能となる.
そこでVBでコードを書くと以下の通りになる.(なおプロット点も描画するようにしている.)'あらかじめPictureコントロールを配置しておくこと上記を実行してみると,半円が表示されるはずである.
Option ExplicitConst PAI = 3.141592 '円周率PAIを定義しておく.次のプログラムで利用します.
Private Sub Form_Load()
Picture1.ScaleMode = 3
End Sub'Command1を配置する.
Private Sub Command1_Click()
Dim x As Single, y As Single, r As Single
Picture1.Cls 'picture1をクリアする
r = 100
For x = 0 To 100
y = Sqr(r ^ 2 - x ^ 2) 'yの値を計算
If x = 0 Then
Picture1.PSet (x, y) '最初の1点を点として打ち込む
Else
Picture1.Line -(x, y) '直前の描画点との間で直線を引く
End If
Picture1.Circle (x, y), 2, &HFF '実際のプロット点を表示
Next
End Sub
なお,Picture1.ClsはPicture1のグラフィックスを消去しろ!というメソッドである.
(ピクセル座標は上下反転していることに注意)
しかし,実際の円を構成する点の配置を見てみると,その疎密が円上で明らかに異なる.
これは以下のようにプログラムを変更するとより明確になる.変更部分 For x = 0 To 100
変更後 For x = 0 To 100 STEP 10つまりXの値をより疎として計算した場合である.つまり描画速度を向上させようとプロット点の計算を減らすことにより,円を表示する場合に問題が生じる.
このような問題に対してパラメトリック曲線の考え方がある.これはある媒介変数(パラメータ)をもとにして,x, y それぞれの値を別々に計算しようというものである.
円の描画については,角度をパラメータとして取り扱うことになり,以下のように表現される.
x = r * cos(t)
y = r * sin(t)これらを実際のプログラムに反映させよう.
'Command2を配置する.上記のプログラムを実行すれば明らかであるが,より少ないプロット点で円を描画することが可能である.
Private Sub Command2_Click()
Dim t As Single, x As Single, y As Single, r As Single
Picture1.Cls 'picture1をクリアする
r = 100
For t = 0 To 90 Step 15
x = Cos(t * PAI / 180) * r 't*PAI/180で度をラジアンに変換
y = Sin(t * PAI / 180) * r
If t = 0 Then
Picture1.PSet (x, y) '最初の1点を点として打ち込む
Else
Picture1.Line -(x, y) '直前の描画点との間で直線を引く
End If
Picture1.Circle (x, y), 2, &HFF '実際のプロット点を表示
Next
End Sub
ここでx2+y2 = r2のように,求めるべきx, yが相互依存した式の表現をノンパラメトリック表現といい,一方,パラメータを介して x, yの値を個別に計算する式の表現(例えばx = r * cos(t),y = r * sin(t) )を パラメトリック表現という.
上述したようにパラメトリック表現はコンピュータ上での曲線の描画を行なう上で非常に有効な手法であり,また非常に重要な考え方である.パラメトリック曲線の応用については後述.なお,円の描画は,VB上でメソッドとして提供されており,実際には上記のようにプログラミングする必要はないが,概念だけは十分に知っておくように..
VB上での円の描画メソッドは以下の通りである.
円を描画するコマンドは以下の通りである.
object.Circle [Step] (x, y), radius, [color, start, end, aspect]
[Step] 省略可:相対座標で中心座標を指示
radius 半径
color 省略可:色
start, end 省略可:それぞれ円の開始角度,終了角度(半円などを描画する場合)
ラジアン(360度はラジアン表記で2π,つまり2*3.1415...)
aspect 省略可:楕円を書く場合に指定例えば,
Picture1.Circle (100,128), 50
Picture1.Circle Step (10, 10), 50
Picture1.Circle (100,128), 50, RGB(0,255,255)
Picture1.Circle (100,128), 50, ,0,1.57
Picture1.Circle (100,128), 50, , , ,2
ここでは,マウスの操作に応じて線が描かれるという簡単なグラフィックスソフトウェアの作成を行なってみよう.それは,以下の段階で行なってみよう.1)ピクチャウィンドウ上でクリックした点を結ぶ線を描く.
2)クリック操作による線分の始点と終点の決定プログラムの考え方をフローで示すと図-3の通りとなる.
そのコートは以下の通りである.'まず,PictureBoxオブジェクトをForm1上に貼り付けておく.
'コード
Option Explicit
Private Sub Form_Load()
' Picture1.ScaleMode = vbPixels 'ピクセルモードを指定してしておく
End Sub
Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Picture1.Line -(X, Y), &H0 'マウスを押した場所のX,Y座標とその直前の
' 描画点とを結ぶ直線を描く(&H0=黒)
End Sub上記のプログラムにより,直前の描画点とマウスが押された座標とを結ぶ直線が描画される.
しかし,上記のままでは始点も終点も指定することができない.それではどうしたらよいか.
とりあえず手書きのフロー図(図-3;汚い!!)に示すように,自分の打っている点が何点目かというカウンタを設け,それを元に始点であるか否かを判断させればよい..そして,もし最初の1点だったら点だけ描画させておこう.これで次の直線を引くコマンド(line)コマンドで直前の描画点として設定できる.さてコードは,以下のように修正しておこう.
なお,Form_Unloadはそのフォームが閉じられたときに発生するイベントで,これで完全にプログラムを終了しておくために書いておこう(コンパイルして使用する場合に,終わったつもりのプログラムが残存しているという問題が生じるおそれがあります)
Option Explicit
Dim pcount As Integer 'カウンタ,複数のSubルーチンで使うので広域的に宣言しておくPrivate Sub Form_Load()
Picture1.ScaleMode = vbPixels 'ピクセルモードを指定
pcount = 0 'カウンタを初期化
End SubPrivate Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If pcount = 0 Then
Picture1.PSet (X, Y), &H0 'とりあえず黒で描画(&H0=RGB(0,0,0)
Else
Picture1.Line -(X, Y) , &H0
End If
pcount = pcount + 1 'カウンタに1加える
End SubPrivate Sub Form_Unload(Cancel As Integer)
End 'フォームが閉じられたら終わり.
'これを付け加えておくとようやくEndに達する.
End Sub上記を打ち込んだらとりあえず実行してみよう.
このプログラムには,まだ問題がある.それは線を終了するということができないということである.線を終了するためには,どうしたらいいか..それは描画点カウンタを0に戻すという操作により可能である.カウンタが0になれば,プログラムは次は線の始点であると判断し,Psetコマンドを送るからである.
これを変更すると,コードは以下の通りになる.'ダブルクリックイベントの追加
Private Sub Picture1_DblClick()
'ダブルクリックされたら..
'まずMouseDownイベントを感知してMouseDownルーチンを実行します.
'その後にこのルーチンが呼び出されます.
pcount = 0 'ダブルクリックされた場合はカウンタを0に
End Subコード中にも記載されているようにDblClickが感知する前に,MouseDownが感知されます.そのため,ダブルクリックした点まで線を描画した後にカウンタをリセットするという期待すべき効果が得られるわけです.
それでは描いた図形を塗りつぶすということをやってみよう.図形の塗りつぶしには,いくつかの方法があるが,Windowsでは標準で境界となる線までを塗りつぶす機能を提供しているので,ここではこれを用いる.
この方法は,ディスプレイ上に描画されている図形内のある1ピクセルを指定することにより,境界線,つまりピクセルの色の値が異なる範囲までのピクセルをすべて,塗りつぶし色に変更するという方法である.ただし,完全に閉じた図形と判断されない場合には,色が図形外にあふれる.
この塗りつぶしのコマンドはVBでは直接的にサポートされていない.そこで,Windowsのシステム自体が有しているこのコマンドを,VBから直接的に呼び出して操作するという操作が必要となる.
それでは,実際に図形の塗りつぶしについてVBを用いて習得することにしよう.
注)標準モジュールを追加したことにより,保存時にはModule1.basというファイルも保存しなければなりません.他のファイル(.frm .vbpなどのファイル)と同一のフォルダに保存しておくこと.
- プロジェクト>>標準モジュールの追加>>挿入>>プロジェクトで標準モジュールを選択し,開くを押す.これにより,画面右上のプロジェクトウィンドウに標準モジュール(Module1.bas)が追加される.
注)標準モジュールはプログラムが読み込まれる場合に同時に読み込まれます.- プロジェクトウィンドウの中からModule1.basを選択し,右クリックで「コードの表示」を選択する
- 表示されたModule1.basの中に以下のようにコードを記載する.
Declare Sub FloodFill Lib "GDI32" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long, ByVal crColor As Long)
注)改行を入れずに一行で打ち込むこと.
この1行でWindowsのシステム自体が持っているAPI(アプリケーションプログラミングインターフェース)を呼び出し- Form1のMouseDownイベントに,塗りつぶしの色の設定及びコマンドを加える.
実際のコードは以下の通りになる.
'標準モジュールの中に記載
Declare Sub FloodFill Lib "GDI32" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long, ByVal crColor As Long) '改行無しで入れること
'Formモジュールに追加するコード (太字)
Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = 1 Then 'If Button = vbLeftButton Thenでもよい
If pcount = 0 Then
Picture1.PSet (X, Y), &H0
Else
Picture1.Line -(X, Y), &H0
End If
pcount = pcount + 1 'カウンタに1加える
ElseIf Button = 2 Then 'Button = vbRightButtonでもよい
Picture1.FillStyle = 0 ' FillStyle プロパティを塗りつぶし(0)に設定
Picture1.FillColor = &HFF ' FillColor プロパティ(&HFF=RGB(255,0,0)を
'設定します。
FloodFill Picture1.hDC, X, Y, &H0
'X,Yから境界線の色(&H0 黒)までの範囲を塗りつぶす
End If
End Sub注)なお,Picture1.hDCはデバイスコンテキストと呼ばれ,WindowsAPIにPicture1の情報を手渡すために用いられる.これについては,ここでは詳しく述べません.Win32API関連の本を参照してください.
これまでのプログラムでは線のプログラムを黒,塗りつぶし色を赤に設定してあるが,これを自由に設定できるようにしてみよう.
1)CommonDialogコントロールによる色の設定
塗りつぶしの色は,Picture1.FillColorプロパティにより決定されているが,これに対して値を与えればよいということである.コード中は,現在,
Picture1.FillColor = &HFF
が記載されているが,これを変えてやればよい.すなわち,Picture1.FillColor = &HFF あるいはPicture1.FillColor = RGB(255,0,0)
としてやれば,その塗りつぶし色を赤に変えることができる.
つまり,Picture1.FillColorプロパティに色番号を与えるようにすればよいということである.ここで,VBのコントロールであるCommonDialogControlを使う.(これはファイルの読み込みにも使ったものである.)
プロジェクト>>コンポーネント>>Microsoft CommonDialogControlにチェックする.これでコントロールウィンドウ上にコントロールが表示されるから,これをForm1上に貼り付けておく.これを呼び出して色を得ることは極めて簡単である.
CommonDialog1.ShowColor
というコマンドを実行すれば,設定された色は,プロパティCommonDialog1.Colorに設定される.
それでは,コマンドボタンを押すことにより,色の設定が可能となるようプログラムを変えることにしよう.
しかし,これまでのプログラムでは塗りつぶしの直前でPicture1.FillColorを操作していたので,
- コマンド1ボタンをForm1上に配置する.
- さらに以下のプログラムを追加する.
Private Sub Command1_Click()
CommonDialog1.ShowColor
Picture1.FillColor = CommonDialog1.Color
End Sub
いくらコマンドボタンを押しても,その変更は更新されないはずである.そこで,以下のコードを
削除しておこう.
Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = 1 Then 'If Button = vbLeftButton Thenでもよい
If pcount = 0 Then
Picture1.PSet (X, Y), &H0
Else
Picture1.Line -(X, Y) , &H0
End If
pcount = pcount + 1 'カウンタに1加える
ElseIf Button = 2 Then 'Button = vbRightButtonでもよい
Picture1.FillStyle = 0
' Picture1.FillColor = &HFF ' 'を付けてコメント文にするか削除する.
FloodFill Picture1.hDC, X, Y, &H0
'X,Yから境界線の色(&H0 黒)までの範囲を塗りつぶす
End If
End Sub
2)AutoRedrawの設定
上述のプログラムには問題がある.それは実行すればわかることであるが,CommonDialogコントロールが呼び出され,色の設定をした後に,それが表示されていた部分の画像が失われるということである.
これは,これまでVBでプログラムして描かれた画像が一時的なグラフィックス用のメモリ(現在,表示されている画面のみを記憶している;VRAM)に対する操作のみを行なっており,それ以前に表示されていた画像を一切記憶していないということを意味する.
これを回避するために,Picture1のグラフィックスをメモリ上に記憶できるようにしておく必要がある.これを有効にするために,Picture1のAutoRedrawというプロパティを操作する.
- 設定は,Picture1を選択し,プロパティウィンドウの中のAutoRedrawというプロパティをTrueにしておくだけでよい.
(ただし,これに伴い,画像の描画速度は著しく低下する. 後の授業で行なうダブルバッファリングという機能と同様である)- AutoRedrawを設定した場合,WindowsAPIを呼び出した塗りつぶしの描画が直ちには行なわれないので,強制的に画面の描画更新(Refresh)を行なう.そのために,MouseDownイベントに以下の1行を追加する.
Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
If Button = 1 Then 'If Button = vbLeftButton Thenでもよい
If pcount = 0 Then
Picture1.PSet (X, Y), &H0
Else
Picture1.Line -(X, Y) , &H0
End If
pcount = pcount + 1 'カウンタに1加える
ElseIf Button = 2 Then 'Button = vbRightButtonでもよい
Picture1.FillStyle = 0
FloodFill Picture1.hDC, X, Y, &H0
'X,Yから境界線の色(&H0 黒)までの範囲を塗りつぶす
Picture1.Refresh
End If
End Sub
(参考)
例えばShapeコントロールを貼り付け,以下のようなコードをCommand1_Click及びForm_Loadプロシージャに記載しておけば,設定色が確認できるようになります.
Shape1.FillStyle = 0
Shape1.FillColor = Picture1.FillColor
演習
|
演習を増やしたため,若干授業の進み方が当初予定より遅れます.
また3次元CGについてもそれに伴い,ずれこみます.
(またCGの情勢が昨今,大きく変化しつつあることから,将来(卒業後)役立つ内容となるよう,
その内容について再検討中です)