其他分享
首页 > 其他分享> > MFC绘制任意B样条曲面

MFC绘制任意B样条曲面

作者:互联网

MFC绘制任意B样条曲面


在这里插入图片描述
黑色为控制点形成的控制点网,红色为绘制的3*2次B样条曲面

参考《计算几何算法与实现》–孔令德

给定,u、v方向的控制点数与坐标及次数,根据Hartely-Judd法计算控制节点,根据de-Boor递推公式绘制曲面;完整的绘制类代码如下

#pragma once
#include"P3.h"

class CBSplineSurface
{
public:
	CBSplineSurface(void);
	~CBSplineSurface(void);

	void Initialize(CP3** ptr,int m,int p,int n,int q);//初始化
	void GetKnotVector(double* T,int nCount,int num,int order, BOOL bU);//获取节点矢量
	void DrawCurvedSurface(CDC* pDC);//绘制曲面
	double BasisFunctionValue(double u,int i,int k,double* T);//根据节点矢量T,计算基函数
	void DrawControlGrid(CDC* pDC);//绘制控制网格
	CP2 ObliqueProjection(CP3 Point3);//斜二测投影
public:
	int m,p;//u向的顶点数减1与次数    
	int n,q;//v向的顶点数减1与次数
	double **V;//u向节点矢量数组
	double **U;//v向节点矢量数组
	CP3 **P;//三维控制点
};



#include "StdAfx.h"
#include "BSPlineSurface.h"
#include<math.h>
#define ROUND(d) int(d+0.5)
#define PI 3.1415926

CBSplineSurface::CBSplineSurface(void)
{
	P = NULL;
	U = NULL;
	V = NULL;

	// TODO: add construction code here
	int m2=4, p2=3;//u向顶点数5个,曲线次数为3
	int n2=3, q2=2;//v向顶点数4个,曲线次数为2
	CP3** P3_2=new CP3*[n2+1];//建立动态二维数组
	for(int i=0;i<n2+1;i++)
		P3_2[i]=new CP3[m2+1];
	P3_2[0][0]=CP3(20,0,200),   P3_2[0][1]=CP3(100,100,150),P3_2[0][2]=CP3(230,130,140),   P3_2[0][3]=CP3(350,100,150),P3_2[0][4]=CP3(450,30,150);
	P3_2[1][0]=CP3(0,100,150),  P3_2[1][1]=CP3(30,100,120), P3_2[1][2]=CP3(110,120,80),    P3_2[1][3]=CP3(200,150,50), P3_2[1][4]=CP3(300,150,50);
	P3_2[2][0]=CP3(-130,100,50),P3_2[2][1]=CP3(-40,120,50), P3_2[2][2]=CP3(30,150,30),     P3_2[2][3]=CP3(50,150,0),   P3_2[2][4]=CP3(150,200,0);
	P3_2[3][0]=CP3(-250,50,0),  P3_2[3][1]=CP3(-150,100,0), P3_2[3][2]=CP3(-100,150,-50),  P3_2[3][3]=CP3(0,150,-70),  P3_2[3][4]=CP3(80,220,-70);
	Initialize(P3_2,n2,q2,m2,p2);
}

CBSplineSurface::~CBSplineSurface(void)
{
	if(NULL != P)
	{
		for(int i=0;i<n+1;i++)
		{
			delete []P[i];
			P[i]=NULL;
		}
		delete []P;
		P=NULL;
	}
	if(NULL!=U)
	{
		for(int i=0;i<n+1;i++)
		{
			delete []U[i];
			U[i]=NULL;
		}
		delete []U;
		U=NULL;
	}
	if(NULL!=V)
	{
		for(int i=0;i<m+1;i++)
		{
			delete []V[i];
			V[i]=NULL;
		}
		delete []V;
		V=NULL;
	}
}

