计算机图形学——利用MFC库绘制直线(DDA算法和Bresenham算法)

问题描述

利用MFC库实现直线的绘制(分别使用DDA算法和Bresenham算法)

求解思路

创建MFC APP项目,选择Dialog Based模板,如下:

在这里插入图片描述

1. 对话框设计

利用TOOL BOX中的工具修改主对话框(如果TOOL BOX没有的话,在view中打开)

  • 添加两个绘图框,分别对应DDA算法和Bresenham算法,ID为IDC_STATIC_DRAW_AREA1IDC_STATIC_DRAW_AREA2

  • 添加两个按钮,ID为IDC_BUTTON_DDAIDC_BUTTON_BRESENHAM

  • 添加四个文本编辑框便于输入坐标值,ID为IDC_EDIT_START1, IDC_EDIT_START2, IDC_EDIT_END1, IDC_EDIT_END2

  • 添加静态文本框

注:修改ID需要选中对象,点击属性,然后在属性中修改ID

效果如下图:

在这里插入图片描述

2. 算法设计

DDA算法

算法描述略

Bresenham算法

  1. 获取起点和终点坐标:首先获取直线的起点坐标 (x0, y0) 和终点坐标 (x1, y1)

  2. 计算dx和dy:计算 dx = x1 - x0dy = y1 - y0

  3. 计算p0初始值p_0 = 2*dy - dx,这是初始的决策参数。p_k 用于决定下一个要绘制的像素的位置

  4. Bresenham主循环:在每一步中,根据 p_k 的值来决定下一个要绘制的像素

  • 如果 p_k 小于0,则下一个像素位于 (x_k + 1, y_k),并且 p_{k+1} = p_k + 2*dy

  • 如果 p_k 大于等于0,则下一个像素位于 (x_k + 1, y_k + 1),并且 p_{k+1} = p_k + 2*dy - 2*dx

  1. 重复绘制直线:重复步骤4的过程,共计算 dx 次,以绘制整条直线

程序代码

需要修改LineDlg.cppLineDlg.h两个文件

注意:在点击按钮获取坐标后,需要将坐标映射到两个绘图框内,并在坐标超出绘图框范围时给出提示

1. LineDlg.cpp

主要需要添加四个函数,分别为两种算法绘制的函数和对应的按钮点击函数,实现代码如下:

DDA算法

// 点击DDA按钮将执行的操作
void CLineDlg::OnBnClickedButtonDDA() {
    // 从编辑框中获取坐标
    CString strX0, strY0, strX1, strY1;
    // 起点坐标
    GetDlgItemText(IDC_EDIT_START1, strX0);
    GetDlgItemText(IDC_EDIT_START2, strY0);
    // 终点坐标
    GetDlgItemText(IDC_EDIT_END1, strX1);
    GetDlgItemText(IDC_EDIT_END2, strY1);

    int startX = _ttoi(strX0);
    int startY = _ttoi(strY0);
    int endX = _ttoi(strX1);
    int endY = _ttoi(strY1);

    // 获取绘图框的边界矩形
    CRect rectDrawArea1;
    GetDlgItem(IDC_STATIC_DRAW_AREA1)->GetClientRect(rectDrawArea1);

    // 映射坐标到绘图框的坐标系
    startX -= rectDrawArea1.left;
    startY -= rectDrawArea1.top;
    endX -= rectDrawArea1.left;
    endY -= rectDrawArea1.top;

    // 在绘制直线之前检查坐标是否在绘图框内
    if (startX >= 0 && startX < rectDrawArea1.Width() &&
        startY >= 0 && startY < rectDrawArea1.Height() &&
        endX >= 0 && endX < rectDrawArea1.Width() &&
        endY >= 0 && endY < rectDrawArea1.Height()) {
        // 坐标在绘图框内,可以绘制直线
        // 调用DDA函数
        DrawLineDDA(startX, startY, endX, endY);
    } else {
        // 坐标超出了绘图框的边界,错误提示
        CString message = _T("坐标超出绘图框的边界,请重新输入");
        CString caption = _T("错误");
        MessageBox(message, caption, MB_ICONERROR);
    }
}
// DDA算法
void CLineDlg::DrawLineDDA(int x1, int y1, int x2, int y2) {
    CDC *pDC = GetDlgItem(IDC_STATIC_DRAW_AREA1)->GetDC();

    int dx = x2 - x1;
    int dy = y2 - y1;
    int L = abs(dx) > abs(dy) ? abs(dx) : abs(dy);

    float dx = static_cast<float>(dx) / static_cast<float>(L);
    float dy = static_cast<float>(dy) / static_cast<float>(L);

    float x = static_cast<float>(x1);
    float y = static_cast<float>(y1);

    pDC->MoveTo(x1, y1);

    for (int i = 0; i < L; i++) {
        x += dx;
        y += dy;

        pDC->LineTo(static_cast<int>(x), static_cast<int>(y));
    }
}

Bresenham算法

// Bresenham算法
void CLineDlg::DrawLineBresenham(int x1, int y1, int x2, int y2) {

        // 计算dx和dy
        int dx = abs(x2 - x1);
        int dy = abs(y2 - y1);

        // 计算步长方向,根据步长方向来决定递增还是递减
        int stepX = (x1 < x2) ? 1 : -1;
        int stepY = (y1 < y2) ? 1 : -1;

        // 初始化p0和起始点
        int p = 2 * dy - dx;
        int x = x1;
        int y = y1;

        // 获取IDC_STATIC_DRAW_AREA2
        CStatic* pStaticDrawArea2 = (CStatic*)GetDlgItem(IDC_STATIC_DRAW_AREA2);
        CDC* pDC = pStaticDrawArea2->GetDC();

        // 绘制起点
        pDC->SetPixel(x, y, RGB(0, 0, 0)); // 在指定位置绘制像素点

        // 循环绘制直线
        for (int k = 0; k < dx; k++)
        {
            // 根据pk决定下一个点的位置
            if (p < 0)
            {
                x += stepX;
                p += 2 * dy;
            }
            else
            {
                x += stepX;
                y += stepY;
                p += 2 * (dy - dx);
            }

            // 绘制像素点
            pDC->SetPixel(x, y, RGB(0, 0, 0));
        }

        // 释放设备
        pStaticDrawArea2->ReleaseDC(pDC);
}
// 点击Bresenham按钮将执行的操作
void CLineDlg::OnBnClickedButtonBresenham() {
    // 从编辑框中获取坐标
    CString strX0, strY0, strX1, strY1;
    // 起点坐标
    GetDlgItemText(IDC_EDIT_START1, strX0);
    GetDlgItemText(IDC_EDIT_START2, strY0);
    // 终点坐标
    GetDlgItemText(IDC_EDIT_END1, strX1);
    GetDlgItemText(IDC_EDIT_END2, strY1);

    int startX = _ttoi(strX0);
    int startY = _ttoi(strY0);
    int endX = _ttoi(strX1);
    int endY = _ttoi(strY1);

    // 获取绘图框的边界矩形
    CRect rectDrawArea2;
    GetDlgItem(IDC_STATIC_DRAW_AREA2)->GetClientRect(rectDrawArea2);

    // 映射坐标到绘图框的坐标系
    startX -= rectDrawArea2.left;
    startY -= rectDrawArea2.top;
    endX -= rectDrawArea2.left;
    endY -= rectDrawArea2.top;

    // 在绘制直线之前检查坐标是否在绘图框内
    if (startX >= 0 && startX < rectDrawArea2.Width() &&
        startY >= 0 && startY < rectDrawArea2.Height() &&
        endX >= 0 && endX < rectDrawArea2.Width() &&
        endY >= 0 && endY < rectDrawArea2.Height()) {
        // 坐标在绘图框内,可以绘制直线
        // 调用Bresenham函数
        DrawLineBresenham(startX, startY, endX, endY);
    } else {
        // 坐标超出了绘图框的边界,错误提示
        CString message = _T("坐标超出绘图框的边界,请重新输入");
        CString caption = _T("错误");
        MessageBox(message, caption, MB_ICONERROR);
    }
}

2. LineDlg.h

主要需要增加cpp文件中使用的几个函数(添加到public属性中),见下图红框中的四个函数

在这里插入图片描述

实验结果

运行整个项目后,弹出对话框

分别在文本框中输入起点和终点的坐标,并点击DDA或Bresenham按钮,相应的直线就会在对应的绘图框中展示,如下图:

在这里插入图片描述

如果输入的坐标超出了绘图框的限制,则弹出错误提示,如下图:

在这里插入图片描述

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
直线绘制计算机图形学的基本操作之一。利用DDA算法Bresenham算法Bresenham算法可以实现直线绘制,特别是在斜率k介于0和1之间的情况下。 首先来看DDA算法DDA算法是Digital Differential Analyzer的缩写,它基于直线的斜率来绘制直线。对于给定的起点和终点,我们可以根据斜率k来决定x和y方向上的移动步长。从起点开始,我们沿x方向以步长1前进,然后计算出对应的y坐标。如果计算得到的y坐标大于等于实际的y坐标,则向下取整得到最终的y坐标;否则向上取整得到最终的y坐标。以此类推,我们可以按照步长绘制直线。 接下来是Bresenham算法Bresenham算法是对Bresenham算法的一种改进,可以更加高效地绘制直线。该算法利用直线上的点和对称性质,通过递推计算来确定绘制直线时x和y方向上的移动距离。具体算法过程较为复杂,可以通过逐步计算点的位置来绘制直线。 最后是Bresenham算法,它是绘制直线的一种常用算法,特别适用于低分辨率设备。该算法通过计算绘制点到理想直线的距离来选择最接近理想直线的像素点进行绘制。具体算法过程同样较为复杂,需要计算并判断每个点到直线的距离来选择绘制点。 在斜率k介于0和1之间的情况下,以上三种算法都能够有效地绘制直线。具体选择何种算法来实现直线绘制,可以根据需求和实际情况来决定。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值