N阶贝塞尔曲线 [英] Bezier Curve of N order

查看:76
本文介绍了N阶贝塞尔曲线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在程序中实现N阶贝塞尔曲线的公式.在我看来,我所做的一切都正确,但是视觉效果不正确.

在这里是:

红色立方体为P0,蓝色立方体为P8.白色立方体是构成曲线的实际点集.橙色立方体是控制点.

我看到的是,曲线的末端之前有一个循环,曲线连接到最后一个点(蓝色立方体).似乎有一个看不见的点.另一件事是在P0和P1之间也发生了奇怪的事情...

有人可以帮我解决吗?

这是我使用的代码:

  private void Update(){controlPointsCoords = ControlPoints.Select(p => p.transform.position).ToArray();对于(int p = 0; p< PointsSet.Count; p ++){PointsSet [p] .transform.position = CurveDegreeN(controlPointsCoords,Rt(p,PointsSet.Count));}}私有Vector3 CurveDegreeN(Vector3 [] pointsCoords,float u){浮点数X = 0,Y = 0,Z = 0;float n = pointsCoords.Length-1;对于(int i = 0; i< pointsCoords.Length; i ++){var coef =(Factorial(n)/(Factorial((float)i)* Factorial(n-i)))* Mathf.Pow(u,i)* Mathf.Pow(1-u,n-i);X + = coef * pointsCoords [i] .x;Y + = coef * pointsCoords [i] .y;Z + = coef * pointsCoords [i] .z;}返回新的Vector3(X,Y,Z);}私有浮点阶乘(float n){如果(n == 0)返回1;float res = 0.0f;for(int i = 1; i  

我希望对某人来说这很清楚!预先谢谢你!

更新:我将点数减少到3.这是结果: 3点曲线.在这里可以清楚地看到计算出了点问题...还有其他建议吗?

解决方案

首先简化该代码,因为这将使调试变得不可靠.第一步:除非有实际好处,否则不要使用微积分.使用完整的二项式计算和t幂通常与插值一样快(或慢)(Bezier曲线简单地表示为列表约简),但是插值可通过简单的加法和乘法轻松实现,而二项式计算和力量是更多的工作.因此,让我们以几何方式进行评估,而不是使用微积分:

 函数drawCurve(coords []):点= []//步数"越高,则生成的曲线点越多:对于(s = 0,steps = 10; s< = steps; s ++):t = s/步nt = 1-tlist [] = coords.shallowCopy()//我们现在运行列表缩减以获取曲线//使用de Casteljau的算法指向t:while(list.length> 1)for(i = 0,e = list.length; i< e; i ++):list [i] = nt * list [i] + t * list [i + 1]list.pop()//剩下的就是我们在t处的曲线点.//美丽;推动并前进到下一个点.points.push(list [0])返回点 

完成.通过排除二项式和幂,并完全基于迭代插值来实现曲线评估(即,使用 de Casteljau的算法)在这段代码中,没有什么是做错了"的:代码具有很高的质量!

通过使用array [3]而不是3d向量类来明确显示坐标,可以使此代码更加高效,从而在插值步骤中不必依赖运算符重载或函数调用减速,因此您会得到类似的信息:

 函数drawCurve(coords []):coords = flatten(coords)//一次将Vector3转换为平面[x,y,z]数组...while(list.length> 1)for(i = 0,e = list.length; i< e; i ++):v1 =列表[i]v2 =列表[i + 1]list [i] [0] = nt * v1 [0] + t * v2 [0]//xlist [i] [1] = nt * v1 [1] + t * v2 [1]//ylist [i] [2] = nt * v1 [2] + t * v2 [2]//zlist.pop()points.push(new Vector3(list [0]))返回点 

(最后的优化方法虽然通常不值得,但也要同时展开 ,以基于初始的效果为单个 for 循环L = list.length 和计数器 i ,其中 L 减1,而 i i时重置为0== L ,并且在 L == 1 )

时终止

如果您绝对需要演算(实际上不是这种情况),至少有效"生成您的二项式系数:基于 binomial(4,2)得到4选择2而不是5选择3)

I am trying to implement the formula for bezier curves of Nth order in my program. It looks to me that I have done everything right but the visual result is not correct.

Here it is:

The red cube is P0 and the blue is P8. The white cubes are the actual set of points that make the curve. The orange cubes are the control points.

What I see is that there is a loop before the end of the curve where the curve attaches to the last (blue cube) point. Looks like there is an invisible point. And another thing is that between P0 and P1 is also something weird going on...

Can anyone help me to resolve it?

Here is the code I use:

    private void Update()
    {
        controlPointsCoords = ControlPoints.Select(p => p.transform.position).ToArray();

        for (int p = 0; p < PointsSet.Count; p++)
        {
            PointsSet[p].transform.position = CurveDegreeN
            (
                controlPointsCoords,
                Rt(p, PointsSet.Count)
            );
        }
    }

    private Vector3 CurveDegreeN(Vector3[] pointsCoords, float u)
    {
        float X = 0, Y = 0, Z = 0;
        float n = pointsCoords.Length - 1;

        for (int i = 0; i < pointsCoords.Length; i++)
        {
            var coef = (Factorial(n) / (Factorial((float)i) * Factorial(n - i))) * Mathf.Pow(u, i) * Mathf.Pow(1 - u, n - i);
            X += coef * pointsCoords[i].x;
            Y += coef * pointsCoords[i].y;
            Z += coef * pointsCoords[i].z;
        }

        return new Vector3(X, Y, Z);
    }

    private float Factorial(float n)
    {
        if (n == 0) return 1;

        float res = 0.0f;
        for (int i = 1; i < n; i++) res += (float)Math.Log(i);
        return (float)Math.Exp(res);
    }


    private float Rt(int current, int count)
    {
        return ((float)current - 0) / ((float)count - 0) * (1 - 0) + 0;
    }

I hope this will be clear for someone! Thank you in advance!

UPDATE: I reduced amount of points to 3. Here is the result: 3 Points curve. It is clearly visible here that something is wrong with the computations... Any more suggestions?

解决方案

Start by simplifying that code, because this is going to be unreliable to debug. Step one: let's not use calculus unless there is an actual benefit to doing so. Using the full binomial calculation and powers-of-t is typically just as fast (or slow) as interpolation (Bezier curves are trivially expressed as list reductions), but interpolation is dead-easily implemented with simple addition and multiplication, while binomial computation and powers are more work. So let's evaluate geometrically instead of using calculus:

function drawCurve(coords[]):
  points = []
  // the higher you make "steps", the more curve points you generate:
  for (s=0, steps=10; s<=steps; s++):
    t = s/steps
    nt = 1 - t
    list[] = coords.shallowCopy()

    // We now run our list reduction to get our on-curve
    // point at t, using de Casteljau's algorithm:
    while(list.length > 1)
      for(i = 0, e = list.length; i < e; i++):
        list[i] = nt * list[i] + t * list[i+1]
      list.pop()

    // And what's left is our on-curve point at t.
    // Beauty; push and move on to the next point.
    points.push(list[0])
  return points

Done. By ruling out binomials and powers, and implementing curve evaluation purely based on the iterative interpolation (i.e. using de Casteljau's algorithm) there is literally nothing that can be "done wrong" in this code: a great quality for code to have!

You can make this code even more efficient by being explicit about your coordinates, using array[3] instead of 3d vector classes so that you don't have to rely on operator overloading, or function call slowldowns, during the interpolation steps, so you get something like:

function drawCurve(coords[]):
  coords = flatten(coords) // one-time convert Vector3 to flat [x,y,z] arrays
    ...
    while(list.length > 1)
      for(i = 0, e = list.length; i < e; i++):
        v1 = list[i]
        v2 = list[i+1]
        list[i][0] = nt * v1[0] + t * v2[0] // x
        list[i][1] = nt * v1[1] + t * v2[1] // y
        list[i][2] = nt * v1[2] + t * v2[2] // z
      list.pop()
    points.push(new Vector3(list[0]))
  return points

(and a final optimization, though typically not worth it, is to unroll the while as well, to effect a single for loop based on the initial L=list.length and counter i, where L is decremented by one and i resets to 0 when i==L, and which terminates when L==1)

And if you absolutely need calculus (which is honestly not the case here) at the very least generate your binomial coefficients "efficiently": they are super simple to generate based on Pascal's triangle so for the love of your math coprocessor do not use factorials to evaluate them, they can literally be generated by just adding up some integers:

lut = [      [1],           // n=0
            [1,1],          // n=1
           [1,2,1],         // n=2
          [1,3,3,1],        // n=3
         [1,4,6,4,1],       // n=4
        [1,5,10,10,5,1],    // n=5
       [1,6,15,20,15,6,1]]  // n=6

binomial(n,k):
  while(n >= lut.length):
    s = lut.length
    nextRow = []
    nextRow[0] = 1
    for(i=1, prev=s-1; i<prev; i++):
      nextRow[i] = lut[prev][i-1] + lut[prev][i]
    nextRow[s] = 1
    lut.push(nextRow)
  return lut[n][k]

(If you do this, either make sure you remember that you're programming and array offsets start at 0, or add dummy values at row/column positions [0] so that you can "intuitively" call binomial(4,2) to get 4 choose 2 rather than 5 choose 3)

这篇关于N阶贝塞尔曲线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