直线段生成绘制算法

来源:互联网 发布:mac怎么下载office软件 编辑:程序博客网 时间:2024/04/29 14:40

1、DDA直线段生成绘制算法

DDA算法又称数值微分画线法。

根据直线方程y=m*x+b和微分方程dy=m*dx可知:

1)假设(x[i],y[i])是第i步计算生成像素,则第i+1步生成像素(x[i+1],y[i+1])为:x[i+1]=x[i]+dx,y[i+1]=y[i]+m*dx;

2)由于屏空间像素间最小位差|dx|、|dy|<=1,所以,步数steps=Max(|x2-x1|,|y2-y1|)

3)由步数可得每步x、y的增量:dx=(x2-x1)/steps;dy=(y2-y1)/steps;

4)第一个点取(x1,y1),即:x=x1,y=y1一定在直线上;

5)每一步增加一个增量:x=x+dx;y=y+dy,不是整数(即不在直线上的点)四舍五入转换为整数(int)(x+0.5)

void CcgDrawLineView::DDAline(int x1, int y1, int x2, int y2, CDC* pDC)

{

    int steps;

    float m,x,y,xp,yp,dx,dy,averageError;

    CcgDrawLineDoc* pDoc = GetDocument();

    x = x1 + 0.5f;//画点的时候四舍五入

    y = y1 + 0.5f;

    averageError = 0.0f;

    steps = abs(x2-x1) > abs(y2-y1) ? abs(x2-x1) : abs(y2-y1);

    dx = (float) (x2 - x1) / steps;

    dy = (float) (y2 - y1) / steps;

    m = (float) (y2 - y1) / (float) (x2 - x1);

 

    for (int i = 0; i <= steps; i++) {

        pDC->SetPixel((int)x+pDoc->m_wndWidth/2, (int)pDoc->m_wndHeight/2-y, RGB(255, 0, 0));

        // 计算生成点到直线距离

        xp = (y-y1+x/m+m*x1)/(m+1/m);

        yp = -1/m*(xp-x)+y;

       averageError +=sqrtf((x-xp)*(x-xp)+(y-yp)*(y-yp));

        x += dx;

        y += dy;

    }

    pDoc->m_DDALineAverageError += averageError/steps;

    // 计算终点与直线终点误差

    pDoc->m_DDALineEndPointError += sqrtf((x-x2)*(x-x2)+(y-y2)*(y-y2));

}

2Bresenham直线段生成绘制算法

如果x1大于x2,则交换(x1,y1)和(x2,y2)的坐标,使x1始终小于x2。即将八种情况分成四种:

1)斜率>1; 2)0<斜率<1; 3)-1<斜率<0; 4)斜率<-1

1):斜率大于1时,每次描点y一定加一,通过计算(x,y+1)到直线的距离是否小于二分之一来判断x是加一还是不变。

2):0<斜率<1时,每次描点x一定加一,通过计算(x+1,y)到直线的距离是否小于二分之一来判断y是加一还是不变。

3):-1<斜率<0时,每次描点x一定加一,通过计算(x+1,y)到直线的距离是否小于二分之一来判断y是减一还是不变。

4):斜率<-1时,每次描点y一定减一,通过计算(x,y+1)到直线的距离是否小于二分之一来判断x是加一还是不变。

 按照这种思想就有如下代码:

void CcgWXXDrawLineView::Bline(int x1, int y1, int x2, int y2, CDC* pDC)
{
int x, y;
if (x1 > x2)//令x2大于x1,即x只有+1,没有-1
{
x = x1;
x1 = x2;
x2 = x;
y = y1;
y1 = y2;
y2 = y;
}
int dx = x2 - x1, dy = y2 - y1;
float m = dy*1.0 / dx;
CcgWXXDrawLineDoc *pDoc = GetDocument();     // 获取Doc类指针
if (m>0&&m<1)//x++,y++或不变
{
int e = 2 * dy - dx;
x = x1;
y = y1;
while (x<x2)

pDC->SetPixel(x + pDoc->wm_width / 2, y + pDoc->wm_height/2, RGB(0, 255, 0));
if (e >= 0)
{
e = e - 2 * dx;
y = y + 1;
}
e = e + 2 * dy;
x = x + 1;
}
pDC->SetPixel(x + pDoc->wm_width / 2, y + pDoc->wm_height/2, RGB(0, 255, 0));
}
else if (m>-1&&m<0)//x++,y--或不变
{
int e = 2 * dy +dx;
x = x1;
y = y1;
while (x<x2)
{
pDC->SetPixel(x + pDoc->wm_width / 2, y + pDoc->wm_height/2, RGB(0, 255, 0));
if (e <= 0)
{
e = e + 2 * dx;
y = y - 1;
}
e = e + 2 * dy;
x = x + 1;
}
pDC->SetPixel(x + pDoc->wm_width / 2, y + pDoc->wm_height / 2, RGB(0, 255, 0));
}
else if (m > 1)//y++,x++或不变
{
int e = 2 * dx - dy;
x = x1;
y = y1;
while (y<y2)
{
pDC->SetPixel(x + pDoc->wm_width / 2, y + pDoc->wm_height/2, RGB(0, 255, 0));
if (e >= 0)
{
e = e - 2 * dy;
x = x + 1;
}
e = e + 2 * dx;
y = y + 1;
}
pDC->SetPixel(x + pDoc->wm_width / 2, y + pDoc->wm_height / 2, RGB(0, 255, 0));
}
else if(m<-1)//y--,x++或不变
{
int e = 2 * dx - dy;
x = x1;
y = y1;
while (y>y2)
{
pDC->SetPixel(x + pDoc->wm_width / 2, y + pDoc->wm_height / 2, RGB(0, 255, 0));
if (e >= 0)
{
e = e + dy;  //理论上,这里应该是e=e+2*dy,可是e=e+2*dy画出来的线不对,e = e + dy才对,不明白为什么。大神们有空的话帮忙看看吧委屈

x = x + 1;
}
e = e +  dx;
y = y - 1;
}
pDC->SetPixel(x + pDoc->wm_width / 2, y + pDoc->wm_height / 2, RGB(0, 255, 0));
}
}


这样用把各种条件一一列出来太low了,能不能把各种可能再整合一下呢?将上述思想归纳一下,即有下述函数:

void CcgDrawLineView::Bline(int x1, int y1, int x2, int y2, CDC* pDC)

{

    float m,xp,yp,averageError;

    CcgDrawLineDoc* pDoc = GetDocument();

    int x,y,dx,dy,e,xSign,ySign,interChange = 0;

    averageError = 0.0f;

    m = (float) (y2 - y1) / (float) (x2 - x1); 

    dx = abs(x2 - x1);

    dy = abs(y2 - y1);

    if (dx < dy) {

        int temp;

        interChange = 1;

        temp = dx;  dx = dy;  dy = temp;

    }

    xSign = (x2 >= x1) ? 1 : -1;

    ySign = (y2 >= y1) ? 1 : -1;

    x = x1;

    y = y1;

    e = 2*dy - dx;

    for (int i = 0; i <= dx; i++) {

        pDC->SetPixel(x+pDoc->m_wndWidth/2, pDoc->m_wndHeight/2-y, RGB(0, 0, 255));

        // 计算生成点到直线距离

        xp = (y-y1+x/m+m*x1)/(m+1/m);

        yp = -1/m*(xp-x)+y;

        averageError +=sqrtf((x-xp)*(x-xp)+(y-yp)*(y-yp));

        if (e > 0) {

            e = e - 2*dx;

            if (interChange)  x += xSign;

            else                      y += ySign;

        }

        if (interChange)  y += ySign;

        else                      x += xSign;

        e = e + 2*dy;

    }

    pDoc->m_BLineAverageError += averageError/dx;

     // 计算终点与直线终点误差

    pDoc->m_BLineEndPointError += sqrtf((x-x2)*(x-x2)+(y-y2)*(y-y2));

}

3中点分割直线段生成绘制算法

利用递归原理,将(x1,y1)和(x2,y2)两点间的线段不断二分,直到分成的子线段的两个端点的距离小于一个像素,然后对子线段进行描绘。

即:如果(x1,y1)和(x2,y2)的距离小于一个像素,则描点;

    否则取(x1,y1)和(x2,y2)的中点(x,y),调用直线段生成算法 

(此函数自身)描绘直线段(x1,y1)--(x,y)和(x,y)--(x2,y2)。

//中点分割直线段生成绘制算法

void CcgWXXDrawLineView::Midpointline(float x1, float y1, float x2, float y2, CDC* pDC)

{

    float x = (x1 + x2) / 2, y = (y1 + y2) / 2;

    if (fabs(x1 - x2) <= 1 && fabs(y1 - y2) <= 1)

    {

        CcgWXXDrawLineDoc *pDoc = GetDocument();     // 获取Doc类指针

        pDC->SetPixel((int)(x + 0.5) + pDoc->wm_width / 2, (int)(y + 0.5) + pDoc->wm_height / 2, RGB(255, 0, 0));//输出绘制像素

        return;

    }

    else

    {

        Midpointline(x1, y1, x, y, pDC);

        Midpointline(x, y, x2, y2, pDC);

    }

}

0 0
原创粉丝点击