计算机图形学-直线及圆弧的图形扫描转换及绘制算法

图形学封面

原文来自本人博客网站只是又搬到这里来做一下推广,希望大家喜欢。

使用软件:Microsoft Visual Studio

用DDA算法无交互绘制固定直线

1 创建MFC应用程序。建一个基于单个文档的默认程序。

创建项目

找到MFC应用程序,起名字,选择路径

创建单个文档的程序

2 找到资源视图,打开资源文件后找到Menu文件夹下的IDR_MAINFRAME文件,双击打开它。

找到IDR_MAINFRAME并打开

3 在菜单栏键入自己要设置的菜单项以及菜单子项,并编辑ID值。

创建菜单子项编辑ID

4 为菜单子项创建响应函数,通过建立类向导实现。

找到创建类向导位置

为菜单子项创建响应函数

5 在函数体内写上绘制算法的代码,保存,编译,执行,得到结果。

写DDA算法

执行得到结果

DDA算法如下:

// CMFCGoderyuView 消息处理程序

void CMFCGoderyuView::OnDda()
{
    // 获得设备指针
    CDC *pDC = GetDC();
    // 定义直线两端点和直线颜色(红色)
    int x0 = 100, y0 = 100, x1 = 300, y1 = 200, c = RGB(255, 0, 0); 
    float x, y;
    float dx, dy, k;
    dx = (float)(x1 - x0);
    dy = (float)(y1 - y0);
    k = dy / dx;
    y = y0; x = x0;
    if (fabs(k)<1)
    {
        for (; x <= x1; x++)
        {
            pDC->SetPixel(x, int(y + 0.5), c);
            y = y + k;
        }
    }
    if (fabs(k) >= 1)
    {
        for (; y <= y1; y++)
        {
            pDC->SetPixel(int(x + 0.5), y, c);
            x = x + 1 / k;
        }
    }
    //释放设备指针
    ReleaseDC(pDC);     
}

用中点画线算法交互式绘制直线

因为此处需要实现的是交互式绘制直线,说一下大致思路:

1 当点击中点画线子菜单项时,我们可以在画布上绘制直线。

2 在画布上当鼠标左键按下时,记录鼠标当前对应的坐标点作为直线的起点,鼠标左键松开时记录终点。

3 接着调用中点画线算法进行绘制。

注:说的都是直线,但是画出来有两个端点算是线段,这里不必要纠结这种属于上的问题,理解即可。

创建响应函数步骤和DDA类似,就不贴图啦,照葫芦画瓢做一下。这里需要强调的是交互式。我们除此之外还要创建鼠标左键按下响应函数鼠标左键抬起响应函数。这个创建过程如下:

1 创建类向导,找到CMFCGoderyuView,接着找到WM_LBUTTONDOWN,创建处理程序,名字可以默认。建成之后会跳转到此函数代码位置,代码如下所示:

void CMFCGoderyuView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值

    CView::OnLButtonDown(nFlags, point);
}


void CMFCGoderyuView::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值

    CView::OnLButtonUp(nFlags, point);
}

步骤图如下:

创建鼠标响应函数

你只需要记住这里的point代表着鼠标按下以及鼠标抬起时记录的坐标点即可,其他的不需要太过考虑。

2 鼠标按下的作用只是为了获取直线的起点,故相应程序里只需要将point传给全局变量s_point即可。在此之前要先定义全局变量CPoint s_point

3 在鼠标抬起是将point的值传给e_point,这样两个坐标点就有了,接下来调用中点画线算法就行绘制即可。思想就是和DDA类似,只是将DDA中定义的int x0 = 100, y0 = 100, x1 = 300, y1 = 200换成了s_point=point这样的形式。

最后修改后的代码为:

// 定义中点画线子菜单项的标志判断变量
bool zdhxsign = false;

void CMFCGoderyuView::OnZdhx()
{
    // 点击此菜单项后,将标志位设为真值
    zdhxsign = true;
}

// 定义起点和终点变量
CPoint s_point;
CPoint e_point;

void CMFCGoderyuView::OnLButtonDown(UINT nFlags, CPoint point)
{
    s_point = point;
    CView::OnLButtonDown(nFlags, point);
}


