基于 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 菜单编辑器

挖坑…