当前位置:   article > 正文

三种直线段的扫描转换算法

直线段的扫描转换算法

直线段的扫描转换算法

确定最佳逼近于直线的一组象素,且按扫描线顺序,对这些象素进行写操作

三个常用算法:

  • 数值微分法(DDA)
  • 中点画线法
  • Bresenham算法

数值微分

基本思想

知道了两个端点 P 0 ( x 0 , y 0 ) , P 1 ( x 1 , y 1 ) P_0(x_0,y_0),P_1(x_1,y_1) P0(x0,y0),P1(x1,y1),则可以求出直线段的斜率 k k k,根据公式 y = k x + b y=kx+b y=kx+b来计算响应y点的坐标,取像素点 ( x , ( round ( y ) ) ) (x,(\text{round}(y))) (x,(round(y))) 为当前点的坐标

评价:方法直观,效率低

增量算法

在一个迭代算法中,每一步的x,y值使用前一步的值加上一个增量来获得

计算 y i + 1 = y i + k Δ x y_{i+1}=y_i+k\Delta x yi+1=yi+kΔx,当 Δ x = 1 \Delta x =1 Δx=1时, y i + 1 = y i + k y_{i+1}=y_i+k yi+1=yi+k

即:当x每递增1,y递增k(即直线斜率)

  • 由此注意上述分析的算法仅适用于|k| ≤1的情形。在这种情况下,x每增加1,y最多增加1
  • 当 |k| >1时,必须把x,y位置互换
void DDALine(int x0,int y0,int x1,int y1,int color)     
{
    int x;							
	float dx, dy, y, k;						
	dx= x1-x0, dy=y1-y0;					   
	k=dy/dx, y=y0;	 					   
	for (x=x0; x<=x1, x++)					   
    {  drawpixel (x, int(y+0.5), color);		   
	   y=y+k;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

例子

注意这里额外加了0.5,就可以省掉round运算了

image-20220213192556035

中点画线法

基本思想

设当前像素点为 ( x p , y p ) (x_p,y_p) (xp,yp),下一个像素点为右侧 P 1 P_1 P1或右上角 P 2 P_2 P2两个点中的一个,设 M = ( x p + 1 , y p + 0.5 ) M=(x_p+1,y_p+0.5) M=(xp+1,yp+0.5) P 1 , P 2 P_1,P_2 P1,P2的中点,Q为理想直线与 x = x p + 1 x=x_p+1 x=xp+1垂线的交点。将Q与M的y坐标值进行比较

  • 当M在Q点的下方,则右上角的 P 2 P_2 P2为下一个像素点
  • 当M在Q点的上方,则右侧的 P 1 P_1 P1为下一个像素点

构造判别式:
d = F ( M ) = F ( x p + 1 , y p + 0.5 ) = a ( x p + 1 ) + b ( y p + 0.5 ) + c

d=F(M)=F(xp+1,yp+0.5)=a(xp+1)+b(yp+0.5)+c
d=F(M)=F(xp+1,yp+0.5)=a(xp+1)+b(yp+0.5)+c
其中 a = y 0 − y 1 , b = x 1 − x 0 , c = x 0 y 1 − x 1 y 0 a=y_0-y_1,b=x_1-x_0,c=x_0y_1-x_1y_0 a=y0y1,b=x1x0,c=x0y1x1y0

  • 当d<0,M在L(Q点)下方,取右上方 P 2 P_2 P2为下一个象素
  • 当d>0,M在L(Q点)上方,取右侧 P 1 P_1 P1为下一个象素
  • 当d=0,选 P 1 P_1 P1 P 2 P_2 P2均可,约定取 P 1 P_1 P1为下一个象素

增量算法

d是 x p , y p x_p,y_p xp,yp的线性函数,因此可以采取增量计算,提高运算效率

  • 若当前像素处于 d ≥ 0 d\ge0 d0的情况,则去右侧像素 P 1 ( x p + 1 , y p ) P_1(x_p+1,y_p) P1(xp+1,yp),要判断下一个像素位置,应计算

    d 1 = F ( x p + 2 , y p + 0.5 ) = a ( x p + 2 ) + b ( y p + 0.5 ) = d + a d_1=F(x_p+2, y_p+0.5)=a(x_p+2)+b(y_p+0.5)=d+a d1=F(xp+2,yp+0.5)=a(xp+2)+b(yp+0.5)=d+a;增量为a

  • d < 0 d\lt0 d<0时,则要取右上方像素 P 2 ( x o + 1 , y p + 1 ) P_2(x_o+1,y_p+1) P2(xo+1,yp+1),要判断下一个像素,应计算

    $d_2= F(x_p+2, y_p+1.5)=a(x_p+2)+b(y_p+1.5)+c=d+a+b ; 增 量 为 ;增量为 a+b$

具体算法:

  • 画线从 ( x 0 , y 0 ) (x_0,y_0) (x0,y0)开始,d的初值

    d 0 = F ( x 0 + 1 , y 0 + 0.5 ) = F ( x 0 , y 0 ) + a + 0.5 b = a + 0.5 b d_0=F(x_0+1, y_0+0.5)=F(x_0, y_0)+a+0.5b =a+0.5b d0=F(x0+1,y0+0.5)=F(x0,y0)+a+0.5b=a+0.5b

    a = y 0 − y 1 , b = x 1 − x 0 a=y_0-y_1,b=x_1-x_0 a=y0y1,b=x1x0

  • 可以使用2d代替d来摆脱小数,提高运算效率

  • d 0 = 2 a + b , d 1 = 2 a , d 2 = 2 a + 2 b d_0=2a+b, d_1=2a, d_2=2a+2b d0=2a+b,d1=2a,d2=2a+2b,我们有如下算法

void Midpoint Line (int x0,int y0,int x1, int y1,int color)
{   int a, b, d1, d2, d, x, y;
    a=y0-y1, b=x1-x0, d=2*a+b;
    d1=2*a, d2=2* (a+b);
    x=x0, y=y0;
    drawpixel(x, y, color);
    while (x<x1){
     	if (d<0){
            // d<0,应该加增量a+b,这里是d2
            x++, y++, d+=d2;
    	}else {
            // d>=0 应该加增量a,这里是d1
     		x++, d+=d1;
        }
     	drawpixel (x, y, color);
    }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

例子

先计算 d 0 , d 1 , d 2 d_0,d_1,d_2 d0,d1,d2,然后写成左下角的形式

  1. 假设从0,0开始,当前d大于0,因此应该加d1得到第二步的d,且y不自增
  2. 到第二步,当前d小于0,应该加d2得到第三步的d,且y要加一
  3. 到第三步,当前d大于0,应该加d1得到第四步的d,且y不自增,
  4. 重复下去直到最后一个点

image-20220213194219867

Bresenham算法

基本思想

设直线方程为 y i + 1 = y i + k ( x i + 1 − x i ) = y i + k y_{i+1}=y_i+k(x_{i+1}-x_i)=y_i+k yi+1=yi+k(xi+1xi)=yi+k,其中 k = d y / d x k=dy/dx k=dy/dx,因为直线的起始点在像素中心,所以误差项d的初值d0=0

X的下标每增加1,d的值相应递增直线的斜率k,即d=d+k。一旦 d ≥ 0.5 d\ge0.5 d0.5,就把他减去1

  • d ≥ 0.5 d\ge0.5 d0.5时,最接近于当前像素的右上方像素 ( x i + 1 , y p + 1 ) (x_{i+1},y_{p+1}) (xi+1,yp+1)
  • d < 0.5 d\lt0.5 d<0.5时,更接近于右侧的像素 ( x i + 1 , y i ) (x_{i+1},y_i) (xi+1,yi)

为了方便计算,令 e = d − 0.5 e=d-0.5 e=d0.5,e的初值为-0.5,增量为k(这样比较时候直接与0比较即可)

  • e ≥ 0 e\ge0 e0时,最接近于当前像素的右上方像素 ( x i + 1 , y p + 1 ) (x_{i+1},y_{p+1}) (xi+1,yp+1)
  • e < 0 e\lt0 e<0时,更接近于右侧的像素 ( x i + 1 , y i ) (x_{i+1},y_i) (xi+1,yi)
void Bresenhamline (int x0,int y0,int x1, int y1,int color)
{   int x, y, dx, dy;
    float k, e;
    dx = x1-x0, dy = y1- y0, k=dy/dx; 
    e=-0.5, x=x0, y=y0;
    for (i=0; idx; i++){
        drawpixel (x, y, color);
        x=x+1,e=e+k;
        if (e>=0){ 
        	y++, e=e-1;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

例题

首先计算出斜率k,然后写成左下角的形式

  1. 第一步从p0出发,当前e小于0,因此y不自增,e+=0.4
  2. 第二步:当前e小于0,因此y不自增,e+=0.4
  3. 第三步:当前e大于0,因此y自增,e= e + 0.4 - 1
  4. 后面一直这样计算下去,注意当e大于0时不止要减1还要再加0.4

image-20220213195111336

总结

  • DDA方法:根据直线方程 X → Y X\rightarrow Y XY
  • 其他两种的原理: 0 < K < 1 0<K<1 0<K<1时,下一个像素只能是当前像素的右侧点或者右上方点
    • 中点画线法:将中点坐标代入方程得到判别式,然后根据判别式的结果确定下一次的y是在右侧还是右上方
    • Bresenham算法:计算误差项,从0开始,每次增加k,然后判断是否大于0.5,后面化成e,只需要判断是否大于0即可
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/394983
推荐阅读
相关标签
  

闽ICP备14008679号