HSP3 プログラミングの基礎W
前へ 目次へ 次へ 

(7) グラフィックス

 グラフィックスについては「プログラミングの基礎V (1) グラフィックス」で学習した。ここでは、すでに学習したグラフィックス命令を使って、少し便利なグラフィックス命令を作成していく。

* 基本的なグラフィクス命令
命令 機能
pset  カレントポジションにカレントカラーで点を描画する。
line  終点と始点を結ぶ直線を描画する。
circle  指定した四角形に収まる円を描画する。
boxf  塗りつぶしの四角形を描画する。


円(中心と半径)描画命令(g_circle)作成

 HSP3の円の描画命令circleは、指定した四角形に収まる円を描画する。一般的に円は中心座標と半径で指定するので、そのように指定して描画できる円描画の命令g_circleを作成する。

 命令g_circleの仕様は次の通りとする。

g_circle 座標x,座標y,半径,塗りつぶし

 図のように、中心座標(x, y)と半径rから円を囲む四角形の左上と右下の座標を求めることができる。
 塗りつぶしは、0で塗りつぶしなし、1で塗りつぶしありとする。

サンプル中心座標と半径で円を描画する命令を作成する。

#module
#deffunc g_circle int x, int y, int r, int nuri
;
;g_circle  座標x,座標y,半径,塗りつぶし
;  塗りつぶしは、0=塗りつぶしなし(省略時)、1=塗りつぶしあり
;
    circle x - r, y - r, x + r, y + r, nuri
    return
#global
    g_circle 100, 100, 50        ;※1
    g_circle 200, 100, 50, 1     ;※2
    color 255, 0, 0
    g_circle 100, 250, 50        ;※3
    color 255, 255, 0
    g_circle 100, 250, 49, 1     ;※4
    stop

実行※1で中心座標(100, 100)、半径50の黒色の円が描画される。※2で※1の右隣に黒色塗りつぶしの円が描画される。※3で赤色の円を描画し、※4でその中を黄色で塗りつぶした円が描画される。



直線(点線、破線など)描画命令(g_line)作成

 HSP3の直線描画命令lineは、終点と始点を結ぶ直線を描画する。この直線を点線や破線のように線の種類を指定して描画できる直線描画命令g_lineを作成する。

 命令g_lineの仕様は次の通りとする。

g_line 終点座標x,終点座標y,始点座標x,始点座標y,線の種類

 線の種類は、ビットパターンで指定することにする。ビットパターンは2バイト(16ビット)とする。

サンプル点を描画する命令psetで直線を描画する。

#module
#deffunc g_circle int x, int y, int r, int nuri
 <<省略>>
#deffunc g_line int x1, int y1, int x2, int y2, int pat
;
;g_line  終点座標x,終点座標y,始点座標x,始点座標y,線の種類
;  線の種類は、ビットパターンで指定する。
; 
    ;傾きaと切片b
    a = double(y1 - y2) / double(x1 - x2)  ;※1
    b = double(y1) - a * x1                ;※2
    if x1 < x2 : sp = x1 : else : sp = x2 
    repeat abs(x1 - x2), sp
        pset cnt, a * cnt + b
    loop
    return
#global
    g_line 100, 200, 200, 200       ;水平
    g_line 100, 200, 200, 150       ;<45゜
    g_line 100, 200, 200, 100       ;=45゜
    g_line 100, 200, 200, 50        ;>45゜
    g_line 100, 200, 100, 50        ;垂直
    stop

実行3本の直線が描画される。4本目の垂直はエラーになる。

 直線の式は、y = ax + b で求められる。aは傾き(※1の式)、bは切片(y軸との交点、※2の式)を表す。
 まず、2点の座標から傾きaと切片bを計算して、直線の式を求める。次に、x1からx2まで1ずつ変化させて、その時のyを直線の式で計算し、pset命令で点を描画している。
 直線のx座標はシステム変数cntで1ずつ増加させているので、システム変数cntの初期値は2点のx座標の内、小さい方でなければならない。そこで、if文で小さい方の値を変数spに格納している。これで、「g_line 100, 100, 200, 200」と「g_line 200, 200, 100, 100」のどちらでも同じ直線が描画できるようになる。
 ※1、※2の計算式でdouble()を使って実数に変換している。以前にも説明したように、HSP3は最初のデータの型に合わせて計算する。a=(y1-y2)/(x1-x2)はy1が整数型であるから計算結果も整数になるので、double(y1-y2)として実数型にしている。※1、※2の計算式は次のように書くこともできる。

