補間について少し書いてみます。間を補うと書いて補間。点と点の間を繋ぐ。私がプログラムを書き始めた頃は、直線描画のハードあるいはコードで作っていた。
o点とp点を繋ぐ。
X方向に3、Y方向に5進む。
-
●
●
●
●
●
●
多分上の図が望まれる。
除算機能のないCPUがまれになった現在では、増分(Y/X、あるいはX/Y)を
求めておいて、累積させれば求まる。
上の例で言うと、増分は3/5(=Y/X)となる。
累積の結果1を超えた場合は上と右へ、超えない場合は右のみへ進める。
Yの累積値 |
Xの座標 |
Yの座標 |
Yの目標値 |
誤差 |
|
始点 |
0 |
0 |
0 |
0 |
0 |
1回補間後 |
3/5 |
1 |
0 |
3/5 |
-3/5 |
2回補間後 |
6/5->1/5 |
2 |
1 |
6/5 |
-1/5 |
3回補間後 |
4/5 |
3 |
1 |
9/5 |
-4/5 |
4回補間後 |
7/5->2/5 |
4 |
2 |
12/5 |
-2/5 |
4回補間後 |
5/5->0/5 |
5 |
3 |
15/5 |
0 |
-
●
●
●
●
●
●
となる。
少し変ですね。
これは丸め処理(四捨五入)が入ってないためです。
丸め処理を入れる。
累積結果に1/2を加えて判定すればよい。累積の初期値を1/2とする。
Yの累積値 |
Xの座標 |
Yの座標 |
Yの目標値 |
誤差 |
|
始点 |
1/2(初期値) |
0 |
0 |
0 |
0 |
1回補間後 |
1/2+3/5->1/10+1 |
1 |
1 |
3/5 |
+2/5 |
2回補間後 |
1/10+3/5->7/10+0 |
2 |
1 |
6/5 |
-1/5 |
3回補間後 |
7/10+3/5->3/10+1 |
3 |
2 |
9/5 |
+1/5 |
4回補間後 |
3/10+3/5->9/10+0 |
4 |
2 |
12/5 |
-2/5 |
4回補間後 |
9/10+3/5->0/10+0 |
5 |
3 |
15/5 |
0 |
-
●
●
●
●
●
●
となる。
さてコードだが、右へr、上へu進ませる場合(2点間の距離が[r、u])、増分u/rを求める必要がある。
r/uの性質上Real(float,double)を使いたくなるが、整数演算で間に合わす。
補間の距離に依存するが、2の16乗未満の画素数なら、16ビットのunsignedで足りる。
増分は16ビットの下駄を履かせて演算する。
a=65536×u/r
こうすると、Msb(最上位ビット)が1/2となる。
同様に初期値1/2は32768となる。
この初期値にaを累積させ、Xを1画素進めた時にYを増加させるかをキャリーで判定する。
しかし、キャリービットを検知する言語でない場合、累積値の減少を判定する。
sample code
void LineInterpolation( Cpoint p0, CPoint p1)
{
CPoint d= p1 – p0;
if ( d.x == 0 && d.y == 0 ) { return; }
unsigned short a= 65536 * d.y / d.x; // 16ビットの下駄を履かせて増分算出
unsigned short ds= 0×8000; // 初期値を1/2に
Cpoint p;
for( p= p0; p != p1; p.x++ ) { // X方向歩進
// ここに補間点pに対する処理を入れる。
If ( ds + a < ds ) { p.y++; } // キャリー判定とY方向歩進
ds += a; // 誤差の更新
}
}
これはモデルであり、使えるのは
r>=0 && u>=0 && |r|>=|u|、すなわち0~45度の区間に限る。
老婆心ながら付け加えると、45~90度の区間はX座標とY座標を入れ替えればよい。
更に、-45度~0度の区間であれば、Yの符号を反転しY方向の増分を-1とする。
他の区間も同様で、X,Yの入れ替えと増分の符号反転で対応すればよい。
お気づきかもしれないが、このコードでは終点は描画されない。
これは、On purpose!
ちょっとした意味があります。
次回、円弧補間を書くつもりですのでその時に。