MFC绘制三次参数样条
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/b388d1ca9ab55c9874ddeeeb6af8750b.png)
三次样条不具有几何不变性,所以引入了三次参数样条曲线。
参考《计算几何算法与实现》–孔令德
1、添加二维点类;
#pragma once
//为了避免按照x和y方向进行重复运算,重载运算对象
class CP2
{
public:
CP2(void);
~CP2(void);
CP2(double x,double y);
friend CP2 operator+(const CP2&p0,const CP2&p1);//运算符重载
friend CP2 operator-(const CP2&p0,const CP2&p1);
friend CP2 operator-(double scalar,const CP2&p);
friend CP2 operator-(const CP2&p,double scalar);
friend CP2 operator*(const CP2&p,double scalar);
friend CP2 operator*(double scalar,const CP2&p);
friend CP2 operator/(const CP2&p0,const CP2&p1);
friend CP2 operator/(const CP2&p,double scalar);
public:
double x;
double y;
};
#include "StdAfx.h"
#include "P2.h"
#include"math.h"
CP2::CP2(void)
{
}
CP2::~CP2(void)
{
}
CP2::CP2(double x,double y)
{
this->x=x;
this->y=y;
}
CP2 operator+(const CP2&p0,const CP2&p1)//运算符重载
{
CP2 result;
result.x=p0.x+p1.x;
result.y=p0.y+p1.y;
return result;
}
CP2 operator-(const CP2&p0,const CP2&p1)
{
CP2 result;
result.x=p0.x-p1.x;
result.y=p0.y-p1.y;
return result;
}
CP2 operator-(double scalar,const CP2&p)
{
CP2 result;
result.x=scalar-p.x;
result.y=scalar-p.y;
return result;
}
CP2 operator-(const CP2&p,double scalar)
{
CP2 result;
result.x=p.x-scalar;
result.y=p.y-scalar;
return result;
}
CP2 operator*(const CP2&p,double scalar)
{
return CP2(p.x*scalar,p.y*scalar);
}
CP2 operator*(double scalar,const CP2&p)
{
return CP2(p.x*scalar,p.y*scalar);
}
CP2 operator/(const CP2&p0, CP2&p1)
{
if(fabs(p1.x)<1e-6)
{
p1.x=1.0;
}
if(fabs(p1.y)<1e-6)
{
p1.y=1.0;
}
CP2 result;
result.x=p0.x/p1.x;
result.y=p0.y/p1.y;
return result;
}
CP2 operator/(const CP2&p,double scalar)
{
if(fabs(scalar)<1e-6)
{
scalar=1.0;
}
if(fabs(scalar)<1e-6)
{
scalar=1.0;
}
CP2 result;
result.x=p.x/scalar;
result.y=p.y/scalar;
return result;
}
2、添加头文件
#include"P2.h"
#include"math.h"
#define ROUND(h) int((h)+0.5)
3、添加成员属性
public:
CP2 P[8];
void ReadPoint(void);
void DrawCubicParameterSpline(CDC*pDC);
void DrawDataPoint(CDC*pDC);
//读入型值点
void CcubicParametricSplineView::ReadPoint(void)
{
P[1].x = -400, P[1].y = -150;
P[2].x = -100, P[2].y = 0;
P[3].x = -300, P[3].y = 100;
P[4].x = 0, P[4].y = 200;
P[5].x = 300, P[5].y = 100;
P[6].x = 100, P[6].y = 0;
P[7].x = 400, P[7].y = -150;
}
void CcubicParametricSplineView::DrawDataPoint(CDC*pDC)
{
CBrush NewBrush, *OldBrush;
NewBrush.CreateSolidBrush(RGB(0, 0, 0));
OldBrush = pDC->SelectObject(&NewBrush);
for(int i = 1; i < 8; i++)
pDC->Ellipse(ROUND(P[i].x - 5), ROUND(P[i].y - 5), ROUND(P[i].x + 5), ROUND(P[i].y + 5));
pDC->SelectObject(OldBrush);
}
//绘制三次样条
void CcubicParametricSplineView::DrawCubicParameterSpline(CDC*pDC)
{
int n=7;
const int dim=8;//二维数组维数
double b1=1,bn=1;//起点和终点的一阶导数
double L[dim];//参数样条曲线的弦长
double lambda[dim],mu[dim];
double l[dim],m[dim],u[dim];//追赶法参数
CP2 c1,cn;//起点和终点的方向余弦
CP2 D[dim];//D数组
CP2 M[dim],K[dim];//追赶法过渡数组
CP2 B1[dim],B2[dim],B3[dim],B4[dim];//函数的系数
CP2 delt[dim];//x和y的增量
//计算弦长
for(int i=1;i<n;i++)
{
delt[i]=P[i+1]-P[i];
L[i]=sqrt(delt[i].x*delt[i].x+delt[i].y*delt[i].y);
}
//边界条件的投影
c1.x=b1*cos(delt[1].x/L[1]);//起点
c1.y=b1*cos(delt[1].y/L[1]);
cn.x=bn*cos(delt[n-1].x/L[n-1]);//终点
cn.y=bn*cos(delt[n-1].y/L[n-1]);
for(int i=2;i<n;i++)
{
lambda[i]=L[i-1]/(L[i-1]+L[i]);
mu[i]=L[i]/(L[i-1]+L[i]);
D[i]=6/(L[i-1]+L[i])*((P[i+1]-P[i])/L[i]-(P[i]-P[i-1])/L[i-1]);//计算D
}
D[1]=6*((P[2]-P[1])/L[1]-b1)/L[1];//夹持端的数据
D[n]=6*(bn-(P[n]-P[n-1])/L[n-1])/L[n-1];
mu[1]=1;
lambda[n]=1;
//追赶法求解三弯矩方程
l[1]=2;
u[1]=mu[1]/l[1];
for(int i=2;i<=n;i++)
{
m[i]=lambda[i];
l[i]=2-m[i]*u[i-1];
u[i]=mu[i]/l[i];
}
K[1]=D[1]/l[1];//解LD=K
for(int i=2;i<=n;i++)
{
K[i]=(D[i]-m[i]*K[i-1])/l[i];
}
M[n]=K[n];//解UM=K;
for(int i=n-1;i>=1;i--)
{
M[i]=K[i]-u[i]*M[i+1];
}
//计算三次样条函数的系数
for(int i = 1; i < n; i++)
{
B1[i]=P[i];
B2[i]=(P[i+1]-P[i])/L[i]-L[i]*(M[i]/3+M[i+1]/6);
B3[i]=M[i]/2;
B4[i]=(M[i+1]-M[i])/(6*L[i]);
}
pDC->MoveTo(ROUND(P[1].x), ROUND(P[1].y));
double tStep=0.5;//步长
CP2 p;
for(int i=1;i<n;i++)
{
for(double t=0;t<=L[i];t+=tStep)
{
p =B1[i]+B2[i]*t+B3[i]*t*t+B4[i]*t*t*t;
pDC->LineTo(ROUND(p.x), ROUND(p.y));//绘制参数样条曲线
}
}
}
4、OnDraw中添加
// TODO: 在此处为本机数据添加绘制代码
CRect rect;//定义客户区矩形
GetClientRect(&rect);//获得客户区矩形的信息
pDC->SetMapMode(MM_ANISOTROPIC);//自定义二维坐标系
pDC->SetWindowExt(rect.Width(), rect.Height());//设置窗口范围
pDC->SetViewportExt(rect.Width(), -rect.Height());//设置视区范围,且x轴水平向右为正,y轴垂直向上为正
pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);//设置客户区中心为二维坐标系原点
rect.OffsetRect(-rect.Width() / 2, -rect.Height() / 2);//rect矩形与客户区重合
ReadPoint();
DrawDataPoint(pDC);
DrawCubicParameterSpline(pDC);