【计算机图形学入门】MFC项目模拟像素点绘制直线和圆(中点画线法、中点画圆法)(附源码)

本文详细介绍了如何在VisualStudio2019中利用MFC框架创建一个桌面应用程序,包括设置UI界面、编写代码实现像素网格、鼠标事件响应以及使用中点法和Bresenham算法绘制直线和椭圆。开发过程中涉及了网格模拟、图形交互和颜色设置功能。
摘要由CSDN通过智能技术生成

一、前言

1.1.开发环境:Visual Studio 2019

1.2.开发准备:

【Visual Studio 2019】创建 MFC 桌面程序 ( 安装 MFC 开发组件 | 创建 MFC 应用 | MFC 应用窗口编辑 | 为按钮添加点击事件 | 修改按钮文字 | 打开应用 )

1.3.题目要求:

采用你学过的任意一种算法(DDA/中点法/Bresenham)绘制任意直线及(椭)圆。具体要求如下:
1.将象素网格表现出来,建立网格坐标系(如下图示例所示,或任意其他形式皆可以,只要能展现出一个个独立的像素点形象就行);
2.用橡皮筋的形式输入参数(仿照windows画板程序用鼠标拖动画线,鼠标左键按下位置表示直线段起始点,左键弹起位置为终止点);
3.鼠标移动时,显示鼠标当前位置。

图1-1

二、创建项目

2.1.创建MFC项目

图2-1

2.2.项目命名、存储位置

图2-2

2.3.应用程序类型选择

图2-3

点击完成以后项目就创建成功了。

三、编写代码

3.1.设置UI界面

点击右下角资源视图,选择Menu下的IDR_MAINFRAME,进入页面。如果右侧没有资源视图选项卡,请看图3-2。

图3-1

图3-2

接下来就可以设计自己的菜单了,双击“在此输入”一栏,可以添加自己想要的菜单选项,我这里设置了两个菜单,一个是选择图形,一个是选择颜色,选择图形可以绘制直线,圆,颜色只设置了红色和绿色。

图3-3

3.2代码编辑

3.2.1绘制网格

题目需要用网格模拟像素,因此在绘制图形之前先绘制网格,在CMFCxxxxView.cpp文件下(xxxx指自定义的项目名称),找到OnDraw函数,添加以下代码,绘制边长为10的网格。

void CMFCApplication2View::OnDraw(CDC *pDC)
{
	CMFCApplication2Doc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此处为本机数据添加绘制代码
	for (int i = 0; i < 1000; i++)
	{
		pDC->MoveTo(0, i * 10);
		pDC->LineTo(6000, i * 10);
	}

	for (int i = 0; i < 1000; i++) {
		pDC->MoveTo(i * 10, 0);
		pDC->LineTo(i * 10, 6000);
	}
}

3.2.2像素填充

题目需要模拟算法选点,像素填充,接下来是像素填充算法,首先在类视图下,右CMFCxxxxVie类,选择添加->添加函数,步骤如图。

图3-4

图3-5

点击确定后,在set_pixel函数中添加如下代码。由于绘制的网格是边长为10的,为了更好的模拟像素点,所以用Rectangle函数绘制放大十倍的矩形填充。

void CMFCApplication2View::set_pixel(int x,int y,COLORREF color)
{
	// TODO: 在此处添加实现代码.
	CClientDC dc(this);
	CBrush newbrush;
	newbrush.CreateSolidBrush(color);
	CBrush* oldbrush = dc.SelectObject(&newbrush);
	dc.Rectangle(x*10 , y*10 , x*10-10, y*10-10);
	dc.SelectObject(oldbrush);
	return;
}

3.2.3绘制直线

绘制直线采用中点画线法。用图3-4、图3-5的方法添加函数MidPointLine(int x0, int y0, int x1, int y1, COLORREF color),代码如下。

由于上文set_pixel函数绘制放大十倍的矩形填充,会导致绘制的图形坐标以十倍的数量偏移,导致鼠标点击位置与图像呈现位置有巨大偏移量,所以在绘制直线,将选取的点填充时,调用set_pixel函数的实参都要缩小十倍,因此除以十。

