基于 MFC 计算机图形学 实验1(Visual C++图形程序设计 )


1 MFC应用程序开发方法

1.1 创建MFC工程文件

挖坑,待填

2 图形设备接口和图形程序设计

2.2 绘制基本图形

(1)画点

textout和textoutw

TextOutW()用于Unicode的宽字符 TextOut()用于窄字符 在MFC里头,你只要看见带w的函数都是针对宽字符的,譬如w_char什么的

char * 转为 CString

CString.format(“%s”,char*); help here

// Ccglab1View 绘图

void Ccglab1View::OnDraw(CDC* pDC)
{
	/*Ccglab1Doc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	CString x("zsz憨批!");
	pDC->TextOut(30, 30, x);
	if (!pDoc)
		return;*/
	
	//绘制一组彩色点
	
	//Ccglab1Doc* pDC = GetDocument();
	CString x("point:");
	pDC->TextOut(20, 20, x);

	pDC->SetPixel(100, 20, RGB(255, 0, 0));/*红*/
	pDC->SetPixel(110, 20, RGB(0, 255, 0));/*绿*/
	pDC->SetPixel(120, 20, RGB(0, 0, 255));/*蓝*/
	/*pDC->SetPixel(100, 20, RGB(255, 255, 0));
	pDC->SetPixel(100, 20, RGB(255, 0, 255));
	pDC->SetPixel(100, 20, RGB(0, 255, 255));
	pDC->SetPixel(100, 20, RGB(0, 0, 0));
	pDC->SetPixel(100, 20, RGB(255, 255, 255));*/


	// TODO: 在此处为本机数据添加绘制代码
}

效果

image-20200611211440337

(2)画直线和折线

image-20200611213307435

折线

image-20200611213338992

image-20200611213408334

// Ccglab1View 绘图

void Ccglab1View::OnDraw(CDC* pDC)
{
	// 绘制直线
	CString x("Line:");
	pDC->TextOutW(20, 60, x);
	pDC->MoveTo(20, 90);
	pDC->LineTo(160, 90);

	// 绘制折线1
	POINT polylinepoint[4] = { {70, 240}, {20, 190}, {70, 190}, {20, 240} };/*定义四个点*/
	pDC->Polyline(polylinepoint, 4);/*根据四个点,画出折线*/

	// 绘制折线2
	POINT polypolulinePt[9] = { {95, 160}, {120, 185}, {120, 250}, {145, 160}, {120, 185}, {90, 185}, {150, 185}, {80, 210}, {160, 210} };/*定义9个点*/
	DWORD dwPolyPoints[4] = { 3, 2, 2, 2 };/*分四段折线,分别占用3,2,2,2个顶点*/
	pDC->PolyPolyline(polypolulinePt, dwPolyPoints, 4);/*四段折线的顶点,以及每条折线的顶点数,折线数量*/


	// TODO: 在此处为本机数据添加绘制代码
}

由于一条折线至少需要 2 个顶点,因此 dwPolyPoints 数组中的数不应该小于 2。

(3)画弧线和曲线

image-20200611213452725

image-20200611213511522

image-20200611215528536

image-20200611215545470

// Ccglab1View 绘图

void Ccglab1View::OnDraw(CDC* pDC)
{
	// 用Arc() 绘制圆、圆弧和椭圆

	// 同心圆
	for (int i = 0; i < 6; i++) {
		pDC->Arc(260 - 5 * i, 70 - 5 * i, 260 + 5 * i, 70 + 5 * i, 260 + 5 * i, 70, 260 + 5 * i, 70);
	}
	const double pi = 3.1415926;
	double angel = 60 * pi / 180;
	for (int i = 3; i < 6; i++) {
		// 右半圆弧
		pDC->Arc(260 - 10 * i, 70 - 10 * i, 260 + 10 * i, 70 + 10 * i,
			(int)260 + 10 * i * cos(angel),
			(int)70 + 10 * i * sin(angel),
			(int)260 + 10 * i * cos(angel),
			(int)70 - 10 * i * sin(angel));
		
		// 左半圆弧
		pDC->Arc(260 - 10 * i, 70 - 10 * i, 260 + 10 * i, 70 + 10 * i,
			(int)260 - 10 * i * cos(angel),
			(int)70 - 10 * i * sin(angel),
			(int)260 - 10 * i * cos(angel),
			(int)70 + 10 * i * sin(angel));
	}
    
    // 绘制Bezier曲线

	// 画出一条Bezier曲线和特征多边形
	POINT polyBezier[4] = { {20, 310}, {60, 240}, {120, 300}, {160, 330} };
	pDC->Polyline(polyBezier, 4);
	pDC->PolyBezier(polyBezier, 4);

	// TODO: 在此处为本机数据添加绘制代码
}