a = double(y1 - y2) / (x1 - x2)  ;実数/整数→実数、変数aは実数
b = -a * x1 + y1                 ;実数*整数+整数→実数、変数bは実数



【対策】 垂直は、(x1-x2)が0になるのでエラーとなる。また、45度以上の直線は点線のようになるので、次のような対策をする。

 図の範囲は、xを変化させながらyを計算して点を描画していくことにする。
 abs(x1 - x2) >= abs(y1 - y2) のときである。
 図の範囲は、yを変化させながらxを計算して点を描画していくことにする。
 abs(x1 - x2) < abs(y1 - y2) のときである。
 ただし、x1 = x2 のときは垂直であるから別の処理とする。

サンプル点を描画する命令psetで直線を描画する。エラーが起こらないように、また点線のようにならないようにする。

#module
#deffunc g_circle int x, int y, int r, int nuri
 <<省略>>
#deffunc g_line int x1, int y1, int x2, int y2, int pat
;
;g_line  終点座標x,終点座標y,始点座標x,始点座標y,線の種類
;  線の種類は、ビットパターンで指定する。
;
    if x1 = x2 {         ;垂直の処理
        if y1 < y2 : sp = y1 : else : sp = y2
        repeat abs(y1 - y2), sp
            pset x1, cnt
        loop
        return    ;終了
    }
    ;傾きaと切片b
    a = double(y1 - y2) / double(x1 - x2)
    b = double(y1) - a * x1
    if abs(x1 - x2) >= abs(y1 - y2) {    ;xを変化させyを計算
        if x1 < x2 : sp = x1 : else : sp = x2
        repeat abs(x1 - x2), sp
            pset cnt, a * cnt + b    ;※1
        loop
    } else {                             ;yを変化させxを計算
        if y1 < y2 : sp = y1 : else : sp = y2
        repeat abs(y1 - y2), sp
            pset (-b + cnt) / a, cnt ;※2
        loop
    }
    return
#global
    g_line 100, 200, 200, 200       ;水平
    g_line 100, 200, 200, 150       ;<45゜
    g_line 100, 200, 200, 100       ;=45゜
    g_line 100, 200, 200, 50        ;>45゜
    g_line 100, 200, 100, 50        ;垂直
    stop

実行5本の直線が描画される。
 
 ※1のa*cnt+bは、実数*整数+実数→実数となる。
 ※2の(-b+cnt)/aは、(実数+整数)/実数→実数となる。式を変形すると(cnt-b)/aと書きたくなるが、これでは(整数-実数)/実数→整数となるため正しく直線が描画されない。


 次に、ビットパターンによって点を描画する/しないを判定する処理を追加する。

 例えば点線は、1ドット間隔で点を描画するのでビットパターンは"1010101010101010"または"0101010101010101"となる。1が点の描画で0は描画しないことを意味する。実際には、このビットパターンを16進数または10進数に変換して指定する。"1010101010101010"は0xAAAA、"0101010101010101"は0x5555と指定することになる。

サンプル直線をビットパターンにしたがって描画する。

#module
#deffunc g_circle int x, int y, int r, int nuri
 <<省略>>
