.-
一、设计内容与要求
1.1、设计题目
算法实现时钟运动
1.2、总体目标和要求
(1)目标:以图形学算法为目标,深入研究。继而策划、设计并实现一个能够表现计算机图形学算法原理的或完整过程的演示系统,并能从某些方面作出评价和改进意见。通过完成一个完整程序,经历策划、设计、开发、测试、总结和验收各阶段,达到巩固和实践计算机图形学课程中的理论和算法;学习表现计算机图形学算法的技巧;培养认真学习、积极探索的精神。
(2)总体要求:策划、设计并实现一个能够充分表现图形学算法的演示系统,界面要求美观大方,能清楚地演示算法执行的每一个步骤。
(3)开发环境:Viusal C++ 6.0
1.3、设计要求
内容:
(1)掌握动画基本原理;
(2)实现平面几何变换;
功能要求:
(1)显示时钟三个时针,实现三根时针间的相互关系;
(2)通过右键菜单切换时钟背景与时针颜色;
1.4设计方案
通过使用OpenGL提供的标准库函数,综合图形学Bresenham画线和画圆的算法,OpenGL颜色模型中颜色表示模式等实现指针式时钟运动,并通过点击右键菜单实习时钟背景与时针颜色的转换。根据Bresenham画线和画圆的算法,画出时钟的指针和表盘。再根据OpenGL颜色模型定义当前颜色。设置当时钟运行时交换的菜单,运行程序时可变换时钟背景与时针的颜色。最后再设置一个恢复菜单恢复开始时表盘与指针的颜色。
二、总体设计
2.1、过程流程图
开始
画线
画椭圆
画表盘及各指针
预定义表盘、各指针的颜色
右键设置秒针颜色菜单可以改变秒针颜色
右键设置分针颜色菜单可以改变分针颜色
右键设置时针颜色菜单可以改变时针颜色
右键设置背景色菜单可以改变表盘颜色
右键恢复菜单可以恢复表盘及各指针原来颜色
结束
2.2、椭圆的中点生成算法
1、椭圆对称性质原理:
(1)圆是满足x轴对称的,这样只需要计算原来的1/2点的位置;
(2)圆是满足y轴对称的,这样只需要计算原来的1/2点的位置;
通过上面分析可以得到实际上我们计算椭圆生成时候,只需要计算1/4个椭圆就可以实现对于所有点的生成了。
2、中点椭圆算法内容:
(1)输入椭圆的两个半径r1和r2,并且输入椭圆的圆心。设置初始点(x0,y0)的位置为(0,r2);
(2)计算区域1中央决策参数的初始值
p = ry*ry - rx*rx*ry + 1/4*(rx*rx);
(3)在区域1中的每个Xn为止,从n = 0 开始,直到|K|(斜率)小于-1时后结束;
<1>如果p < 0 ,绘制下一个点(x+1,y),并且计算
p = p + r2*r2*(3+2*x);
<2>如果P >=0 ,绘制下一个点(x+1,y-1),并且计算
p = p + r2*r2*(3+2*point.x) - 2*r1*r1*(y-1)
(4)设置新的参数初始值;
p = ry*ry(X0+1/2)*(X0+1/2) + rx*rx*(Y0-1) - rx*rx*ry*ry;
(5)在区域2中的每个Yn为止,从n = 0开始,直到y = 0时结束。
<1>如果P>0的情况下,下一个目标点为(x,y-1),并且计算
p = p - 2rx*rx*(Yn+1) + rx*rx;
<2>如果p<=0的情况下,下一个目标点为(x+1,y-1),并且计算
p = p - 2rx*rx*Y(n+1) + 2ry*ry*(Xn+1)+rx*rx;
(6)更具对称性原理计算其他3个象限的坐标。
(7)急速拿出中心位置在(x1,y1)的位置x = x + x1; y = y + y1;
2.3、直线的Bresenham算法原理
这种生成直线的算法与数值微分法类似,每次迭代在增量最大方向上均走一步,其方向由增量的正负而定;另一方向上是否也走,取决于计算出来的误差项,误差项所记录的方向同最大增量方向垂直。下面讨论误差项,如图1 所示。
图1 误差项计算示意图
设图1 中直线满足:0,即:0,所以X为最大增量方向,有-=1,故有每点的坐标计算: (4)
因此直线上点的显示坐标为[,round()],round()表示最靠近y的整数。从图1可以看出,对于计算出来的(,)点,的取之为;计算出来的( ,)点,的取值为。其根据就是因为更靠近,更靠近。
图1 中A点为与的中心点,计算BC长度,若值大于0.5,说明在A点之上,应取,否则取值。
设误差: (5) 当,B点在A点上方,有;当<0,B点在A点下方,有。
由公式(4)(5)得:
(6)
2.4、图形变换基本原理
1、平移变换
平移变换函数如下:
void glTranslate{fd}(TYPE x, TYPE y, TYPE z);
三个函数参数就是目标分别沿三个轴向平移的偏移量。这个函数表示用于这三个偏移量生成的矩阵乘以当前矩阵。当参数是(0.0,0.0,0.0)时,表示对函数glTranslate*()的操作是单位矩阵,也就是对物体没有影响。
2、旋转变换
旋转变换函数如下:
Void glRota{fd}TYPE angle, TYPE x, TYPE y, TYPE z);
函数中第一个参数是表示目标沿从点(x,y,z)到原点方向逆时针旋转的角度,后三个参数是旋转的方向点坐标。这个函数表示用这四个参数生成的矩阵乘以当前矩阵。当角度参数是0.0时,表示对物体没有影响。
3、比例变换
比例变换函数如下:
Void glScale{fd}(TYPE x, TYPE y, TYPE z);
单个函数参数值就是目标分别沿三个轴方向缩放的比例因子。这个函数表示用这三个比例因子生成的矩阵乘以当前矩阵。这个函数能完成沿相应的轴对目标进行拉伸、压缩和反射三项功能。以参数x为例,若当x大于1.0时,表示沿x方向拉伸目标;若x小于1.0,表示沿x轴方向收缩目标;若x=-1.0表示沿x轴反射目标。其中参数为负值时表示对目标进行相应轴的反射变换。
三、详细设计
3.1、主程序代码及解释
#include "stdafx.h"
#include "Clock.h"
#include "math.h"
#include "ClockDoc.h"
#include "ClockView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CClockView
IMPLEMENT_DYNCREATE(CClockView, CView)
BEGIN_MESSAGE_MAP(CClockView, CView)
//{{AFX_MSG_MAP(CClockView)
ON_WM_TIMER()
ON_WM_CREATE()
ON_WM_CONTEXTMENU()
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
ON_COMMAND(ID_CONTEXT_BK,OnContextBK)
ON_COMMAND(ID_CONTEXT_HOUR,OnContextHour)
ON_COMMAND(ID_CONTEXT_MINUTE,OnContextMinute)
ON_COMMAND(ID_CONTEXT_SECOND,OnContextSecond)
ON_COMMAND(ID_CONTEXT_RESTORE,OnContextRestore)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CClockView construction/destruction
CClockView::CClockView()
{ // TODO: add construction code here
brushColor = RGB(240,255,255);
HourColor = RGB(0,255,0);
MinuteColor = RGB(0,0,255);
SecondColor = RGB(255,0,255);
}
CClockView::~CClockView(){}
BOOL CClockView::PreCreateWindow(CREATESTRUCT& cs)
{ // TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return CView::PreCreateWindow(cs);
}
/////////////////////////////////////////////////////////////////////////////
// CClockView drawing
void CClockView::OnDraw(CDC* pDC)
{ CClockDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
//获取客户区
RECT Rect;
GetClientRect(&Rect);
//计算椭圆中心位置
int CenterX = Rect.right/2;
int CenterY = Rect.bottom/2;
//取当前时间
CTime Time = CTime::GetCurrentTime();
CString str;
int i,x,y;
CSize size;
//创建一支黄色的笔,用来画椭圆
CPen Pen(PS_SOLID,5,RGB(0,0,0));
//设置当前画笔,并记下以前的画笔
CPen *OldPen = pDC->SelectObject(&Pen);
CBrush brush(brushColor);
pDC ->SelectObject(&brush);
//绘制钟面椭圆
pDC->Ellipse(5,5,Rect.right-5,Rect.bottom-5);
double Radians;
//设置字体颜色为红色
pDC->SetTextColor(RGB(255,0,0));
for(i = 1;i <= 12;i++){
//格式化钟点值
str.Format("%d",i);
size = pDC->GetTextExtent(str,str.GetLength());
Radians = (double)i*6.28/12.0;
//计算钟点放置的位置
x = CenterX - (size.cx/2) + (int)((double)(CenterX - 20)*
sin(Radians));
y = CenterY - (size.cy/2) - (int)((double)(CenterY - 20)*
cos(Radians));
//绘制钟点
pDC->TextOut(x,y,str);
}
//计算时钟指针的夹角
Radians = (double)Time.GetHour() + (double)Time.GetMinute()/60.0 +
(double)Time.GetSecond()/3600.0;
Radians *= 6.28/12.0;
//创建时钟指针画笔
CPen HourPen(PS_SOLID,5,HourColor);
pDC->SelectObject(&HourPen);
//绘制时钟指针
pDC->MoveTo(CenterX,CenterY);
pDC->LineTo(CenterX + (int)((double)(CenterX/3)*sin(Radians)),
CenterY - (int)((double)(CenterY/3)*cos(Radians)));
Radians = (double)Time.GetMinute()+(double)Time.GetSecond()/60.0;
Radians *= 6.28/60.0;
//创建分钟指针画笔
CPen MinutePen(PS_SOLID,3,MinuteColor);
pDC->SelectObject(&MinutePen);
//绘制分钟指针
pDC->MoveTo(CenterX,CenterY);
pDC->LineTo(CenterX + (int)((double)(CenterX*2)/3)*sin(Radians),
CenterY - (int)((double)(CenterY*2/3)*cos(Radians)));
Radians = (double)Time.GetSecond();
Radians *= 6.28/60.0;
//创建秒钟指针画笔
CPen SecondPen(PS_SOLID,1,SecondColor);
pDC->SelectObject(&SecondPen);
//绘制秒钟指针
pDC->MoveTo(CenterX,CenterY);
pDC->LineTo(CenterX + (int)((double)(CenterX*4)/5)*sin(Radians),
CenterY - (int)((double)(CenterY*4)/5*cos(Radians)));
pDC->SelectObject(OldPen);
}
/////////////////////////////////////////////////////////////////////////////
// CClockView printing
BOOL CClockView::OnPreparePrinting(CPrintInfo* pInfo)
{ // default preparation
return DoPreparePrinting(pInfo);
}
void CClockView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{ // TODO: add extra initialization before printing
}
void CClockView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{ // TODO: add cleanup after printing
}
////////////////////////////////////////////////////////////////////////////
// CClockView diagnostics
#ifdef _DEBUG
void CClockView::AssertValid() const
{ CView::AssertValid();
}
void CClockView::Dump(CDumpContext& dc) const
{CView::Dump(dc);
}
CClockDoc* CClockView::GetDocument() // non-debug version is inline
{ ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CClockDoc)));
return (CClockDoc*)m_pDocument;
}
#endif //_DEBUG
///////////////////////////////////////////////////////////////////////////
// CClockView message handlers
void CClockView::OnTimer(UINT nIDEvent)
{ // TODO: Add your message handler code here and/or call default
InvalidateRect(NULL,true);
UpdateWindow();
CView::OnTimer(nIDEvent);
}
int CClockView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{ if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
SetTimer(1,1000,NULL);
return 0;
}
void CClockView::OnContextMenu(CWnd* pWnd, CPoint point)
{ // TODO: Add your message handler code here
CMenu menu;
menu.LoadMenu(IDR_CONTEXTMENU);
CMenu* pContextMenu = menu.GetSubMenu(0);
pContextMenu ->TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RIGHTBUTTON,point.x,point.y,AfxGetMainWnd());
}
void CClockView::OnContextBK()
{ brushColor = RGB(255,228,181);
}
void CClockView::OnContextHour()
{ HourColor = RGB(255,0,0);
}
void CClockView::OnContextMinute()
{ MinuteColor = RGB(128,128,0);
}
void CClockView::OnContextSecond()
{ SecondColor = RGB(0,192,255);
}
void CClockView::OnContextRestore()
{ brushColor = RGB(240,255,255);
HourColor = RGB(0,255,0);
MinuteColor = RGB(0,0,255);
SecondColor = RGB(255,0,255);
四、程序运行说明
4.1程序运行结果分析
单击运行,生成一个椭圆表盘时钟,其表盘与各指针颜色各不相同,用右键五个菜单修改表盘与指针颜色,菜单“设置背景色”来改变表盘颜色、菜单“设置时针颜色”来改变时针指针颜色、菜单“设置分针颜色”来改变分针指针颜色、菜单“设置秒针颜色”来改变秒针指针颜色、用“恢复”菜单来还原成表盘与指针原来的颜色。改变后的颜色与原来颜色也不各不相同,改变后只能是指定的颜色,不足就是没有对话框多种颜色用来选择,颜色有点单一,用恢复菜单可以恢复原来颜色。因为程序里调用了windows的时间函数,所以显示的时间永远跟电脑时间同步。此结果的实现是通过MFC方法实现的。基本上达到了要求,但不足之处仍有很多,因为个人能力有限。
4.2 程序运行结果截图
4.2.1 开始时运行时效果
4.2.2 右键单步修改颜色效果
4.2.3 右键修改颜色后效果
五、总结
5.1系统不足
在本次课程设计中,设计方案存在着很多的死板化的实现方法,也是很不人性化的一点,就是生成结果后通过右键改变表盘与指针的颜色的实现方法,只有一种颜色改变的方案,没有设置对话框来设置多种颜色供选择的,所以改编后的颜色比较单一,此为本次设计方案的不足之一;然后就是表盘的形状是椭圆形的,有点不符合常理,因为现实里的时钟都是圆形的,不过个人认为椭圆可以设计出来,圆形的肯定更容易设计,因为圆是椭圆的特殊情况,只要通过修改个别参数就可以改变此效果,此为设计方案不足之二。再者就是表盘中没有设置分钟小刻度,此为设计方案不足之三。鉴于个人能力真的有限,所以只能设计出此种级别的效果了。因为实验重要的只是思想过程,效果的不美观只能是因为学习的知识还远远不够,所以不能设计出像现实里的时钟一样的效果。
5.2 改进方案
通过修改我的程序中的右键更改颜色的函数,更改成生成效果是用对话框提示替换颜色的函数就可以达到可以选择改变的颜色了,查找程序中的此函数进行修改,可以达到选择颜色修改的效果。至于把表盘修改成圆形的,通过修改生成表盘的函数参数就可以达到预期修改效果。
5.3 心得体会
在本次课程设计过程中,基本掌握了 OpenGL提供的标准库函数,Bresenham画线和画椭圆的方法,了解的很多有关于图形学的知识。不过都只是皮毛而已,从中锻炼了自己的动手做实验的能力,但同时也让自己看清了自己的水平,以便在以后的生活里多加强有关这方面的学习,从而提升自己在图形学方面的知识水平。
参考文献
1.杨旭等. Visual C++编程篇[M].北京:电子工业出版社, 2004.
2.朱亚平等. OpenGL编程实例[M].北京:人民邮电出版社,1999.
3.倪明田,吴良芝. 计算机图形学[M]. 北京:北京大学出版社,2000.
4.彭达等. Visual C++多媒体编程技术[M]. 北京:人民邮电出版社,1999.
展开阅读全文