示例

image-20200611215236326

(4)画封闭曲线

image-20200611220248895

image-20200611220302256

image-20200611220317646

image-20200611220332750

image-20200611220344557

// Ccglab1View 绘图

void Ccglab1View::OnDraw(CDC* pDC)
{
	// 画封闭曲线

	// 绘制矩形、圆角矩形、椭圆和多边形

	pDC->Rectangle(190, 270, 250, 310);
	pDC->RoundRect(265, 270, 330, 310, 30, 20);
	pDC->Ellipse(260 - 50, 200 - 30, 260 + 50, 200 + 30);
	POINT polygonPts[3] = { {390, 160}, {430, 220}, {350, 210} };
	pDC->Polygon(polygonPts, 3);

	// TODO: 在此处为本机数据添加绘制代码
}

效果

image-20200611220428854

2.3 画笔与画刷

关于lopnWidth的数据类型

在lopnWidth的类型是POINT,但是y值未使用,可以大胆的使用POINT{1, 0}

The y value in the POINT structure for the lopnWidth member is not used.

(1)画笔

image-20200611225122037

image-20200611225131395

image-20200611225150484

// Ccglab1View 绘图

void Ccglab1View::OnDraw(CDC* pDC)
{
	// 创建画笔
	// 方法1
	CPen pen1(PS_SOLID, 1, RGB(255, 0, 0));

	// 方法2
	CPen Pen2;
	Pen2.CreatePen(PS_SOLID, 1, RGB(255, 0, 0));

	// 方法3
	CPen Pen3;
	LOGPEN LogPen;
	LogPen.lopnStyle = PS_SOLID;
	LogPen.lopnWidth = POINT{1, 0};/*X field contains the width of the pen in logical units. The Y field is not used.*/
	LogPen.lopnColor = RGB(255, 0, 0);
	Pen3.CreatePenIndirect(&LogPen);

	// TODO: 在此处为本机数据添加绘制代码
}

不同笔的测试

// Ccglab1View 绘图

