贝塞尔曲线评估

时间:2011-10-30 01:51:12

标签: c++ algorithm math geometry bezier

我在这里使用de Casteljau的算法http://www.cgafaq.info/wiki/B%C3%A9zier_curve_evaluation来关注本文,我尝试使用主题Drawing Bezier curves using De Casteljau Algorithm in C++ , OpenGL来帮助。没有成功。

评估后,我的bezier曲线看起来像这样

正如你所看到的,即使它不起作用我想要它,所有的点确实在曲线上。我不认为这个算法因此而不准确。

以下是该图片顶部曲线上的点数: (0,0) (2,0) (2,2) (4,2)第二条曲线使用相同的点集,但第三点是(0,2),即第一点上方两个单位,形成更陡峭的曲线。

出了点问题。我应该为t输入0.25,它应该为X值吐出1.0,而.75应该总是返回3.假设t是时间。它应该以恒定的速度发展,是吗?恰好25%的方式,X值应该是1.0,然后Y应该与该值相关联。

是否有足够的方法来评估贝塞尔曲线?有谁知道这里发生了什么?

感谢您的帮助! :)

修改------

我在google搜索http://www.tsplines.com/resources/class_notes/Bezier_curves.pdf中找到了这本书,这是我在显式/非参数贝塞尔曲线上找到的页面。它们是多项式,表示为贝塞尔曲线,这就是我在这里要做的。这是书中的那个页面:

任何人都知道如何将贝塞尔曲线转换为参数曲线?我现在可以开一个不同的主题......

自2011年11月1日起编辑-------

我意识到我只是问了一半问题,就像我应该的那样清楚。我正在尝试构建的就像Maya的动画图形编辑器,例如这个http://www.youtube.com/watch?v=tckN35eYJtg&t=240,其中用于修改曲线的贝塞尔控制点更像是相等长度的切线修改器。老实说,我不记得他们是等长的。通过强制使用这样的系统,您可以100%确保结果是一个函数并且不包含重叠的段。

我找到了这个,可能有我的答案http://create.msdn.com/en-US/education/catalog/utility/curve_editor

5 个答案:

答案 0 :(得分:12)

在这里,您可以看到在the nomenclature in your link之后在Mathematica中实现的算法,以及您的两个图:

(*Function Definitions*)

lerp[a_, b_, t_] := (1 - t) a + t b;
pts1[t_] := {
   lerp[pts[[1]], pts[[2]], t],
   lerp[pts[[2]], pts[[3]], t],
   lerp[pts[[3]], pts[[4]], t]};
pts2[t_] := {
   lerp[pts1[t][[1]], pts1[t][[2]], t],
   lerp[pts1[t][[2]], pts1[t][[3]], t]};
pts3[t_] := {
   lerp[pts2[t][[1]], pts2[t][[2]], t]};

(*Usages*)

pts = {{0, 0}, {2, 0}, {2, 2}, {4, 2}};
Framed@Show[ParametricPlot[pts3[t], {t, 0, 1}, Axes -> True], 
            Graphics[{Red, PointSize[Large], Point@pts}]]

pts = {{0, 0}, {2, 0}, {0, 2}, {4, 2}};
Framed@Show[ParametricPlot[pts3[t], {t, 0, 1}, Axes -> True], 
            Graphics[{Red, PointSize[Large], Point@pts}]]

enter image description here

BTW,曲线由以下参数方程定义,这些方程是上面代码中的函数pts3[t]

c1[t_] := {2 t (3 + t (-3 + 2 t)), (* <- X component *)
                 2 (3 - 2 t) t^2}  (* <- Y component *)

c2[t_] := {2 t (3 + t (-6 + 5 t)), (* <- X component *)
               , 2 (3 - 2 t) t^2}  (* <- Y component *)

尝试绘制它们!

采用任何这些曲线方程,并通过求解三次多项式,在这些情况下,你可以得到y [x]的表达式,这当然不总是可行的。只是为了让你了解它,从你得到的第一条曲线(C语法):