void CBSplineSurface::Initialize(CP3** ptr,int n,int q,int m,int p)//初始化
{
	P=new CP3*[n+1];//建立动态二维数组
	for(int i=0;i<n+1;i++)
		P[i]=new CP3[m+1];
	for(int i=0;i<n+1;i++)//二维数组初始化
		for(int j=0;j<m+1;j++)
			P[i][j]=ptr[i][j];
	this->m=m,this->p=p;
	this->n=n,this->q=q;
	U=new double*[n+1];//建立u向节点矢量动态数组
	for(int i=0;i<n+1;i++)
		U[i]=new double[m+p+2];	
	V=new double*[m+1];//建立v向节点矢量动态数组
	for(int i=0;i<m+1;i++)
		V[i]=new double[n+q+2];
}

void CBSplineSurface::DrawCurvedSurface(CDC* pDC)//绘制曲面
{
	for(int i=0;i<m+1;i++)
		GetKnotVector(V[i],i,n,q, FALSE);//获取V
	for(int i=0;i<n+1;i++)
		GetKnotVector(U[i],i,m,p, TRUE);//获取U
	CPen NewPen(PS_SOLID,1,RGB(255,0,0));//曲线颜色
	CPen* pOldPen=pDC->SelectObject(&NewPen);
	double Step=0.1;//步长
	for(double u=0.0;u<=1.0;u+=Step)//绘制v向曲线
	{
		for(double v=0.0;v<=1.0;v+=Step)
		{
			CP3 pt(0,0,0);
			for(int i=0;i<n+1;i++)
			{
				for(int j=0;j<m+1;j++)
				{
					double BValueU=BasisFunctionValue(u,j,p,U[i]);
					double BValueV=BasisFunctionValue(v,i,q,V[j]);
					pt=pt+P[i][j]*BValueU*BValueV;
				}
			}
			CP2 Point2=ObliqueProjection(pt);//斜投影
			if (v==0.0)
				pDC->MoveTo(ROUND(Point2.x),ROUND(Point2.y));
			else
				pDC->LineTo(ROUND(Point2.x),ROUND(Point2.y));
		}
	}
	for(double v=0.0;v<=1.0;v+=Step)//绘制u向曲线
	{
		for(double u=0.0;u<=1.0;u+=Step)
		{
			CP3 pt(0,0,0);
			for(int i=0;i<n+1;i++)
			{		
				for(int j=0;j<m+1;j++)
				{		
					double BValueU=BasisFunctionValue(u,j,p,U[i]);
					double BValueV=BasisFunctionValue(v,i,q,V[j]);
					pt=pt+P[i][j]*BValueU*BValueV;
				}
			}
			CP2 Point2=ObliqueProjection(pt);//斜投影
			if (u==0.0)
				pDC->MoveTo(ROUND(Point2.x),ROUND(Point2.y));
			else
				pDC->LineTo(ROUND(Point2.x),ROUND(Point2.y));
		}
	}
	pDC->SelectObject(pOldPen);
	NewPen.DeleteObject();
}

void CBSplineSurface::DrawControlGrid(CDC* pDC)//绘制控制网格
{
	CP2** P2=new CP2*[n+1];
	for(int i=0;i<n+1;i++)
		P2[i]=new CP2[m+1];
	for(int i=0;i<n+1;i++)
		for(int j=0;j<m+1;j++)
			P2[i][j]=ObliqueProjection(P[i][j]);
	CPen NewPen,*pOldPen;
	NewPen.CreatePen(PS_SOLID,3,RGB(0,0,0));
	pOldPen=pDC->SelectObject(&NewPen);
	for(int i=0;i<n+1;i++)
	{
		pDC->MoveTo(ROUND(P2[i][0].x),ROUND(P2[i][0].y));
		for(int j=1;j<m+1;j++)
			pDC->LineTo(ROUND(P2[i][j].x),ROUND(P2[i][j].y));
	}
	for(int j=0;j<m+1;j++)
	{
		pDC->MoveTo(ROUND(P2[0][j].x),ROUND(P2[0][j].y));
		for(int i=1;i<n+1;i++)
			pDC->LineTo(ROUND(P2[i][j].x),ROUND(P2[i][j].y));
	}
	pDC->SelectObject(pOldPen);
	NewPen.DeleteObject();
	if(NULL!=P2)
	{
		for(int i=0;i<n+1;i++)
		{
			delete []P2[i];
			P2[i] = NULL;
		}
		delete []P2;
		P2 = NULL;
	}
}