void CMFCApplication2View::MidPointLine(int x0, int y0, int x1, int y1, COLORREF color)
{

	// TODO: 在此处添加实现代码.
	int a, b, deta1, deta2, d, x, y;
	//没有斜率的时候直接绘制
	if (x0 == x1)//垂直x轴的直线
	{
		if (y0 < y1)
		{
			for (int i = y0; i <= y1; i++)
			{
				set_pixel(x0/10, i/10, color);
			}
		}
		else
		{
			for (int i = y1; i <= y0; i++)
			{
				set_pixel(x0/10, i/10, color);
			}

		}
		return;
	}
	//m为真说明斜率绝对值小于等于1
	BOOL m = (fabs(y1 - y0) <= fabs(x1 - x0));
	//用户从起点在右
	if (x0 > x1)
	{
		d = x0; x0 = x1; x1 = d;
		d = y0; y0 = y1; y1 = d;
	}
	a = y0 - y1;
	b = x1 - x0;
	x = x0;
	y = y0;
	//k绝对值小于等于1
	if (m)
	{
		//也就是k小于等于1大于0
		if (y0 < y1)
		{
			d = 2 * a + b;
			deta1 = 2 * a;
			deta2 = 2 * (a + b);
			while (x < x1)
			{
				if (d < 0)
				{
					x++;
					y++;
					d += deta2;
				}
				else {
					x++;
					d += deta1;
				}
				set_pixel(x/10, y/10, color);
			}
		}
		//也就是k大于等于-1小于0
		else
		{
			d = 2 * a - b;
			deta1 = 2 * a;
			deta2 = 2 * (a - b);
			while (x < x1)
			{
				if (d < 0)
				{
					x++;
					d += deta1;
				}
				else
				{
					x++;
					y--;
					d += deta2;
				}
				set_pixel(x/10, y/10, color);
				
			}
		}
	}
	//k绝对值大于等于1
	else
	{
		//k大于等于1到正无穷
		if (y0 < y1)
		{
			d = a + 2 * b;
			deta1 = 2 * b;
			deta2 = 2 * (a + b);
			while (y < y1)
			{
				if (d < 0)
				{
					y++;
					d += deta1;
				}
				else
				{
					y++;
					x++;
					d += deta2;
				}
				set_pixel(x/10, y/10, color);
				//pDC->SetPixel(x, y, color);
			}
		}
		//k大于等于-1到负无穷
		else
		{
			d = a - 2 * b;
			deta1 = -2 * b;
			deta2 = 2 * (a - b);
			while (y > y1)
			{
				if (d < 0)
				{
					y--;
					x++;
					d += deta2;
				}
				else
				{
					y--;
					d += deta1;
				}
				set_pixel(x/10, y/10, color);
			}
		}
	}
}

3.2.4绘制圆

类视图->右击CMFCxxxView类->添加->添加变量

图3-6

同样的方法添加圆上的点m_br,标志位Flag

图3-7

图3-8

添加函数,计算半径

int CMFCApplication4View::ComputeRadius(CPoint cenp, CPoint ardp)
{
	// TODO: 在此处添加实现代码.

	int dx = cenp.x - ardp.x;

	int dy = cenp.y - ardp.y;

	return (int)sqrt(dx * dx + dy * dy);
}

添加函数,绘制圆

void CMFCApplication4View::Midpoint_DrawCircle(CPoint O, int r, COLORREF color)
{
	// TODO: 在此处添加实现代码.
	CDC* pDC = GetDC();

	int d0, x = 0, y = r;
	d0 = 1.25 - r;
	while (x < y)
	{
		if (d0 >= 0)
		{
			d0 = d0 + 2 * (x - y) + 5;
			x += 1;
			y -= 1;
			set_pixel((x + O.x) / 10, (y + O.y) / 10, color);         //(x,y)
			set_pixel((-x + O.x) / 10, (y + O.y) / 10, color);        //(-x,y)
			set_pixel((y + O.x) / 10, (x + O.y) / 10, color);         //(y,x)
			set_pixel((-y + O.x) / 10, (x + O.y) / 10, color);        //(-y,x)
			set_pixel((x + O.x) / 10, (-y + O.y) / 10, color);        //(x,-y)
			set_pixel((-x + O.x) / 10, (-y + O.y) / 10, color);       //(-x,-y)
			set_pixel((y + O.x) / 10, (-x + O.y) / 10, color);        //(y,-y)
			set_pixel((-y + O.x) / 10, (-x + O.y) / 10, color);       //(-y,-x)
			Sleep(50);
		}
		else
		{

			d0 = d0 + 2 * x + 3;
			x += 1;
			y = y;
			set_pixel((x + O.x) / 10, (y + O.y) / 10, color);         //(x,y)
			set_pixel((-x + O.x) / 10, (y + O.y) / 10, color);        //(-x,y)
			set_pixel((y + O.x) / 10, (x + O.y) / 10, color);         //(y,x)
			set_pixel((-y + O.x) / 10, (x + O.y) / 10, color);        //(-y,x)
			set_pixel((x + O.x) / 10, (-y + O.y) / 10, color);        //(x,-y)
			set_pixel((-x + O.x) / 10, (-y + O.y) / 10, color);       //(-x,-y)
			set_pixel((y + O.x) / 10, (-x + O.y) / 10, color);        //(y,-y)
			set_pixel((-y + O.x) / 10, (-x + O.y) / 10, color);       //(-y,-x)
			Sleep(50);
		}
	}
}

3.2.5 添加单击鼠标响应事件

再添加CPoint类型成员变量,和COLORREF类型成员变量。

图3-9

图3-10

类视图->右击CMFCxxxView类->类向导,添加两个鼠标消息,分别是何时按下左键,何时释放左键,添加后点击确定。

图3-11

在刚才添加的鼠标消息后,编译器自动生成两个函数框架,在内部添加以下代码。

