前回の線分生成と同様な手法で,円の走査変換をプログラミングしてみよう.
次のような設定のもとで, Fig.1 のような円周を描くことにする:
もっとも単純な 1/8 円弧の描画アルゴリズムは次の通り:
この方法では,実数演算(平方根)があるので,処理速度的に不利である.
なお,プログラムを実装する際には,x0 = 0, y0 = 0 とみなして計算し, 画素(x0+x,y0+y) を点灯する方が簡単. また,この場合には,円の対称性より, 次のような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);
低速アルゴリズムでは,この式を利用することになる.
前回紹介したブレゼンハムのアルゴリズムを応用すれば, 直線だけでなく,円についても, 整数のインクリメンタル計算による高速描画が可能である.
円の方程式より次の誤差関数(定義式)が得られる:
この関数の値は, 判定点(x,y)が円周上にあれば f = 0, 円外であれば f > 0, 円内であれば f < 0 である.
アルゴリズムについては,Fig.2 の説明図および 前回の高速アルゴリズム (ブレゼンハムの線分生成アルゴリズム) を参考にして考えてみよう.
なお,アルゴリズム中で利用される計算式(漸化式)は次の通り:
これで,元々は2乗を含んでいた誤差関数を, 簡単な整数の足し算とシフト演算(2n 倍)だけで計算できるようになった.
前回同様,コンピュータには,定義式では計算させない. 漸化式で計算させる.
右辺の関数値 f の部分には, 既に計算済みの変数値を使う. ループ内で関数 f を定義式通りに何度も再計算してしまうのは非効率.
なお,このアルゴリズムでも, x0=y0=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年度の課題) に説明がある.