void CBSplineSurface::GetKnotVector(double* T,int nCount,int num,int order, BOOL bU)//Hartley-Judd算法获取节点矢量数组
{
	for(int i=0;i<=order;i++) //小于曲线次数order的节点值为0
		T[i]=0.0;
	for(int i=num+1;i<=num+order+1;i++)//大于控制点个数num的节点值为1
		T[i]=1.0;
	//计算num-order个内节点
	for(int i=order+1;i<=num;i++)  
	{
		double sum=0.0;
		for(int j=order+1;j<=i;j++)
		{ 
			double numerator=0.0;
			for(int loop=j-order;loop<=j-1;loop++)
			{
				if(bU)
					numerator+=(P[nCount][loop].x-P[nCount][loop-1].x)*(P[nCount][loop].x-P[nCount][loop-1].x)+
					           (P[nCount][loop].y-P[nCount][loop-1].y)*(P[nCount][loop].y-P[nCount][loop-1].y);
				else
					numerator+=(P[loop][nCount].x-P[loop-1][nCount].x)*(P[loop][nCount].x-P[loop-1][nCount].x)+
					           (P[loop][nCount].y-P[loop-1][nCount].y)*(P[loop][nCount].y-P[loop-1][nCount].y);
			}
			double denominator=0.0;//计算分母
			for(int loop1=order+1;loop1<=num+1;loop1++)
			{
				for(int loop2=loop1-order;loop2<=loop1-1;loop2++)
				{
					if(bU)
						denominator+=(P[nCount][loop2].x-P[nCount][loop2-1].x)*(P[nCount][loop2].x-P[nCount][loop2-1].x)+(P[nCount][loop2].y-P[nCount][loop2-1].y)*(P[nCount][loop2].y-P[nCount][loop2-1].y);
					else
						denominator+=(P[loop2][nCount].x-P[loop2-1][nCount].x)*(P[loop2][nCount].x-P[loop2-1][nCount].x)+(P[loop2][nCount].y-P[loop2-1][nCount].y)*(P[loop2][nCount].y-P[loop2-1][nCount].y);
				}
			} 
			sum+=numerator/denominator;			
		}
		T[i]=sum;
	}
}

double CBSplineSurface::BasisFunctionValue(double t,int i,int order,double* T)//计算B样条基函数
{
	double value1,value2,value;
	if(order==0)
	{
		if(t>=T[i]&&t<T[i+1])
			return 1.0;
		else
			return 0.0;
	}
	if(order>0)
	{
		if(t<T[i]||t>T[i+order+1])
			return 0.0;
		else
		{
			double coffcient1,coffcient2;//凸组合系数1,凸组合系数2
			double denominator=0.0;//分母
			denominator=T[i+order]-T[i];//递推公式第一项分母
			if(0.0==denominator)//约定0/0
				coffcient1=0.0;
			else
				coffcient1=(t-T[i])/denominator;
			denominator=T[i+order+1]-T[i+1];//递推公式第二项分母
			if(0.0==denominator)//约定0/0
				coffcient2=0.0;
			else
				coffcient2=(T[i+order+1]-t)/denominator;
			value1=coffcient1*BasisFunctionValue(t,i,order-1,T);//递推公式第一项的值
			value2=coffcient2*BasisFunctionValue(t,i+1,order-1,T);//递推公式第二项的值
			value=value1+value2;//基函数的值
		}
	}
	return value;
}


CP2 CBSplineSurface::ObliqueProjection(CP3 Point3)//斜二测投影
{
	CP2 Point2;
	Point2.x=Point3.x-Point3.z*sqrt(2.0)/2.0;
	Point2.y=Point3.y-Point3.z*sqrt(2.0)/2.0;
	return Point2;
}

标签:MFC,样条,int,double,nCount,曲面,loop2,ROUND,loop
来源: https://blog.csdn.net/ha0ha0xuexi/article/details/118056037