void CMFCApplication4View::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	if (Flag == 1)
	{
		z_beginMousePoint = point;
	}
	else if (Flag == 2)
	{
		CDC* pDC = GetDC();
		pDC->SelectStockObject(NULL_BRUSH);
		m_b0 = point; //纪录第一次单击鼠标位置,定圆心
		ReleaseDC(pDC); //释放设备环境
		CView::OnLButtonDown(nFlags, point);
	}


	CView::OnLButtonDown(nFlags, point);
}


void CMFCApplication4View::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	if (Flag == 1)
	{
		
		MidPointLine(z_beginMousePoint.x, z_beginMousePoint.y, point.x, point.y, color);
	}
	else if (Flag == 2)
	{
		// TODO: 在此添加消息处理程序代码和/或调用默认值
		CDC* pDC = GetDC();
		pDC->SelectStockObject(NULL_BRUSH);
		m_br = point; //纪录松开鼠标左键位置,定半径
		int r = ComputeRadius(m_b0, m_br);
		Midpoint_DrawCircle(m_b0, r, color);
		ReleaseDC(pDC); //释放设备环境

	}

	
	
	CView::OnLButtonUp(nFlags, point);
}

将菜单中的每个选项ID与函数关联,首先在资源视图打开设计好的菜单,双击菜单项,编译器右下角会显示菜单项的对应的ID,在CMFCxxxxView类中右击选择类向导,搜索查询到的菜单项对应ID,然后添加处理程序,步骤如图所示,ID和函数一定要一一对应。

图3-12

图3-13

之后,在编译器自动生成的这四个处理程序函数框架中添加以下代码。

void CMFCApplication4View::CreateLine()
{
	// TODO: 在此添加命令处理程序代码
	Flag = 1;
}


void CMFCApplication4View::CreateCircle()
{
	// TODO: 在此添加命令处理程序代码
	Flag = 2;
}


void CMFCApplication4View::SetColor_Green()
{
	// TODO: 在此添加命令处理程序代码
	color = RGB(100, 200, 100);
}


void CMFCApplication4View::SetColor_Red()
{
	// TODO: 在此添加命令处理程序代码
	color = RGB(250, 0, 0);
}

3.2.6 实时显示鼠标坐标

void CMFCApplication4View::OnMouseMove(UINT nFlags, CPoint point)
{
		// TODO: 在此添加消息处理程序代码和/或调用默认值
		CString str;
		str.Format(_T("[%d,%d]"), point.x, point.y);
		GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowTextW(str);
	    CView::OnMouseMove(nFlags, point);
}

接下来就可以运行了!效果如下。


计算机图形学初学者,如有错误,请指正。


参考博客:

VC++的MFC单文档画图程序

计算机图形学之MFC实现数值微分画线、中点画线、Bresenham画线

C语言——中点画圆算法和Bresenham画圆算法(easyx图形库)

计算机图形学MFC实验--画线、画圆、画椭圆、区域填充算法(C++)


完整代码:

http://计算机图形学-绘制直线和圆 链接:https://pan.baidu.com/doc/share/Ojf7qNqNiG4zZzbMOL1IpQ-450070235226288 提取码:jvcx

  • 28
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,我可以为您提供一个简单的MFC工程,用于绘制一个椭。以下是代码: 1. 在Visual Studio中创建一个新的MFC应用程序,在“应用程序类型”中选择“对话框”。 2. 打开“资视图”,双击“IDD_DIALOG1”对话框以打开对话框编辑器。 3. 在对话框中添加一个“布”控件,大小为您想要的椭大小。将其ID设置为“IDC_CANVAS”。 4. 右键单击对话框并选择“添加类”,创建一个名为“CEllipseDlg”的新类。 5. 打开“CEllipseDlg.cpp”文件,并添加以下代码: ``` void CEllipseDlg::OnPaint() { CPaintDC dc(this); // 获取布控件的DC CWnd* pWnd = GetDlgItem(IDC_CANVAS); CDC* pDC = pWnd->GetDC(); // 绘制 CRect rect; pWnd->GetWindowRect(rect); ScreenToClient(rect); pDC->Ellipse(rect); // 释放DC pWnd->ReleaseDC(pDC); } ``` 6. 在“CEllipseDlg.h”文件中添加以下成员变量和函数声明: ``` private: CStatic m_canvas; protected: afx_msg void OnPaint(); DECLARE_MESSAGE_MAP() ``` 7. 在“CEllipseDlg.cpp”文件中添加以下消息映射: ``` BEGIN_MESSAGE_MAP(CEllipseDlg, CDialogEx) ON_WM_PAINT() END_MESSAGE_MAP() ``` 8. 在“CEllipseDlg.cpp”文件中,将“OnInitDialog()”函数中的以下代码替换为: ``` // 创建布控件 CRect rect; m_canvas.GetClientRect(rect); m_canvas.MapWindowPoints(this, &rect); CWnd* pWnd = GetDlgItem(IDC_CANVAS); pWnd->MoveWindow(rect); ``` 9. 在“CEllipseDlg.cpp”文件中,将以下代码添加到“CEllipseDlg”的构造函数中: ``` m_canvas.SubclassDlgItem(IDC_CANVAS, this); ``` 10. 编译并运行程序,您应该能够看到绘制的椭。 希望这可以帮助到您!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值