贝塞尔曲线及插值全解
- 格式:doc
- 大小:141.50 KB
- 文档页数:13
贝塞尔曲线及插值2010-07-01 21:41贝塞尔曲线介绍可参考中文维基百科,图文并茂,这里就不啰嗦了/zh-cn/%E8%B2%9D%E8%8C%B2%E6%9B%B2%E7%B7%9A这里主要讲一下如何在excel及vb中实现贝塞尔曲线插值,程序来源于互联网(程序作者: 海底眼(Mr. Dragon Pan在excel中用宏实现),本文作为少量修改,方便在vb中调用,经运行证明是没错的,下面程序可作成一个模块放到vb或vba中调用:-------------------------------------------------------------------------------------' Excel的平滑线散点图,可以根据两组分别代表X-Y坐标的散点数值产生曲线图' 但是,却没有提供这个曲线图的公式,所以无法查找曲线上的点坐标' 后来我在以下这个网页找到了详细的说明和示例程序' ......................................................................... .....' /Smooth_curve_bezier_example_file.zip' ......................................................................... .....' 根据其中采用的算法,进一步增添根据X坐标求Y坐标,或根据Y坐标求X坐标,更切合实际需求' 这个自定义函数按照Excel的曲线算法(三次贝塞尔分段插值),计算平滑曲线上任意一点的点坐标'' Excel的平滑曲线的大致算法是:' 给出了两组X-Y数值以后,每一对X-Y坐标称为节点,然后在每两个节点之间画出三次贝塞尔曲线(下面简称曲线)' 贝塞尔曲线的算法网上有很多资源,这里不介绍了,只作简单说明' 每条曲线都由四个节点开始,计算出四个贝塞尔控制点,然后根据控制点画出唯一一条曲线' 假设曲线的源数据是节点1,节点2,节点3,节点4(Dot1,Dot2,Dot3,Dot4)' 那么贝塞尔控制点的计算如下' Dot2是第一个控制点,也是曲点的起点,Dot3是第四个控制点也是曲线的终点'' 第二个控制点的位置是:' 过第一个控制点(Dot2,起点),与Dot1, Dot3的连线平行,且与Dot2距离为 1/6 * 线段Dot1_Dot3的长度' 假如是图形的第一段曲线,取节点1,1,2,3进行计算,即 Dot2 = Dot1' 且第二个控制点与第一控制点距离取 1/3 * |Dot1_Dot3|,而不是1/6 *|Dot1_Dot3|' 假如 1/2 * |Dot2_Dot3| < 1/6 * |Dot1_Dot3|' 那么第二个控制点与第一控制点距离取 1/2 * |Dot2_Dot3|,而不是1/6 *|Dot1_Dot3|'' 第三个控制点的位置是:' 过第四个控制点(Dot3,终点),与Dot2, Dot4的连线平行,且与Dot3距离为 1/6 * |Dot2_Dot4|' 假如是图形的最后一段曲线,取节点Last-2,Last-1,Last,Last进行计算,即 Dot4 = Dot3' 且第三个控制点与第四控制点距离取 1/3 * |Dot2_Dot4|,而不是1/6 *|Dot2_Dot4|' 假如 1/2 * |Dot2_Dot3| < 1/6 * |Dot2_Dot4|' 那么第二个控制点与第一控制点距离取 1/2 * |Dot2_Dot4|,而不是1/6 * |Dot2_Dot4| '.............................................................................. .................' 这个自定义函数的计算流程是' Step1: 检查输入的X-Y数值是否有错误,如(输入不够三个点,X-Y的数量不一致,起始搜索节点超过范围等等)' Step2: 从参数指定的节点开始,计算出四个贝塞尔控制点,得到贝塞尔插值多项式方程,' 然后代入已知的待求数值,看它能不能满足 f(t)=0 有解 (即曲线包含待查数值) ' Step3: 如果 f(t)=0 有解,根据解出来的 t 值计算X-Y坐标,退出程序,否则继续检查下一段曲线' Step4: 如果所有分段曲线都不包含待查数值,退出程序'.............................................................................. .................Option ExplicitOption Base 1 '所有数组的第一个元素编号为1(默认为0)Type Vector '自定义数据结构(用二维向量代表坐标系里面的点坐标) x As Doubley As DoubleEnd TypeConst NoError = "No error" '错误提示信息Const Error1 = "Error: The size of known_x must equal to size of known_y"Const Error2 = "Error: The size of known_x must equal to or greater than 3" Const Error3 = "Error: StartKnot must be >=1 and <=count(known_x)-1"Const Error4 = "Error: known_value_type must be ""x"",""y"",or ""t"" "Const Error5 = "Error: When known_value_type is ""t"" , known_value must >=0 and <=1"Const Error10 = "Error: known_value is not on the curve (defined by given known_x and known_y)"Const NoRoot = "No Root"Const MaxErr = 0.00000001Const MaxLoop = 1000Dim SizeX, SizeY As Long '输入区域的大小Dim Dot1 As Vector '输入区域里面,用作计算贝塞尔控制点的四个节点Dim Dot2 As VectorDim Dot3 As VectorDim Dot4 As VectorDim BezierPt1 As Vector '生成贝塞尔曲线的四个贝塞尔控制点Dim BezierPt2 As VectorDim BezierPt3 As VectorDim BezierPt4 As VectorDim OffsetTo2 As Vector '第二,三个贝塞尔控制点跟起点,终点的距离关系Dim OffsetTo3 As VectorDim ValueType As Variant '输入待查数值的类型,"x"代表输入的是X坐标,求对应的Y坐标Dim Interpol_here As Boolean '当前分段曲线是否包含待查数值Dim key_value, a, b, c, d As Double '贝塞尔曲线插值多项式的系数Dim t1, t2, t3 As Variant '贝塞尔曲线插值多项式的根Dim a3, a2, a1, a0 As DoubleDim size%Public Sub befit(ByRef known_x() As Double, ByRef known_y() As Double, size As Integer, known_value As Double, result() As Variant, Optional StartKnot As Long = 1, Optional known_value_type As Variant = "x")''--------------------------------------子过程方便VB中调用-----------------------------------------------------------'主程序开始,至少要输入五个参数,第一个是X坐标系列,然后是Y坐标系列,第三个是坐标点数,第四个是待查数值,第五个是返回值'第六个参数是从哪一段曲线开始查找,如果曲线可以返回多个值,那么分别指定起始节点就可以找出全部合要求的点'第七个参数是待查数值的类型,"x"代表输入x坐标求对应y坐标,"y"则相反,"t"是直接输入贝塞尔插值多项式的参数'-------------------------------------------------------------------------------------------------Dim j As LongDim x1Value, y1Value, x2Value, y2Value, x3Value, y3Value As VariantDim ErrorMsg As VariantValueType = LCase(known_value_type) '待查数值的类型转化为小写,并赋值到全局变量ValueTypekey_value = known_value '待查数值赋值到全局变量key_valueErrorMsg = ErrorCheck(known_x, known_y, StartKnot) '检查输入错误If ErrorMsg <> NoError Then '有错误就返回错误信息,退出程序result = Array(ErrorMsg, ErrorMsg, ErrorMsg, ErrorMsg, ErrorMsg, ErrorMsg) Exit SubEnd If'SizeX = UBound(known_x)For j = StartKnot To size 'SizeX - 1 '从指定的节点开始,没有指定节点就从1开始Call FindFourDots(known_x, known_y, j) '找出输入X-Y点坐标里面,应该用于计算的四个结点Call FindFourBezierPoints(Dot1, Dot2, Dot3, Dot4) '根据四个结点计算四个贝塞尔控制点Call FindABCD '根据待查数值的类型,和贝塞尔控制点,计算贝塞尔插值多项式的系数Call Find_t '检查贝塞尔曲线是否包含待查数值If Interpol_here = True Then Exit ForNext jIf Interpol_here = True Then '计算点坐标,并返回'以下是由四个贝塞尔控制点决定的,贝塞尔曲线的参数方程x1Value = (1 - t1) ^ 3 * BezierPt1.x + 3 * t1 * (1 - t1) ^ 2 * BezierPt2.x + 3 * t1 ^ 2 * (1 - t1) * BezierPt3.x + t1 ^ 3 * BezierPt4.xy1Value = (1 - t1) ^ 3 * BezierPt1.y + 3 * t1 * (1 - t1) ^ 2 * BezierPt2.y + 3 * t1 ^ 2 * (1 - t1) * BezierPt3.y + t1 ^ 3 * BezierPt4.yx2Value = (1 - t2) ^ 3 * BezierPt1.x + 3 * t2 * (1 - t2) ^ 2 * BezierPt2.x + 3 * t2 ^ 2 * (1 - t2) * BezierPt3.x + t2 ^ 3 * BezierPt4.xy2Value = (1 - t2) ^ 3 * BezierPt1.y + 3 * t2 * (1 - t2) ^ 2 * BezierPt2.y + 3 * t2 ^ 2 * (1 - t2) * BezierPt3.y + t2 ^ 3 * BezierPt4.yx3Value = (1 - t3) ^ 3 * BezierPt1.x + 3 * t3 * (1 - t3) ^ 2 * BezierPt2.x + 3 * t3 ^ 2 * (1 - t3) * BezierPt3.x + t3 ^ 3 * BezierPt4.xy3Value = (1 - t3) ^ 3 * BezierPt1.y + 3 * t3 * (1 - t3) ^ 2 * BezierPt2.y + 3 * t3 ^ 2 * (1 - t3) * BezierPt3.y + t3 ^ 3 * BezierPt4.yresult = Array(x1Value, y1Value, x2Value, y2Value, x3Value, y3Value)Elseresult = Array(Error10, Error10, Error10, Error10, Error10, Error10)End IfEnd SubFunction ErrorCheck(ByRef known_x() As Double, ByRef known_y() As Double, StartKnot) As VariantErrorCheck = NoErrorSizeX = UBound(known_x) 'known_x.CountSizeY = UBound(known_y) 'known_y.CountIf SizeX <> SizeY Then '假如输入的X坐标数目不等于Y坐标数目ErrorCheck = Error1Exit FunctionEnd IfIf SizeX < 3 Then '输入的X-Y坐标对少于三个ErrorCheck = Error2Exit FunctionEnd IfIf (StartKnot < 1 Or StartKnot >= SizeX) Then '指定的起始节点超出范围ErrorCheck = Error3Exit FunctionEnd IfIf (ValueType <> "x" And ValueType <> "y" And ValueType <> "t") Then '输入的待查数值类型不是x, y, tErrorCheck = Error4Exit FunctionEnd IfIf ((ValueType = "t" And key_value > 1) Or (ValueType = "t" And key_value < 0)) Then ' t 类型的范围是0-1ErrorCheck = Error5Exit FunctionEnd IfEnd FunctionSub FindFourDots(ByRef known_x() As Double, ByRef known_y() As Double, j) '根据X-Y数值,及起始节点,找出用于计算的四个结点坐标If j = 1 Then '第一个结点 Dot2 = Dot1Dot1.x = known_x(1)Dot1.y = known_y(1)ElseDot1.x = known_x(j - 1)Dot1.y = known_y(j - 1)End IfDot2.x = known_x(j)Dot2.y = known_y(j)Dot3.x = known_x(j + 1)Dot3.y = known_y(j + 1)If j = SizeX - 1 Then '最后一个结点 Dot4 = Dot3 Dot4.x = Dot3.xDot4.y = Dot3.yElseDot4.x = known_x(j + 2)Dot4.y = known_y(j + 2)End IfEnd SubSub FindFourBezierPoints(Dot1 As Vector, Dot2 As Vector, Dot3 As Vector, Dot4 As Vector)Dim d12, d23, d34, d13, d14, d24 As Doubled12 = DistAtoB(Dot1, Dot2) '计算平面坐标系上的两点距离d23 = DistAtoB(Dot2, Dot3)d34 = DistAtoB(Dot3, Dot4)d13 = DistAtoB(Dot1, Dot3)d14 = DistAtoB(Dot1, Dot4)d24 = DistAtoB(Dot2, Dot4)BezierPt1 = Dot2BezierPt4 = Dot3OffsetTo2 = AsubB(Dot3, Dot1) '向量减法OffsetTo3 = AsubB(Dot2, Dot4)If ((d13 / 6 < d23 / 2) And (d24 / 6 < d23 / 2)) ThenIf (Dot1.x <> Dot2.x Or Dot1.y <> Dot2.y) Then OffsetTo2 = AmultiF(OffsetTo2, 1 / 6)If (Dot1.x = Dot2.x And Dot1.y = Dot2.y) Then OffsetTo2 = AmultiF(OffsetTo2, 1 / 3)If (Dot3.x <> Dot4.x Or Dot3.y <> Dot4.y) Then OffsetTo3 = AmultiF(OffsetTo3, 1 / 6)If (Dot3.x = Dot4.x And Dot3.y = Dot4.y) Then OffsetTo3 = AmultiF(OffsetTo3, 1 / 3)ElseIf ((d13 / 6 >= d23 / 2) And (d24 / 6 >= d23 / 2)) ThenOffsetTo2 = AmultiF(OffsetTo2, d23 / 12)OffsetTo3 = AmultiF(OffsetTo3, d23 / 12)ElseIf (d13 / 6 >= d23 / 2) ThenOffsetTo2 = AmultiF(OffsetTo2, d23 / 2 / d13)OffsetTo3 = AmultiF(OffsetTo3, d23 / 2 / d13)ElseIf (d24 / 6 >= d23 / 2) ThenOffsetTo2 = AmultiF(OffsetTo2, d23 / 2 / d24)OffsetTo3 = AmultiF(OffsetTo3, d23 / 2 / d24)End IfBezierPt2 = AaddB(BezierPt1, OffsetTo2) '向量加法BezierPt3 = AaddB(BezierPt4, OffsetTo3)End SubFunction DistAtoB(dota As Vector, dotb As Vector) As DoubleDistAtoB = ((dota.x - dotb.x) ^ 2 + (dota.y - dotb.y) ^ 2) ^ 0.5End FunctionFunction AaddB(dota As Vector, dotb As Vector) As VectorAaddB.x = dota.x + dotb.xAaddB.y = dota.y + dotb.yEnd FunctionFunction AsubB(dota As Vector, dotb As Vector) As VectorAsubB.x = dota.x - dotb.xAsubB.y = dota.y - dotb.yEnd FunctionFunction AmultiF(dota As Vector, MultiFactor As Double) As VectorAmultiF.x = dota.x * MultiFactorAmultiF.y = dota.y * MultiFactorEnd FunctionSub FindABCD()If ValueType = "x" Then '参数类型是x, 需要解参数方程 f(t) = x,这里设定参数方程的系数a = -BezierPt1.x + 3 * BezierPt2.x - 3 * BezierPt3.x + BezierPt4.xb = 3 * BezierPt1.x - 6 * BezierPt2.x + 3 * BezierPt3.xc = -3 * BezierPt1.x + 3 * BezierPt2.xd = BezierPt1.x - key_valueEnd IfIf ValueType = "y" Then '参数类型是x, 需要解参数方程 f(t) = y,这里设定参数方程的系数a = -BezierPt1.y + 3 * BezierPt2.y - 3 * BezierPt3.y + BezierPt4.yb = 3 * BezierPt1.y - 6 * BezierPt2.y + 3 * BezierPt3.yc = -3 * BezierPt1.y + 3 * BezierPt2.yd = BezierPt1.y - key_valueEnd IfEnd SubSub Find_t() '计算当 f(t) = 待查数值时, t应该是什么数值Dim tArr As VariantInterpol_here = TrueIf ValueType = "t" Then '待查数值类型为t,那么无需计算t1 = key_valuet2 = key_valuet3 = key_valueExit SubEnd IftArr = Solve_Order3_Equation(a, b, c, d) '否则,解三次贝塞尔参数方程 f(t) = 待查数值t1 = tArr(1) '解得方程的三个根t2 = tArr(2)t3 = tArr(3)If (t1 > 1 Or t1 < 0) Then '参数方程的 t 值范围应该是 0-1t1 = NoRootEnd IfIf (t2 > 1 Or t2 < 0) Thent2 = NoRootEnd IfIf (t3 > 1 Or t3 < 0) Thent3 = NoRootEnd IfIf (IsNumeric(t1) = False And IsNumeric(t2) = False And IsNumeric(t3) = False) Then Interpol_here = FalseEnd If ' 三个根都不合要求,代表曲线上没有包含待查数值的点If (t1 = NoRoot And t2 <> NoRoot) Then '至少有一个根,则用它代替NoRoot的结果,方便Excel画图t1 = t2End IfIf (t1 = NoRoot And t3 <> NoRoot) Thent1 = t3End IfIf (t2 = NoRoot) Then t2 = t1If (t3 = NoRoot) Then t3 = t1End Sub'.............................................................................. ..................' 牛顿法解三次方程,先求解方程的导函数,得到方程的拐点(导数等于0的点)' 然后分三段用迭代法分别求三个根'.............................................................................. ..................Public Function Solve_Order3_Equation(p3, p2, p1, P0, Optional Starting As Double = -10000000000#, Optional Ending As Double = 10000000000#) As VariantDim Two_X, TurningPoint, x1, x2, x3 As VariantDim x As Doublea3 = p3a2 = p2a1 = p1a0 = P0x1 = NoRootx2 = NoRootx3 = NoRootx1 = Newton_Solve(Starting)If a3 = 0Then ' 如果三次方程没有三次项Two_X = Solve_Order2_Equation(a2, a1, a0) ' 解释法直接求二次方程的解x1 = Two_X(1)x2 = Two_X(2)ElseTurningPoint = Solve_Order2_Equation(3 * a3, 2 * a2, 1 * a1) ' 求解 f'(t) = 0If (TurningPoint(1) = NoRoot And TurningPoint(2) = NoRoot) Then ' 分段求根x = 0x1 = Newton_Solve(x)ElseIf (TurningPoint(1) <> NoRoot And TurningPoint(2) = NoRoot) Then If f_x(Starting) * f_x(TurningPoint(1)) < 0 Thenx = (Starting + TurningPoint(1)) / 2x1 = Newton_Solve(x)End IfIf f_x(TurningPoint(2)) * f_x(Ending) < 0 Thenx = (TurningPoint(2) + Ending) / 2x3 = Newton_Solve(x)End IfElseIf (TurningPoint(1) <> NoRoot And TurningPoint(2) <> NoRoot) ThenIf f_x(Starting) * f_x(TurningPoint(1)) < 0 Thenx = (Starting + TurningPoint(1)) / 2x1 = Newton_Solve(x)End IfIf f_x(TurningPoint(1)) * f_x(TurningPoint(2)) < 0 Thenx = (TurningPoint(1) + TurningPoint(2)) / 2x2 = Newton_Solve(x)End IfIf f_x(TurningPoint(2)) * f_x(Ending) < 0 Thenx = (TurningPoint(2) + Ending) / 2x3 = Newton_Solve(x)End IfEnd IfEnd IfSolve_Order3_Equation = Array(x1, x2, x3)End FunctionFunction f_x(xValue) As Double ' f_x = f(x) 求贝塞尔参数方程 f(t)的值f_x = a3 * xValue ^ 3 + a2 * xValue ^ 2 + a1 * xValue + a0End FunctionFunction Df_x(xValue As Double) As Double ' Df_x = f'(x) ' f_x = f(x) 求贝塞尔参数方程导函数 f'(t)的值Df_x = 3 * a3 * xValue ^ 2 + 2 * a2 * xValue + a1End FunctionFunction Solve_Order2_Equation(k2, k1, k0 As Double) As VariantDim b2SUB4ac As DoubleIf (k2 = 0) ThenIf k1 = 0 ThenSolve_Order2_Equation = Array(NoRoot, NoRoot)Exit FunctionElseIf (k1 <> 0 And k0 = 0) ThenSolve_Order2_Equation = Array(0, 0)Exit FunctionElseIf (k1 <> 0 And k0 <> 0) ThenSolve_Order2_Equation = Array(-k0 / k1, -k0 / k1)Exit FunctionEnd IfEnd Ifb2SUB4ac = (k1) ^ 2 - 4 * k2 * k0 ' 二次方程可以直接用公式求解,b^2-4*a*c If b2SUB4ac < 0 ThenSolve_Order2_Equation = Array(NoRoot, NoRoot)End IfIf b2SUB4ac = 0 ThenSolve_Order2_Equation = Array(-k1 / 2 / k2, -k1 / 2 / k2)End IfIf b2SUB4ac > 0 ThenIf (-k1 + b2SUB4ac ^ 0.5) / 2 / k2 < (-k1 - b2SUB4ac ^ 0.5) / 2 / k2 Then Solve_Order2_Equation = Array((-k1 + b2SUB4ac ^ 0.5) / 2 / k2, (-k1 - b2SUB4ac ^ 0.5) / 2 / k2)ElseSolve_Order2_Equation = Array((-k1 - b2SUB4ac ^ 0.5) / 2 / k2, (-k1 + b2SUB4ac ^ 0.5) / 2 / k2)End IfEnd IfEnd FunctionFunction Newton_Solve(x0 As Double) As VariantDim i, eps As Doublei = 0eps = Abs(f_x(x0))Do While (eps > MaxErr) '如果取初值,函数的绝对值大于允许误差If (Df_x(x0) <> 0 And i < MaxLoop) Then '而且分子不等于0,没有超出最大迭代次数x0 = x0 - f_x(x0) / Df_x(x0) '牛顿法求下一个值 x' = x0 - f(x0) / f'(x0)eps = Abs(f_x(x0))i = i + 1ElseNewton_Solve = NoRootExit FunctionEnd IfLoopNewton_Solve = x0End Function。
贝塞尔曲线:贝塞尔曲线又称贝兹曲线或贝济埃曲线,一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。
当然在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等。
在Flash4中还没有完整的曲线工具,而在Flash5里面已经提供出贝塞尔曲线工具。
贝塞尔曲线是应用于二维图形应用程序的数学曲线。
曲线的定义有四个点:起始点、终止点(也称锚点)以及两个相互分离的中间点。
滑动两个中间点,贝塞尔曲线的形状会发生变化。
十九世纪六十年代晚期,Pierre Bézier应用数学方法为雷诺公司的汽车制造业描绘出了贝塞尔曲线。
贝塞尔曲线就是这样的一条曲线,它是依据四个位置任意的点坐标绘制出的一条光滑曲线。
在历史上,研究贝塞尔曲线的人最初是按照已知曲线参数方程来确定四个点的思路设计出这种矢量曲线绘制法。
贝塞尔曲线的有趣之处更在于它的“皮筋效应”~也就是说,随着点有规律地移动,曲线将产生皮筋伸引一样的变换,带来视觉上的冲击。
19世纪70年代,法国数学家Pierre Bézier第一个研究了这种矢量绘制曲线的方法,并给出了详细的计算公式,因此按照这样的公式绘制出来的曲线就用他的姓氏来命名~是为贝塞尔曲线。
【作用】由于用计算机画图大部分时间是操作鼠标来掌握线条的路径,与手绘的感觉和效果有很大的差别。
即使是一位精明的画师能轻松绘出各种图形,拿到鼠标想随心所欲的画图也不是一件容易的事。
这一点是计算机万万不能代替手工的工作,所以到目前为止人们只能颇感无奈。
使用贝塞尔工具画图很大程度上弥补了这一缺憾。
【发现者】“贝赛尔曲线”是由法国数学家Pierre Bézier所发现,由此为计算机矢量图形学奠定了基础。
它的主要意义在于无论是直线或曲线都能在数学上予以描述。
【贝赛尔工具】“贝赛尔”工具在photoshop中叫“钢笔工具”;在CorelDraw中翻译成“贝赛尔工具”;而在Fireworks中叫“画笔”。
Bezier曲线、B样条曲线和NURBS曲线0.概述1. 贝塞尔曲线(Bezier Curve):贝塞尔曲线由一组控制点和控制点上的权重组成。
贝塞尔曲线的阶数由控制点的数量决定,阶数为n的贝塞尔曲线需要n+1个控制点。
贝塞尔曲线具有局部控制的特性,即曲线上的一段由相邻的几个控制点决定,不受其他控制点的影响。
贝塞尔曲线的计算相对简单,但在变形过程中可能会出现形状扭曲的问题。
2. B样条(B-Spline): B样条曲线是一种基于分段多项式的曲线表示方法。
与贝塞尔曲线不同,B样条曲线的每个控制点都有一个关联的基函数。
这些基函数决定了曲线上每一点的形状。
B样条曲线的阶数可以是任意的,较高阶的B样条曲线能够更灵活地描述复杂的曲线形状。
B样条曲线具有良好的局部控制性和平滑性,可以很好地避免贝塞尔曲线的形状扭曲问题。
3. NURBS曲线(Non-Uniform Rational B-Spline Curve):NURBS曲线是对B样条曲线的扩展,它引入了有理权重的概念。
NURBS曲线的每个控制点都有一个关联的权重,这些权重可以调节曲线上各个点的影响程度。
NURBS曲线能够表示更复杂的曲线形状,如圆弧和椭圆等。
总的来说Bezier曲线中的每个控制点都会影响整个曲线的形状,而B样条中的控制点只会影响整个曲线的一部分,显然B样条提供了更多的灵活性;Bezier和B样条都是多项式参数曲线,不能表示一些基本的曲线,比如圆,所以引入了NURBS,即非均匀有理B样条来解决这个问题;贝塞尔曲线适用于简单的曲线形状设计,B样条曲线具有更好的局部控制和平滑性,适用于复杂曲线的建模而NURBS曲线在B样条的基础上引入了有理权重,可以更准确地描述各种曲线形状Bezier曲线是B样条的一个特例,而B样条又是NURBS的一个特例1.Bezier曲线1.1 贝塞尔曲线的历史:贝塞尔曲线于 1962 年,由法国工程师皮埃尔·贝济埃(PierreBézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计,贝塞尔曲线最初由保尔·德·卡斯特里奥于1959年运用德卡斯特里奥算法开发,以稳定数值的方法求出贝塞尔曲线。
贝塞尔曲线插值算法
贝塞尔曲线插值算法使用贝塞尔曲线来连接给定的控制点,使得曲线平滑地通过这些点。
贝塞尔曲线的插值算法包括以下几个步骤:
1. 确定控制点:给定一组控制点,通常是起点、终点和几个中间点。
2. 计算贝塞尔曲线上的点:通过递归计算,在给定的t值(取值范围为0到1)下,计算贝塞尔曲线上的点。
3. 插值计算:对于每对相邻控制点,通过计算一系列t值(通常为0到1之间的等差数列),得到贝塞尔曲线上的一系列插值点。
4. 平滑曲线过渡:通过调整控制点的位置和数量,使得曲线平滑地通过给定的控制点。
贝塞尔曲线插值算法可用于图形绘制、动画制作等领域,常用于实现平滑曲线的绘制和路径动画。
ease in out 贝塞尔曲线-概述说明以及解释1.引言1.1 概述概述贝塞尔曲线是计算机图形学中常用的一种曲线描述方法,由法国数学家贝塞尔(Pierre Bézier)于20世纪60年代提出。
它通过控制点的位置和权重来确定曲线的形状,具有灵活性和可调节性,被广泛应用于各种设计领域,如动画、游戏开发、网页设计等。
贝塞尔曲线的特点在于平滑且变化连续,不会出现突变或折线的现象。
通过调整控制点的位置和权重,可以改变曲线的形状,实现各种各样的动画效果。
其中,ease in out 贝塞尔曲线是一种特殊的曲线形式,常用于制作平滑的过渡动画,使动画变化起始和结束时的速度较慢,中间过程速度较快,给人一种自然流畅的感觉。
本文将重点介绍贝塞尔曲线的基本原理和ease in out 贝塞尔曲线的应用。
首先,我们将详细解释贝塞尔曲线的计算方法和控制点的作用,以及曲线的插值原理。
然后,我们将重点讨论ease in out 贝塞尔曲线的应用场景,并通过实例演示如何使用该曲线制作平滑过渡的动画效果。
最后,我们将对本文内容进行总结,并展望贝塞尔曲线在未来的发展前景。
通过阅读本文,读者将能够全面了解贝塞尔曲线的基本原理和应用方法,掌握如何利用ease in out 贝塞尔曲线制作流畅的动画效果。
同时,本文还将为读者提供一些实用的技巧和建议,帮助他们在设计和开发过程中更好地应用贝塞尔曲线,提升产品的用户体验。
希望本文能对读者在相关领域的工作和学习有所帮助,引起他们对贝塞尔曲线的深入思考和探索。
1.2 文章结构文章结构部分主要描述了本文的组织结构和内容安排。
通过清晰的文章结构,读者可以更好地理解和把握文章的主旨,并能够有条理地阅读文章的各个部分。
文章结构包括引言、正文和结论三个主要部分。
在引言部分,我们将概述本文的主题和背景,简要介绍贝塞尔曲线及其应用,并明确本文的目的和意义。
通过引言,读者可以对文章的主要内容有一个初步的了解,为后续的阅读打下基础。
路径点插值是计算机图形学、动画制作、机器人运动规划等领域中常用的一种技术,主要用于在两个已知路径点之间生成一系列连续的中间点,使得整个路径平滑且连续。
以下是一些常见的路径点插值方法:1.线性插值(Linear Interpolation, LERP):线性插值是最简单的插值方式,假设我们有两个路径点P0和P1,t为参数(0<=t<=1),则线性插值公式为:P = (1-t) * P0 + t * P1。
当t=0时,结果为P0;当t=1时,结果为P1。
2.二次贝塞尔曲线插值(Quadratic Bézier Interpolation):二次贝塞尔曲线需要三个控制点P0、P1和P2,其中P0和P2是端点,P1是控制点。
通过计算贝塞尔曲线公式得到路径上任意点的位置。
3.三次贝塞尔曲线插值(Cubic Bézier Interpolation):类似于二次贝塞尔曲线,但需要四个控制点,能生成更复杂的曲线形状。
4.样条插值(Spline Interpolation):包括自然三次样条插值、Catmull-Rom样条插值等,这类插值方法可以生成更为平滑的曲线,尤其适用于路径规划。
例如,在一个由多个路径点定义的曲线上,每个点与其相邻点共同决定该点处曲线的形状。
5.圆弧插值(Circular Arc Interpolation):在特定情况下,如机器人关节运动规划,可能会使用圆弧插值来模拟关节的旋转运动。
6.赫尔曼-赫茨插值(Hermite Interpolation):赫尔曼-赫茨插值不仅考虑了路径点的位置,还考虑了它们的速度或方向信息,从而能够生成更符合物理规律或视觉效果的路径。
每种插值方法都有其适用场景和特点,选择哪种方法取决于实际需求,比如路径复杂程度、速度变化要求、平滑度需求等因素。
opencv 贝塞尔曲线贝塞尔曲线简介贝塞尔曲线是一种数学曲线,由法国工程师Pierre Bézier在20世纪50年代提出,用于计算机图形学和计算机辅助设计中的曲线插值。
它通过在给定的控制点上进行插值来生成曲线,具有平滑、连续和可调控的特点。
贝塞尔曲线广泛应用于计算机图形学、计算机辅助设计、动画与游戏开发等领域。
贝塞尔曲线的控制点贝塞尔曲线的生成是基于一组控制点的插值计算。
通常情况下,贝塞尔曲线的控制点包括起始点、终止点和若干个中间点。
起始点和终止点为曲线的起点和终点,而中间点则决定了曲线的形状。
通过控制点的位置和数量的调整,可以获得不同形状的贝塞尔曲线。
贝塞尔曲线的计算方法贝塞尔曲线的计算方法主要有两种:de Casteljau算法和求解贝塞尔曲线参数值的方程求解法。
de Casteljau算法de Casteljau算法是一种递归的计算方法,通过不断地对控制点进行插值,最终得到贝塞尔曲线的坐标。
其具体步骤如下:1.将控制点按照递增的参数值t进行排序;2.初始化插值的控制点集合为原始控制点集合;3.对插值的控制点集合中的每个点,根据参数值t进行插值计算,得到插值后的新点,并将新点加入到插值的控制点集合中;4.重复步骤3,直到插值的控制点集合中只剩下一个点,该点即为贝塞尔曲线上的一个坐标。
方程求解法求解贝塞尔曲线参数值的方程求解法通过求解多项式方程组来计算贝塞尔曲线的坐标。
设贝塞尔曲线的控制点为P0,P1,...,P n,贝塞尔曲线的参数值为t,则贝塞尔曲线的坐标可以表示为:n⋅B i n(t)B(t)=∑P ii=0其中,B i n(t)是n次贝塞尔基函数,可以表示为:B i n(t)=C n i⋅(1−t)n−i⋅t i求解方程组的方法有多种,可以使用牛顿法、二分法等。
opencv中的贝塞尔曲线绘制opencv是一个开源的计算机视觉库,提供了丰富的图像处理和计算机视觉算法。
opencv中可以通过函数cv::bezierCurveTo来绘制贝塞尔曲线。
连续贝塞尔曲线连续贝塞尔曲线是一种在计算机图形学中常用的曲线绘制方法。
它通过控制点的位置和权重来定义曲线的形状,可以绘制出平滑且连续的曲线。
贝塞尔曲线是一种基于数学原理的曲线绘制方法,它通过控制点来定义曲线的形状。
在贝塞尔曲线中,有两种类型的控制点:锚点和控制点。
锚点是曲线的起点和终点,而控制点则决定了曲线的弯曲程度。
在绘制连续贝塞尔曲线时,我们需要至少三个锚点和两个控制点。
首先,我们需要确定曲线的起点和终点,这两个点将作为锚点。
然后,我们需要确定曲线的弯曲程度,这可以通过调整控制点的位置来实现。
在绘制连续贝塞尔曲线时,我们需要使用贝塞尔曲线的插值公式。
这个公式可以根据给定的控制点和权重来计算曲线上的点的位置。
通过不断调整控制点的位置和权重,我们可以得到不同形状的曲线。
绘制连续贝塞尔曲线的过程可以分为以下几个步骤:1. 确定锚点的位置。
锚点是曲线的起点和终点,我们需要确定它们的位置。
2. 确定控制点的位置。
控制点决定了曲线的弯曲程度,我们需要调整它们的位置来实现所需的形状。
3. 确定控制点的权重。
权重决定了控制点对曲线形状的影响程度,我们需要调整它们的值来实现所需的形状。
4. 计算曲线上的点的位置。
使用贝塞尔曲线的插值公式,我们可以计算出曲线上的点的位置。
5. 绘制曲线。
将计算出的点连接起来,就可以得到连续贝塞尔曲线。
绘制连续贝塞尔曲线需要一定的数学知识和计算能力,但它可以绘制出平滑且连续的曲线,非常适合在计算机图形学中使用。
通过调整控制点的位置和权重,我们可以绘制出各种形状的曲线,从简单的直线到复杂的曲线都可以实现。
总之,连续贝塞尔曲线是一种常用的曲线绘制方法,通过控制点的位置和权重来定义曲线的形状。
它可以绘制出平滑且连续的曲线,非常适合在计算机图形学中使用。
通过调整控制点的位置和权重,我们可以绘制出各种形状的曲线,实现丰富多样的图形效果。
贝塞尔曲线曲率
贝塞尔曲线是一种数学曲线,由法国数学家皮埃尔·贝塞尔在19世纪提出。
它可以用来描述平面或三维空间中的曲线形状。
贝塞尔曲线的曲率是指在曲线上某一点处的曲线弯曲程度。
贝塞尔曲线的曲率是通过计算曲线在该点处的切线和曲率圆的半径之比来确定的。
曲率圆是与曲线在该点处具有相同切线和曲率的圆。
如果曲线在该点处凸向外部,则曲率为正值;如果曲线在该点处凸向内部,则曲率为负值。
曲率的计算可以使用贝塞尔曲线的导数来实现。
根据贝塞尔曲线的定义,我们可以通过递归地求解贝塞尔曲线的控制点来计算其导数。
一种常用的方法是使用de Casteljau 算法,通过对曲线进行分割来逐步逼近所需点的导数。
需要注意的是,贝塞尔曲线的曲率是在每个曲线段上计算的。
因此,在连接多个贝塞尔曲线段时,每个曲线段的曲率可能不连续。
为了获得连续的曲线,可以使用G1连续或更高阶的曲线插值方法。
总之,贝塞尔曲线的曲率可以通过计算曲线在某一点处的切线和曲率圆的半径之比来确定,它描述了曲线在该点处的弯曲程度。
空间曲线插值
空间曲线插值是指通过一系列已知的空间点,推导出两个或多个点之间的中间位置,从而形成一条平滑的曲线。
这种插值方法广泛应用于计算机图形学、计算机辅助设计以及三维动画等领域。
在空间曲线插值中,常用的方法包括线性插值、贝塞尔曲线插值和样条曲线插值。
1. 线性插值:线性插值是最简单的插值方法,它假设两个点之间的曲线段为一条直线。
通过计算两个点之间的距离和方向,可以得到中间点的位置。
线性插值适用于需要简单粗暴的连接两个点的情况,但不能提供更复杂的曲线形状。
2. 贝塞尔曲线插值:贝塞尔曲线插值是通过控制点来定义曲线形状的一种方法。
贝塞尔曲线可以通过调整控制点的位置和权重来改变曲线的形状。
常见的贝塞尔曲线包括二次贝塞尔曲线和三次贝塞尔曲线,它们分别由2个和3个控制点定义。
贝塞尔曲线插值可以提供更加自由和灵活的曲线形状。
3. 样条曲线插值:样条曲线插值是一种基于局部控制的插值方法,它通过一系列的支撑点和控制点来定义曲线。
样条曲线可以保持平滑,并且可以通过调整控制点的位置来改变曲线的形状。
常见的样条曲线包括B样条曲线和NURBS曲线。
样条曲线插值适用于需要精确控制曲线形状的情况,例如在计算机辅助设计中绘制曲线路径。
总之,空间曲线插值是一种通过已知点推导出中间点位置的方法,可以通过线性插值、贝塞尔曲线插值和样条曲线插值等方法实现。
选
择合适的插值方法取决于需要的曲线形状和应用场景。
贝塞尔曲线的位置切线速度加速度贝塞尔曲线的位置、切线、速度和加速度在计算机图形学和动画设计中,贝塞尔曲线是一种常用的数学工具,它能够精确地描述复杂的路径和形状,是实现流畅和自然动画效果的重要手段之一。
贝塞尔曲线的位置、切线、速度和加速度是理解和应用贝塞尔曲线的关键概念,本文将从深度和广度两个方面对这四个概念进行全面评估,并探讨它们在实际应用中的意义。
一、贝塞尔曲线的位置1.1 控制点贝塞尔曲线由控制点决定,控制点的位置决定了曲线的形状和路径。
通常情况下,贝塞尔曲线由两个端点和若干个控制点组成,通过调整这些控制点的位置,可以精确地控制曲线的形状。
1.2 插值性质贝塞尔曲线具有插值性质,即曲线经过端点和控制点所确定的路径,这意味着我们可以通过调整控制点的位置,来实现对曲线的精细调整。
1.3 空间曲线贝塞尔曲线可以描述二维和三维的路径,它不仅可以用于平面图形的绘制,还可以用于描述三维物体的运动轨迹,具有广泛的应用价值。
二、贝塞尔曲线的切线2.1 切线向量在贝塞尔曲线上的任意一点,都存在一个切线向量,它表示曲线在该点的方向。
切线向量的方向由曲线的参数决定,可以通过参数方程求取。
2.2 切线的应用切线向量在计算机图形学中有着重要的应用,它可以用于计算物体在运动状态下的速度和加速度,也可以用于光照和阴影的计算。
2.3 曲率贝塞尔曲线的曲率描述了曲线在某一点的弯曲程度,它是切线向量的导数,可以用来评估曲线的平滑性和曲线段的曲率变化情况。
三、贝塞尔曲线的速度3.1 参数方程贝塞尔曲线的速度可以通过参数方程来求取,参数方程描述了曲线上点的位置随时间的变化规律,可以根据参数方程对曲线进行分析和计算。
3.2 瞬时速度在贝塞尔曲线上的任意一点,都存在一个瞬时速度,它描述了曲线在该点的瞬时运动状态,可以通过参数方程的导数来计算。
3.3 速度的应用贝塞尔曲线的速度可以用于模拟物体在运动状态下的轨迹和速度变化,也可以用于计算动画中物体的自然运动效果。
贝塞尔曲线应⽤(贝塞尔插值)所谓贝塞尔插值就是指给定n个顶点,要求把这n个顶点连接成为平滑的曲线。
那肯定得在这些顶点之间插值了,但这些插值的点怎么找到,可不能随便插值,否则整体上未必是平滑曲线,所以必须找到⼀个曲线⽅程,根据这个曲线⽅程来找到这些插值的点,⽽且要求这条曲线⽅程过原来条件中规定的n个顶点。
由于贝塞尔曲线可以由⼏个控制点决定,所以想到⽤⼀条贝塞尔曲线作为所求的曲线⽅程,这就是所谓的贝塞尔插值【个⼈理解哈】。
下⾯是⼀个典型的例⼦,他根据每两个顶点作为⼀条贝塞尔曲线的端点(即起始点和终⽌点),并由这两个端点结合相邻的其它两个顶点求得和这两个端点对应的贝塞尔曲线的控制点,然后根据端点和控制点就绘制⼀条过两个顶点的贝塞尔曲线了。
由于平均每两个顶点决定⼀条贝塞尔曲线,如果画的多条贝塞尔曲线在相邻交接处也是平滑的,那么画出来的整个曲线就是平滑的。
原⽂地址:Interpolation with Bezier Curves 意思是:贝塞尔插值A very simple method of smoothing polygons 意思是:⼀种⾮常简单的多边形平滑⽅法上⾯的⽂章是英⽂的,英⽂不好的请看下⾯的翻译翻译:翻译转⾃:⽂章内容:之前 comp.graphic.algorithms 上有⼀个讨论,是关于怎么样使⽤曲线对多边形进⾏插值处理,使得最终产⽣的曲线是光滑的⽽且能通过所有的顶点。
Gernot Hoffmann 建议说使⽤著名的 B-Spline 来进⾏插值。
这⾥有他当时的。
B-Spline 在这⾥效果很好,它看起来就像是⼀个固定在多边形顶点上的橡⽪尺(elastic ruler)。
但我有个⼤胆的推测,我觉得肯定还存在更简单的⽅法。
⽐如,使⽤三次贝塞曲线(cubic Bezier)进⾏近似。
贝塞尔曲线有两个固定点(起点和终点),另加两个决定曲线形状的控制点(CP)。
关于贝塞尔曲线的更多知识可以在搜索引擎中找到,⽐如,你可以参考。
bezier曲线绘制算法贝塞尔曲线绘制算法贝塞尔曲线是一种常用于计算机图形学中的数学曲线,具有平滑弯曲的特性。
通过控制点的位置和数量,可以绘制出各种形状的曲线,如圆弧、曲线等。
本文将介绍贝塞尔曲线绘制算法的基本原理和实现方法。
1. 贝塞尔曲线的基本概念贝塞尔曲线由两个或多个控制点决定,通过这些控制点的位置,可以确定曲线的形状和轨迹。
其中,起始点和结束点称为锚点,而其他点称为控制点。
贝塞尔曲线的形状由控制点之间的插值和权重决定,权重决定了每个控制点对曲线形状的影响程度。
2. 二次贝塞尔曲线绘制算法二次贝塞尔曲线由三个点决定,分别是起始点P0、控制点P1和结束点P2。
绘制二次贝塞尔曲线的算法如下:(1) 将曲线分为若干个线段,每段用t从0到1进行插值。
(2) 根据插值参数t,计算控制点P0、P1和P2在x和y轴上的值。
(3) 绘制连接P0和P1的线段,连接P1和P2的线段。
3. 三次贝塞尔曲线绘制算法三次贝塞尔曲线由四个点决定,分别是起始点P0、控制点P1、P2和结束点P3。
绘制三次贝塞尔曲线的算法如下:(1) 将曲线分为若干个线段,每段用t从0到1进行插值。
(2) 根据插值参数t,计算控制点P0、P1、P2和P3在x和y轴上的值。
(3) 绘制连接P0和P1的线段,连接P1和P2的线段,以及连接P2和P3的线段。
4. 高阶贝塞尔曲线的绘制算法除了二次和三次贝塞尔曲线,还存在更高阶的贝塞尔曲线。
对于n 阶贝塞尔曲线,需要n+1个点来确定。
其绘制算法与二次和三次贝塞尔曲线类似,通过插值参数t来计算各个控制点的值,并连接相邻控制点。
5. 贝塞尔曲线的应用贝塞尔曲线在计算机图形学中有广泛的应用,常用于绘制平滑曲线、图形变形、字体设计等方面。
在计算机动画、游戏开发等领域,贝塞尔曲线的应用也非常广泛。
贝塞尔曲线是一种常用于计算机图形学中的数学曲线,通过控制点的位置和数量,可以绘制出各种形状的曲线。
本文介绍了贝塞尔曲线的基本概念,以及二次、三次和高阶贝塞尔曲线的绘制算法。
math 贝塞尔曲线
贝塞尔曲线是一种数学曲线,它由贝塞尔曲线算法生成。
这种曲线被广泛应用于计算机图形学、计算机辅助设计(CAD)和矢量图形等领域。
贝塞尔曲线由一系列控制点确定,通常由起始点(P0)、结束点(Pn)、以及中间的控制点(P1, P2, ..., Pn-1)组成。
这些控制点确定了曲线的形状。
起始点和结束点是曲线的端点,而中间的控制点则会影响曲线的弯曲和形状。
贝塞尔曲线具有平滑、可变形和可逼近任意形状的特点。
根据控制点的个数不同,贝塞尔曲线可以分为一阶贝塞尔曲线(线段)、二阶贝塞尔曲线(二次曲线)、三阶贝塞尔曲线(三次曲线)等。
其中,二阶和三阶贝塞尔曲线是最常见的。
贝塞尔曲线的生成主要依赖于贝塞尔曲线算法,其中最著名的是De Casteljau算法。
该算法通过对控制点进行插值和递归运算,逐步生成曲线上的点,从而绘制出整条贝塞尔曲线。
贝塞尔曲线的使用可以实现平滑的曲线绘制、形状变换和动画效果等。
贝塞尔曲线及插值这里主要讲一下如何在excel及vb中实现贝塞尔曲线插值,程序来源于互联网(程序作者: 海底眼(Mr. Dragon Pan在excel中用宏实现),本文作为少量修改,方便在vb中调用,经运行证明是没错的,下面程序可作成一个模块放到vb或vba中调用:' Excel的平滑线散点图,可以根据两组分别代表X-Y坐标的散点数值产生曲线图' 但是,却没有提供这个曲线图的公式,所以无法查找曲线上的点坐标' 后来我在以下这个网页找到了详细的说明和示例程序' ............................................................... ...............' /Smooth_curve_bezier_example_file.zip' ............................................................... ...............' 根据其中采用的算法,进一步增添根据X坐标求Y坐标,或根据Y坐标求X坐标,更切合实际需求' 这个自定义函数按照Excel的曲线算法(三次贝塞尔分段插值),计算平滑曲线上任意一点的点坐标'' Excel的平滑曲线的大致算法是:' 给出了两组X-Y数值以后,每一对X-Y坐标称为节点,然后在每两个节点之间画出三次贝塞尔曲线(下面简称曲线)' 贝塞尔曲线的算法网上有很多资源,这里不介绍了,只作简单说明' 每条曲线都由四个节点开始,计算出四个贝塞尔控制点,然后根据控制点画出唯一一条曲线' 假设曲线的源数据是节点1,节点2,节点3,节点4(Dot1,Dot2,Dot3,Dot4) ' 那么贝塞尔控制点的计算如下' Dot2是第一个控制点,也是曲点的起点,Dot3是第四个控制点也是曲线的终点'' 第二个控制点的位置是:' 过第一个控制点(Dot2,起点),与Dot1, Dot3的连线平行,且与Dot2距离为 1/6 * 线段Dot1_Dot3的长度' 假如是图形的第一段曲线,取节点1,1,2,3进行计算,即 Dot2 = Dot1' 且第二个控制点与第一控制点距离取 1/3 * |Dot1_Dot3|,而不是1/6 * |Dot1_Dot3|' 假如 1/2 * |Dot2_Dot3| < 1/6 * |Dot1_Dot3|' 那么第二个控制点与第一控制点距离取 1/2 * |Dot2_Dot3|,而不是1/6 * |Dot1_Dot3|'' 第三个控制点的位置是:' 过第四个控制点(Dot3,终点),与Dot2, Dot4的连线平行,且与Dot3距离为 1/6 * |Dot2_Dot4|' 假如是图形的最后一段曲线,取节点Last-2,Last-1,Last,Last 进行计算,即 Dot4 = Dot3' 且第三个控制点与第四控制点距离取 1/3 * |Dot2_Dot4|,而不是1/6 * |Dot2_Dot4|' 假如 1/2 * |Dot2_Dot3| < 1/6 * |Dot2_Dot4|' 那么第二个控制点与第一控制点距离取 1/2 * |Dot2_Dot4|,而不是1/6 * |Dot2_Dot4| '.................................................................... ...........................' 这个自定义函数的计算流程是' Step1: 检查输入的X-Y数值是否有错误,如(输入不够三个点,X-Y的数量不一致,起始搜索节点超过范围等等)' Step2: 从参数指定的节点开始,计算出四个贝塞尔控制点,得到贝塞尔插值多项式方程,' 然后代入已知的待求数值,看它能不能满足 f(t)=0 有解 (即曲线包含待查数值)' Step3: 如果 f(t)=0 有解,根据解出来的 t 值计算X-Y坐标,退出程序,否则继续检查下一段曲线' Step4: 如果所有分段曲线都不包含待查数值,退出程序'.................................................................... ...........................Option ExplicitOption Base 1 '所有数组的第一个元素编号为1(默认为0)Type Vector '自定义数据结构(用二维向量代表坐标系里面的点坐标)x As Doubley As DoubleEnd TypeConst NoError = "No error" '错误提示信息Const Error1 = "Error: The size of known_x must equal to size of known_y" Const Error2 = "Error: The size of known_x must equal to or greater than 3"Const Error3 = "Error: StartKnot must be >=1 and <=count(known_x)-1" Const Error4 = "Error: known_value_type must be ""x"",""y"",or ""t"" " Const Error5 = "Error: When known_value_type is ""t"" , known_value must >=0 and <=1"Const Error10 = "Error: known_value is not on the curve (defined by given known_x and known_y)"Const NoRoot = "No Root"Const MaxErr = 0.00000001Const MaxLoop = 1000Dim SizeX, SizeY As Long '输入区域的大小Dim Dot1 As Vector '输入区域里面,用作计算贝塞尔控制点的四个节点Dim Dot2 As VectorDim Dot3 As VectorDim Dot4 As VectorDim BezierPt1 As Vector '生成贝塞尔曲线的四个贝塞尔控制点Dim BezierPt2 As VectorDim BezierPt3 As VectorDim BezierPt4 As VectorDim OffsetTo2 As Vector '第二,三个贝塞尔控制点跟起点,终点的距离关系Dim OffsetTo3 As VectorDim ValueType As Variant '输入待查数值的类型,"x"代表输入的是X坐标,求对应的Y坐标Dim Interpol_here As Boolean '当前分段曲线是否包含待查数值Dim key_value, a, b, c, d As Double '贝塞尔曲线插值多项式的系数Dim t1, t2, t3 As Variant '贝塞尔曲线插值多项式的根Dim a3, a2, a1, a0 As DoubleDim size%Public Sub befit(ByRef known_x() As Double, ByRef known_y() As Double, size As Integer, known_value As Double, result() As Variant, Optional StartKnot As Long = 1, Optional known_value_type As Variant = "x")''--------------------------------------子过程方便VB中调用-----------------------------------------------------------'主程序开始,至少要输入五个参数,第一个是X坐标系列,然后是Y坐标系列,第三个是坐标点数,第四个是待查数值,第五个是返回值'第六个参数是从哪一段曲线开始查找,如果曲线可以返回多个值,那么分别指定起始节点就可以找出全部合要求的点'第七个参数是待查数值的类型,"x"代表输入x坐标求对应y坐标,"y"则相反,"t"是直接输入贝塞尔插值多项式的参数'-------------------------------------------------------------------------------------------------Dim j As LongDim x1Value, y1Value, x2Value, y2Value, x3Value, y3Value As Variant Dim ErrorMsg As VariantValueType = LCase(known_value_type) '待查数值的类型转化为小写,并赋值到全局变量ValueTypekey_value = known_value '待查数值赋值到全局变量key_valueErrorMsg = ErrorCheck(known_x, known_y, StartKnot) '检查输入错误If ErrorMsg <> NoErrorThen '有错误就返回错误信息,退出程序result = Array(ErrorMsg, ErrorMsg, ErrorMsg, ErrorMsg, ErrorMsg, ErrorMsg)Exit SubEnd If'SizeX = UBound(known_x)For j = StartKnot To size 'SizeX - 1 '从指定的节点开始,没有指定节点就从1开始Call FindFourDots(known_x, known_y, j) '找出输入X-Y点坐标里面,应该用于计算的四个结点Call FindFourBezierPoints(Dot1, Dot2, Dot3, Dot4) '根据四个结点计算四个贝塞尔控制点CallFindABCD '根据待查数值的类型,和贝塞尔控制点,计算贝塞尔插值多项式的系数CallFind_t '检查贝塞尔曲线是否包含待查数值If Interpol_here = True Then Exit ForNext jIf Interpol_here = True Then '计算点坐标,并返回'以下是由四个贝塞尔控制点决定的,贝塞尔曲线的参数方程x1Value = (1 - t1) ^ 3 * BezierPt1.x + 3 * t1 * (1 - t1) ^ 2 * BezierPt2.x + 3 * t1 ^ 2 * (1 - t1) * BezierPt3.x + t1 ^ 3 * BezierPt4.x y1Value = (1 - t1) ^ 3 * BezierPt1.y + 3 * t1 * (1 - t1) ^ 2 * BezierPt2.y + 3 * t1 ^ 2 * (1 - t1) * BezierPt3.y + t1 ^ 3 * BezierPt4.y x2Value = (1 - t2) ^ 3 * BezierPt1.x + 3 * t2 * (1 - t2) ^ 2 * BezierPt2.x + 3 * t2 ^ 2 * (1 - t2) * BezierPt3.x + t2 ^ 3 * BezierPt4.x y2Value = (1 - t2) ^ 3 * BezierPt1.y + 3 * t2 * (1 - t2) ^ 2 * BezierPt2.y + 3 * t2 ^ 2 * (1 - t2) * BezierPt3.y + t2 ^ 3 * BezierPt4.y x3Value = (1 - t3) ^ 3 * BezierPt1.x + 3 * t3 * (1 - t3) ^ 2 * BezierPt2.x + 3 * t3 ^ 2 * (1 - t3) * BezierPt3.x + t3 ^ 3 * BezierPt4.x y3Value = (1 - t3) ^ 3 * BezierPt1.y + 3 * t3 * (1 - t3) ^ 2 * BezierPt2.y + 3 * t3 ^ 2 * (1 - t3) * BezierPt3.y + t3 ^ 3 * BezierPt4.yresult = Array(x1Value, y1Value, x2Value, y2Value, x3Value,y3Value)Elseresult = Array(Error10, Error10, Error10, Error10, Error10, Error10)End IfEnd Sub//******************************************************************* ********************Function ErrorCheck(ByRef known_x() As Double, ByRef known_y() As Double, StartKnot) As VariantErrorCheck = NoErrorSizeX = UBound(known_x) 'known_x.CountSizeY = UBound(known_y) 'known_y.CountIf SizeX <> SizeY Then '假如输入的X坐标数目不等于Y坐标数目ErrorCheck = Error1Exit FunctionEnd IfIf SizeX < 3 Then '输入的X-Y坐标对少于三个ErrorCheck = Error2Exit FunctionEnd IfIf (StartKnot < 1 Or StartKnot >= SizeX) Then '指定的起始节点超出范围ErrorCheck = Error3Exit FunctionEnd IfIf (ValueType <> "x" And ValueType <> "y" And ValueType <> "t")Then '输入的待查数值类型不是x, y, tErrorCheck = Error4Exit FunctionEnd IfIf ((ValueType = "t" And key_value > 1) Or (ValueType = "t" And key_value < 0)) Then ' t 类型的范围是0-1ErrorCheck = Error5Exit FunctionEnd IfEnd Function//******************************************************************* ********************Sub FindFourDots(ByRef known_x() As Double, ByRef known_y() As Double, j) '根据X-Y数值,及起始节点,找出用于计算的四个结点坐标 If j = 1 Then '第一个结点 Dot2 = Dot1Dot1.x = known_x(1)Dot1.y = known_y(1)ElseDot1.x = known_x(j - 1)Dot1.y = known_y(j - 1)End IfDot2.x = known_x(j)Dot2.y = known_y(j)Dot3.x = known_x(j + 1)Dot3.y = known_y(j + 1)If j = SizeX - 1 Then '最后一个结点Dot4 = Dot3Dot4.x = Dot3.xDot4.y = Dot3.yElseDot4.x = known_x(j + 2)Dot4.y = known_y(j + 2)End IfEnd Sub//******************************************************************* ********************Sub FindFourBezierPoints(Dot1 As Vector, Dot2 As Vector, Dot3 As Vector, Dot4 As Vector)Dim d12, d23, d34, d13, d14, d24 As Doubled12 = DistAtoB(Dot1, Dot2) '计算平面坐标系上的两点距离d23 = DistAtoB(Dot2, Dot3)d34 = DistAtoB(Dot3, Dot4)d13 = DistAtoB(Dot1, Dot3)d14 = DistAtoB(Dot1, Dot4)d24 = DistAtoB(Dot2, Dot4)BezierPt1 = Dot2BezierPt4 = Dot3OffsetTo2 = AsubB(Dot3, Dot1) '向量减法OffsetTo3 = AsubB(Dot2, Dot4)If ((d13 / 6 < d23 / 2) And (d24 / 6 < d23 / 2)) ThenIf (Dot1.x <> Dot2.x Or Dot1.y <> Dot2.y) Then OffsetTo2 = AmultiF(OffsetTo2, 1 / 6)If (Dot1.x = Dot2.x And Dot1.y = Dot2.y) Then OffsetTo2 = AmultiF(OffsetTo2, 1 / 3)If (Dot3.x <> Dot4.x Or Dot3.y <> Dot4.y) Then OffsetTo3 = AmultiF(OffsetTo3, 1 / 6)If (Dot3.x = Dot4.x And Dot3.y = Dot4.y) Then OffsetTo3 = AmultiF(OffsetTo3, 1 / 3)ElseIf ((d13 / 6 >= d23 / 2) And (d24 / 6 >= d23 / 2)) Then OffsetTo2 = AmultiF(OffsetTo2, d23 / 12)OffsetTo3 = AmultiF(OffsetTo3, d23 / 12)ElseIf (d13 / 6 >= d23 / 2) ThenOffsetTo2 = AmultiF(OffsetTo2, d23 / 2 / d13)OffsetTo3 = AmultiF(OffsetTo3, d23 / 2 / d13)ElseIf (d24 / 6 >= d23 / 2) ThenOffsetTo2 = AmultiF(OffsetTo2, d23 / 2 / d24)OffsetTo3 = AmultiF(OffsetTo3, d23 / 2 / d24)End IfBezierPt2 = AaddB(BezierPt1, OffsetTo2) '向量加法BezierPt3 = AaddB(BezierPt4, OffsetTo3)End Sub//******************************************************************* ********************Function DistAtoB(dota As Vector, dotb As Vector) As DoubleDistAtoB = ((dota.x - dotb.x) ^ 2 + (dota.y - dotb.y) ^ 2) ^ 0.5 End FunctionFunction AaddB(dota As Vector, dotb As Vector) As VectorAaddB.x = dota.x + dotb.xAaddB.y = dota.y + dotb.yEnd FunctionFunction AsubB(dota As Vector, dotb As Vector) As VectorAsubB.x = dota.x - dotb.xAsubB.y = dota.y - dotb.yEnd FunctionFunction AmultiF(dota As Vector, MultiFactor As Double) As VectorAmultiF.x = dota.x * MultiFactorAmultiF.y = dota.y * MultiFactorEnd Function//******************************************************************* ********************Sub FindABCD()If ValueType = "x" Then '参数类型是x, 需要解参数方程 f(t) = x,这里设定参数方程的系数a = -BezierPt1.x + 3 * BezierPt2.x - 3 * BezierPt3.x + BezierPt4.xb = 3 * BezierPt1.x - 6 * BezierPt2.x + 3 * BezierPt3.xc = -3 * BezierPt1.x + 3 * BezierPt2.xd = BezierPt1.x - key_valueEnd IfIf ValueType = "y" Then '参数类型是x, 需要解参数方程 f(t) = y,这里设定参数方程的系数a = -BezierPt1.y + 3 * BezierPt2.y - 3 * BezierPt3.y + BezierPt4.yb = 3 * BezierPt1.y - 6 * BezierPt2.y + 3 * BezierPt3.yc = -3 * BezierPt1.y + 3 * BezierPt2.yd = BezierPt1.y - key_valueEnd IfEnd Sub//******************************************************************* ********************Sub Find_t() '计算当 f(t) = 待查数值时, t应该是什么数值Dim tArr As VariantInterpol_here = TrueIf ValueType = "t" Then '待查数值类型为t,那么无需计算 t1 = key_valuet2 = key_valuet3 = key_valueExit SubEnd IftArr = Solve_Order3_Equation(a, b, c, d) '否则,解三次贝塞尔参数方程 f(t) = 待查数值t1 =tArr(1)'解得方程的三个根t2 = tArr(2)t3 = tArr(3)If (t1 > 1 Or t1 < 0) Then '参数方程的 t 值范围应该是 0-1t1 = NoRootEnd IfIf (t2 > 1 Or t2 < 0) Thent2 = NoRootEnd IfIf (t3 > 1 Or t3 < 0) Thent3 = NoRootEnd IfIf (IsNumeric(t1) = False And IsNumeric(t2) = False And IsNumeric(t3) = False) ThenInterpol_here = FalseEnd If ' 三个根都不合要求,代表曲线上没有包含待查数值的点If (t1 = NoRoot And t2 <> NoRoot) Then '至少有一个根,则用它代替NoRoot 的结果,方便Excel画图t1 = t2End IfIf (t1 = NoRoot And t3 <> NoRoot) Thent1 = t3End IfIf (t2 = NoRoot) Then t2 = t1If (t3 = NoRoot) Then t3 = t1End Sub//******************************************************************* ********************'.................................................................... ............................' 牛顿法解三次方程,先求解方程的导函数,得到方程的拐点(导数等于0的点)' 然后分三段用迭代法分别求三个根'.................................................................... ............................Public Function Solve_Order3_Equation(p3, p2, p1, P0, Optional Starting As Double = -10000000000#, Optional Ending As Double = 10000000000#) As VariantDim Two_X, TurningPoint, x1, x2, x3 As VariantDim x As Doublea3 = p3a2 = p2a1 = p1a0 = P0x1 = NoRootx2 = NoRootx3 = NoRootx1 = Newton_Solve(Starting)If a3 = 0Then' 如果三次方程没有三次项Two_X = Solve_Order2_Equation(a2, a1, a0) ' 解释法直接求二次方程的解x1 = Two_X(1)x2 = Two_X(2)ElseTurningPoint = Solve_Order2_Equation(3 * a3, 2 * a2, 1 *a1) ' 求解 f'(t) = 0If (TurningPoint(1) = NoRoot And TurningPoint(2) = NoRoot)Then ' 分段求根x = 0x1 = Newton_Solve(x)ElseIf (TurningPoint(1) <> NoRoot And TurningPoint(2) = NoRoot) ThenIf f_x(Starting) * f_x(TurningPoint(1)) < 0 Thenx = (Starting + TurningPoint(1)) / 2x1 = Newton_Solve(x)End IfIf f_x(TurningPoint(2)) * f_x(Ending) < 0 Thenx = (TurningPoint(2) + Ending) / 2x3 = Newton_Solve(x)End IfElseIf (TurningPoint(1) <> NoRoot And TurningPoint(2) <> NoRoot) ThenIf f_x(Starting) * f_x(TurningPoint(1)) < 0 Thenx = (Starting + TurningPoint(1)) / 2x1 = Newton_Solve(x)End IfIf f_x(TurningPoint(1)) * f_x(TurningPoint(2)) < 0 Thenx = (TurningPoint(1) + TurningPoint(2)) / 2x2 = Newton_Solve(x)End IfIf f_x(TurningPoint(2)) * f_x(Ending) < 0 Thenx = (TurningPoint(2) + Ending) / 2x3 = Newton_Solve(x)End IfEnd IfEnd IfSolve_Order3_Equation = Array(x1, x2, x3)End Function//******************************************************************* ********************Function f_x(xValue) As Double ' f_x = f(x) 求贝塞尔参数方程 f(t)的值f_x = a3 * xValue ^ 3 + a2 * xValue ^ 2 + a1 * xValue + a0End FunctionFunction Df_x(xValue As Double) As Double ' Df_x = f'(x) ' f_x = f(x) 求贝塞尔参数方程导函数 f'(t)的值Df_x = 3 * a3 * xValue ^ 2 + 2 * a2 * xValue + a1End FunctionFunction Solve_Order2_Equation(k2, k1, k0 As Double) As VariantDim b2SUB4ac As DoubleIf (k2 = 0) ThenIf k1 = 0 ThenSolve_Order2_Equation = Array(NoRoot, NoRoot)Exit FunctionElseIf (k1 <> 0 And k0 = 0) ThenSolve_Order2_Equation = Array(0, 0)Exit FunctionElseIf (k1 <> 0 And k0 <> 0) ThenSolve_Order2_Equation = Array(-k0 / k1, -k0 / k1)Exit FunctionEnd IfEnd Ifb2SUB4ac = (k1) ^ 2 - 4 * k2 * k0 ' 二次方程可以直接用公式求解,b^2-4*a*cIf b2SUB4ac < 0 ThenSolve_Order2_Equation = Array(NoRoot, NoRoot)End IfIf b2SUB4ac = 0 ThenSolve_Order2_Equation = Array(-k1 / 2 / k2, -k1 / 2 / k2)End IfIf b2SUB4ac > 0 ThenIf (-k1 + b2SUB4ac ^ 0.5) / 2 / k2 < (-k1 - b2SUB4ac ^ 0.5) / 2 / k2 ThenSolve_Order2_Equation = Array((-k1 + b2SUB4ac ^ 0.5) / 2 / k2, (-k1 - b2SUB4ac ^ 0.5) / 2 / k2)ElseSolve_Order2_Equation = Array((-k1 - b2SUB4ac ^ 0.5) / 2 / k2, (-k1 + b2SUB4ac ^ 0.5) / 2 / k2)End IfEnd IfEnd Function//******************************************************************* ********************Function Newton_Solve(x0 As Double) As VariantDim i, eps As Doublei = 0eps = Abs(f_x(x0))Do While (eps > MaxErr) '如果取初值,函数的绝对值大于允许误差If (Df_x(x0) <> 0 And i < MaxLoop) Then '而且分子不等于0,没有超出最大迭代次数x0 = x0 - f_x(x0) / Df_x(x0) '牛顿法求下一个值 x' = x0 - f(x0) /f'(x0)eps = Abs(f_x(x0))i = i + 1ElseNewton_Solve = NoRootExit FunctionEnd If LoopNewton_Solve = x0 End Function。