N阶贝塞尔曲线

Geo*_*nko 2 c# math bezier

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

这里是:曲线问题

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

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

谁能帮我解决这个问题吗?

这是我使用的代码:

    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;
    }
Run Code Online (Sandbox Code Playgroud)

我希望这对某人来说是清楚的!先感谢您!

更新: 我将点数减少到 3。结果如下:3 点曲线。这里清楚地可见计算有问题......还有更多建议吗?

Mik*_*ans 9

首先简化该代码,因为这对于调试来说是不可靠的。第一步:我们不要使用微积分,除非这样做确实有好处。使用完整的二项式计算和 t 幂通常与插值一样快(或慢)(贝塞尔曲线简单地表示为列表缩减),但插值可以通过简单的加法和乘法轻松实现,而二项式计算和权力意味着更多的工作。因此,让我们用几何方法而不是微积分来计算:

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
Run Code Online (Sandbox Code Playgroud)

完毕。通过排除二项式和幂,并纯粹基于迭代插值(即使用de Casteljau 算法)来实现曲线评估,这段代码中实际上没有什么可以“做错”:代码具有很高的品质!

您可以通过明确坐标、使用 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
Run Code Online (Sandbox Code Playgroud)

(最后的优化,虽然通常不值得,但也展开,以基于初始值和计数器while实现单个循环,其中减一并在 时重置为 0 ,并在 时终止)forL=list.lengthiLii==LL==1

如果你绝对需要微积分(老实说这里不是这种情况),至少“有效”地生成你的二项式系数:它们基于帕斯卡三角形生成起来非常简单,所以为了你的数学协处理器的热爱,不要使用阶乘来评估它们,它们实际上可以通过添加一些整数来生成:

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]
Run Code Online (Sandbox Code Playgroud)

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