简体   繁体   English

N阶贝塞尔曲线

[英]Bezier Curve of N order

I am trying to implement the formula for bezier curves of Nth order in my program. 我正在尝试在程序中实现N阶贝塞尔曲线的公式。 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. 红色立方体是P0,蓝色立方体是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... 另一件事是在P0和P1之间也发生了奇怪的事情...

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 . 更新:我将点数减少到3。这是结果: 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. 使用完整的二项式计算和t幂通常与插值一样快(或慢)(Bezier曲线简单地表示为列表约简),但是插值可通过简单的加法和乘法轻松实现,而二项式计算和力量是更多的工作。 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 (ie using de Casteljau's algorithm ) there is literally nothing that can be "done wrong" in this code: a great quality for code to have! 通过排除二项式和幂,并仅基于迭代插值(即使用de Casteljau的算法 )来实现曲线评估,实际上在此代码中没有什么可以“做错”的:代码具有很高的质量!

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: 通过使用array [3]而不是3d向量类来明确表示坐标,可以使代码更高效,从而在插值步骤中不必依赖运算符重载或函数调用减速。得到类似的东西:

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 ) (最后的优化,尽管通常不值得,但也要while进行,以基于初始L=list.length和counter i来实现单个for循环,其中L递减1且i重置为0当i==L ,终止于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: 而且,如果您绝对需要微积分(实际上不是这种情况),至少可以“高效”生成二项式系数:它们非常容易基于Pascal的三角形生成,因此出于对数学协处理器的热爱, 请勿使用阶乘来对它们进行评估,就可以通过将一些整数相加来生成它们:

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) (如果这样做,请确保记住正在编程并且数组偏移量从0开始,或者在行/列位置[0]处添加虚拟值,以便您可以“直观”地将binomial(4,2)调用为得到4选择2而不是5选择3)

Apart from the fact that this is a most inefficient computation of the binomials, that you should pre-compute the binomial sequence and not recompute them for every point you plot, and that you could avoid most of the calls to the power function by employing a Horner like method, .... 除了这是对二项式函数的最无效率的计算之外,您还应该预先计算二项式序列,而不是针对绘制的每个点都重新计算它们,并且可以通过使用a来避免对幂函数的大多数调用。霍纳式的方法,....

your code seems to be correct and also the visual is consistent with the control points forcing the middle part into a straight line. 您的代码似乎是正确的,并且视觉效果与强制中间部分成一直线的控制点一致。 High order polynomial interpolation can have some complicated wiggles at segments where the sample or control points have (relatively) large variations in value. 高阶多项式插值法在样本或控制点的值(相对)变化较大的部分可能会出现一些复杂的摆动。


Update : I did not at first look too closely at the helper functions as I would not compute binomials using separate computation of factorials, but your factorial function does not contain the factor n for the call with argument n , ie, it computes (n-1)! 更新 :我没有先看看过于紧密的辅助功能,因为我不会用计算阶乘的独立计算二项式,但你的阶乘函数不包含的因素n与参数调用n ,即,它计算(n-1)! .

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM