计算机图形学——利用MFC库绘制直线(DDA算法和Bresenham算法)
问题描述
利用MFC库实现直线的绘制(分别使用DDA算法和Bresenham算法)
求解思路
创建MFC APP项目,选择Dialog Based模板,如下:
1. 对话框设计
利用TOOL BOX中的工具修改主对话框(如果TOOL BOX没有的话,在view中打开)
-
添加两个绘图框,分别对应DDA算法和Bresenham算法,ID为
IDC_STATIC_DRAW_AREA1
和IDC_STATIC_DRAW_AREA2
-
添加两个按钮,ID为
IDC_BUTTON_DDA
和IDC_BUTTON_BRESENHAM
-
添加四个文本编辑框便于输入坐标值,ID为
IDC_EDIT_START1
,IDC_EDIT_START2
,IDC_EDIT_END1
,IDC_EDIT_END2
-
添加静态文本框
注:修改ID需要选中对象,点击属性,然后在属性中修改ID
效果如下图:
2. 算法设计
DDA算法
算法描述略
Bresenham算法
-
获取起点和终点坐标:首先获取直线的起点坐标
(x0, y0)
和终点坐标(x1, y1)
-
计算dx和dy:计算
dx = x1 - x0
和dy = y1 - y0
-
计算p0初始值:
p_0 = 2*dy - dx
,这是初始的决策参数。p_k
用于决定下一个要绘制的像素的位置 -
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
- 重复绘制直线:重复步骤4的过程,共计算
dx
次,以绘制整条直线
程序代码
需要修改LineDlg.cpp
和LineDlg.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按钮,相应的直线就会在对应的绘图框中展示,如下图:
如果输入的坐标超出了绘图框的限制,则弹出错误提示,如下图: