视频演示

自制简易画图软件演示(BV1nk4y1r72y),包含铅笔、直线、矩形、椭圆、橡皮等功能。

开发环境

开发工具为 Visual Studio 2019\text{Visual Studio 2019}。画图软件基于 MFC\text{MFC} 类库进行设计。

自定义类介绍

CMyButton

CMyButton\text{CMyButton}CDC\text{CDC} 的派生类,继承 CDC\text{CDC} 的目的是创建位图。一个 CMyButton\text{CMyButton} 的对象只对应一个按钮,按钮有铅笔、直线等。

位图与编号

每一个按钮都有其索引。每个按钮都有一个位图,位图所覆盖的区域在内存中有其对应的颜色作为按钮的映射。
程序启动时,根据编号加载对应的位图资源。

映射

当鼠标左键点击按钮后,可以得到光标所在坐标的颜色,因此可以用按钮所在方形区域的颜色作为按钮的映射,即一种颜色对应一个按钮。
我们在按钮区域填充对应颜色即可构建映射。

1
2
3
4
5
void CMyButton::DrawButtonBackground(CDC* pdc)
{
//将按钮区域颜色填充至内存中
pdc->FillRect(&ButtonRect, &CBrush(ButtonColor));
}

状态

按钮从 Windows\text{Windows} 画图 3D\text{3D} 中截取,初始状态为第一个状态,当鼠标浮动在按钮上为第二个状态,当选中按钮时为第三个状态。

1
2
3
4
5
6
7
8
//移动至按钮上
void CMyButton::MouseMove() { if (!ButtonState) ButtonState = 1; }
//左键点击选中
void CMyButton::LButtonUp() { ButtonState = 2; }
//鼠标移开
void CMyButton::MouseLeave() { if (ButtonState == 1) ButtonState = 0; }
//转移到初始状态
void CMyButton::ToClear() { ButtonState = 0; }

构造函数

综上所述,对一个按钮的描述包括:状态、位图、区域、颜色、索引。
同时,创建一个按钮时应该给予其兼容性 DC\text{DC},用于在内存中进行操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
CMyButton::CMyButton(CRect _rect,COLORREF _color,int _index, CDC* pdc)
{
ButtonIndex = _index;//按钮编号
ButtonRect = _rect;//按钮区域
ButtonColor = _color;//按钮颜色
ButtonState = 0;//按钮状态
this->CreateCompatibleDC(pdc);//创建兼容性dc
switch (ButtonIndex)
{
case 1:
ButtonBmp.LoadBitmapW(IDB_PENCIL);
break;
case 2:
ButtonBmp.LoadBitmapW(IDB_RECTANGLE);
break;
case 3:
ButtonBmp.LoadBitmapW(IDB_LINE);
break;
case 4:
ButtonBmp.LoadBitmapW(IDB_OVAL);
break;
case 5:
ButtonBmp.LoadBitmapW(IDB_ERASER);
default: break;
}
this->SelectObject(ButtonBmp);//此对象用于操作位图
}

显示按钮

将按钮显示在屏幕上。

1
2
3
4
5
6
7
void CMyButton::ShowButton(CDC* pdc)//显示在客户区
{
//复制位图
//CMyButton继承自CDC
//CMyButton指向位图
pdc->BitBlt(ButtonRect.left, ButtonRect.top, 50, 50, this, ButtonState * 50, 0, SRCCOPY);
}

CMouseSelector

**CMouseSelector\text{CMouseSelector} 用于管理按钮的集合。**利用 STL\text{STL} 中的 vector\text{vector} 储存所有 CMyButton\text{CMyButton} 对象的指针。

OnCreate