y[x]= 3 - x - 3/Power(-2 + x + Sqrt(5 + (-4 + x)*x),1/3) + 
              3*Power(-2 + x + Sqrt(5 + (-4 + x)*x),1/3)

Try plotting it!

修改

只是一种娱乐:

Mathematica是一种功能非常强大的函数式语言,事实上整个算法可以表示为一个单一的算法:

f = Nest[(1 - t) #[[1]] + t #[[2]] & /@ Partition[#, 2, 1] &, #, Length@# - 1] &

f@{{0, 0}, {2, 0}, {0, 2}, {4, 2}}

给出了上述结果,但支持任意数量的点。

让我们尝试六个随机点:

p = RandomReal[1, {6, 2}];
Framed@Show[
  Graphics[{Red, PointSize[Large], Point@p}],
  ParametricPlot[f@p, {t, 0, 1}, Axes -> True]]

enter image description here

此外,相同的功能在3D中起作用:

p = RandomReal[1, {4, 3}];
Framed@Show[
  Graphics3D[{Red, PointSize[Large], Point@p}],
  ParametricPlot3D[f[p], {t, 0, 1}, Axes -> True]]

enter image description here

答案 1 :(得分:9)

贝塞尔曲线可以通过求解x,y和z坐标的以下参数方程来解决(如果它只是2D,只做x和y):

Px = (1-t)^3(P1x) + 3t(1-t)^2(P2x) + 3t^2(1-t)(P3x) + t^3(P4x)
Py = (1-t)^3(P1y) + 3t(1-t)^2(P2y) + 3t^2(1-t)(P3y) + t^3(P4y)
Pz = (1-t)^3(P1z) + 3t(1-t)^2(P2z) + 3t^2(1-t)(P3z) + t^3(P4z)

您也可以通过将矩阵方程ABC = X乘以其中来解决此问题:

  1. 矩阵A是1x4矩阵,表示t
  2. 的幂的值
  3. 矩阵B是t的幂的系数,是一个下三角形的4x4矩阵
  4. 矩阵C是一个4x3矩阵,表示3D空间中的四个贝塞尔点中的每一个(它将是2D空间中的4x2矩阵)
  5. 这将如下所示:

    matrix equation

    (更新 - 左下角1应该是-1)

    方程的两种形式(参数形式和矩阵形式)的一个重要注意事项是t在[0,1]范围内。

    而不是尝试解决t的值,它会给你xy的整数值,这将是非常耗时的,因为你基本上是在解决对于三次多项式的实根,最好只在t值中创建足够小的差值,使得曲线上任意两点之间的差异小于像素值增量。换句话说,两个点P(t1)P(t2)之间的距离使得它小于像素值。或者,您可以在t中使用较大的差异,并在P(t1)P(t2)之间进行线性插值,请注意,如果{P(t1)之间的差异,曲线可能不会“平滑”。 1}}和P(t2)对于来自[0,1]的t的给定范围来说不够小。

    t中找到必要的微分以从视觉角度创建相当“平滑”曲线的好方法是实际测量定义贝塞尔曲线的四个点之间的距离。测量从P1到P2,P2到P3和P3到P4的距离。然后取最长距离,并使用该值的倒数作为t的差值。您可能仍需要在点之间进行一些线性插值,但每个“线性”子曲线中的像素数应该相当小,因此曲线本身看起来相当平滑。您始终可以从此初始值减小t上的差值,使其“更平滑”。

    最后,回答你的问题:

      

    假设是时间。它应该以恒定的速度发展,是吗?恰好25%的方式,X值应该是1.0,然后Y应该与该值相关联。

    不,这是不正确的,原因是矢量(P2 - P1)和(P3 - P4)不仅与P1和P4的贝塞尔曲线相切,而且它们的长度定义了速度沿着这些点的曲线。因此,如果向量(P2-P1)是一个短距离,那么这意味着在给定的时间t,你将不会离点P1很远...这转换为x,y值对于t的给定固定微分,沿着曲线非常紧密地堆积在一起。当你向P1移动时,你有效地“减速”速度。根据矢量的长度(P3-P4),在曲线上的P4处发生相同的效果。沿着曲线的速度唯一的方式是“恒定”,因此t的共同微分的任何点之间的距离将是相同的,如果所有三个部分的长度(P2-P1) ),(P3-P2)和(P4-P3)相同。这表明沿曲线的速度没有变化。

