画线算法:已知点A(x1,y1)和点B(x2,y2),画出点A和点B之间的所有的点。
已知直线方程可以表示为 y = kx + b
以下公式仅考虑 |k|<=1(这种情况下,直线在y轴方向增长的速度小等于在x轴方向的速度)
已知:
y1 = kx1 + b
y2 = kx2 + b
因为
x2 = x1 + 1
所以
y2 = k(x1 + 1) + b
y2 - y1 = k(x1 + 1) + b - (kx1 + b) = k
可以推导出,x,每递增1,y递增k
增量算法
在一个迭代算法中,如果每一步中的x和y值均由上次计算得到的值加上一个增量来得到,这种算法为增量算法。
DDA算法是一种增量算法,在 |k|<=1 的情况下,Δx =1,Δy = k。
实现代码如下
void DrawLine_DDA(int x1,int y1,int x2,int y2){
int dx = x2 - x1;
int dy = y2 - y1;
int step = 0;
if(Math.Abs(dy) > Math.Abs(dx)){
step = Math.Abs(dy);
}
else{
step = Math.Abs(dx);
}
float stepX = 1.0f * dx / step;
float stepY = 1.0f * dy / step;
int i = 0;
float x = x1;
float y = y1;
DrawPoint((int)x, (int)y);
while ((i++)<step){
x += stepX;
y += stepY;
DrawPoint((int)x, (int)(y+0.5f));
}
}
原理:假设一条直线,0
可以设直线方程为 ax + by + c = 0
将点A和点B代入方程式中,可得
a b = y 1 − y 2 x 2 − x 1 \frac{a}{b} = \frac{y_1-y_2}{x_2-x_1} ba=x2−x1y1−y2
那么,可设
a = y1 - y2
b = x2 - x1
c = x1y2 - x2y1
则 F(x) = ax + by + c
F(x) > 0时,点在直线上方
F(x) = 0时,点在直线上
F(x) < 0时,点在直线下方
故而需要判断中点M是否在Q点的上方还是下方,只需要将点M代入到F(x)中,判断计算结果的正负即可。
F(M) = F(Xp+1,Yp+0.5) = a(Xp+1) + b(Yp+0.5) + c
如果F(M) > 0,则中点在直线上方,下一点应取P1,下一中点为(Xp+2,Yp+0.5)
F(M)的增量d = a(Xp+2) + b(Yp+0.5) + c - [a(Xp+1) + b(Yp+0.5) + c] = a
如果F(M) < 0,则中点在直线下方,下一点应取P2,下一中点为(Xp+2,Yp+1.5)
F(M)的增量d = a(Xp+2) + b(Yp+1.5) + c - [a(Xp+1) + b(Yp+0.5) + c] = a+b
d的初始值d0 = F(Xp+1,Yp+0.5) - F(Xp,Yp) = a(Xp+1) + b(Yp+0.5) + c - (axp+byp+c) = a+0.5b
实现代码如下
void DrawLine_MidPoint(int x1,int y1,int x2,int y2){
int dx = x2 - x1;
int dy = y2 - y1;
int a = y1 - y2;
int b = x2 - x1;
int xStep = (b >= 0 ? 1 :-1);
int yStep = a <= 0 ? 1 : -1;
int xStep2 = xStep;
int yStep2 = yStep;
int steps = 0;
//当dy > dx时,需交换a,b
if (Math.Abs(dy) > Math.Abs(dx)){
int temp = -a;
a = -b;
b = temp;
a *= xStep;
b *= yStep;
xStep2 = 0;
steps = Math.Abs(dy);
}
else{
a *= yStep;
b *= xStep;
yStep2 = 0;
steps = Math.Abs(dx);
}
int d = 2 * a + b;
int d1 = 2 * a;
int d2 = 2 * a + 2 * b;
int x = x1;
int y = y1;
DrawPoint(x, y);
int i = 1;
while ((i++)<=steps){
if(d < 0){
x += xStep;
y += yStep;
d += d2;
}
else{
x += xStep2;
y += yStep2;
d += d1;
}
DrawPoint(x, y);
}
}
原理:假设一条直线,0
直线在X = Xp+1轴上的交点为(Xp+1,d1)
d 的实际含义为直线与交点的y值与上一取点y值的长度。当d>1时,d=d - 1
设 e = d - 0.5,可确定以下
e > 0 时,则下一点取(xp+1,xp+1)
e < 0 时,则下一点取(xp+1,xp)
由于e和d只有加减关系,所以e的增量 = d的增量 = k
通常情况下,k为浮点数,而k = Δy/Δx。Δy和Δx均为整数。
可以将关于e的计算乘以Δx,避免浮点运算。
实现代码如下
void DrawLine_Bresenham2(int x0, int y0, int x1, int y1){
int dx = Math.Abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = Math.Abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = (dx > dy ? dx : -dy) / 2;
while (true){
DrawPoint(x0, y0);
if (x0 == x1 && y0 == y1) break;
if (err > -dx){
err -= dy;
x0 += sx;
}
if (err < dy){
err += dx;
y0 += sy;
}
}
}