直线画线算法描述

238 阅读3分钟

​本文已参与「新人创作礼」活动,一起开启掘金创作之路。

 一、直线段的扫描转换算法

1.描述

  数学上画一条直线需要无穷多的点,但计算机光栅显示器由像素点构成,故用屏幕上的点进行逼近。

  为了在显示器上用离散的像素点逼近这条直线,首先需要知道这些像素点的(x,y)坐标

用的是斜截式方程:    y = kx+b​  其中  k = \frac{y1 - y0}{x1 - x0} (x1 \neq x0)

画直线的过程:已知起点P0(x0,y0)与k和b,然后让x1 = x0 +1,进而求y1,然后用同样的方法求y2,y3......

[注]:像素只能是整数,故为了减少误差,求出的y值采取四舍五入,如假设求出P1(1.8,0.8)则转化成(2,1)。

2.算法效率的提高

     由于求y只是一个乘法和加法,且计算机做加法快,如果能够将乘法用一定的方法转换成加法,则可以提升算法的效率。下面是三个基于乘法换成加法这一思想的常用改进方法。

2.1数值微分法(DDA)

a.算法的基本思想

    由于x每一次最小的变化是以一个像素点为单位,故我们可以得到在这条直线上的相邻两个坐标的递推式:y_{k+1} = y_{k} + k

【注】:当|k| < 1时,点取得较密集,但是,当|k| >>1 时,这个直线上的点就很稀疏了。

b.算法的改进

一般情况下k与y都是小数,而且每一步运算都要对y进行四舍五入取整,唯一改进途径是把浮点运算变成整数加法。

2.2 中点画线法

a基本思想

采用直线的一般是f(x,y) = ax + by +c = 0;根据点与直线的关系:

                         f(x0,y0) > 0  则(x0,y0)在直线上方

                         f(x0,y0) < 0  则(x0,y0)在直线下方

                         f(x0,y0)  = 0  则(x0,y0)在直线上

我们记 f(x0+1,y0+0.5) = d,根据: 若 d<0,点在直线下方,则下一个点取(x0+1,y0+1);否则,取(x0+1,y0);

用式子来描述就是

b中点画线算法的改进

根据上面的式子看出,中点画线法计算量明显比DDA多,那么如何改进?能不能也采用增量的方式弄出个d_{i+1} = d_{i} + x​?

我们只是想知道下一个点是(x_{i+1},y_{i+1})​还是(x_{i+1},y_{i})

下面贴出推导:

                         d_{i} = f(x_{i+1},y_{i+0.5}) = A(x_{i} + 1) + B(y_{i} + 0.5) +c

    当d_{i}​<0时

                          d_{i+1} = f(x_{i+2},y_{i+1.5}) = A(x_{i} + 2) + B(y_{i} + 1.5) +c​ =  d_{i}​ + A + B

    当d_{i}​>0时

                          d_{i+1} = f(x_{i+2},y_{i+0.5}) = d_{i} +A

也就是说,根据d_{i}​的正负可以判断选择哪个点,根据这个递推关系d_{i+1}=f(d_{i})​可以判断下一个点选啥。

那么初始值d_{0}​:      d_{0} = f(x_{0}+1,y_{0}+0.5) = Ax_{0} + By_{0} + C +A + 0.5B = A + 0.5B

综上:   ​     

c.总结:

采用直线一般式

只通过判断中点的符号,最终可以只进行整数加法

2.3 Bresenham算法

a.算法基本思想

  由于上述算法都限定在了直线的范围,BBresenham算法相当于中点画线算法的扩展。

  该算法思想是通过各行各列像素中心构造一组虚拟网络网格线,按照直线起点到终点的顺序,计算直线与个垂直网格线的交点,然后根据误差项的符号判断该列像素中与此交点最近像素。

用图表示就是这样:

每次x+1,y要吗加1要么加0,取决于实际直线与最近光栅网格点的距离,这个距离的最大误差为0.5(当实际直线正好在

y_{i}​与y_{i+1}​的中点时)。

误差项d_{i+1}​ = d_{i}​+k,其中由图可以看出d_{0}​为0。

 

当d>1时,为保证d的相对性,且在0、1之间,令d = d - 1。

将此算法进行改进到整数加法:

可以理解为:e>0,y方向递增1;e<0,y方向不递增

                      e = 0 时,可任取上、下光栅点显示

                      e初 = -0.5,每走一步有e = e+k,if (e>0) then e=e-1

改进2:由于算法中只用到误差项的符号,于是可以用e2△x来替换e。


