您的位置:首页 > 其它

图形学课程设计总结

2009-10-22 16:45 253 查看
马上要毕业的人的,学校给开了一些奇怪的课程,如java,asp.net,图形学,人工智能,信息安全.真不知道学校到底怎么想的.这些课程可以说都是很好的课程,学校早干嘛去了,现在才开.郁闷啊,对于图形学,人工智能,信息安全,只能用剩下的时间学个皮毛了,了解了解也是挺不错的..而至于Java,asp.net就算了,一直坚持学习C/C++,现在再去学那些,到毕业也只是个皮毛,对于找工作也没多大帮助,就不在在哪方面下劲了.这不,图形学老师布置了一个任务,实现画直线,画圆,填充,裁剪的算法,并演示出来。花了一段时间做了这个程序。在此做一下总结,因为从里面学到了一些东西.
首先说一下自己做的程序:
界面:

设个界面是动态生成的,包括那个画卷和字体。其中只是用到了定时器和一些画图的东西.
本想弄一只白鸽飞翔着拉开这个画卷的,却不知咋的,图片弄上去
总是不合自己的意思.看多遍都没明白.只好放弃的那个想法.
各个模块:
(画直线)
(画圆)

(填充)

裁剪:

介绍完毕.现在开始自己的总结:
1设置窗口的图标

针对于文档的应用程序:
方法1:
在CmainFrame窗口类中,添加下列代码:
HICONhIcon=AfxGetApp()->LoadIcon(IDI_ICON1);
SetIcon(hIcon,TRUE);
IDI_ICON1为自定义图标ID.

方法2:
单击Wordspace窗口中的ResourceView标签,选中资源ID为IDR_MAINFRAM图标资源,这个是系统默认的图标资源.默认的图标就是这个。现在我们把它删除掉,然后定义我们自己的图标资源来替换掉他.不过要记得将我们自定义的图标资源的名字改为IDR_MAINFRAM.也可以改为AFX_IDI_STD_MDIFRAME(如果是MDI应用程序)或改为AFX_IDI_STD_FRAME(如果是SDI应用程序).这两个资源ID是系统预定义了的.然后编译运行就OK了.

针对于基于对话框的应用程序:
方法1:
针对于基于对话框的程序用一个更加便宜的方法:
在对话框的构造函数中有这样一句:
m_hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);
直接将IDR_MAINFRAME换成你自定义的图标ID就可以了。
方法2:
上面的方法2同样适用于这里.

静态设置图标,上面的两种方法已经很够用了,如果我们想动态的改变图标呢,如点击一个按钮,或是在打开一个对话框的时候改变主窗口的图标.我们可以这样做:
HICONhIcon=AfxGetApp()->LoadIcon(IDI_ICON1);
ASSERT(hIcon);
AfxGetMainWnd()->SendMessage(WM_SETICON,TRUE,(LPARAM)hIcon);
//给系统发送一个WM_SETICON消息.对于前面的那个AfxGetMainWnd()函数,它是一个全局函数,返回值为CWnd*用来获得一个窗口类指针,这个函数可以用到任何地方,因为它是全局的嘛.但在主窗口中我们就不必用这个函数了,应为自己调用自己的函数,当然不用什么对象调用了啊,内部调用嘛.

如果朋友不喜欢发送消息,这样使用也是OK的.
HICONhIcon=AfxGetApp()->LoadIcon(IDI_ICON1);
ASSERT(hIcon);
AfxGetMainWnd()->SetIcon(hIcon,TRUE);
2设置窗口背景图片

对于这个问题一直是一个很烦人的问题,我一直都是在用填充的方法来解决这个问题.我的方法如下:
CRectrect;
GetClientRect(&rect);
CBitmapbitmap;
bitmap.LoadBitmap(IDB_BITMAP1);
CBrushbrush;
brush.CreatePatternBrush(&bitmap);
dc.FillRect(&rect,&brush);
这个方法一直用着还不错,就没另寻它法,不过我在网上看到了这样一篇文章,说的很是不错,这里推荐一下:

问题是这样产生的.在OnEraseBkGnd中,如果你不调用原来缺省

的OnEraseBkGnd只是重画背景则不会有闪烁.而在OnPaint里面,

由于它隐含的调用了OnEraseBkGnd,而你又没有处理OnEraseBkGnd

函数,这时就和窗口缺省的背景刷相关了.缺省的