void Ccglab1View::OnDraw(CDC* pDC)
{
	// 创建画笔

	// 画笔的样式、宽度和颜色

	int nPenStyle[] = { PS_SOLID, PS_DASH, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL, PS_INSIDEFRAME };
	CPen* pNewPen;
	CPen* pOldPen;
	// 用不同样式的画笔
	for (int i = 0; i < 7; i++) {
		// 构造新笔
		pNewPen = new CPen;
		if (pNewPen->CreatePen(nPenStyle[i], 1, RGB(0, 0, 0))) {
			pOldPen = pDC->SelectObject(pNewPen);/*选择新笔,并保存旧笔*/
			// 画直线
			pDC->MoveTo(20, 60 + i * 20);
			pDC->LineTo(160, 60 + i * 20);
			// 恢复原有的笔
			pDC->SelectObject(pOldPen);
		}
		else {
			// 出错提示
			CString error("CreatePen Error!");
			AfxMessageBox(error);
		}
		// 删除新笔
		delete pNewPen;
	}

	// 用不同的宽度的笔绘图
	for (int i = 0; i < 7; i++) {
		// 构造新笔
		pNewPen = new CPen;
		if (pNewPen->CreatePen(PS_SOLID, i + 1, RGB(0, 0, 0))) {
			pOldPen = pDC->SelectObject(pNewPen);
			// 画直线
			pDC->MoveTo(200, 60 + i * 20);
			pDC->LineTo(340, 60 + i * 20);

			// 恢复原有的笔
			pDC->SelectObject(pOldPen);
		}
		else {
			// 出错提示
			CString error("CreatePen error!!");
			AfxMessageBox(error);
		}
		// 删除新笔
		delete pNewPen;
	}

	// 设置颜色表
	struct tagColor {
		int r, g, b;
	}color[7] = { {255, 0, 0}, {0, 255, 0}, {0, 0, 255}, 
		{255, 255, 0}, {255, 0, 255}, {0, 255, 255}, 
		{0, 0, 0} };
	
	// 用不同颜色绘图
	for (int i = 0; i < 7; i++) {
		// 构造新笔
		pNewPen = new CPen;
		if (pNewPen->CreatePen(PS_SOLID, 2, RGB(color[i].r, color[i].g, color[i].b))) {
			pOldPen = pDC->SelectObject(pNewPen);
			// 画直线
			pDC->MoveTo(380, 60 + i * 20);
			pDC->LineTo(520, 60 + i * 20);
			//恢复原有的笔
			pDC->SelectObject(pOldPen);
		}
		else {
			// 出错提示
			CString error("CreatePen error!!");
			AfxMessageBox(error);
		}
		// 删除新笔
		delete pNewPen;
	}
	// 画笔程序结束

	// TODO: 在此处为本机数据添加绘制代码
}

效果

image-20200611233826243

(2)画刷

image-20200611233914968

image-20200611233927333

// Ccglab1View 绘图

void Ccglab1View::OnDraw(CDC* pDC)
{
	// 画刷程序
	pDC->Rectangle(300, 300, 400, 400);/*缺省的画刷,白色*/

	// 纯色画刷
	CBrush* pNewBrush1;
	CBrush* pOldBrush1;
	pNewBrush1 = new CBrush;
	if (pNewBrush1->CreateSolidBrush(RGB(255, 0, 0))); {
		// 选择新画刷
		pOldBrush1 = pDC->SelectObject(pNewBrush1);
		pDC->Rectangle(200, 200, 300, 400);
		// 恢复原有画刷
		pDC->SelectObject(pOldBrush1);
	}
	delete pNewBrush1;
	
	// 阴影画刷
	CBrush Brush(HS_DIAGCROSS, RGB(255, 255, 255));
	CBrush* pOldBrush;
	pOldBrush = pDC->SelectObject(&Brush);
	pDC->SetBkColor(RGB(192, 192, 192));
	pDC->Rectangle(0, 0, 100, 100);
	pDC->SelectObject(pOldBrush);


	// TODO: 在此处为本机数据添加绘制代码
}

效果

image-20200611234841134

2.4 文本显示

image-20200612002351456

(1)文本显示

image-20200612002437929

(2)设置文本颜色

image-20200612003238848

// Ccglab1View 绘图

void Ccglab1View::OnDraw(CDC* pDC)
{
	// 设置文本颜色

	CDC* pDC1 = GetDC();/*声明一个设备描述表pDC1*/
	pDC1->SetTextColor(RGB(255, 0, 0));/*设置文本颜色为红色*/

	// 可以通过GetBkColor()函数检索到当前文本的颜色
	COLORREF color = pDC->GetTextColor();

	// TODO: 在此处为本机数据添加绘制代码
}

(3)设置字符间距

image-20200612003309024

image-20200612003327762

(4)设置文本的对齐方式

image-20200612003355900

3 鼠标编程

image-20200612003644992

3.1 鼠标消息处理

image-20200612004734339

image-20200612004757702

3.2 捕捉鼠标

image-20200612004837928

3.3 鼠标编程综合示例

示例1 显示鼠标坐标及状态

image-20200612004937810

