MFC绘制球体网格模型

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);
  • 3
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值