贝塞尔曲线及插值全解
- 格式: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来绘制贝塞尔曲线。
贝塞尔曲线及插值这里主要讲一下如何在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。