2022-09-11
双缓冲算法的一个实例即为如下动画:

由结果图可以看出,
(1)其中有一个白色的小球,因此需要创建一个小球类。在创建小球类之前,先创建一个MFC项目。
创建一个MFC项目的步骤:
首先打开VS,点击新建;点击MFC应用;更改存放路径,更改项目名称,例如:“DoubleBufferes”,点击下一步;在弹出的“MFC应用程序”窗口中,找到“应用程序类型”,点击下拉菜单,选择“单个文档”;在这一页旁边的“项目样式”中选择“MFC standard”样式,之后点击完成。即创建好了一个MFC应用项目。
创建好项目后,添加一个小球类,步骤如下:
在右侧的“解决方案”中,选中项目名称“DoubleBufferes”,右击点击“添加”,选择“类”;在弹出的类的设置中,填写类名,例如:“CSphere”
CSphere.h文件
1 #pragma once
2 class CSphere
3 {
4 public:
5 CSphere(void);//构造函数
6 virtual ~CSphere(void);//析构函数
7 void SetParameter(int R,CPoint CenterPoint);
8 void Draw(CDC* pDC);
9 public:
10 int R;//球体半径
11 CPoint CenterPoint;
12
13 };
说明:
①一直以来,我不太明白,构造函数明明是 “CSphere()”,那为什么第5行内部要加一个void,很不理解。后来我查了一下,给出的解释是这样的,如果将void 作为参数传给构造函数或者其他函数,则说明该函数不支持参数的传入,就相当于CSphere(),可能就是一个习惯写法吧。
②代码第6行:是一个析构函数的声明,那为啥析构函数前面要加上“virtual”虚函数的关键字呢?这里要特别加入“virtual”关键字是为了防止内存泄露。那为啥不使用“virtual”关键字修饰就会发生内存泄露呢?这里有一种这样的情况,如果该类作为父类,创建了子类后,创建子类对象后,子类对象占用了一定的内存空间后,当删除子类对象时,要调用子类析构函数,由于没有使用“virtual”虚关键字,就构不成动态绑定,实际上只调用了父类的析构函数,但没有调用子类的析构函数,那么子类占用的那部分内存空间就会得不到释放,造成内存泄露。
③代码第7行:设置球心的坐标,以及半径。
④代码第8行:表示使用CDC绘制小球。
注意:
在上面的定义,在第7行代码中,写到"CPoint”和第8行“CDC”时,发现飘红了,就很奇怪。这后又重新看了一下讲课视频,发现了这个问题产生的原因是还未写完全,没有声明。需要点击“DoubleBufferesView.h”文件,打开后,从上往下找到第三个“protected:”,在里面添加要声明的内容,如下:
protected:
int nWidth, nHeight;
CSphere sphere;
CPoint direction;
BOOL bPlay;
注意:
在定义“CSphere”类的变量时,需要添加一个头文件。
#include"CSphere.h"
CSphere.cpp文件:
1 #include "pch.h"
2 #include "CSphere.h"
3 CSphere::CSphere(void){
4 }
5 CSphere::~CSphere(void){
6 }
7 void CSphere::SetParameter(int R, CPoint CenterPoint) {
8 this->R = R;
9 this->CenterPoint = CenterPoint;
10 }
11
12 void CSphere::Draw(CDC* pDC) {
13 CPoint TopLet(CenterPoint.x - R, CenterPoint.y - R);
14 CPoint BottomRight(CenterPoint.x + R, CenterPoint.y + R);
15 CRect Square(TopLet, BottomRight);
16 pDC->Ellipse(Square);
17 }
说明:
在绘制小球的函数中(第12行代码),先设置了两个点,一个是左上角点,另一个是右下角点。需要说明的一点是,客户区内的x-y轴中的圆点初始是设置在左上角点,x轴是用上到下,y轴是从左到右。设置两个点后,将他们作为“CRect”类的两个参数进行传入。最后使用绘图指针“pDC”进行绘制椭圆,参入的参数即为上面设置好的矩形。这里表示的是在给定的矩形内绘制它的内切椭圆。
(2)由运行结果图中可以看出,
菜单栏中有“文件”、“帮助”、“图形(G)”,其中在图形的下拉菜单中还有一个“绘图(A)”的选项。而且在第二行中有一个“播放的红色的按钮”。在这一步中需要设置菜单栏的选项。
①首先,点击菜单栏中的“视图”,找到“其他窗口”,点击“资源视图”。
②在资源视图中,找到“Menu”文件夹,点击其文件夹下的“IDR_MAINFRAME”,打开后会看到一横排“文件”、“编辑”...工具栏。现在可以将不用的选项删除,只留下“文件(F)”,“帮助(H)”;在这一排的后面会有一个文本框,显示“请在此处键入”,可以输入“图形(G)”;后选中“图形(G)”,在它的内部设置一个选项“绘图(A)”。
选中“绘图(A)”,右击属性,找到“ID”,将“ID”设置为“ID_ANIMATION”,ctrl+s保存。这一步一定要记住设置,因为后面会设置响应函数,需要用到。
③设置“播放按钮图标”,点击“资源视图”中的“Toolbar”,"Toolbar"是设置图标的地方。点击“Toolbar”内部的“IDR_MAINFRAME”,会看到第一排是一些常用的图标,此处将这些图标删除,留下最后一个就可以。如果这里直接选中图标然后点击“Delete”会发现里面的图标是删除了,但是会遗留下很多白色的小方块。so如果要删除,那么要将第一排的小方块拖到第二排左边就删除了。
在最后一个小方块中,可以先用上面工具栏中的放大镜放大,之后使用画笔工具画一个“播放按钮”。此时,点击第一排绘制好的“播放按钮”,右击,选择属性,将ID仍然设置为“ID_ANIMATION”。
此时,运行结果图中的菜单栏已设置完成。
(3)有一个小的细节,结果图中的名称是“双缓冲算法”。那么它是如何设置的呢?
找到“解决方案资源管理器”,点击“DoubleBufferes.cpp”,找到第125行代码,在它下面加入一条语句,用于设置标题的名称,如下
m_pMainWnd->SetWindowTextW(CString("双缓冲算法"));
(4)由结果图可以看出小球有规律地在运动,so里面使用了一个定时器,那么就设置一个定时器
首先,点击"DoubleBufferesView.cpp"文件,在该文件中的构造函数中,写入如下代码,设置小球
1 CDoubleBufferesView::CDoubleBufferesView() noexcept
2 {
3 // TODO: 在此处添加构造代码
4 bPlay = FALSE;
5 sphere.R = GetSystemMetrics(SM_CXFULLSCREEN) / 20;
6 sphere.CenterPoint.x = 200, sphere.CenterPoint.y = 200;
7 direction.x = 1, direction.y = 1;
8
9 }
说明:
在第5行代码中,“GetSystemMetrics(SM_CXFULLSCREEN)”语句表示“客户区的宽度1280”。第4行先设置了一个动画开始按钮的标志,初始值设为“FALSE”。第5,6行设置小球对象的球心与半径,第7行设置x轴与y轴移动的步长。
绘制小球(DrawObject):
添加函数的步骤:
点击“项目”,点击“类向导”,在类名中,定位到“CDoubleBufferesView”。点击“方法”,添加方法,设置好函数名,返回值,参数。点击确定。代码如下:
1 void CDoubleBufferesView::DrawObject(CDC* pDC)//绘制图形
2 {
3 // TODO: 在此处添加实现代码.
4 sphere.Draw(pDC);
5 }
其次,设置定时器。
步骤:
①点击"项目",之后点击“类向导”。在弹出的“类向导”中,找到“类名(N)”,在下拉菜单中,定位到“CDoubleBufferesView”;之后点击“消息”,在输入框中输入“WM——TIMER”,点击它,右边有一个“添加处理程序”点击。
②同时设置映射函数,此时设置映射函数是为了在里面设置控制定时器的内容。点击“命令”找到刚刚设置的“ID_ANIMATION”,点击右边“消息”中的“COMMAND”,点击“添加处理程序”;统一的操作再来一次,这回“消息”设置为“UPDATE_COMMAND_UI”。
③最后,点击“确定”。
定时器内部的代码:
1 void CDoubleBufferesView::OnTimer(UINT_PTR nIDEvent)
2 {
3 // TODO: 在此添加消息处理程序代码和/或调用默认值
4 sphere.CenterPoint += direction;
5 Invalidate(FALSE);
6
7 CView::OnTimer(nIDEvent);
8 }
说明:
在第4行代码中,每次移动一次,则将小球中心点的位置增加一个步长的单位。第5行代码表示停止画面一直刷新。
OnAnimation函数中的代码:
1 void CDoubleBufferesView::OnAnimation()
2 {
3 // TODO: 在此添加命令处理程序代码
4 bPlay = !bPlay;
5 if (bPlay)
6 SetTimer(1, 10, NULL);
7 else
8 KillTimer(1);
9
10 }
说明:
该函数表明,如果动画按钮标志“bPlay”为“True”,即点击了单数次按钮,那么动画开始播放;如果动画按钮标志“bPlay”为“False”,即点击了双数次按钮,那么动画停止播放。
OnUpdateAnimation函数:
1 void CDoubleBufferesView::OnUpdateAnimation(CCmdUI *pCmdUI)
2 {
3 // TODO: 在此添加命令更新用户界面处理程序代码
4 if (bPlay)
5 pCmdUI->SetCheck(TRUE);
6 else
7 pCmdUI->SetCheck(FALSE);
8 }
(4)从运行结果图,可以看到当小球碰到客户区四周后,会反弹,那么当碰到后,会有一个检查函数,用来改变方向。碰撞函数“CollisionDetection”的添加,点击“项目”,点击“类向导”,在弹出的“类向导”中,定位“类名”为“CDoubleBufferesView”。点击“方法”,点击“添加方法”,设置“函数名”、“返回类型”、“参数”等,点击确定。
CollisionDetection函数
1 void CDoubleBufferesView::CollisionDetection(void)
2 {
3 // TODO: 在此处添加实现代码.
4 if (sphere.CenterPoint.x - sphere.R < 0)
5 direction.x = 1;
6 if (sphere.CenterPoint.x + sphere.R > nWidth)
7 direction.x = -1;
8 if (sphere.CenterPoint.y - sphere.R < 0)
9 direction.y = 1;
10 if (sphere.CenterPoint.y + sphere.R > nHeight)
11 direction.y = -1;
12 }
说明:
第4行是碰到左边框、第6行是碰到右边框、第8行是碰到上边框、第10行是碰到下边框。
当碰到左边框时,小球的x坐标向右增加。其他的同理。
(5)从运行结果图可以看出,小球的运动图像是平滑的,不会一帧一阵的闪,那是因为设置了双缓冲函数。
添加函数步骤添同添加碰撞函数类似。代码如下:
1 void CDoubleBufferesView::DoubleBuffer(CDC* pDC)//双缓冲绘图
2 {
3 // TODO: 在此处添加实现代码.
4 CRect rect;
5 GetClientRect(&rect);
6 nWidth = rect.Width(), nHeight = rect.Height();
7 CDC memDC;
8 memDC.CreateCompatibleDC(pDC);
9 CBitmap NewBitmap, *poldBitmap;
10 NewBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
11 memDC.FillSolidRect(rect, RGB(0, 0, 0));
12 poldBitmap = memDC.SelectObject(&NewBitmap);
13 DrawObject(&memDC);
14 CollisionDetection();
15 pDC->BitBlt(0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY);
16 memDC.SelectObject(poldBitmap);
17 NewBitmap.DeleteObject();
18 memDC.DeleteDC();
19 }
说明:
第4,5,6行代码是设置了一个客户区矩形。
第7行:定义内存DC。
第8行:创建一个与显示DC兼容的内存DC。
第9行设置一个新的矢量图和一个旧的矢量图指针。
第10行创建兼容内存位图。
第11行使用黑色填充客户区。
第12行将兼容位图选入内存DC。
第13行绘制小球。
第14行碰撞检测。
第15行显示内存位图。
第16,17,18恢复内存缓冲区,并删除新位图和缓冲区。
(6)最后在OnDraw函数中调用双缓冲函数,完成。
注意:此处参数中“pDC”是注释的,要先将注释去掉。
1 void CDoubleBufferesView::OnDraw(CDC* pDC)
2 {
3 CDoubleBufferesDoc* pDoc = GetDocument();
4 ASSERT_VALID(pDoc);
5 if (!pDoc)
6 return;
7
8 // TODO: 在此处为本机数据添加绘制代码
9 DoubleBuffer(pDC);
10 }
目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
我的Rails应用程序中安装了carrierwave。但是,当用户上传多页pdf时,我只希望应用程序获取文档中的第一页并将其转换为jpeg。这可能吗?用什么命令?这是我的uploader。#encoding:utf-8classImageUploader[200,300]##defscale(width,height)##dosomething#end#Createdifferentversionsofyouruploadedfiles:version:thumbdoprocess:resize_to_fill=>[150,210]process:convert=>:jpgdefful
有没有办法跳过CSV文件的第一行,让第二行作为标题?我有一个CSV文件,第一行是日期,第二行是标题,所以我需要能够在遍历它时跳过第一行。我尝试使用slice但它会将CSV转换为数组,我真的很想将其读取为CSV,以便我可以利用header。 最佳答案 根据您的数据,您可以使用另一种方法和skip_lines-option此示例跳过所有以#开头的行require'csv'CSV.parse(DATA.read,:col_sep=>';',:headers=>true,:skip_lines=>/^#/#Markcomments!)do|
我的任务是从数组中选择最高和最低的数字。我想我很清楚我想做什么,但只是努力以正确的格式访问信息以满足通过标准。defhigh_and_low(numbers)array=numbers.split("").map!{|x|x.to_i}array.sort!{|a,b|ba}putsarray[0,-1]end数字可能看起来像"80917234100",要通过,我需要输出"9234"。我正在尝试putsarray.first.last,但一直无法弄明白。 最佳答案 有Array#minmax完全满足您需要的方法:array=[80,
或者好像我必须自己写方法?(保持DHA不变):ruby-1.9.2-p180:001>s='omega-3(DHA)'=>"omega-3(DHA)"ruby-1.9.2-p180:002>s.capitalize=>"Omega-3(dha)"ruby-1.9.2-p180:003>s.titleize=>"Omega3(Dha)"ruby-1.9.2-p180:005>s[0].upcase+s[1..-1]=>"Omega-3(DHA)" 最佳答案 如果我的回答只是垃圾,我深表歉意(我不做ruby)。但我相信我已经为您找到了答
我有这个字符串:auteur="comtedeFlandreetHainaut,Baudouin,Jacques,Thierry"我想删除第一个逗号之前的所有内容,即在这种情况下保留“Baudouin,Jacques,Thierry”试过这个:nom=auteur.gsub(/.*,/,'')但这会删除最后一个逗号之前的每个逗号,只保留“Thierry”。 最佳答案 auteur.partition(",").last#=>"Baudouin,Jacques,Thierry" 关于rub
我有一个以时间戳为键的哈希。hash={"2016-05-31T22:30:58+02:00"=>{"path"=>"/","method"=>"GET"},"2016-05-31T22:31:23+02:00"=>{"path"=>"/tour","method"=>"GET"},"2016-05-31T22:31:05+02:00"=>{"path"=>"/contact_us","method"=>"GET"}}我订购了这个系列并得到了第一双这样的:hash.sort_by{|k,_|k}.first.first但是我该如何删除它呢?删除方法requiresyou知道key的准确
我有一个字符串数组,我需要从中提取第一个单词,将它们转换为整数并获得它们的总和。示例:["5Apple","5Orange","15Grapes"]预期输出=>25我的尝试:["5","5","15"].map(&:to_i).sum 最佳答案 我从你的问题中找到了答案。["5Apple","5Orange","15Grapes"].map(&:to_i).sum在数组中,如果存在任何整数可转换值,那么它将自动转换为整数。 关于arrays-字符串数组中字符串第一部分的总和,我们在Sta
1.问题描述使用Python的turtle(海龟绘图)模块提供的函数绘制直线。2.问题分析一幅复杂的图形通常都可以由点、直线、三角形、矩形、平行四边形、圆、椭圆和圆弧等基本图形组成。其中的三角形、矩形、平行四边形又可以由直线组成,而直线又是由两个点确定的。我们使用Python的turtle模块所提供的函数来绘制直线。在使用之前我们先介绍一下turtle模块的相关知识点。turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。面向对象的接口类如下:1)TurtleScreen类:定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas或ScrolledCanva