OnEraseBkGnd操作使用窗口的缺省背景刷刷新背景(一般情况

下是白刷),而随后你又自己重画背景造成屏幕闪动.

另外一个问题是OnEraseBkGnd不是每次都会被调用的.如果你

调用Invalidate的时候参数为TRUE,那么在OnPaint里面隐含

调用BeginPaint的时候就产生WM_ERASEBKGND消息,如果参数

是FALSE,则不会重刷背景.


所以解决方法有三个半:

1.用OnEraseBkGnd实现,不要调用原来的OnEraseBkGnd函数.

2.用OnPaint实现,同时重载OnEraseBkGnd,其中直接返回.

3.用OnPaint实现,创建窗口时设置背景刷为空

4.用OnPaint实现,但是要求刷新时用Invalidate(FALSE)这样

的函数.(不过这种情况下,窗口覆盖等造成的刷新还是要闪一

下,所以不是彻底的解决方法)

都挺简单的.


从这篇文章中我们都应该明白WM_PAINT消息处理函数为什么会使得我们写上去的的文字或是画上去的图片消失的原因的.另外我们还应该明白一个东西,就是很多时候我们在OnPaint()函数中重绘了我们想要的文字或是图片.其他一切正常,并且也达到了我们满意的效果,可是有时窗口会出现闪动的现象.从这篇文章中我们应该明白原因了.即这个函数每次在刷新我们的窗口时默认调用了OnEraseBkGnd,这个函数使用默认的画刷即白色画刷来刷新我们的窗口。至于怎么改变这个画刷,我找到了这样一个函数:
BOOLCreateSysColorBrush(intnIndex);
介绍如下:
Initializesabrushcolor.Thebrushcansubsequentlybeselectedasthecurrentbrushforanydevicecontext.
nIndex
Specifiesthehatchstyleofthebrush.Itcanbeanyoneofthefollowingvalues:

HS_BDIAGONALDownwardhatch(lefttoright)at45degrees

HS_CROSSHorizontalandverticalcrosshatch

HS_DIAGCROSSCrosshatchat45degrees

HS_FDIAGONALUpwardhatch(lefttoright)at45degrees

HS_HORIZONTALHorizontalhatch

HS_VERTICALVerticalhatch

然而不管我如何定义画刷,该画刷都不会被系统使用来刷新背景.不只是何原因.可问题还是被解决了,我众里寻他千”Google”+“百度”.终于让我找到了,原来我们注册窗口类的时候,WNDCLASS结构体中有这么一个成员:
typedefstruct_WNDCLASS{
UINTstyle;
WNDPROClpfnWndProc;
intcbClsExtra;
intcbWndExtra;
HINSTANCEhInstance;
HICONhIcon;
HCURSORhCursor;
HBRUSHhbrBackground;
LPCTSTRlpszMenuName;
LPCTSTRlpszClassName;
}WNDCLASS,*PWNDCLASS;
我们只需修改这个成员就可以了,在这里我们可以定义我们自己的画刷.在WIN32程序下可以这么修改,对于MFC程序就不清楚怎么获得这个结构体了.好啦,言归正传。我们还把话题放到窗口背景的设置上.
1.用OnEraseBkGnd实现,不要调用原来的OnEraseBkGnd函数.

2.用OnPaint实现,同时重载OnEraseBkGnd,其中直接返回.

3.用OnPaint实现,创建窗口时设置背景刷为空

4.用OnPaint实现,但是要求刷新时用Invalidate(FALSE)这样

的函数.(不过这种情况下,窗口覆盖等造成的刷新还是要闪一

下,所以不是彻底的解决方法)

都挺简单的.

这个是上面文章中提到的方法,这里仅仅提到了实现的地方,并没有提到实现的方法.有点遗憾,如果哪位朋友知道其他的方法不妨分享一下.

3改变对话框中控件的颜色
这个需要为对话框重载这么一个函数.OnCtlColor(….).
重载这个函数之后我们就会发现系统为我们生成了这样一句话:
//TODO:Returnadifferentbrushifthedefaultisnotdesired
即在这里我们可以创建我们自己的画刷来改变控件的颜色,并返回他即可.这样我们就改变了控件的背景颜色.如果我们想改变特定空间的背景色可以这样做:
if(pWnd->GetDlgCtrlID()==IDC_DISPLAYLINECOLOR)
{
hbr=CreateSolidBrush(m_LineColor);
pDC->SetBkColor(m_LineColor);
returnhbr;
}
这个问题的引出是我设置了一个设置颜色的对话框,这个对话框是这样的:

我使用了静态文本来显示用户当前设置的颜色,这样我们就需要为特定的控件设置特定的背景色的.这个功能需要当用户点击颜色设置对话框的确定按钮后,静态文本能够显示用户刚刚设定的颜色,所以那里就需要调用一个:
InvalidateRect(NULL,TRUE);函数来刷新窗口.
如果我们想让空间背景透明可以使用:
pDC->SetBkMode(TRANSPARENT);
设置空间字体颜色:
pDC->SetTextColor(RGB(245,0,0));

4画圆的时候遇到的一个问题
如果哪位朋友用过画图工具的话,你们都应该知道画图工具里画圆可以随着鼠标的移动画圆,即当我们按下鼠标左键并移动的时候就会有圆来响应我们的鼠标的移动,从而给我们提供一个参考.我想在我的画圆功能中提供这样一个功能,当然这个功能需要相应一些消息.关键是实现:
实现方法有两种:
方法1:
每次鼠标移动时都要刷新窗口,以抹去鼠标上个位置画出来的那个圆,然后画当前位置的圆即可.
方法2:
每次鼠标移动的时候,我们都要使用背景色的画笔来重画鼠标上个位置的那个圆,用来抹去。然后再用自定义画刷或默认画刷话当前位置的圆即可.
两种方法看似都很好,可是如你使用过第一种方法后就会知道,它的窗口的刷新率是很大的,以至于我们都无法看清当前位置的圆,除非鼠标停下。所以我选择了使用方法2.这个方法使用起来很好.所说多做了一些工作,但相比刷新窗口会好很多.

5递归问题
递归是一个很耗费时间和空间的东西,虽说有些问题离了他不行。但若可以使用非递归能解决的问题,我们应该尽量避免使用递归.这里举两个例子就很清楚了:
第一个例子:
四连通递归填充算法:
voidFloodPadRegion(CDC*dc,intx,inty)
{
if(dc->GetPixel(x,y)==RGB(255,255,255))
{
dc->SetPixel(x,y,m_PadColor);
FloodPadRegion(dc,x,y+1);
FloodPadRegion(dc,x,y-1);
FloodPadRegion(dc,x-1,y);
FloodPadRegion(dc,x+1,y);
}
}
我们使用这个函数去填充局域,如果区域稍微大点,程序就会被自动关闭。原因是它导致大量的递归调用,使栈溢出。为了证明这个问题,我们再来看另一个递归的例子,我们对此算法进行改进,即不再是一个点一次的递归,而是一条线一次的递归,这算法是我对这个算法的改进:
voidScanfLinePadRegion(CDC*dc,intx,inty)
{
CPointpt(x,y);
intxr,xl;
while(GetPixel(dc->m_hDC,x,y)==RGB(255,255,255))//向右填充
{
dc->SetPixel(x,y,m_PadColor);
x++;
}
xr=x-1;
x=pt.x-1;
while(GetPixel(dc->m_hDC,x,y)==RGB(255,255,255))//向左填充
{
dc->SetPixel(x,y,m_PadColor);
--x;
}
xl=x+1;
x=xl;
if(GetPixel(dc->m_hDC,xl,pt.y+1)==RGB(255,255,255))
{
ScanfLinePadRegion(dc,xl,pt.y+1);
}
else
{
y=pt.y+1;
x=xl;
while(GetPixel(dc->m_hDC,x,y)!=RGB(255,255,255)&&x<xr)
{
++x;
}
if(x<xr)
{
ScanfLinePadRegion(dc,x,y);
}
}
if(GetPixel(dc->m_hDC,xl,pt.y-1)==RGB(255,255,255))
{
ScanfLinePadRegion(dc,pt.x,pt.y-1);
}
else
{
y=pt.y-1;
x=xl;
while(GetPixel(dc->m_hDC,x,y)!=RGB(255,255,255)&&x<xr)
{
++x;
}
if(x<xr)
{
ScanfLinePadRegion(dc,x,y);
}
}
}
使用这个函数,我用来填充整个屏幕都不会发生栈溢出的现象,由此可见,递归是一个很耗费资源的东西。虽然本函数也使用了递归,但是它比上一个函数改进了很多。其实我觉得更好的方法是书本山的扫描线算法,它使用栈完成了整个填充的过程,虽说他也会占用很多的内存单元,但我认为会比递归好的多.

好了,先总结这么多吧,再想出其它问题再补上.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: