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;
}