OnCreate\text{OnCreate} 是一个消息响应函数,是响应 WM_CREATE 消息的一个函数,而 WM_CREATE 消息是由 Create\text{Create} 函数调用的。一个窗口创建之后,会向操作系统发送 WM_CREATE 消息,因为在MFC里面用一种消息映射的机制来响应消息,也就是可以用函数来响应相应的消息。我们可以在 OnCreate\text{OnCreate} 函数里实现我们要在窗口里面增加的东西,例如按扭,状态栏,工具栏等。OnCreat\text{OnCreat} 不产生窗口,只是在窗口显示前设置窗口的属性如风格、位置等。
(关于 OnCreat\text{OnCreat} 的描述引用自:Windows平台编程之OnCreate函数的说明
在执行 OnDraw\text{OnDraw} 之前,要把所有按钮的映射在 OnCreat\text{OnCreat} 中设置好,由 OnDraw\text{OnDraw} 将按钮呈现出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int C我的画图软件View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;

// TODO: 在此添加您专用的创建代码
CClientDC dc(this);
for (int i = 1;i<=5; i++)
{
CRect rect;
rect.top = 5;
rect.bottom = 55;
if (i == 5)//5是橡皮,分开放
{
rect.left = 1080;
rect.right = 1160;
}
else
{
rect.left = 450 + i * 50;
rect.right = 530 + i * 50;
}
//设置按钮的区域、颜色、编号,给一个pdc
CMyButton* pMyButton = new CMyButton(rect,RGB(i,i,i), i, &dc);
MouseSelector.AddButton(pMyButton);//添加一个按钮
}
return 0;
}

设置按钮映射

当新添加一个按钮后,将该按钮的指针存入 CMouseSelector\text{CMouseSelector} 内的vector\text{vector} 中,并根据按钮的颜色构建映射。由于不在意按钮的顺序,采用 \text{unordered_map} ,查询是 O(1)O(1) 的。

1
2
3
4
5
6
void CMouseSelector::AddButton(CMyButton* pMyButton)
{
ButtonSet.push_back(pMyButton);
//类模板pair<typename T1,typename T2>
ButtonHash.insert(std::pair<COLORREF, CMyButton*>(pMyButton->ButtonColor, pMyButton));
}

显示所有按钮

依次调用 vector\text{vector} 中元素的 ShowButton\text{ShowButton}DrawButtonBackground\text{DrawButtonBackground}。这样就能显示所有按钮,并填充上按钮对应的颜色。

1
2
3
4
5
6
7
8
void CMouseSelector::ShowAllButton(CDC* pClientDC,CDC* pMemDC)
{
for (auto p : ButtonSet)//auto为C++11新特性
{
p->ShowButton(pClientDC);
p->DrawButtonBackground(pMemDC);
}
}

管理按钮状态

至多有一个按钮处于被选中的状态,至多有一个按钮处于悬浮状态。
定义两个 CMyButton*\text{CMyButton*} 类型的指针,SuspendedButton\text{SuspendedButton}PressedButton\text{PressedButton},分别指向悬浮状态的按钮和被点击的按钮。
在构造函数中将两个指针指向 nullptr\text{nullptr}

1
2
3
4
5
CMouseSelector::CMouseSelector()
{
SuspendedButton = nullptr;
PressedButton = nullptr;
}

消息响应

当鼠标在按钮的区域上进行操作时,要注意随时改变 SuspendedButton\text{SuspendedButton}PressedButton\text{PressedButton} 指向的对象。
要时刻注意 SuspendedButton\text{SuspendedButton}PressedButton\text{PressedButton} 指向空的情况。

选中按钮
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
LocMes CMouseSelector::LButtonUp(COLORREF LocColor)
{
LocMes res(0, 0);
auto it = ButtonHash.find(LocColor);
if (it != ButtonHash.end())//LocColor对应一个按钮
{
//PressedButton已经指向该按钮,直接返回
if (PressedButton == it->second) return res;
res.NeedUpdate = 1;//需要重新显示按钮
//得到按钮的索引
res.index = it->second->GetButtonIndex();
//若PressedButton非空,将原来所指向的按钮初始化
if (PressedButton) PressedButton->ToClear();
//更新PressedButton指向
PressedButton = it->second;
//更改新指向按钮的状态
PressedButton->LButtonUp();
}
return res;//LocMes包括重新显示按钮的标志和对应按钮的索引
}
鼠标移动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool CMouseSelector::MouseMove(COLORREF LocColor)
{
bool res=0;
auto it = ButtonHash.find(LocColor);
if (it == ButtonHash.end() && SuspendedButton)//没有这个按钮
{
res = 1;//需要重新显示按钮
SuspendedButton->MouseLeave();
SuspendedButton = nullptr;//指针指向空
}
else if (it != ButtonHash.end())//找到按钮
{
if (SuspendedButton && SuspendedButton == it->second) return res;
res = 1;//需要重新显示按钮
//原来指向的按钮更改状态
if (SuspendedButton) SuspendedButton->MouseLeave();
//指向新的按钮
SuspendedButton = it->second;
//现有按钮更改状态
SuspendedButton->MouseMove();
}
return res;
}

CMyTool

CMyTool\text{CMyTool} 作为所有几何图形的基类。

变量

设置所有几何图形的起点,终点,索引,以及开始绘画的标志。
考虑到使用习惯和编程的方便性,图形索引和按钮索引是一致的,如长方形按钮的索引是 22,其图形的索引也是 22

1
2
3
4
5
protected:
CPoint StartPoint;//起点
CPoint EndPoint;//终点
int index;//图形索引
bool IsDraw;//标志

函数

对所有几何图形的共性进行操作,用虚函数体现其多态性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "CMyTool.h"
//设置开始绘画标志
void CMyTool::StartDraw(bool x) { IsDraw = x; }
//查询是否可以开始绘画
bool CMyTool::IsDrawing() { return IsDraw; }
//设置图形的索引
void CMyTool::SetIndex(int _id) { index = _id; }
//返回图形的索引
int CMyTool::GetIndex() { return index; };
//设置起点和终点
void CMyTool::ResetPoint(CPoint _s, CPoint _e)
{
StartPoint = _s;
EndPoint = _e;
}
//返回起点
CPoint CMyTool::start() { return StartPoint; }
//返回终点
CPoint CMyTool::end() { return EndPoint; }
//体现其多态性
void CMyTool::Draw() {}

CMyPen

CMyTool\text{CMyTool} 的派生类,无需添加任何特性。

1
2
3
4
5
6
7
#pragma once
#include "CMyTool.h"
#include "pch.h"
class CMyPen :
public CMyTool
{
};

CMyLine

CMyTool\text{CMyTool} 的派生类,无需添加任何特性。

1
2
3
4
5
6
7
#pragma once
#include "CMyTool.h"
#include "pch.h"
class CMyLine :
public CMyTool
{
};

CMyEraser

CMyTool\text{CMyTool} 的派生类,无需添加任何特性。

1
2
3
4
5
6
7
#pragma once
#include "CMyTool.h"
#include "pch.h"
class CMyEraser :
public CMyTool
{
};

CMyRectangle

为实现鼠标右键进行正方形和长方形的转换,需要添加一个画正方形的标志,并新设计了两个函数。

CMyRectangle.h

1
2
3
4
5
6
7
8
9
10
11
12
#pragma once
#include "CMyTool.h"
#include "pch.h"
class CMyRectangle
:public CMyTool
{
private:
bool IsDrawSquare;
public:
bool IsDrawingSquare();
void ToDrawSquare(bool x);
};

CMyREctangle.cpp

1
2
3
4
5
#include "CMyRectangle.h"
//查询是否在画正方形
bool CMyRectangle::IsDrawingSquare() { return IsDrawSquare; }
//设置画正方形的标志
void CMyRectangle::ToDrawSquare(bool x) { IsDrawSquare = x; }

CMyEllipse

为实现鼠标右键进行圆和椭圆的转换,需要添加一个画圆的标志,并新设计了两个函数。

CMyEllipse.h

1
2
3
4
5
6
7
8
9
10
11
12
#pragma once
#include "CMyTool.h"
#include "pch.h"
class CMyEllipse :
public CMyTool
{
private:
bool IsDrawCircle;
public:
void ToDrawCircle(bool x);
bool IsDrawingCircle();
};

CMyEllipse.cpp

1
2
3
4
5
#include "CMyEllipse.h"
//设置画圆的标志
void CMyEllipse::ToDrawCircle(bool x) { IsDrawCircle = x; }
//查询是否在画圆
bool CMyEllipse::IsDrawingCircle() { return IsDrawCircle; };

设置背景

对每个分区创建内存 DC\text{DC} 和位图,创建 CRect\text{CRect} 对象设置每个分区的区域范围,用画刷在各个区域内填充对应的颜色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void C我的画图软件View::DrawBackground()
{
// TODO: 在此处添加实现代码.
CClientDC dc(this);
CBrush brush;
CRect rect;
GetWindowRect(&rect);//返回指定窗口的边框矩形的大小
ScreenToClient(&rect);//在屏幕上鼠标的位置转换为你打开的程序的客户区的坐标
/*-------------------------------------------------------创建一堆pdc画背景-------------------------------------------------------*/
m_BackgroundDC = new CDC;
m_BackgroundDC->CreateCompatibleDC(&dc);
m_ButtonDC = new CDC;
m_ButtonDC->CreateCompatibleDC(&dc);
m_BackgroundBit = new CBitmap;
m_BackgroundBit->CreateCompatibleBitmap(&dc, rect.right, rect.bottom);
m_ButtonBit = new CBitmap;
m_ButtonBit->CreateCompatibleBitmap(&dc, rect.right, rect.bottom);
m_BackgroundDC->SelectObject(m_BackgroundBit);
m_ButtonDC->SelectObject(m_ButtonBit);
/*====================================================*/
/*------------------------------------------------------------设置区域范围-------------------------------------------------------------*/
brush.CreateSolidBrush(RGB(229, 251, 255));
//一整块大背景
m_BackgroundDC->FillRect(&rect, &brush);
brush.DeleteObject();
//底部状态栏
m_state_rect.bottom = rect.bottom;
m_state_rect.left = rect.left;
m_state_rect.right = rect.right;
m_state_rect.top = rect.bottom - 25;
//顶部工具栏
m_tool_rect = rect;
m_tool_rect.top = 0;
m_tool_rect.bottom = 60;
m_paper_rect = rect;
//画画区域
m_paper_rect.top = m_tool_rect.bottom;
m_paper_rect.bottom =m_state_rect.top ;
/*------------------------------------------------------------画刷填充区域-------------------------------------------------------------*/
brush.CreateSolidBrush(RGB(240, 240, 240));
m_BackgroundDC->FillRect(&m_tool_rect, &brush);
brush.DeleteObject();
brush.CreateSolidBrush(RGB(101, 101, 101));
m_BackgroundDC->FillRect(&m_state_rect, &brush);
brush.DeleteObject();
}

在内存中画好图后呈现在客户区。

1
2
3
4
5
6
7
8
9
void C我的画图软件View::ShowBackground(CDC* _)
{
// TODO: 在此处添加实现代码.
CRect rect;
GetWindowRect(&rect);
ScreenToClient(&rect);
//将内存内容显示
_->BitBlt(0, 0, rect.right, rect.bottom, m_BackgroundDC, 0, 0, SRCCOPY);
}

功能实现

显示坐标

