直线和圆的扫描转化算法
给定两点 A ( x a , y a ) , B ( x b , y b ) , ( x a ≤ x b ) ~A(x_a,y_a),B(x_b,y_b),(x_a\le x_b)~ A(xa,ya),B(xb,yb),(xa≤xb) ,在像素整点上画出该线段。
我们不妨先假设 0 ≤ k ≤ 1 ~0\le k\le1 0≤k≤1,我们探讨如下三种算法:
数值微分算法DDA(Digital Differential Analyzer)
显然直线方程 A B : y − y a x − x a = y b − y a x b − x a ~AB:\displaystyle\frac{y-y_a}{x-x_a}=\frac{y_b-y_a}{x_b-x_a} AB:x−xay−ya=xb−xayb−ya
转化为直线 y = k x + b ( k = y b − y a x b − x a , b = y a − k x a = x b y a − x a y b x b − x a ) ~\displaystyle y=kx+b~~(k=\frac{y_b-y_a}{x_b-x_a},b=y_a-kx_a=\frac{x_by_a-x_ay_b}{x_b-x_a}) y=kx+b (k=xb−xayb−ya,b=ya−kxa=xb−xaxbya−xayb),
我们可以枚举 x = x a , x a + 1 , x a + 2 , … , x b x=x_a,x_a+1,x_a+2,\dots,x_b~ x=xa,xa+1,xa+2,…,xb 得要相应的 y = k x + b ~y=kx+b y=kx+b,计算量为一次加法一次乘法和一次取整运算。如何优化?
用增量思想代替原式中的乘法,递推计算 y i = k x i + b ~y_i=kx_i+b~ yi=kxi+b ,有
y i + 1 = k x i + 1 + b = k ( x i + 1 ) + b = ( k x i + b ) + k = y i + k y_{i+1}=kx_{i+1}+b=k(x_i+1)+b=(kx_i+b)+k=y_i+k yi+1=kxi+1+b=k(xi+1)+b=(kxi+b)+k=yi+k
我们得到算法的递推式:
P i ( x i , y i ) = { ( x a , y a ) i = 1 ( x i − 1 + 1 , y i − 1 + k ) i ≥ 2 P_i(x_i,y_i)=\left\{\begin{aligned} &(x_a,y_a)&i=1\\ &(x_{i-1}+1,y_{i-1}+k)&i\ge2 \end{aligned}\right. Pi(xi,yi)={
(xa,ya)(xi−1+1,yi−1+k)i=1i≥2
完整的代码如下(包括 ∣ k ∣ ≥ 1 |k|\ge1 ∣k∣≥1的部分)
void DDA(int x1,int y1,int x2,int y2)
{
int dx=std::abs(x1-x2),dy=std::abs(y1-y2);
// if |k| greater than 1, enumerate x and calculate y=kx+b
if(dx>dy)
{
// we assume that x1<=x2
if(x1>x2) std::swap(x1,x2),std::swap(y1,y2);
double k=double(y2-y1)/(x2-x1),y=y1;
// x_{i+1}=x_i+1,y_{i+1}=y_i+k for x \in [x1+1,x2]
for(int x=x1;x<=x2;x++,y+=k)
{
// draw current pixel
draw_pixel(x,int(y));
}
}
// if |k| less than 1, enumerate y
else
{
// same as above
if(y1>y2) std::swap(y1,y2),std::swap(x1,x2);
double k=double(x2-x1)/(y2-y1),x=x1;
for(int y=y1;y<=y2;y++,x+=k)
{
draw_pixel(int(x),y);
}
}
}
中点画线法
将直线转化为一般式 f ( x , y ) = A x + B y + C = 0 ~f(x,y)=~Ax+By+C=0~ f(x,y)= Ax+By+C=0 ,即 ( y a − y b