打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
曲线参数化(一)
userphoto

2023.10.12 北京

关注

本文描述曲线参数化的具体内容,由于篇幅限制,会分几篇叙述。曲线参数化的目的是将曲线使用参数u表示,如直线表示为:

C(u)=P+uD,u∈(−∞,∞)

P为起始点,D为方向向量,u为参数。然而很多曲线的参数化表示并不像这么简单,一般分为如下三种:

  1. 直接写出。对于基本曲线(Elementary curves),其参数方程由其本身性质可直接写出。如圆,距离定点(圆心)的距离为定值的点构成的曲线。C(u)=P+r⋅(cos(u)⋅Dx+sin(u)⋅Dy),u∈[o,2⋅π)。基本曲线的参数方程本文不做叙述,可参考OCC文档,《BREP Format Description White Paper 》
  2. 插值。仅给出曲线上的某些关键点,插值形成曲线。主要分为:仅过关键点,C0连续,不光滑;过关键点,且斜率连续,C1连续,光滑曲线。
  3. 控制多边形。给定由控制点组成的控制多边形,得到的曲线。如Bezier,BSpline等,其本质是多项式插值。

1. 点集插值

意味着获取参数对{ti,Pi},ti对应点集中的唯一点ti,ti即是点Pi的“abscissa”。此方法很多,分为均匀和非均匀,没有具体区别。

Uniform parameterization:

Parameterization conform to length ratio: 与点之间的长度成比例,

2. 插值方法

主要分为,Language法、Hermite法,多项式法。

2.1 Lagrange

有一组控制点P0,P1…….,Pn,计算其参数坐标ti(取值范围0到1),使其满足下式:

此步称之为“Parameterization of a set of points”,意为将点可参数坐标t对应起来,但是如何求取?此处的ti可以任意规整求得,“只要能对应即可”。如以下采取定间距(uniform interval),

其插值公式如下,主要任务是求解Pi前的系数。有两种形式,基本式,迭代形式(recursive form)和矩阵形式(matrix form)。

  • Language插值推导:其基本思想是,假设过每个点的曲线是二次曲线,此二次曲线在本点取系数为1(过此点),在其余点取值为0,即上式中的Φ,称之为某个点的权值。然后由过所有点的二次曲线*权值,累加,表示这条曲线本身。如果有n+1个点,权值系数就是n次多项式,称为n次插值。所有的插值方法都有共同特点,高阶插值计算不稳定且计算量大。故实际使用时,使用低阶差值,然后做曲线拼接。

  • Language插值方法的矩阵形式:由插值公式可以看出,对于特定的n次Language插值,其系数是确定的。系数的具体形式取决于参数坐标ti的选取,如下述2阶(3个控制点)Language曲线插值公式:

故可以使用如下形式的矩阵相乘表示,

对三个点使用uniform Interpolation取得的参数坐标ti为{0,0.5,1},故曲线表示为,

故其M矩阵为,实际使用中,使用矩阵形式这个是很方便的。

以下采用按基本式实现Language插值,

