ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

MFC绘制任意B样条曲面

2021-06-19 16:58:24  阅读:212  来源: 互联网

标签:MFC 样条 int double nCount 曲面 loop2 ROUND loop


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有