原文来自本人博客网站只是又搬到这里来做一下推广,希望大家喜欢。
使用软件:Microsoft Visual Studio
用DDA算法无交互绘制固定直线
1 创建MFC应用程序。建一个基于单个文档
的默认程序。
2 找到资源视图
,打开资源文件后找到Menu
文件夹下的IDR_MAINFRAME
文件,双击打开它。
3 在菜单栏键入自己要设置的菜单项以及菜单子项,并编辑ID
值。
4 为菜单子项创建响应函数,通过建立类向导
实现。
5 在函数体内写上绘制算法的代码,保存,编译,执行,得到结果。
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);
}
运行图:
总结
啊为了计算机图形学课业的一个实验课,花费了不少功夫来做这篇博客,说实在的技术含量的东西不多,主要是让大家学习熟悉VS的基本操作。只是做了很简单的一个步骤介绍,大家可以思考改进的。比如:
- 算法代码都写在了响应函数里,其实为了可读性和维护性,可以创建单独的算法函数,然后在响应函数里调用即可,井井有条。
- 在Bresenham算法中,只是对输入圆心坐标做了交互,半径是定死的,也可以思考如何交互式地传递半径,能不能点一下画布后,弹出一个小对话框,让输入半径值,然后提交之后,交互式地画圆呢?这里小小埋一个伏笔,其实这个用到了MFC程序中
Dialog
对话框的知识了。 - 同样地,我该如何动态地选择绘制图形的颜色?等等等等。
当然了,对于算法的原理并没有给出心得,期待我的下一次更新吧!如果你喜欢这篇文章,可以分享给更多人,让大家共同学习。并且如果你善于思考想要与我讨论,可以加我的QQ133077719,记得备注哦~