实验五:图形变换
一、实验目的:
1、掌握图形变换的基本方法。
2、初步掌握映射菜单消息和捕获键盘消息的方法。
二、实验内容及要求:
1、
2、
3、 以三角形为例,使用Visual C++实现二维图形的平移、旋转和缩放功能。 每人单独完成实验。 按要求撰写实验报告,写出实验心得,并在实验报告中附上程序的核心算法代码。
三、实验设备:
微机,Visual C++6.0
四、实验内容和步骤:
1、 打开VC,新建一个MFC Appwizard项目,选择创建单文档工程(SDI工程)。假设工程名为Transform。如图1和图2所示。
图1
图2
2、 在图2的界面上点击Finish,完成工程的创建。
3、 在TransformView.h文件中,加入如下代码:
public:
CPoint Pt[3]; //存储三角形的三个顶点
float dAngle; //存储三角形旋转的角度
4、 在类CTransformView的构造函数中定义三角形的三个顶点的初始坐标和dAngle的初值,代码如
下;
CTransformView::CTransformView()
{
// TODO: add construction code here
Pt[0].x = 200; Pt[0].y = 220;
Pt[1].x = 260; Pt[1].y = 300;
Pt[2].x = 360; Pt[2].y = 180;
dAngle = 0.0;
}
5、 在类CTransformView中添加成员函数void DrawTriangle(CDC *pDC),并实现该函数。(该部分代
码请同学们自己实现,为了简便编程,可以使用MoveTo和LineTo函数,也可以调用自己在实验2中编写的DDA或者Bresenham画线函数);
6、 在类CTransformView的OnDraw()函数中添加绘制三角形的代码;
void CTransformView::OnDraw(CDC* pDC)
{
CTransformDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
DrawTriangle(pDC);
}
7、 映射菜单消息,方法是打开ResourceView菜单,依次展开MENU \ IDR_MAINFRAME,添加“图
形变换”主菜单项,在其下添加“平移”,如图3所示。
图3
8、 在TransformView.cpp文件中(类CTransformView中)映射平移的菜单消息,触发平移函数。代
码如下:
void CTransformView::OnMenuTransformTranslate()
{
// TODO: Add your command handler code here
int nX = 50;
int nY = 80; //平移的X坐标和Y坐标
for(int i=0; i<3; i++)
{
Pt[i].x += nX;
Pt[i].y += nY;
}
RedrawWindow(); //该行代码必不可少,用于触发OnDraw()函数
}
9、 与平移类似,请同学们自己依次添加“缩放”和“旋转”菜单,并实现相应的“缩放”菜单和“旋
转”菜单的消息映射函数。
10、 添加捕获键盘的消息,实现用键盘控制三角形的平移、缩放和旋转。在TransformView.cpp
文件中单击鼠标右键,选择ClassWizard,如图4所示,为键盘消息WM_KEYDOWN添加消息映射函数,代码如下:
void CTransformView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
int i=0;
CPoint TmpPt = Pt[0];
switch (nChar){
case VK_UP: //用上箭头控制三角形向上移动
for(i=0; i<3; i++)
{
Pt[i].y -= 5;
}
break;
case VK_DOWN: //用下箭头控制三角形向下移动
for(i=0; i<3; i++)
{
Pt[i].y += 5;
} } break; 。。。。。。 // } RedrawWindow(); CView::OnKeyDown(nChar, nRepCnt, nFlags);
图4
11、 请同学们在WM_KEYDOWN的消息映射函数中,再自己实现用左、右箭头控制三角形移动
的代码。左右箭头的虚键分别为VK_LEFT, VK_RIGHT。
12、 请同学们在WM_KEYDOWN的消息映射函数中,再自己实现用Z和X键控制三角形放大1
倍和缩小1倍的代码。在消息映射函数中,按下Z键,说明nChar=Z的ASCII码;其他字母相同。
13、 请同学们在WM_KEYDOWN的消息映射函数中,再自己实现用R键控制三角形旋转的代
码。多按几下R键,看看会发生什么情况?为什么会这样?如何解决?
五、核心代码:
这里将自己编写的上机程序中的主要代码拷贝粘贴过来;
Ondraw:
void CShiyqan5View::OnDraw(CDC* pDC)
{
CShiyqan5Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
DrawTriangle(pDC);
}
用DDA画出三角形
void CShiyqan5View::DrawTriangle(CDC *pDC)
{
DDALine(Pt[0].x,Pt[0].y,Pt[1].x,Pt[1].y,RGB(0,147,145));
DDALine(Pt[1].x,Pt[1].y,Pt[2].x,Pt[2].y,RGB(0,147,145));
DDALine(Pt[2].x,Pt[2].y,Pt[0].x,Pt[0].y,RGB(0,147,145));
}
void CShiyqan5View::DDALine(int x0, int y0, int x1, int y1, COLORREF color) {
CDC *pDC = this->GetDC();
int i;
float dx, dy, length,x,y;
if (fabs(x1-x0)>=fabs(y1-y0))
length=fabs(x1-x0);
else
length=fabs(y1-y0);
dx= (x1-x0)/length;
dy=(y1-y0)/length;
i=1;x= x0;y= y0;
while(i<=length)
{
pDC->SetPixel(int (x + 0.5), int (y + 0.5), color);
x=x+dx;
y=y+dy;
i++;
}
实现图形平移
void CShiyqan5View::OnMenuTransformTranslate()
{
// TODO: Add your command handler code here
int nX = 50;
int nY = 80; //平移的X坐标和Y坐标
for(int i=0; i<3; i++)
{
Pt[i].x += nX;
Pt[i].y += nY;
}
RedrawWindow();
}
缩放
void CShiyqan5View::Onsuofang()
{
int nX=200;
int nY=200;
for(int i=0 ;i<3;i++)
{
Pt[i].x=Pt[i].x*0.5;
Pt[i].y=Pt[i].y*0.5;
Pt[i].x+=nX;
Pt[i].y+=nY;
}
RedrawWindow();
}
旋转
void CShiyqan5View::Onxuanzhuan()
{
double dAngle=0.1;
for(int i=0;i<3;i++)
{
Pt[i].x=Pt[i].x*cos(dAngle)-Pt[i].y*sin(dAngle);
Pt[i].y=Pt[i].x*sin(dAngle)+Pt[i].y*cos(dAngle);
}// TODO: Add your command handler code here
RedrawWindow();
}
键盘
void CShiyqan5View::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
// TODO: Add your message handler code here and/or call default int i=0;
int nX=200;
int nY=200;
CPoint TmpPt = Pt[0];
switch (nChar){
case VK_UP: //用上箭头控制三角形向上移动 for(i=0; i<3; i++)
{
Pt[i].y -= 5;
}
break;
case VK_DOWN: //用下箭头控制三角形向下移动 for(i=0; i<3; i++)
{
Pt[i].y += 5;
}
break;
case VK_LEFT: //用上箭头控制三角形向左移动 for(i=0; i<3; i++)
{
Pt[i].x -= 5;
}
break;
case VK_RIGHT: //用下箭头控制三角形向右移动 for(i=0; i<3; i++)
{
Pt[i].x += 5;
}
break;
case 90: //Z缩小
for(i=0 ;i<3;i++)
{
Pt[i].x=Pt[i].x*0.5;
Pt[i].y=Pt[i].y*0.5;
Pt[i].x+=nX;
Pt[i].y+=nY;
}
break;
case 88://x放大
for(i=0 ;i<3;i++)
{
Pt[i].x=Pt[i].x*2.0;
Pt[i].y=Pt[i].y*2.0;
Pt[i].x+=nX;
Pt[i].y+=nY;
}
break;
case 82://R的旋转
double circular=15*3.1415/180;
double x1=(Pt[0].x)*cos(circular)-(Pt[0].y)*sin(circular);
double y1=(Pt[0].x)*sin(circular)+(Pt[0].y)*cos(circular);
double x2=(Pt[1].x)*cos(circular)-(Pt[1].y)*sin(circular);
double y2=(Pt[1].x)*sin(circular)+(Pt[1].y)*cos(circular);
double x3=(Pt[2].x)*cos(circular)-(Pt[2].y)*sin(circular);
double y3=(Pt[2].x)*sin(circular)+(Pt[2].y)*cos(circular);//进行逆时针旋转变化 double sX=Pt[0].x-x1;
double sY=Pt[0].y-y1;
x1+=sX;
y1+=sY;
x2+=sX;
y2+=sY;
x3+=sX;
y3+=sY;
Pt[0].x=x1;
Pt[0].y=y1;
Pt[1].x=x2;
Pt[1].y=y2;
Pt[2].x=x3;
Pt[2].y=y3; }
break;
}
RedrawWindow();
CView::OnKeyDown(nChar, nRepCnt, nFlags);
});
六、屏幕显示结果:
这里将绘出的图形界面拷贝下来;
画出三角形:
缩小一半:
旋转
实现向上移动:
实现向下移动:
向左
键盘向右
实现Z的缩放
实现X的放大
实现R的旋转
七、思考题(尽量做,计入成绩):
1、将三角形A(250,200),B(100,100),C(300,200)绕P(200,100)点旋转45°,求旋转后的三角形的坐标。 要求画出原三角形、坐标系、点P,旋转后得到的新三角形。――要求上机完成
2、如何较好地绘出一个平面直角坐标系?
(1)Scale法
通过自定义左上角和右下角坐标来设置新的坐标系统。用户通过自定义坐标时,一般先选定ScaleMode=0,然后设定用户自定义坐标ScaleMode=3,最后用Scale方法定义坐标系。
(2)通过对象属性ScaleLeft和ScaleTop直接赋值自定义坐标系
3、在Turbo C中如果要编写菜单的实现程序,是什么步骤?
首先建立类向导,然后在Project的Cshiyan5View下的,Message是command下建立一个新的成员函数。然后设好ID,并且名字可以改成想要的。建好后就可以在函数体中添加代码,来实现菜单。 例如:
void CShiyqan5View::OnMenuTransformTranslate()
{
// TODO: Add your command handler code here
int nX = 50;
int nY = 80; //平移的X坐标和Y坐标
for(int i=0; i<3; i++)
{
Pt[i].x += nX;
Pt[i].y += nY;
}
RedrawWindow();
4、如果OnKeyDown函数中不加RedrawWindow()语句,会出现什么结果?为什么?
RedrawWindow()是用来触发Ondraw函数的,如果不用则无法进行调用。
5、为什么旋转程序的结果和预计的不一样?
因为P点是左上角的一个点,随着三角形的旋转,围绕着的中心点不断向外延伸出去。这样就和预想的结果有些不同了。
八、实验体会
通过这次试验,我学到了图形变换的基本方法,同时初步掌握映射菜单消息和捕获键盘消息的方法。根据实验指导书的要求,以三角形为例,使用Visual C++实现二维图形的平移、旋转和缩放功能。
在映射平移的菜单消息中,触发平移函数。使用RedrawWindow();这条语句,该行代码必不可少,用于触发OnDraw()函数。然后在实现缩放和平移的内容。
在做实验时我一直编译不通过,经过老师的提点之后,发现我一开始是在Frame下建立类向导,而实际是应该在View下建立,但是我只是把相应的代码从程序中删除了,而没有实际把他们删掉,使得程序总是编译不通过。经过老师的提点后,发现其实错误根本还没有完全删除,再改正之后,程序才得以编译成功。
添加捕获键盘的消息,在实现键盘上下左右的位移,通过上面的实验指导书中的内容加以改编,可以完成,还需加上case 和要用的按钮。在添加ZXR的虚键时,算了下他们相对应的ASCII码,之后进行编写代码,把之前做的缩放、旋转加进去,这样即可实现。
按照实验内容,共做了13步实验,并加以实现。
九、注意及特别说明
1、 实验内容及步骤中的前9步为必须完成的实验内容,第10步至第13步,请同学们根据自己的兴趣选
做;
2、 如对Visual C++的编译环境不熟悉,请课下提前参考Visual C++的相关书籍和MSDN。
第二篇:计算机图形学投影变换
计算机图形学实验报告
实验三 三维图形的投影变换
学
专
指
成
生业导、姓班教名 级 师 绩
电子与信息工程系
2010 年 11月 6日
一、实验目的:
绘制三维物体的轴测投影图和一点透视图 ,在程序中给定控制多面体的顶点坐标及点线信息。视点固定在z轴某点上,输入物体的旋转角度及平移量。正轴测投影图和一点透视图。
二、题目:
三维图象的基本变换
三、设计思想:
因为电脑显示的是2维持坐标图象,所以在设计时要将三维的图象坐标转换成电脑能显示的二维持坐标图,然后根据图象的基本变换矩阵计算出变换后的坐标点,最后绘制出图象变换效果。
四、原程序:
CPoint dian22[8];//六面体的8个2维坐标点
CPoint dian[8]; float coordinate2[8][3];
float coordinate3[8][3] 六面体的8个3维坐标点
Void draw6angle()//绘制一个六面方体。
dc.MoveTo(0,0);//yuan dian zuo biao dian(400,400)
dc.LineTo(400,0);//x //绘制一x轴,Y,Z轴
dc.MoveTo(0,0); dc.LineTo(0,400);//y dc.MoveTo(50,50);//z dc.LineTo(-200,-200); dc.MoveTo(dian[0]);//0-1 dc.LineTo(dian[1]); dc.MoveTo(dian[0]);//0-3 dc.LineTo(dian[3]); dc.MoveTo(dian[0]);//0-7 dc.LineTo(dian[7]);
} dc.MoveTo(dian[4]);//4-3 dc.LineTo(dian[3]); dc.MoveTo(dian[4]);//4-5 dc.LineTo(dian[5]); dc.MoveTo(dian[4]);//4-7 dc.LineTo(dian[7]); dc.MoveTo(dian[6]);//6-5 dc.LineTo(dian[5]); dc.MoveTo(dian[6]);//6-7 dc.LineTo(dian[7]); dc.MoveTo(dian[6]);//6-1 dc.LineTo(dian[1]); dc.MoveTo(dian[2]);//2-1 dc.LineTo(dian[1]); dc.MoveTo(dian[2]);//2-3 dc.LineTo(dian[3]); dc.MoveTo(dian[2]);//2-5 dc.LineTo(dian[5])}
void CCBod_3DView::switch_point()//将三维坐标点转换成电脑上能表示的二维坐标点
{ float canshu=0.7071;
for(int i=0;i<8;i++)
{ dian[i].x=float (coordinate3[i][0]+float((-coordinate3[i][2]*canshu))); dian[i].y=float (coordinate3[i][1]+float((-coordinate3[i][2]*canshu))); } }
void CCBod_3DView::OnMoveOnz() //图象在Z轴上的移动。
{
// TODO: Add your command handler code here for(int i=0;i<8;i++)
{
coordinate3[i][2]=coordinate3[i][2]+50; }
this->Invalidate();
}
void CCBod_3DView::OnSpinz() //Z轴上旋转
{ // TODO: Add your command handler code here
{ coordinate3[i][0]=float(coordinate3[i][0]*b)+float(coordinate3[i][1]*(-a)); coordinate3[i][1]=float(coordinate3[i][0]*a)+coordinate3[i][1]*b; } float b=cos(0.23); float a=sin(0.23); for(int i=0;i<8;i++)
OnLarge();
this->Invalidate(); }
void CCBod_3DView::OnDUICHeng() //关于X对称 {// TODO: Add your command handler code here
for(int i=0;i<8;i++) {coordinate3[i][2]= -coordinate3[i][2]; } this->Invalidate(); }
void CCBod_3DView::OnZhengzhouce() //轴测投影图 {// TODO: Add your command handler code here
if(flage==false) { flage=true; }
else
{ flage=false;
}
float dian3[8][3];
for(int i=0;i<8;i++)
{
dian3[i][0]=coordinate3[i][0];
dian3[i][1]=coordinate3[i][1];
dian3[i][2]=coordinate3[i][2];
}
float a,b,c;
float angle_a=3.1415/4;
float angle_b=3.1415/6;
for(int j=0;j<8;j++)
{
a=dian3[j][0];
b=dian3[j][1];
c=dian3[j][2];
dian3[j][0]=float ( a*cos(angle_a) -b*sin(angle_a) );
dian3[j][1]=0;
dian3[j][2]= float( -sin(angle_b)*( a*sin(angle_a)+b*cos(angle_a) + c*cos(angle_b) ); }
float canshu=0.7071;
for(int i2=0;i2<8;i2++)
{ dian22[i2].x=float (dian3[i2][0]+float((-dian3[i2][2]*canshu))); dian22[i2].y=float (dian3[i2][1]+float((-dian3[i2][2]*canshu))); }
this->Invalidate();
} )
void CCBod_3DView::OnONEView() //一点透视图象 {
}
五、运行结果:
1.初始化界面。
// TODO: Add your command handler code her this->Invalidate(); if(m_toushi==false) { m_toushi=true; }else { m_toushi=false;}
2.沿Z轴旋转的效果
3放大效果
4 旋转效果
5对称
6.轴侧投影图
7.一点透视