当光标移动时,显示当前点的坐标,因此需要在消息响应函数 OnMouseMove\text{OnMouseMove} 中设置函数 ShowPosition\text{ShowPosition}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void C我的画图软件View::ShowPosition(CPoint p)
{
CDC dc;
CClientDC _(this);
CBitmap bmp;
dc.CreateCompatibleDC(&_);//创建兼容性dc
int width = m_state_rect.right - m_state_rect.left;
int height = m_state_rect.bottom - m_state_rect.top;
bmp.CreateCompatibleBitmap(&_,width,height);//创建兼容性位图
dc.SelectObject(&bmp);
CString pos;
dc.SetTextColor(RGB(255, 255, 255));//设置显示颜色
dc.SetBkMode(TRANSPARENT);//设置背景模式
pos.Format(_T("坐标 x: %d y: %d"), p.x, p.y);//格式化输出
dc.FillRect(&CRect(0, 0, width, height), &CBrush(RGB(101, 101, 101)));//填充状态栏
//显示坐标,注意逻辑坐标与设备坐标的区别
dc.TextOut(600, 1, pos);
_.BitBlt(m_state_rect.left, m_state_rect.top, width, height, &dc, 0, 0, SRCCOPY);
}

交互式绘图

定义 CMyTool*\text{CMyTool*} 类型的指针 \text{now_choose},绘制不同图形时指向对应的派生类。定义 CMouseSelector\text{CMouseSelector} 类型的对象 MouseSelector\text{MouseSelector} 展示不同按钮的效果。

左键弹起

当左键弹起时,获取当前点的颜色, 由 CMouseSelector\text{CMouseSelector} 内部函数 LButtonUp\text{LButtonUp} 判断是否选中按钮并返回当前位置的信息,若需要重新显示按钮(选中了新的按钮或按钮的悬浮状态解除),则调用内部函数 ShowAllButtonyiwei\text{ShowAllButtonyiwei} ;若内部函数 LButtonUp\text{LButtonUp} 返回的按钮索引非零,那么表示选中了按钮,需要将 \text{now_choose} 指向新的对象。
同时,左键弹起也意味着当前选择的几何图形完成绘制,需要将一切绘画的状态初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/********************************************************/
/* 消息:左键完成点击 */
/* 获取光标位置的RGB值并映射按钮 */
/* 将指针指向所选择的类 */
/********************************************************/
void C我的画图软件View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CClientDC dc(this);
ShowPosition(point);
//获取当前点COLORREF
LocMes res = MouseSelector.LButtonUp(GetPixel(*m_ButtonDC, point.x, point.y));
//需要更新,重新显示按钮
if (res.NeedUpdate) MouseSelector.ShowAllButton(&dc,m_ButtonDC);
//初始化
if (now_choose)
{
switch (now_choose->GetIndex())
{
case 1:
now_choose->StartDraw(0);
break;
case 2:
now_choose->StartDraw(0);
dynamic_cast<CMyRectangle*>(now_choose)->ToDrawSquare(0);
break;
case 3:
now_choose->StartDraw(0);
break;
case 4:
now_choose->StartDraw(0);
dynamic_cast<CMyEllipse*>(now_choose)->ToDrawCircle(0);
case 5:
now_choose->StartDraw(0);
default:
break;
}
}
//选择新按钮
//条件:now_choose指向空,或者返回的按钮编号非零且不是当前选择按钮
if (!now_choose||(res.index && res.index != now_choose->GetIndex()))
{
if (now_choose)//若非空则释放内存
{
delete now_choose;
now_choose = nullptr;
}
switch (res.index)//根据按钮编号进行操作
{
case 1:
now_choose = new CMyPen;
now_choose->StartDraw(0);
now_choose->SetIndex(1);
break;
case 2:
now_choose = new CMyRectangle;
now_choose->StartDraw(0);
now_choose->SetIndex(2);
dynamic_cast<CMyRectangle*>(now_choose)->ToDrawSquare(0);
break;
case 3:
now_choose = new CMyLine;
now_choose->StartDraw(0);
now_choose->SetIndex(3);
break;
case 4:
now_choose = new CMyEllipse;
now_choose->StartDraw(0);
now_choose->SetIndex(4);
dynamic_cast<CMyEllipse*>(now_choose)->ToDrawCircle(0);
break;
case 5:
now_choose = new CMyEraser;
now_choose->StartDraw(0);
now_choose->SetIndex(5);
break;
default:
break;
}
}
CView::OnLButtonUp(nFlags, point);
}

