VC++课程设计文档Word下载.docx
- 文档编号:19203564
- 上传时间:2023-01-04
- 格式:DOCX
- 页数:37
- 大小:227.66KB
VC++课程设计文档Word下载.docx
《VC++课程设计文档Word下载.docx》由会员分享,可在线阅读,更多相关《VC++课程设计文档Word下载.docx(37页珍藏版)》请在冰豆网上搜索。
2.4附加功能
(1)可选择打开或关闭工具栏。
(2)应用程序的标题栏上有程序的图标。
(3)将图形转换成位图文件的形式保存。
(4)在选择一个图形元素后(如直线),会有进一步选择线型或线宽的界面。
(5)仿Word,选择“线型”、“粗细”图标后,会出现进一步选择。
3.系统设计与实现
3.1SetROP2实现重绘,消除闪烁
在画图状态下,鼠标移动时既要擦除旧图形,又要绘制新图形。
这里主要有两种实现方法:
一是全部重绘,二是先擦除旧图形。
如果使用矢量图全部重绘,频繁的绘图动作消耗很大,很容易造成屏幕闪动。
但是如果将已有图形保存为位图,然后重绘的时候只要绘制位图即可,这样能避免闪动。
第二种方法要考虑的就是擦除旧图形的问题,本程序使用SetROP2函数设置MASK的方式,每次绘图时采用非异或运算的方式擦除旧图形:
pDC->
SetROP2(R2_NOTXORPEN);
//设置ROP2
DrawStroke(pDC);
//画图擦除旧线(自定义函数)
SetCurrentPoint(point);
//设置新点的坐标(自定义函数)
//画新线(自定义函数)
3.2嵌套View实现画布
m_drawView=newCHDrawView();
//创建画布View
if(!
m_drawView->
CreateEx(WS_EX_LEFT|WS_EX_LTRREADING|WS_EX_RIGHTSCROLLBAR,
AfxRegisterWndClass(CS_VREDRAW|CS_HREDRAW,LoadCursor(NULL,IDC_CROSS),
(HBRUSH)GetStockObject(WHITE_BRUSH),NULL),///白色画布
"
"
WS_CHILDWINDOW|WS_VISIBLE|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
m_tracker.m_rect.left,m_tracker.m_rect.top,
m_tracker.m_rect.right-1,m_tracker.m_rect.bottom-1,
this->
m_hWnd,NULL)){
TRACE0("
Failedtocreatetoolbar\n"
);
return-1;
//failtocreate
}
m_drawView->
SetDocument((CHDrawDoc*)m_pDocument);
//传递CDocument给新View
ShowWindow(SW_NORMAL);
UpdateWindow();
//设置背景View颜色为灰色
SetClassLong(m_hWnd,GCL_HBRBACKGROUND,(long)GetStockObject(GRAY_BRUSH));
3.3鼠标靠近目标时突出显示
在鼠标移动的时候,OnMouseMove函数会遍历已有图形,判断鼠标所在点是否属于已有图形范围,如果是,则高亮显示该图形。
高亮显示的方法比较简单,只要增加CRectTracker即可,而判断当前点是否属于某图形比较有意思:
3.3.1判断一点是否属于矩形HStrokeRect
使用用MFC的CRect类的IsPointIn方法,当鼠标在矩形边框附近时,认为该点属于HStrokeRect。
如图,实线矩形表示HStrokeRect。
外矩形为外面的虚线矩形,内矩形为里面的虚线矩形:
BOOLHStrokeRect:
:
IsPointIn(constCPoint&
point){
//矩形左上角x坐标
intx1=m_points.GetAt(0).x<
m_points.GetAt
(1).x?
m_points.GetAt(0).x:
m_points.GetAt
(1).x;
//矩形左上角y坐标
inty1=m_points.GetAt(0).y<
m_points.GetAt
(1).y?
m_points.GetAt(0).y:
m_points.GetAt
(1).y;
//矩形右下角x坐标
intx2=m_points.GetAt(0).x>
//矩形右下角y坐标
inty2=m_points.GetAt(0).y>
//构建外矩行和内矩形
CRectrect(x1,y1,x2,y2),rect2(x1+5,y1+5,x2-5,y2-5);
//如果在外矩形内并在内矩形外
if(rect.PtInRect(point)&
&
!
rect2.PtInRect(point))
returnTRUE;
else
returnFALSE;
}
3.3.2判断一点是否属于线段
首先判断一点是否属于这条线段所属的直线,根据直线的判定公式y1/x1=y2/x2得到x1*y2-x2*y1=0,但是在画图中应该在直线附近就能选中,所以在本程序中:
|x1*y2-x2*y1|<
偏差,然后判断该点是否属于这条线段。
//计算该点到线段HStrokeLine的两个顶点的线段(x1,y1),(x2,y2)
intx1=point.x-m_points.GetAt(0).x;
intx2=point.x-m_points.GetAt
(1).x;
inty1=point.y-m_points.GetAt(0).y;
inty2=point.y-m_points.GetAt
(1).y;
//计算判断量x1*y2-x2*y1
intmeasure=x1*y2-x2*y1;
//误差允许范围,也就是直线的“附近”
intrule=abs(m_points.GetAt
(1).x-m_points.GetAt(0).x)
+abs(m_points.GetAt(0).y-m_points.GetAt
(1).y);
rule*=m_penWidth;
//将线宽考虑进去
//属于直线
if(measure<
rule&
measure>
-rule){
//判断该点是否属于这条线段
if(x1*x2<
0)
returnTRUE;
;
returnFALSE;
3.3.3判断一点是否属于椭圆
根据椭圆的定义椭圆上的点到椭圆的两个焦点的距离之和为2a,首先计算出椭圆的a,b,c,然后计算出椭圆的两个焦点。
针对某个点,首先根据点坐标和两个焦点的坐标计算出该点到椭圆焦点的距离,然后减去2a,如果在“附近”,则认为其属于HStrokeEllipse,否则不属于。
//计算椭圆的a,b,c
int_2a=abs(m_points.GetAt(0).x-m_points.GetAt
(1).x);
int_2b=abs(m_points.GetAt(0).y-m_points.GetAt
(1).y);
doublec=sqrt(abs(_2a*_2a-_2b*_2b))/2;
//计算椭圆的焦点
doublex1,y1,x2,y2;
if(_2a>
_2b){//横椭圆
x1=(double)(m_points.GetAt(0).x+m_points.GetAt
(1).x)/2-c;
x2=x1+2*c;
y1=y2=(m_points.GetAt(0).y+m_points.GetAt
(1).y)/2;
else{//纵椭圆
_2a=_2b;
x1=x2=(m_points.GetAt(0).x+m_points.GetAt
(1).x)/2;
y1=(m_points.GetAt(0).y+m_points.GetAt
(1).y)/2-c;
y2=y1+2*c;
//点到两个焦点的距离之和,再减去2a
//distance(point-p1)+distance(point-p2)=2*a;
doublemeasure=sqrt((x1-point.x)*(x1-point.x)+(y1-point.y)*(y1-point.y))
+sqrt((point.x-x2)*(point.x-x2)+(point.y-y2)*(point.y-y2))
-_2a;
//计算椭圆的“附近”
doublerule=4*m_penWidth;
-rule)
3.4文档序列化
MFC提供了良好的序列化机制,只要在类定义时加入DECLARE_SERIAL宏,在类构造函数的实现前加入IMPLEMENT_SERIAL宏,然后实现Serialize方法即可。
本程序即使用该方法序列化:
首先在CHDrawDoc类实现Serialize方法,保存画布大小和所有图形信息:
voidCHDrawDoc:
Serialize(CArchive&
ar)
{
if(ar.IsStoring())
{
//保存时,首先保存画布高和宽,然后序列化所有图形
ar<
<
m_cavasH<
m_cavasW;
m_strokeList.Serialize(ar);
//打开时,首先打开画布高和宽,然后打开所有图形
ar>
>
m_cavasH>
m_strokeList.Serialize(ar);
这一句很强大,容器类会自动序列化容器内的元素数量,并调用每个元素的序列化方法序列化,所以还需要对每个图形元素实现序列化,以HStrokeLine为例:
在HStrokeLine的类声明中:
classHStrokeLine:
publicHStroke
public:
HStrokeLine();
DECLARE_SERIAL(HStrokeLine)
然后在HStrokeLine的构造函数实现前:
IMPLEMENT_SERIAL(HStrokeLine,CObject,1)
HStrokeLine:
HStrokeLine()
m_picType=PIC_line;
最后实现HStrokeLine的序列化函数,因为这里HStrokeLine集成自HStroke类而且没有特殊的属性,而HStroke类实现了Serialize函数,所以HStrokeLine类不需要实现Serilize方法,看一下HStroke的Serialize方法即可:
voidHStroke:
if(ar.IsStoring()){
intenumIndex=m_picType;
enumIndex<
m_penWidth<
m_penColor;
m_points.Serialize(ar);
else{
intenumIndex;
enumIndex>
m_penWidth>
m_picType=(enumHPicType)enumIndex;
3.5新建打开保存
文档序列化实现以后,程序的打开和保存功能就已经完成了。
但是从序列化方法可以看出,打开和保存的都是矢量图形,所以这里实现了一个导出为BMP图像的方法,导出:
//保存文件对话框,选择导出路径
CFileDialogdlg(FALSE,"
bmp"
"
hlj.bmp"
if(dlg.DoModal()!
=IDOK){
return;
}
CStringfilePath=dlg.GetPathName();
//
CClientDCclient(this);
//
CDCcdc;
CBitmapbitmap;
RECTrect;
CRectr;
GetClientRect(&
rect);
intcx=rect.right-rect.left;
intcy=rect.bottom-rect.top;
bitmap.CreateCompatibleBitmap(&
client,cx,cy);
cdc.CreateCompatibleDC(NULL);
//获取BMP对象
CBitmap*oldbitmap=(CBitmap*)cdc.SelectObject(&
bitmap);
//白色画布
cdc.FillRect(&
rect,CBrush:
FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
//画图
for(inti=0;
i<
GetDocument()->
m_strokeList.GetSize();
i++){
GetDocument()->
m_strokeList.GetAt(i)->
DrawStroke(&
cdc);
cdc.SelectObject(oldbitmap);
:
OpenClipboard(this->
m_hWnd);
EmptyClipboard();
SetClipboardData(CF_BITMAP,bitmap);
CloseClipboard();
HBITMAPhBitmap=(HBITMAP)bitmap;
HDChDC;
intiBits;
WORDwBitCount;
DWORDdwPaletteSize=0,dwBmBitsSize=0,dwDIBSize=0,dwWritten=0;
BITMAPBitmap;
BITMAPFILEHEADERbmfHdr;
BITMAPINFOHEADERbi;
LPBITMAPINFOHEADERlpbi;
HANDLEfh,hDib,hPal,hOldPal=NULL;
hDC=CreateDC("
DISPLAY"
NULL,NULL,NULL);
iBits=GetDeviceCaps(hDC,BITSPIXEL)*GetDeviceCaps(hDC,PLANES);
DeleteDC(hDC);
if(iBits<
=1)wBitCount=1;
elseif(iBits<
=4)wBitCount=4;
=8)wBitCount=8;
elsewBitCount=24;
GetObject(hBitmap,sizeof(Bitmap),(LPSTR)&
Bitmap);
bi.biSize=sizeof(BITMAPINFOHEADER);
bi.biWidth=Bitmap.bmWidth;
bi.biHeight=Bitmap.bmHeight;
bi.biPlanes=1;
bi.biBitCount=wBitCount;
bi.biCompression=BI_RGB;
bi.biSizeImage=0;
bi.biXPelsPerMeter=0;
bi.biYPelsPerMeter=0;
bi.biClrImportant=0;
bi.biClrUsed=0;
dwBmBitsSize=((Bitmap.bmWidth*wBitCount+31)/32)*4*Bitmap.bmHeight;
hDib=GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER));
lpbi=(LPBITMAPINFOHEADER)GlobalLock(hDib);
*lpbi=bi;
hPal=GetStockObject(DEFAULT_PALETTE);
if(hPal)
hDC=:
GetDC(NULL);
hOldPal=:
SelectPalette(hDC,(HPALETTE)hPal,FALSE);
RealizePalette(hDC);
GetDIBits(hDC,hBitmap,0,(UINT)Bitmap.bmHeight,(LPSTR)lpbi+sizeof(BITMAPINFOHEADER)
+dwPaletteSize,(BITMAPINFO*)lpbi,DIB_RGB_COLORS);
if(hOldPal)
:
SelectPalette(hDC,(HPALETTE)hOldPal,TRUE);
ReleaseDC(NULL,hDC);
fh=CreateFile(filePath,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if(fh==INVALID_HANDLE_VALUE)
return;
bmfHdr.bfType=0x4D42;
//"
BM"
dwDIBSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwPaletteSize+dwBmBitsSize;
bmfHdr.bfSize=dwDIBSize;
bmfHdr.bfReserved1=0;
bmfHdr.bfReserved2=0;
bmfHdr.bfOffBits=(DWORD)sizeof(BITMAPFILEHEADER)+(DWORD)sizeof(BITMAPINFOHEADER)+dwPaletteSize;
WriteFile(fh,(LPSTR)&
bmfHdr,sizeof(BITMAPFILEHEADER),&
dwWritten,NULL);
WriteFile(fh,(LPSTR)lpbi,dwDIBSize,&
GlobalUnlock(hDib);
GlobalFree(hDib);
CloseHandle(fh);
3.6友好用户界面
菜单项选中和工具栏图标下沉。
第一步:
增加3个菜单项
名称ID
直线ID_DRAW_LINE
椭圆ID_DRAW_ELLIPSE
矩形ID_DRAW_RECT
第二步:
在工具栏上增加3个工具栏项,注意ID要和上面的三个ID相同。
第三步:
在CHDrawDoc类的ClassWizard中增加消息响应函数,分别为以上三个ID增加COMMAND和UPDATE_COMMAND_UI的Handler,COMMAND的Handler就是针对按下工具栏按钮或菜单项的响应函数,而UPDATE_COMMAND_UI则是显示菜单栏时执行的操作,有点类似OnDraw。
以直线为例,ID_DRAW_LINE的COMMAND的Handler为OnDrawLine
OnDrawLine()
//设置当前画图的图形类型为直线
ID_DRAW_LINE的UPDATE_COMMAND_UI的Handler为OnUpdateDrawLine:
OnUpdateDrawLine(CCmdUI*pCmdUI)
//如果当前画图类型为直线,设置菜单项前加对号,工具栏项下沉
pCmdUI->
SetCheck(PIC_line==m_picType);
3.7右键菜单修改选中图形的属性
实现方法如下:
在资源视图中增加一个菜单
在CHDrawVie
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- VC 课程设计 文档