void CMFCGoderyuView::OnLButtonUp(UINT nFlags, CPoint point)
{
    if(zdhxsign)
    {
        e_point = point;
        // 中点画线算法
        CDC *pDC = GetDC();
        // 获取起点和终点横纵坐标值
        int x0 = s_point.x, y0 = s_point.y;
        int x1 = e_point.x, y1 = e_point.y;
        // 定义直线颜色为黑色
        int c = RGB(0, 0, 0);
        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;
        pDC->SetPixel(x, y, c);
        while (x<x1)
        {
            if (d<0)
            {
                x++;
                y++;
                d += d2;
            }
            else
            {
                x++;
                d += d1;
            }
            pDC->SetPixel(x, y, c);
        }
        ReleaseDC(pDC);
    }
    // 绘制直线完成后记得标志位归假
    zdhxsign = false;
    CView::OnLButtonUp(nFlags, point);
}

用Bresenham算法交互式绘制圆形

相信大家对鼠标的交互式已经通过上面中点画线有了一定的了解。下面是同样的道理,交互式绘制圆形,点击子菜单项Bresenham后,再在画布上鼠标左键点一下,得到圆形的圆心坐标,之后会按照给定的半径,通过Bresenham算法绘制圆形。同样的加入bool类型的标志位,再在OnLButtonUP函数内加入Bresenham算法,前提是先加上标志位真假的判断。最终实现绘制圆形。

请熟练掌握创建响应函数这个动作:

创建响应函数

给出代码:

bool bresign = false;

// 子菜单项Bresenham的响应函数
void CMFCGoderyuView::OnBresenham()
{
    // TODO: 在此添加命令处理程序代码
    bresign = true;
}

// 在鼠标左键按下函数内加入Bresenham算法绘制圆形
void CMFCGoderyuView::OnLButtonDown(UINT nFlags, CPoint point)
{
    if (bresign)
    {
        CDC *pDC = GetDC();
        // 在鼠标按下时获取横纵坐标
        int x0 = point.x, y0 = point.y;
        // 给定圆的半径和圆的边界颜色(黑色)    
        int r = 80, c = 0;
        float e;
        int x = 0, y = r;
        e = 3 - 2 * r;
        while (x <= y)
        {
            if (e<0)
            {
                e = e + 4 * x + 6; 
                x++;
            }
            else 
            { 
                e = e + 4 * (x - y) + 10;
                x++; 
                y--; 
            }
            pDC->SetPixel(x + x0, y + y0, c);
            pDC->SetPixel(-x + x0, y + y0, c);
            pDC->SetPixel(-x + x0, -y + y0, c);
            pDC->SetPixel(x + x0, -y + y0, c);
            pDC->SetPixel(y + x0, x + y0, c);
            pDC->SetPixel(-y + x0, x + y0, c);
            pDC->SetPixel(-y + x0, -x + y0, c);
            pDC->SetPixel(y + x0, -x + y0, c);
        }
        ReleaseDC(pDC);
    }
    // 绘制圆形完成后记得标志位归假
    bresign = false;
    CView::OnLButtonDown(nFlags, point);
}

运行图:

Bresenham画圆

总结

啊为了计算机图形学课业的一个实验课,花费了不少功夫来做这篇博客,说实在的技术含量的东西不多,主要是让大家学习熟悉VS的基本操作。只是做了很简单的一个步骤介绍,大家可以思考改进的。比如:

  1. 算法代码都写在了响应函数里,其实为了可读性和维护性,可以创建单独的算法函数,然后在响应函数里调用即可,井井有条。
  2. 在Bresenham算法中,只是对输入圆心坐标做了交互,半径是定死的,也可以思考如何交互式地传递半径,能不能点一下画布后,弹出一个小对话框,让输入半径值,然后提交之后,交互式地画圆呢?这里小小埋一个伏笔,其实这个用到了MFC程序中Dialog对话框的知识了。
  3. 同样地,我该如何动态地选择绘制图形的颜色?等等等等。

当然了,对于算法的原理并没有给出心得,期待我的下一次更新吧!如果你喜欢这篇文章,可以分享给更多人,让大家共同学习。并且如果你善于思考想要与我讨论,可以加我的QQ133077719,记得备注哦~

猜你喜欢

转载自blog.csdn.net/y133077719/article/details/80617190
今日推荐