左键按下

鼠标左键按下代表开始绘制图形。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*******************************************************/
/* 画图 */
/* 左键按下代表开始画图 */
/*******************************************************/
void C我的画图软件View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (now_choose)
{
//将绘图标志置为ture
now_choose->StartDraw(1);
//起点和终点均设置为当前点
now_choose->ResetPoint(point, point);
}
CView::OnLButtonDown(nFlags, point);
}

右键弹起

开启 shift\text{shift} 功能,画折线、正方形、椭圆。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*************************************************************/
/* 画折线 */
/* 规定当前点击位置为起点和终点 */
/* 右键按下画正方形和圆 */
/*************************************************************/
void C我的画图软件View::OnRButtonUp(UINT /* nFlags */, CPoint point)
{
if (now_choose&& now_choose->IsDrawing())
{
switch(now_choose->GetIndex())
{
case 2:
//长方形正方形转换,更改标记
if (dynamic_cast<CMyRectangle*>(now_choose)->IsDrawingSquare()) dynamic_cast<CMyRectangle*>(now_choose)->ToDrawSquare(0);
else dynamic_cast<CMyRectangle*>(now_choose)->ToDrawSquare(1);
break;
case 3:
//画折线当前点设置为起点和终点
now_choose->ResetPoint(point, point);
break;
case 4:
//椭圆和圆转换,更改标记
if (dynamic_cast<CMyEllipse*>(now_choose)->IsDrawingCircle()) dynamic_cast<CMyEllipse*>(now_choose)->ToDrawCircle(0);
else dynamic_cast<CMyEllipse*>(now_choose)->ToDrawCircle(1);
break;
default:
break;
}
}
}

光标移动