答案 2 :(得分:4)

听起来你实际上只想要1D立方贝塞尔曲线而不是你所拥有的2D曲线。具体来说,你真正想要的只是一个从0开始的三次多项式段,当在0到4的域上进行求值时,它会上升到2。所以你可以使用一些基本的数学并找到多项式:

f(x) = a + b*x + c*x^2 + d*x^3
f(0) = 0
f(4) = 2

留下两个自由度 取该函数的导数:

f'(x) = b + 2*c*x + 3*d*x^2

如果你想让它在开始时陡峭,然后在结束时保持平衡,你可能会说:

f'(0) = 10
f'(4) = 0

然后我们可以插入值。 a b 免费提供,因为我们的评估为零。

a = 0
b = 10

那么我们就有了:

f(4) = 2 = 40 + c*16 + d*64
f'(4) = 0 = 10 + c*8 + d*48

这是一个非常容易解决的线性系统。为了完整起见,我们得到:

16c + 64d = -38
 8c + 48d = -10

所以 -

1/(16*48 - 8*64)|48 -64||-38| = |c| = |-37/8 |
                |-8  16||-10|   |d|   |  9/16|

f(x) = 10*x - (37/8)*x^2 + (9/16)*x^3

相反,如果您决定要使用Bezier控制点,只需选择4个y值控制点并识别为了在[0,1]中获得 t ,只需要说t=x/4(记住,如果你还需要衍生品,你也必须在那里进行改变)。


添加了:

如果您碰巧知道要开始和结束的积分和衍生物,但您想使用Bezier控制点 P1 P2 P3 P4 ,映射就是这样(假设曲线参数化为0到1):

P1 = f(0)
P2 = f'(0)/3 + f(0)
P3 = f(1) - f'(1)/3
P4 = f(1)

如果出于某种原因,您希望坚持使用2D Bezier控制点,并希望确保 x 维度从0到2线性提前,因为 t 高级从0到1,那么你可以用控制点(0,y1) (2/3,y2) (4/3,y3) (2,y4)来做到这一点。您可以看到我只是将 x 维度从0开始,结束于2,并且具有2的恒定斜率(导数)(相对于 t )。然后你只需要y坐标就可以了。不同的维度基本上是相互独立的。

答案 3 :(得分:0)

毕竟这一次,我一直在寻找hermite曲线。隐士是好的,因为在一个维度上,他们保证产生可以评估为XY点的功能曲线。我把隐士与贝齐尔混为一谈。

答案 4 :(得分:0)

&#34;假设是时间。&#34;

这是问题 - 不是时候。该曲线具有它自己的t变化率,具体取决于切线的大小。像Jason所说的那样,后续点之间的距离必须相同,t的顺序与时间相同。这正是Maya曲线编辑器中的非加权模式(默认情况下使用)。所以对于如何解决这个问题,这是一个非常好的答案。要使其适用于任意切线,必须将时间转换为t。您可以通过计算x(或时间)方向的贝塞尔方程来找到t。

Px =(1-t)^ 3(P1x)+ 3t(1-t)^ 2(P2x)+ 3t ^ 2(1-t)(P3x)+ t ^ 3(P4x)

Px是你的时间,所以你知道这里的一切,但是。你必须求解一个三次方程来找到根。找到你需要的确切根,有一个棘手的部分。然后你解决另一个方程式找到Py(你正在寻找的实际值),现在知道t:

Py =(1-t)^ 3(P1y)+ 3t(1-t)^ 2(P2y)+ 3t ^ 2(1-t)(P3y)+ t ^ 3(P4y)

这就是Maya中的加权曲线。 我知道这个问题已经过时了,但是我整整一天都在研究这个简单的事情,并没有人确切地解释发生了什么。否则,计算过程本身就会写在很多地方,比如Maya API手册。 Maya devkit还有一个源代码来执行此操作。

相关问题