e初 = -△x,
每走一步有:e = e+2△y。(k = 2△xk = 2△y)
if (e>0) then e = e-2△x  此时取(x+1,y+1),否则取(x+1,y)

另一种思想:

举个例子:

      Bresenham算法和DDA算法很像,都是加斜率,但Bresenham只是判断符号决定上下两个点,无需求出新y后取整,故应用范围广泛。

任意斜率中点画线算法matlab实现

 首先推导各个斜率下d_{i}​与d_{i+1}​以及d_{0}​的关系

当   0<k<1时

             d_{i} = f(x_{i}+1,y_{i}+0.5) = A(x_{i} + 1) + B(y_{i} + 0.5) +c

             if  d_{i}<0

                  d_{i+1} = f(x_{i}+2,y_{i}+1.5) = A(x_{i} + 2) + B(y_{i} + 1.5) +c\Rightarrow {i+1} = d{i} + A +B

            else

                 d_{i+1} = f(x_{i}+2,y_{i}+0.5) = A(x_{i} + 2) + B(y_{i} + 0.5) +c\Rightarrow {i+1} = d{i} + A

          其中 d0 = A +0.5B

当   k>1 时(y轴加一,看x轴的正负)画图一目了然

             d_{i} = f(x_{i}+0.5,y_{i}+1) = A(x_{i} + 0.5) + B(y_{i} + 1) +c

             if  d_{i}<0

                  d_{i+1} = f(x_{i}+0.5,y_{i}+2) = A(x_{i} + 0.5) + B(y_{i} + 2) +c\Rightarrow {i+1} = d{i} +B

            else

                 d_{i+1} = f(x_{i}+1.5,y_{i}+2) = A(x_{i} + 1.5) + B(y_{i} + 2) +c\Rightarrow {i+1} = d{i} + A +B

         其中 d0 = 0.5A +B

当   k>-1 且k<0 时

             d_{i} = f(x_{i}+1,y_{i}-0.5) = A(x_{i} + 1) + B(y_{i} - 0.5) +c

             if  d_{i}<0

                  d_{i+1} = f(x_{i}+2,y_{i}-0.5) = A(x_{i} + 2) + B(y_{i} -0.5) +c\Rightarrow {i+1} = d{i} +A

            else

                 d_{i+1} = f(x_{i}+2,y_{i}-1.5) = A(x_{i} + 2) + B(y_{i} -1.5) +c\Rightarrow {i+1} = d{i} + A -B

         其中 d0 = A - 0.5B

当k<-1时这里就不再推了,大家可以自己去尝试,

function midpointline(x0,y0,x1,y1)
     k = (y1-y0)/(x1-x0);
     x=x0;y=y0;
     b=x1-x0;a=y0 - y1; %这个式子是用已知直线两个点,求一般式画出来的
    if(k<1 && k>0)
        grid on;hold on;
        plot(x,y,'o');
        d0=2*a+b;
        d1=2*a;
        d2=2*(a+b);
        while (x<x1)
            if(d0 >=0) 
                x=x+1;
                d0 =d0+d1;
             elseif(d0 < 0)
                 x=x+1;y=y+1;d0=d0+d2;
             end
             hold on;
             plot(x,y,'o');
        end
    elseif(k>1)
        grid on;hold on;
        plot(x,y,'o');
        d0=a+2*b;
        d1=2*(a+b);
        d2=2*b;
        while (y<y1)
            if(d0 >=0)
                x = x + 1;
                y = y + 1;
                d0 =d0 + d1;
             elseif(d0 < 0)
                 y=y+1;d0=d0+d2;
             end
             hold on;
             plot(x,y,'o');
        end
    elseif(k>-1 && k<0)
        grid on;hold on;
        plot(x,y,'o');
        d0=2*a-b;
        d1=2*(a-b);
        d2=2*a;
        while (x<x1)
            if(d0 >=0)
                x = x + 1;
                y = y - 1;
                d0 =d0 + d1;
             elseif(d0 < 0)
                 x=x+1;d0=d0+d2;
             end
             hold on;
             plot(x,y,'o');
        end
    elseif(k<-1)
        grid on;hold on;
        plot(x,y,'o');
        d0=a-2*b;
        d1=-2*b;
        d2=2*(a-b);
        while (x<x1)
            if(d0 >=0)
                y = y - 1;
                d0 =d0 + d1;
             elseif(d0 < 0)
                 x=x+1;y=y-1;d0=d0+d2;
             end
             hold on;
             plot(x,y,'o');
        end
    end
    end