具体方法:

  1. 新建项目,名称为myMouse,默认即可

  2. 菜单栏->项目->类向导

    image-20200612011347778

    自动生成类似代码

    image-20200612011409803

  3. 输入事件处理程序

    image-20200612011650321

  4. 重复第二步,第三步

    image-20200612012003462

    // CmyMouseView 消息处理程序
    
    
    void CmyMouseView::OnLButtonDown(UINT nFlags, CPoint point)
    {
    	// TODO: 在此添加消息处理程序代码和/或调用默认值
    	// 获得pDC
    	CDC* pDC = GetDC();
    	CString LButtionDown("LBUTTIONDOWN!");
    	pDC->TextOutW(20, 40, LButtionDown);/*输出显示信息*/
    	CView::OnLButtonDown(nFlags, point);
    }
    
    
    void CmyMouseView::OnLButtonUp(UINT nFlags, CPoint point)
    {
    	// TODO: 在此添加消息处理程序代码和/或调用默认值
    	CDC* pDC = GetDC();
    	CString LButtonUp("LButton Up!");
    	pDC->TextOutW(20, 40, LButtonUp);
    	CView::OnLButtonUp(nFlags, point);
    }
    
    
    void CmyMouseView::OnMouseMove(UINT nFlags, CPoint point)
    {
    	// TODO: 在此添加消息处理程序代码和/或调用默认值
    	CDC* pDC = GetDC();
    	char tbuf[80];
    	sprintf_s(tbuf, "Position:(%3d, %3d)", point.x, point.y);
    	// 输出鼠标当前位置
    	CString ttbuf(tbuf);
    	pDC->TextOutW(20, 20, ttbuf);
    	CView::OnMouseMove(nFlags, point);
    }
    
    
    void CmyMouseView::OnRButtonDown(UINT nFlags, CPoint point)
    {
    	// TODO: 在此添加消息处理程序代码和/或调用默认值
    	CDC* pDC = GetDC();
    	CString RButtonDown("RButton Down!");
    	pDC->TextOutW(20, 60, RButtonDown);
    	CView::OnRButtonDown(nFlags, point);
    }
    
    
    void CmyMouseView::OnLButtonDblClk(UINT nFlags, CPoint point)
    {
    	// TODO: 在此添加消息处理程序代码和/或调用默认值
    	CDC* pDC = GetDC();
    	CString LDbl("LButton is double clicked!");
    	pDC->TextOutW(20, 80, LDbl);
    	CView::OnLButtonDblClk(nFlags, point);
    }
    
    
    void CmyMouseView::OnRButtonDblClk(UINT nFlags, CPoint point)
    {
    	// TODO: 在此添加消息处理程序代码和/或调用默认值
    	CDC* pDC = GetDC();
    	CString RDbl("RButton is double clicked!");
    	pDC->TextOutW(20, 80, RDbl);
    	CView::OnRButtonDblClk(nFlags, point);
    }
    
    // 这个比较特殊,在上边找到
    void CmyMouseView::OnRButtonUp(UINT /* nFlags */, CPoint point)
    {
    	ClientToScreen(&point);
    
    	CDC* pDC = GetDC();
    	CString RButtonUp("RButton Up!");
    	pDC->TextOutW(20, 40, RButtonUp);
    
    	OnContextMenu(this, point);
    }
  5. 编译程序,并验证执行结果。

效果

image-20200612013746019

有些小bug,文字会重合,可能需要每次显示后,清除文本框。

