2015.10.21

円の描画

前回の線分生成と同様な手法で,円の走査変換をプログラミングしてみよう.


問題の設定(円の描画)

次のような仮定のもとで, Fig.1 のような円を描くことにする:

Fig.1. 円


低速アルゴリズム(直接解法)

もっとも単純な 1/8 円弧の描画アルゴリズムは次の通り:

この方法では,実数演算(平方根)があるので,処理速度的に不利である.

他の単純な方法としては,多角形近似がある. 短い線分を組み合わせて,円を表現する. しかし,頂点座標を三角関数で求める必要があり, やはり,処理速度の問題がある.

なお,プログラムを実装する際には,x0 = 0, y0 = 0 とみなして計算し, 画素(x0xy0y) を点灯する方が簡単. また,この場合には,円の対称性より, 次のような8個の画素を点灯すれば 0 〜 360度の円が完成することになる:

ちなみに,y 座標から x 座標を求めるための数式のC言語コードは:

x = (int)round(sqrt((double)(r*r - y*y)));
または
x = (int)(sqrt((double)(r*r - y*y)) + 0.5);

低速アルゴリズムでは,この式を利用することになる.


高速アルゴリズム(インクリメンタル解法)

前回紹介したブレゼンハムのアルゴリズムを応用すれば, 直線だけでなく,円についても, 整数のインクリメンタル計算による高速描画が可能である.

円の方程式より次の誤差関数(定義式)が得られる:

この関数の値は, 判定点(xy)が円周上にあれば f = 0, 円外であれば f > 0, 円内であれば f < 0 である.

アルゴリズムについては,Fig.2 の説明図および 前回の高速アルゴリズム (ブレゼンハムの線分生成アルゴリズム) を参考にして考えてみよう.

Fig.2. ブレゼンハムのアルゴリズムによる円周の描画

なお,アルゴリズム中で利用される計算式(漸化式)は次の通り:

判定点× での誤差:
  
判定点が円内だった場合,次に点灯する画素での誤差:
  
判定点が円外(または円周上)だった場合,次に点灯する画素での誤差:
  

これで,元々は2乗を含んでいた誤差関数を, 簡単な整数の足し算とシフト演算(2n 倍)だけで計算できるようになった.

前回同様,コンピュータには,定義式では計算させない. 漸化式で計算させる.

右辺の関数値 f の部分には, 既に計算済みの変数値を使う. ループ内で関数 f を定義式通りに何度も再計算してしまうのは非効率.

なお,このアルゴリズムでも, x0y0=0 として計算し, (x0+x, y0+y) を点灯するようにする方が簡単.


本日の課題

前回作成したプログラム line.cを元にして, ディジタル円を生成するための関数の低速版 SlowDrawCircle() および高速版 FastDrawCircle() を定義し, 実行速度を比較せよ:

void SlowDrawCircle(int x0, int y0, int r, char c)
{
	...
}

void FastDrawCircle(int x0, int y0, int r, char c)
{
	...
}

高速版では,無駄な計算を極力排除するよう,工夫すること. 前回の注意事項も参考にしよう.

まずは,円を正しく描画できることを確認すること. その後で速度計測だ.

前回の線分描画では効果を実感できなかったかもしれないが, 今回はかなりの高速化が期待できるだろう.

上級者向け:

レポート提出

発展学習

円と三角形の塗り潰しのアルゴリズムについて, 参考資料(2007年度の課題) に説明がある.


(c) 2015, KY