#deffunc g_line int x1, int y1, int x2, int y2, int pat
;
;g_line  終点座標x,終点座標y,始点座標x,始点座標y,線の種類
;  線の種類は、ビットパターンで指定する。
;
    if pat = 0 : pt = 0xFFFF : else : pt = pat      ;ビットパターン
    bt = 0x8000                     ;ビットパターンとの比較用
    if x1 = x2 {
        if y1 < y2 : sp = y1 : else : sp = y2
        repeat abs(y1 - y2), sp
            if pt and bt : pset x1, cnt     ;比較
            bt = bt / 2                     ;ビットシフト
            if bt = 0 : bt = 0x8000       ;初期化
        loop
        return
    }
    ;傾きaと切片b
    a = double(y1 - y2) / double(x1 - x2)
    b = double(y1) - a * x1
    if abs(x1 - x2) >= abs(y1 - y2) {               ;xを変化させyを計算
        if x1 < x2 : sp = x1 : else : sp = x2
        repeat abs(x1 - x2), sp
            if pt and bt : pset cnt, a * cnt + b    ;比較
            bt = bt / 2                     ;ビットシフト
            if bt = 0 : bt = 0x8000       ;初期化
        loop
    } else {                                        ;yを変化させxを計算
        if y1 < y2 : sp = y1 : else : sp = y2
        repeat abs(y1 - y2), sp
            if pt and bt : pset (-b + cnt) / a, cnt    ;比較
            bt = bt / 2                     ;ビットシフト
            if bt = 0 : bt = 0x8000       ;初期化
        loop
    }
    return
#global
    g_line 100, 200, 200, 200, 0x5555       ;水平
    g_line 100, 200, 200, 150, 0x5555       ;<45゜
    g_line 100, 200, 200, 100, 0x5555       ;=45゜
    g_line 100, 200, 200, 50, 0x5555        ;>45゜
    g_line 100, 200, 100, 50, 0x5555        ;垂直
    stop

実行5本の直線が点線で描画される。

  pt=0101010101010101 pt and bt
  bt=1000000000000000 0
bt/2 bt=0100000000000000 0x4000
bt/2 bt=0010000000000000 0
bt/2 bt=0001000000000000 0x2000
 
bt/2 bt=0000000000000001 1
bt/2 bt=0001000000000000 bt初期化→

 ビットパターンが0のとき、何も描画されないので意味がないので、直線を描画するようにビットパターンを0xFFFFにしている。したがって、ビットパターンを省略したときも直線が描画される。
 サンプルではビットパターンが0x5555なので、2進数にすると"0101010101010101"の16ビットとなる。このビットパターンとの比較用として変数btに0x8000が格納されており、2進数にすると"1000000000000000"の16ビットとなる。これら2つの論理積(and)の結果は0で、「if pat and bt」は偽となり、点は描画されない。
 次のビットシフトで、0x8000/2=0x4000となる。2進数にすると"0100000000000000"となる。論理積の結果は0x4000で真となり、点が描画される。
 変数btの値は2で割るたびにビットの1が右にずれていく。最後には0になるので、そのときは0x8000で初期化する。

試してみよういろいろなヒットパターンで線を描画してみよ。



 いよいよ完成版である。
 HSP3のline命令は、始点座標(x,y)を省略すると終点座標(=カレントポジション)が始点座標になる。同じように、始点座標が省略できるようにする。ちなみに今、始点座標を省略すると(0,0)となる。

 作成中のg_line命令はpset命令で直線を描画している。pset命令ではカレントポジションが変化しないので、pos命令で終点座標を指定してカレントポジションを設定する。
 処理の最初では、始点が省略されて(0,0)のときカレントポジションを始点に代入する処理を追加する。

サンプル始点座標が省略できるようにする。

#module
#deffunc g_circle int x, int y, int r, int nuri
 <<省略>>