static void ParaPoint(int n, double* T) { //uniform 计算点参数 double tmin = 0.0; double tmax = 1.0;
T[0] = tmin; T[n] = tmax; for (int i = 1; i < n; i++) { T[i] = tmin + (double(i) / double(n)) * (tmax - tmin); }}static double calculateCoeffi(int i, int n, double *T, double u) { double val = 1.0; for (int j = 0; j <= n; j++) { if (j != i) val *= (u - T[j]) / (T[i] - T[j]); }
return val;}//Language插值//返回n个控制点,参数为u的曲线上的点,u[0,1]static MyPoint interpolateCurve(int n, MyPoint* points,double u) { //计算点的参数化:首先要将离散点参数化,使ti对应于Pi。此处使用最简单的uniform parameterization double* T = new double[n+1]; ParaPoint(n,T); //计算权值 double* weights = new double[n+1]; for (int i = 0; i <= n; i++) { weights[i] = calculateCoeffi(i,n,T,u);  } //计算最终的点 double x = 0.0; double y = 0.0; double z=0.0; for (int i = 0; i <= n; i++) { x += points[i].getX()*weights[i]; y += points[i].getY()*weights[i]; z += points[i].getZ()*weights[i];  }  return MyPoint(x, y, z);}
//计算过5个点的曲线  const int n = 4;  MyPoint points[n+1] = { MyPoint(0,0,0),MyPoint(10,-10,0),MyPoint(20,-5,0),MyPoint(30,20,0),MyPoint(60,20,0) };
//获得此曲线上的n个点,并显示 const int pnts = 100000; std::vector<MyPoint> outPoints; for (int i = 0; i <= pnts; i++) { //MyPoint p1 = interpolateCurve(2, points, i / 100); outPoints.push_back(interpolateCurve(n, points, double(i)/ pnts)); }

  • 2.2 Hermite

  • 同时考虑考虑控制点和控制点处的斜率。每个控制点处的斜率难以控制,故先不描述。

  • 2.3 控制多边形

  • 此类方法的特点是,曲线不通过控制点,曲线的形状控制点连线组成的多边形,及每个点的权系数决定。其要求为曲线“”控制多边形,且光滑。

Bezier曲线,描述不变,由n+1个控制点,形成n阶Bezier曲线。其形式如下,

点Pi前的系数,称之为Bernstein多项式,表示为,

插值的主要任务是求解Bernstein多项式,可以直接求解。

de Casteljau Algorithm:使用上述Bernstein多项式多项式直接求解的缺点是,其不够稳定。实际中多使用de Casteljau Algorithm递归算法,此算法的几何意义更明确,且可以手工作图。其基本思想是,2点之间多次线性插值。如2个控制点,则就是直线本身。3个控制点,3次线性插值。如,

需要说明的是,de Casteljau与Bernstein形式其实是统一的。看如下3阶Bezier曲线的两种形式推导,

可以将3阶Bezier曲线的插值系数表示为矩阵形式,

使用Bernstein形式,实现Bezier曲线,

//阶乘double Factorial(int n){ if (n <= 1) return 1; return n*Factorial(n-1);}
//Bezier曲线static MyPoint interpolateBezierCurve(int n, MyPoint* points, double u) { //Bezier不需要将点参数化, //计算点的参数化:首先要将离散点参数化,使ti对应于Pi。此处使用最简单的uniform parameterization //double* T = new double[n + 1]; //ParaPoint(n, T); //计算权值 double* B = new double[n + 1]; for (int i = 0; i <= n; i++) { B[i] = Factorial(n) / (Factorial(i)*Factorial(n - i))*std::pow(u, i)*std::pow(1.0f - u, n - i);  } //计算最终的点 double x = 0.0; double y = 0.0; double z = 0.0; for (int i = 0; i <= n; i++) { x += points[i].getX()*B[i]; y += points[i].getY()*B[i]; z += points[i].getZ()*B[i]; }
return MyPoint(x, y, z);}
计算过5个点的曲线  MyPoint points[n+1] = { MyPoint(0,0,0),MyPoint(10,-10,0),MyPoint(20,-5,0),MyPoint(30,20,0),MyPoint(60,20,0) };
//获得此曲线上的n个点,并显示 const int pnts = 100000; std::vector<MyPoint> outPoints; for (int i = 0; i <= pnts; i++) { //MyPoint p1 = interpolateCurve(2, points, i / 100); outPoints.push_back(interpolateBezierCurve(n, points, double(i)/ pnts)); }

篇幅问题,后续文章继续。

路漫漫其修远兮,共勉。

参考:

Mesh generation application to Finite Element method

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
样条曲线内容总结及复习题
基于三次非均匀B样条的6DOF机械臂轨迹规划
径向基(Radial basis function)神经网络、核函数的一些理解
数值分析(拟合、插值和逼近)之数据插值方法(线性插值、二次插值、Cubic插值、埃米尔特、拉格朗日多项式插值、牛顿插值、样条插值)(含opengl程序)
曲线拟合法的Mat lab 实现
matlab曲线拟合
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服