光标移动是绘图操作的主体。
当选择铅笔时,将当前节点置为起点和终点,在这一微小距离内画线,宏观上体现为一支流畅的笔。
当画直线、长方形、椭圆时,采用橡皮筋算法。当光标移动时,对原来位置的图形取反色,消除原有位置图形,显示新的图形。需要注意,画正方形时,边长是光标当前点和起点所形成矩形的长和宽中的最小值;画圆时,其外接正方形边长是光标当前点和起点所形成矩形的长和宽中的最小值。
当选择橡皮时,将其颜色置为背景色,进行铅笔的同样操作。橡皮是一支颜色为背景色的笔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/***************************************************/
/* 消息:鼠标移动 */
/* 获取位置信息显示坐标 */
/* 获取光标位置的RGB值并映射按钮 */
/* 进行绘图操作 */
/***************************************************/
void C我的画图软件View::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
ShowPosition(point);
CClientDC dc(this);
if (MouseSelector.MouseMove(GetPixel(*m_ButtonDC, point.x, point.y))) MouseSelector.ShowAllButton(&dc,m_ButtonDC);
CPen pen;
if (now_choose&&now_choose->IsDrawing())
{
switch (now_choose->GetIndex())
{
case 1:
pen.CreatePen(PS_SOLID, 3, RGB(0,0,0));
dc.SelectObject(pen);
dc.MoveTo(now_choose->start());
dc.LineTo(now_choose->end());
//在微小区域内画线
now_choose->ResetPoint(now_choose->end(), point);
break;
case 2:
pen.CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
dc.SelectObject(pen);
dc.MoveTo(now_choose->start());
dc.SetROP2(R2_NOTXORPEN);
dc.Rectangle(CRect(now_choose->start(),now_choose->end()));
dc.MoveTo(now_choose->start());
//若当前画正方形
if (dynamic_cast<CMyRectangle*>(now_choose)->IsDrawingSquare())
{
//计算正方形边长 d=min(abs(point.x-start().x),abs(point.y-start().y))
//分类讨论终点point的位置
if (point.x < now_choose->start().x)
{
if (point.y < now_choose->start().y)
{
int dx = now_choose->start().x - point.x;
int dy = now_choose->start().y - point.y;
int d = min(dx, dy);
point = CPoint(now_choose->start().x - d, now_choose->start().y - d);
}
else
{
int dx = now_choose->start().x - point.x;
int dy = point.y - now_choose->start().y;
int d = min(dx, dy);
point = CPoint(now_choose->start().x - d, now_choose->start().y + d);
}
}
else
{
if (point.y < now_choose->start().y)
{
int dx = point.x-now_choose->start().x;
int dy = now_choose->start().y - point.y;
int d = min(dx, dy);
point = CPoint(now_choose->start().x + d, now_choose->start().y - d);
}
else
{
int dx = point.x - now_choose->start().x;
int dy = point.y - now_choose->start().y;
int d = min(dx, dy);
point = CPoint(now_choose->start().x + d, now_choose->start().y + d);
}
}
}
dc.Rectangle(CRect(now_choose->start(),point));
now_choose->ResetPoint(now_choose->start(), point);
break;
case 3:
pen.CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
dc.SelectObject(pen);
dc.MoveTo(now_choose->start()); //移动到起始点
dc.SetROP2(R2_NOTXORPEN);//设置绘图模式为R2_NOTXORPEN
dc.LineTo(now_choose->end()); //从点StartPoint画线到点EndPoint,即删除原旧线
dc.MoveTo(now_choose->start()); //移动到起始点
dc.LineTo(point); //从StartPoint画线到当前的鼠标位置point
now_choose->ResetPoint(now_choose->start(),point); //改变终止点为当前的鼠标位置point
break;
case 4:
pen.CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
dc.SelectObject(pen);
dc.MoveTo(now_choose->start());
dc.SetROP2(R2_NOTXORPEN);
dc.Ellipse(CRect(now_choose->start(), now_choose->end()));
dc.MoveTo(now_choose->start());
//若当前画圆
if (dynamic_cast<CMyEllipse*>(now_choose)->IsDrawingCircle())
{
//计算外接正方形边长 d=min(abs(point.x-start().x),abs(point.y-start().y))
//分类讨论终点point的位置
if (point.x < now_choose->start().x)
{
if (point.y < now_choose->start().y)
{
int dx = now_choose->start().x - point.x;
int dy = now_choose->start().y - point.y;
int d = min(dx, dy);
point = CPoint(now_choose->start().x - d, now_choose->start().y - d);
}
else
{
int dx = now_choose->start().x - point.x;
int dy = point.y - now_choose->start().y;
int d = min(dx, dy);
point = CPoint(now_choose->start().x - d, now_choose->start().y + d);
}
}
else
{
if (point.y < now_choose->start().y)
{
int dx = point.x - now_choose->start().x;
int dy = now_choose->start().y - point.y;
int d = min(dx, dy);
point = CPoint(now_choose->start().x + d, now_choose->start().y - d);
}
else
{
int dx = point.x - now_choose->start().x;
int dy = point.y - now_choose->start().y;
int d = min(dx, dy);
point = CPoint(now_choose->start().x + d, now_choose->start().y + d);
}
}
}
dc.Ellipse(CRect(now_choose->start(), point));
now_choose->ResetPoint(now_choose->start(), point);
break;
case 5:
pen.CreatePen(PS_SOLID, 20, RGB(229, 251, 255));
dc.SelectObject(pen);
dc.MoveTo(now_choose->start());
dc.LineTo(now_choose->end());
now_choose->ResetPoint(now_choose->end(), point);
break;
default:
break;
}
}
CView::OnMouseMove(nFlags, point);
}