示例2 采用鼠标橡皮筋技术画圆

  1. 新建项目,项目名:MouseSpring

  2. 类向导中添加成员变量

    CPoint m_bO; // 圆心
    /*比较懒,省略了*/

    image-20200612111102290

  3. 添加自定义的成员函数

    image-20200613180956682

  4. 在构造函数中初始化成员变量

    // CMouseSpringView 构造/析构
    
    CMouseSpringView::CMouseSpringView() noexcept
    {
    	// TODO: 在此处添加构造代码
    	m_bO.x = 0;/*圆心*/
    	m_bO.y = 0;/*圆心*/
    	m_bR.x = 0;/*圆上的点*/
    	m_bR.y = 0;/*圆上的点*/
    	m_ist = 0;/*圆心与圆上的点的区别*/
    }
  5. 在OnDraw()中添加代码

    // CMouseSpringView 绘图
    
    void CMouseSpringView::OnDraw(CDC* pDC)
    {
    	CMouseSpringDoc* pDoc = GetDocument();
    	ASSERT_VALID(pDoc);
    	if (!pDoc)
    		return;
    
    	// TODO: 在此处为本机数据添加绘制代码
    
    	pDC->SelectStockObject(NULL_BRUSH);
    	DrawCircle(pDC, m_bO, m_bR);/*调用自定义的成员函数画圆*/
    }
  6. 添加消息响应函数LButtonDown(),MouseMove(),并补充其余部分代码。

    // CMouseSpringView 消息处理程序
    
    
    void CMouseSpringView::DrawCircle(CDC* pDC, CPoint cenp, CPoint ardp)
    {
    	// TODO: 在此处添加实现代码.
    	int radius = ComputeRadius(cenp, ardp);
    	// 由圆心确定所画圆的外切区域
    	CRect rc(cenp.x - radius, cenp.y - radius, cenp.x + radius, cenp.y + radius);
    	pDC->Ellipse(rc);/*画出一个整圆*/
    }
    
    
    int CMouseSpringView::ComputeRadius(CPoint cenp, CPoint ardp)
    {
    	// TODO: 在此处添加实现代码.
    	int dx = cenp.x - ardp.x;
    	int dy = cenp.y - ardp.x;
    	// sqrt()函数的调用,在头文件中加入#include<math.h>
    	return (int)sqrt(dx * dx + dy * dy);
    }
    
    
    void CMouseSpringView::OnLButtonDown(UINT nFlags, CPoint point)
    {
    	// TODO: 在此添加消息处理程序代码和/或调用默认值
    	CDC* pDC = GetDC();
    	pDC->SelectStockObject(NULL_BRUSH);
    	if (!m_ist) {
    		m_bO = m_bR = point;/*记录第一次点击鼠标位置,定圆心*/
    		m_ist++;
    	}
    	else {
    		m_bR = point;/*记录第二次单击鼠标的位置,定圆周上的点*/
    		m_ist--;/*为新绘图做准备*/
    		DrawCircle(pDC, m_bO, m_bR);/*绘制新圆*/
    	}
    	
    	ReleaseDC(pDC);/*释放设备环境*/
    	CView::OnLButtonDown(nFlags, point);
    }
    
    
    void CMouseSpringView::OnMouseMove(UINT nFlags, CPoint point)
    {
    	// TODO: 在此添加消息处理程序代码和/或调用默认值
    	CDC* pDC = GetDC();
    	int nDrawmode = pDC->SetROP2(R2_NOT);/*设置异或绘图模式,并保存原来绘图模式*/
    	pDC->SelectStockObject(NULL_BRUSH);
    	if (m_ist == 1) {
    		CPoint prePnt, curPnt;
    		prePnt = m_bR;/*获得鼠标所在的前一个位置*/
    		curPnt = point;
    		// 绘制橡皮筋线
    		DrawCircle(pDC, m_bO, prePnt);/*用异或模式重复画圆,擦出所画的圆*/
    		DrawCircle(pDC, m_bO, curPnt);/*用当前位置作为圆周上的点画圆*/
    		m_bR = point;
    	}
    	pDC->SetROP2(nDrawmode);/*恢复原绘图模式*/
    	ReleaseDC(pDC);/*释放设备环境*/
    
    	CView::OnMouseMove(nFlags, point);
    }
    
  7. 运行。

    image-20200613181416101

    目测是有bug 的,只能画一次图,第二次就gg了。

4 菜单编辑器

挖坑…


文章作者: 卓木
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 卓木 !
  目录