如需转载本文,请注明出处。
接着前面的两个系列,继续,这次要绘制的是三次样条曲线。
上来先看一下效果:
接下来上源码:
首先是一个函数,可以根据所给的初值,求出三次样条曲线的参数。
/*
本程序借助EasyX图像库,绘制三次样条曲线
*/
#include<iostream>
#include<graphics.h>
#include<math.h>
using namespace std;
#define MAXNUM 50 //定义样条数据区间个数最多为50个
typedef struct SPLINE //定义样条结构体,用于存储一条样条的所有信息
{ //初始化数据输入
float x[MAXNUM + 1]; //存储样条上的点的x坐标,最多51个点
float y[MAXNUM + 1]; //存储样条上的点的y坐标,最多51个点
unsigned int point_num; //存储样条上的实际的 点 的个数
float begin_k1; //开始点的一阶导数信息
float end_k1; //终止点的一阶导数信息
//float begin_k2; //开始点的二阶导数信息
//float end_k2; //终止点的二阶导数信息
//计算所得的样条函数S(x)
float k1[MAXNUM + 1]; //所有点的一阶导数信息
float k2[MAXNUM + 1]; //所有点的二阶导数信息
//51个点之间有50个段,func[]存储每段的函数系数
float a3[MAXNUM], a1[MAXNUM];
float b3[MAXNUM], b1[MAXNUM];
//分段函数的形式为 Si(x) = a3[i] * {x(i+1) - x}^3 + a1[i] * {x(i+1) - x} +
// b3[i] * {x - x(i)}^3 + b1[i] * {x - x(i)}
//xi为x[i]的值,xi_1为x[i+1]的值
}SPLINE, *pSPLINE;
typedef int RESULT; //返回函数执行的结果状态,下面为具体的返回选项
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE -1
#endif
#ifndef NULL
#define NULL 0
#endif
#ifndef ERR
#define ERR -2
#endif
//
/*===============================================================================
*** 函数名称: Spline3()
*** 功能说明: 完成三次样条差值,其中使用追赶法求解M矩阵
*** 入口参数: (pSPLINE)pLine 样条结构体指针pLine中的x[],y[],num,begin_k1,end_k1
*** 出口参数: (pSPLINE)pLine 样条结构体指针pLine中的函数参数
*** 返回参数: 返回程序执行结果的状态TRUE or FALSE
================================================================================*/
RESULT Spline3(pSPLINE pLine)
{
float H[MAXNUM] = { 0 }; //小区间的步长
float Fi[MAXNUM] = { 0 }; //中间量
float U[MAXNUM + 1] = { 0 }; //中间量
float A[MAXNUM + 1] = { 0 }; //中间量
float D[MAXNUM + 1] = { 0 }; //中间量
float M[MAXNUM + 1] = { 0 }; //M矩阵
float B[MAXNUM + 1] = { 0 }; //追赶法中间量
float Y[MAXNUM + 1] = { 0 }; //追赶法中间变量
int i = 0;
计算中间参数
if ((pLine->point_num < 3) || (pLine->point_num > MAXNUM + 1))
{
return ERR; //输入数据点个数太少或太多
}
for (i = 0; i <= pLine->point_num - 2; i++)
{ //求H[i]
H[i] = pLine->x[i + 1] - pLine->x[i];
Fi[i] = (pLine->y[i + 1] - pLine->y[i]) / H[i]; //求F[x(i),x(i+1)]
}
for (i = 1; i <= pLine->point_num - 2; i++)
{ //求U[i]和A[i]和D[i]
U[i] = H[i - 1] / (H[i - 1] + H[i]);
A[i] = H[i] / (H[i - 1] + H[i]);
D[i] = 6 * (Fi[i] - Fi[i - 1]) / (H[i - 1] + H[i]);
}
//若边界条件为1号条件,则
U[i] = 1;
A[0] = 1;
D[0] = 6 * (Fi[0] - pLine->begin_k1) / H[0];
D[i] = 6 * (pLine->end_k1 - Fi[i - 1]) / H[i - 1];
//若边界条件为2号条件,则
//U[i] = 0;
//A[0] = 0;
//D[0] = 2 * begin_k2;
//D[i] = 2 * end_k2;
/追赶法求解M矩阵
B[0] = A[0] / 2;
for (i = 1; i <= pLine->point_num - 2; i++)
{
B[i] = A[i] / (2 - U[i] * B[i - 1]);
}
Y[0] = D[0] / 2;
for (i = 1; i <= pLine->point_num - 1; i++)
{
Y[i] = (D[i] - U[i] * Y[i - 1]) / (2 - U[i] * B[i - 1]);
}
M[pLine->point_num - 1] = Y[pLine->point_num - 1];
for (i = pLine->point_num - 1; i > 0; i--)
{
M[i - 1] = Y[i - 1] - B[i - 1] * M[i];
}
//计算方程组最终结果
for (i = 0; i <= pLine->point_num - 2; i++)
{
pLine->a3[i] = M[i] / (6 * H[i]);
pLine->a1[i] = (pLine->y[i] - M[i] * H[i] * H[i] / 6) / H[i];
pLine->b3[i] = M[i + 1] / (6 * H[i]);
pLine->b1[i] = (pLine->y[i + 1] - M[i + 1] * H[i] * H[i] / 6) / H[i];
}
return TRUE;
}
然后是一个坐标转换函数。由于计算的结果所得到的图形比较小,为了显示的方便,将图形放大了,进行了坐标系的变换。默认的坐标系原点在窗口左上方,向下为Y轴正方向,向右为X轴正方向。
void CoordinateTransform(double &x, double &y)
{//坐标转换函数,X轴从27-31,Y轴从2.5到4.5
x = x*160 - 4320;
y = -240 * y + 1080;
}
接着是绘制三次样条的函数,x坐标每次按1/2000的值进行递增,并得到对应的y坐标的值,然后将对应点的像素点亮,这样画出的线更加细密。
void DrawCubicSpline(pSPLINE line)
{//绘制三次样条的函数,取离散点对,并将相应的像素点打亮
int num = line->point_num;
double y,z;
int rate = 2000;
for (int i = 0; i < num - 1; i++)
{
for (double j= line->x[i];j < line->x[i + 1];j += 1.0 / rate)
{
//打入三次样条的分段解析式,得到函数值
y = line->a3[i] * pow((line->x[i + 1] - j), 3) + line->a1[i] * (line->x[i + 1] - j) + line->b3[i] * pow((j - line->x[i]), 3) + line->b1[i] * (j - line->x[i]);
z = j;
CoordinateTransform(z, y);
putpixel(z, y,GREEN);
}
}
}
最后主函数中输入初始参数,求出三次样条曲线的参数,并绘制图形即可。
SPLINE line1; //定义全局变量
pSPLINE pLine1 = &line1;
int main()
{
line1.x[0] = 27.7; //对构造样条的参数进行初始化
line1.x[1] = 28;
line1.x[2] = 29;
line1.x[3] = 30;
line1.y[0] = 4.1;
line1.y[1] = 4.3;
line1.y[2] = 4.1;
line1.y[3] = 3.0;
line1.point_num = 4;
line1.begin_k1 = 3.0; //开始点出的一阶导数
line1.end_k1 = -4.0; //结尾点的一阶导数
Spline3(pLine1); //求出三次样条的参数方程
initgraph(640, 480); //初始化绘图界面
DrawCubicSpline(pLine1); //绘制三次样条曲线
system("pause");
return 0;
}
所有源码链接:https://github.com/xsgaaaa/Computer-Graphics-Homeworks