#deffunc g_line int x1, int y1, int x2, int y2, int pat
;
;g_line  終点座標x,終点座標y,始点座標x,始点座標y,線の種類
;  線の種類は、ビットパターンで指定する。
;
    xp2 = x2 : yp2 = y2    ;※
    if xp2 = 0 and yp2 = 0 : xp2 = ginfo_cx : yp2 = ginfo_cy    ;カレントポジション代入
    pos x1, y1              ;カレントポジション設定
    if pat = 0 : pt = 0xFFFF : else : pt = pat      ;ビットパターン
    bt = 0x8000                     ;ビットパターンとの比較用
    if x1 = xp2 {
        if y1 < yp2 : sp = y1 : else : sp = yp2
        repeat abs(y1 - yp2), sp
            if pt and bt : pset x1, cnt     ;比較
            bt = bt / 2                     ;ビットシフト
            if bt = 0 : bt = 0x8000         ;初期化
        loop
        return
    }
    ;傾きaと切片b
    a = double(y1 - yp2) / double(x1 - xp2)
    b = double(y1) - a * x1
    if abs(x1 - xp2) >= abs(y1 - yp2) {           ;xを変化させyを計算
        if x1 < xp2 : sp = x1 : else : sp = xp2
        repeat abs(x1 - xp2), sp
            if pt and bt : pset cnt, a * cnt + b    ;比較
            bt = bt / 2                     ;ビットシフト
            if bt = 0 : bt = 0x8000       ;初期化
        loop
    } else {                                        ;yを変化させxを計算
        if y1 < yp2 : sp = y1 : else : sp = yp2
        repeat abs(y1 - yp2), sp
            if pt and bt : pset (-b + cnt) / a, cnt    ;比較
            bt = bt / 2                     ;ビットシフト
            if bt = 0 : bt = 0x8000       ;初期化
        loop
    }
    return
#global
    g_line 100, 200, 200, 200, 0x5555       ;水平
    g_line 100, 200, 200, 150, 0x5555       ;<45゜
    g_line 100, 200, 200, 100, 0x5555       ;=45゜
    g_line 100, 200, 200, 50, 0x5555        ;>45゜
    g_line 100, 200, 100, 50, 0x5555        ;垂直
    g_line 80, 10, 10, 10, 0xCCCC
    g_line 80, 80, , , 0xCCCC
    g_line 10, 80, , , 0xCCCC
    g_line 10, 10, , , 0xCCCC
    stop

実行5本の直線と四角形が点線で描画される。

 パラメータx2, y2が始点座標であるが、パラメータに値が代入できないので、最初の行でxp2=x2:yp2=y2(※)としている。以降、x2とy2はすべてxp2とyp2に変更する。



四角形(塗りつぶしなし)描画命令(g_box)作成

 HSP3の四角形描画命令boxfは、四角形の左上と右下の座標で四角形を描画して塗りつぶす。塗りつぶしをしない四角形の描画命令はない。すでに作成したg_line命令を使って線種を指定して四角形を描画できる四角形描画命令g_boxを作成する。

 命令g_boxの仕様は次の通りとする。

g_box 左上座標x,左上座標y,右下座標x,右下座標y,線の種類

サンプル塗りつぶしなしの四角形を描画する命令を作成する。

#module
#deffunc g_circle int x, int y, int r, int nuri
 <<省略>>
#deffunc g_line int x1, int y1, int x2, int y2, int pat
 <<省略>>
#deffunc g_box int x1, int y1, int x2, int y2, int pat
;
;g_box  左上座標x,左上座標y,右下座標x,右下座標y,線の種類
;  線の種類は、ビットパターンで指定する。
; g_line命令が必要である。
;
    g_line x2, y1, x1, y1, pat
    g_line x2, y2, , , pat
    g_line x1, y2, , , pat
    g_line x1, y1, , , pat    ;左上座標(終点座標(x1,y1))がカレントポジションになる。
    return
#global
    g_box 50, 50, 120, 200
    g_box 150, 50, 220, 200, 0x5555
    g_box 250, 50, 320, 200, 0xAAAA
    stop

実行3つの四角形が描画される。左から直線の四角形、点線の四角形が2つである。

 2つの点線の四角形をよく見ると、左側の四角形の角がとれているように見える。これは、点線のパターンを0x5555("0101…")としているので描画なしから始まるためである。右側の四角形は点線のパターンを0xAAAA("1010…")としているので角があるように見える。

試してみよう三点の座標を指定して、三角形を描画する命令g_triangleを作成せよ。

 

前へ 目次へ 次へ 
2006  © Hiroshi Masuda 

 

 

inserted by FC2 system