MFC绘制三次参数样条

MFC绘制三次参数样条


在这里插入图片描述
三次样条不具有几何不变性,所以引入了三次参数样条曲线。
参考《计算几何算法与实现》–孔令德
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);
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值