今天复习这个算法的时候,有一点没有立马反应过来,故特此记此篇博客作为备忘。
描点原理
原理就是描实距离D点最近的那个点,距离的判断通过比较d1和d2的大小来确定。d1大,说明距离(xi+1,yi)更远,要描实(xi+1,yi+1)。相反同理。
需要说明的是此图将横线和竖线的交点看作像素点。
不妨假设斜率 k 在 0 到 1 之间,并且 x2>x1。
于是我们可以有上图这样一条直线。设在第 i 步已经确定第 i 个像素点是 (xi,yi),现在看第 i+1 步如何确定第 i+1 个像素点的位置:
d1=y−yi=k(xi+1)+b−yi
d2=yi+1−y=(yi+1)−k(xi+1)−b
d1>d2,下一个像素点取(xi+1,yi+1)
d1<d2,下一个像素点取(xi+1,yi)
d1=d2,取两个像素点中的任意一个
我们可以知道 d1和d2 大小的比较可以通过作差来得出。
d1−d2=2k(xi+1)−2yi+2b−1,其中的 k 值为 ΔxΔy,其中 Δx 为 x2−x1,Δy 为 y2−y1。
用 pi 代替 d1−d2
这里有个问题是,k 值是通过除法得出来的,我们知道计算机计算除法都是不精确的,所以这里,我们要做一点处理,来避免除法运算。这里,我们做的是两边同乘 Δx
Δx(d1−d2)=2Δy(xi+1)−Δx⋅2yi+Δx⋅(2b−1)
由于 Δx 在我们假设的时候就是大于0的,于是 d1−d2 的符号并不会因此而改变,于是我们不妨假设 pi=Δx(d1−d2)。
pi=2Δy(xi+1)−Δx⋅2yi+Δx⋅(2b−1)
我们将其中不变的一部分提取出来,并用常数 c 来代替:
pi=2Δy⋅xi−2Δx⋅yi+c
这样,我们就可以通过判断 pi 的正负来判断是描哪个点。
pi 递推
当然,我们仅仅知道一个 pi 是不够的,我们得进一步的递推下去:
pi+1=2Δy⋅xi+1−2Δx⋅yi+1+c
这里的 pi+1 代表下下一个描点的判别式。
通过做一个减法,我们可以得出 pi+1和pi 之间的数值关系:
pi+1−pi=2Δy−2Δx(yi+1−yi)
于是我们可以得出下面结论:
-
pi⩾0,应取 yi+1=yi+1,pi+1=pi+2(Δy−Δx)
-
pi<0,应取 yi+1=yi,pi+1=pi+2Δy
如何确定 p1 呢?
x1=x1, y1=ΔxΔyx1+b => Δx⋅y1=Δy⋅x1+Δx⋅b
在 pi 的公式中,不妨令 i = 1,有:
pi=2Δy(xi+1)−Δx⋅2yi+Δx⋅(2b−1)
p1=2Δy(x1+1)−Δx⋅2y1+Δx⋅(2b−1)
将刚才推导出来的式子带入化简得:
p1=2Δy−Δx
程序代码
void BresenhamLine(int x1, int y1, int x2, int y2)
{
int x, y, dx, dy, p;
x = x1;
y = y1;
dx = x2 - x1;
dy = y2 - y1;
p = 2*dy - dx;
for (; x <= x2; x++) {
SetPixel(x, y);
if (p > 0) {
y++;
p += 2*(dy-dx);
} else {
p += 2*dy;
}
}
}