MFC绘制球体网格模型
参考孔令德老师的《计算几何算法与实现》
源码链接
使用双缓冲机制绘制中心位于窗口客户区中心的立方体线框模型,使用方向键旋转立方体,观察立方体的三维形状。
新建MFC单文档工程drawSphere
1、添加三维点类CP3
#pragma once
class CP3
{
public:
CP3(void);
~CP3(void);
CP3(double x,double y,double z);
public:
double x;
double y;
double z;
double w;
};
#include "StdAfx.h"
#include "P3.h"
CP3::CP3(void)
{
w=1;
}
CP3::~CP3(void)
{
}
CP3::CP3(double x,double y,double z)
{
this->x=x;
this->y=y;
this->z=z;
}
2、添加面表类CFacet
#pragma once
class CFacet
{
public:
CFacet(void);
~CFacet(void);
public:
int pNumber;//表面顶点数
int pIndex[4];//表面顶点索引号
};
#include "StdAfx.h"
#include "Facet.h"
CFacet::CFacet(void)
{
}
CFacet::~CFacet(void)
{
}
3、添加三维变换类CTransform3
#pragma once
#include "P3.h"
class CTransform3
{
public:
CTransform3(void);
~CTransform3(void);
void SetMatrix(CP3* P, int ptNumber);
void Identity();//单位矩阵
void Translate(double tx, double ty, double tz);//平移变换矩阵
void RotateX(double beta);//绕X轴旋转变换矩阵
void RotateY(double beta);//绕Y轴旋转变换矩阵
void RotateZ(double beta);//旋转变换矩阵
void MultiplyMatrix();//矩阵相乘
public:
double T[4][4];
CP3* P;
int ptNumber;
};
#include "StdAfx.h"
#include "Transform3.h"
#define PI 3.1415926
#include <math.h>
CTransform3::CTransform3(void)
{
}
CTransform3::~CTransform3(void)
{
}
void CTransform3::SetMatrix(CP3* P, int ptNumber)
{
this->P=P;
this->ptNumber=ptNumber;
}
void CTransform3::Identity()//单位矩阵
{
T[0][0]=1.0;T[0][1]=0.0;T[0][2]=0.0;T[0][3]=0.0;
T[1][0]=0.0;T[1][1]=1.0;T[1][2]=0.0;T[1][3]=0.0;
T[2][0]=0.0;T[2][1]=0.0;T[2][2]=1.0;T[2][3]=0.0;
T[3][0]=0.0;T[3][1]=0.0;T[3][2]=0.0;T[3][3]=1.0;
}
void CTransform3::Translate(double tx,double ty,double tz)//平移变换矩阵
{
Identity();
T[0][3]=tx;
T[1][3]=ty;
T[2][3]=tz;
MultiplyMatrix();
}
void CTransform3::RotateX(double beta)//绕X轴旋转变换矩阵
{
Identity();
double rad=beta*PI/180;
T[1][1]=cos(rad); T[1][2]=-sin(rad);
T[2][1]=sin(rad);T[2][2]=cos(rad);
MultiplyMatrix();
}
void CTransform3::RotateY(double beta)//绕Y轴旋转变换矩阵
{
Identity();
double rad=beta*PI/180;
T[0][0]=cos(rad);T[0][2]=sin(rad);
T[2][0]=-sin(rad);T[2][2]=cos(rad);
MultiplyMatrix();
}
void CTransform3::RotateZ(double beta)//绕Z轴旋转变换矩阵
{
Identity();
double rad=beta*PI/180;
T[0][0]=cos(rad); T[0][1]=-sin(rad);
T[1][0]=sin(rad);T[1][1]=cos(rad);
MultiplyMatrix();
}
void CTransform3::MultiplyMatrix()//矩阵相乘
{
CP3* PTemp=new CP3[ptNumber];
for(int i=0;i<ptNumber;i++)
PTemp[i]=P[i];
for(int j=0;j<ptNumber;j++)
{
P[j].x=T[0][0]*PTemp[j].x+T[0][1]*PTemp[j].y+T[0][2]*PTemp[j].z+T[0][3]*PTemp[j].w;
P[j].y=T[1][0]*PTemp[j].x+T[1][1]*PTemp[j].y+T[1][2]*PTemp[j].z+T[1][3]*PTemp[j].w;
P[j].z=T[2][0]*PTemp[j].x+T[2][1]*PTemp[j].y+T[2][2]*PTemp[j].z+T[2][3]*PTemp[j].w;
P[j].w=T[3][0]*PTemp[j].x+T[3][1]*PTemp[j].y+T[3][2]*PTemp[j].z+T[3][3]*PTemp[j].w;
}
delete []PTemp;
}
4、将View类修改如下:
包含头文件
#include "P3.h"
#include "Facet.h"
#include "Transform3.h"
#include"math.h"
添加成员属性
protected:
CP3 P[614];//点表
CFacet F[18][36];//面表
int N1,N2;//经纬线的数木
double Alpha,Beta;//绕x y轴旋转的角度
CTransform3 tran;//变换对象
初始化成员属性
CdrawSphereView::CdrawSphereView()
{
// TODO: 在此处添加构造代码
Alpha=0;
Beta=0;
ReadPoint();
ReadFacet();
tran.SetMatrix(P,614);
}
添加自定义宏
#define PI 3.1415926
#define ROUND(d) int(d+0.5)
添加成员函数ReadPoint
void CdrawSphereView::ReadPoint(void)
{
int sAlpha=10,sBeta=10;//维度方向与经度方向的等分线
double sAlpha1,sBeta1,r=200;//r为球半径
N1=180/sAlpha,N2=360/sBeta;
P[0].x=0;//计算北极点坐标
P[0].y=r;
P[0].z=0;
for(int i=0;i<N1-1;i++)
{
sAlpha1=(i+1)*sAlpha*PI/180;
for(int j=0;j<N2;j++)
{
sBeta1=j*sBeta*PI/180;
P[i*N2+j+1].x=r*sin(sAlpha1)*sin(sBeta1);
P[i*N2+j+1].y=r*cos(sAlpha1);
P[i*N2+j+1].z=r*sin(sAlpha1)*cos(sBeta1);
}
}
P[(N1-1)*N2+1].x=0;//计算南极点坐标
P[(N1-1)*N2+1].y=-r;
P[(N1-1)*N2+1].z=0;
}
添加成员函数ReadFacet
void CdrawSphereView::ReadFacet(void)
{
for(int i=0;i<N2;i++)//构造北极三角网格
{
int tempi=i+1;
if(N2==tempi)
tempi=0;
int NorthIndex[3];
NorthIndex[0]=0;
NorthIndex[1]=i+1;
NorthIndex[2]=tempi+1;
for(int k=0;k<3;k++)
{
F[0][i].pIndex[k]=NorthIndex[k];
F[0][i].pNumber=3;
}
}
for(int i=1;i<N1-1;i++)//构造球面四边形网格
{
for(int j=0;j<N2;j++)
{
int tempi=i+1;
int tempj=j+1;
if(tempj==N2)
tempj=0;
int BodyIndex[4];
BodyIndex[0]=(i-1)*N2+j+1;
BodyIndex[1]=(tempi-1)*N2+j+1;
BodyIndex[2]=(tempi-1)*N2+tempj+1;
BodyIndex[3]=(i-1)*N2+tempj+1;
for(int k=0;k<4;k++)
{
F[i][j].pIndex[k]=BodyIndex[k];
F[i][j].pNumber=4;
}
}
}
for(int j=0;j<N2;j++)//构造南极三角形网格
{
int tempj=j+1;
if(N2==tempj)
tempj=0;
int SouthIndex[3];
SouthIndex[0]=(N1-2)*N2+j+1;
SouthIndex[1]=(N1-1)*N2+1;
SouthIndex[2]=(N1-2)*N2+tempj+1;
for(int k=0;k<3;k++)
{
F[N1-1][j].pIndex[k]=SouthIndex[k];
F[N1-1][j].pNumber=3;
}
}
}
添加成员函数DrawGraph
void CdrawSphereView::DrawGraph(CDC*pDC)
{
CPoint Point3[3];//南北极顶点数
CPoint Point4[4];//球体顶点数
for(int i=0;i<N1;i++)
{
for(int j=0;j<N2;j++)
{
if(3==F[i][j].pNumber)//绘制三角形网格
{
for(int m=0;m<F[i][j].pNumber;m++)
{
Point3[m].x=ROUND(P[F[i][j].pIndex[m]].x);//正交投影
Point3[m].y=ROUND(P[F[i][j].pIndex[m]].y);
}
pDC->MoveTo(Point3[0].x,Point3[0].y);
pDC->LineTo(Point3[1].x,Point3[1].y);
pDC->LineTo(Point3[2].x,Point3[2].y);
pDC->LineTo(Point3[0].x,Point3[0].y);
}
else//绘制四边形网格
{
for(int m=0;m<F[i][j].pNumber;m++)
{
Point4[m].x=ROUND(P[F[i][j].pIndex[m]].x);
Point4[m].y=ROUND(P[F[i][j].pIndex[m]].y);
}
pDC->MoveTo(Point4[0].x,Point4[0].y);
pDC->LineTo(Point4[1].x,Point4[1].y);
pDC->LineTo(Point4[2].x,Point4[2].y);
pDC->LineTo(Point4[3].x,Point4[3].y);
pDC->LineTo(Point4[0].x,Point4[0].y);
}
}
}
}
添加成员函数DoubleBuffer
void CdrawSphereView::DoubleBuffer(CDC* pDC)
{
CRect rect;//定义客户区矩形
GetClientRect(&rect);//获得客户区的大小
pDC->SetMapMode(MM_ANISOTROPIC);//pDC自定义坐标系
pDC->SetWindowExt(rect.Width(),rect.Height());//设置窗口范围
pDC->SetViewportExt(rect.Width(),-rect.Height());//设置视区范围,x轴水平向右,y轴垂直向上
pDC->SetViewportOrg(rect.Width()/2,rect.Height()/2);//客户区中心为原点
CDC memDC;//内存DC
memDC.CreateCompatibleDC(pDC);//创建一个与显示pDC兼容的内存memDC
CBitmap NewBitmap,*pOldBitmap;//内存中承载的临时位图
NewBitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());//创建兼容位图
pOldBitmap=memDC.SelectObject(&NewBitmap);//将兼容位图选入memDC
memDC.FillSolidRect(rect,pDC->GetBkColor());//按原来背景填充客户区,否则是黑色
memDC.SetMapMode(MM_ANISOTROPIC);//memDC自定义坐标系
memDC.SetWindowExt(rect.Width(),rect.Height());
memDC.SetViewportExt(rect.Width(),-rect.Height());
memDC.SetViewportOrg(rect.Width()/2,rect.Height()/2);
rect.OffsetRect(-rect.Width()/2,-rect.Height()/2);
DrawGraph(&memDC);//向memDC绘制图形
pDC->BitBlt(rect.left,rect.top,rect.Width(),rect.Height(),&memDC,-rect.Width()/2,-rect.Height()/2,SRCCOPY);//将内存memDC中的位图拷贝到显示pDC中
memDC.SelectObject(pOldBitmap);//恢复位图
NewBitmap.DeleteObject();//删除位图
memDC.DeleteDC();//删除memDC
}
添加键盘响应事件
void CdrawSphereView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CView::OnKeyDown(nChar, nRepCnt, nFlags);
switch(nChar)
{
case VK_UP:
Alpha=+5;
tran.RotateX(Alpha);
break;
case VK_DOWN:
Alpha=-5;
tran.RotateX(Alpha);
break;
case VK_LEFT:
Beta=-5;
tran.RotateY(Beta);
break;
case VK_RIGHT:
Beta=5;
tran.RotateY(Beta);
break;
default:
break;
}
Invalidate(false);
}
最后再OnDraw函数中添加
// TODO: 在此处为本机数据添加绘制代码
DoubleBuffer(pDC);