使用deBoor算法问题的均匀B样条

问题描述 投票:1回答:1

我有一个非常简单的例子,我需要绘制一个2级(4个控制点)的单个均匀B样条段,并且我试图在C#(https://en.wikipedia.org/wiki/De_Boor%27s_algorithm)中实现deBoor的算法,但我遇到了一个问题,没有多少阅读和研究帮助我找出正在发生的事情。

在我的例子中,我只在Point []数组中定义了4个控制点(p1,p2,p3和p4)。所以我只需要点p2和p3之间的曲线段。因此,我构建了一个没有前导和尾随结[0,1,2,3]的统一结阵列 - 基本上我可以在这种情况下使用i,但我这样做是为了坚持公式。我从维基百科构建了一个递归公式的实现:

enter image description here enter image description here

看起来像这样:

    Point deBoor(int i, int k, float t, int[] knots)
    {
        //i - knot span index
        //k - degree
        // t - time [0-knots.Length-1]
        //knots - the knots array
        if (k == 0) return points[knots[i]]/3f;
        return ((t - knots[i]) / (knots[i + k] - knots[i])) * deBoor(i, k - 1, t, knots) + ((knots[i + k + 1] - t) / (knots[i + k + 1] - knots[i + 1])) * deBoor(i + 1, k - 1, t, knots);
    }

我试着从deBtor方法中得到这样的观点:

float t = time * (points.Length - 1); //time ranges from 0 to 1
int[] knots = new int[] { 0, 1, 2, 3 };
point = deBoor(0, 2, t, knots);

不幸的是,我得到的结果是不正确的。这张图片显示了我的控制点是什么样的,我期待得到的以及我实际获得的内容:enter image description here

我查看了其他实现,比如这个:https://gist.github.com/soraphis/61ee9185416ee23d0d40,它们似乎都是一样的,只是编码不同。我试图复制他们的解决方案,但结果更糟。所有这些让我觉得我错过了一些非常明显的东西。

c# recursion bezier curve spline
1个回答
1
投票

你似乎混淆了结和控制点。为了使其成功,我们需要在您的解决方案中改进一些事项。

度零函数

正如@fang已经提到的,你对k==0的解决方案很奇怪。我建议更换

if (k == 0) return points[knots[i]]/3f;

通过更接近原始公式的东西,例如

if (k==0)
{
  if (t <= knots[i] && t < knots[i+1])
    return 1;
  else
    return 0;
}

结矢量

正如@fang所指出的,对于具有四个控制点的二次样条,您需要七节。你提到你想要统一结,并根据你推荐的预期图片

int[] knots = new float[] {0, 1/6, 2/6, 3/6, 4/6, 5/6, 1};

请注意,结现在介于0和1之间;这意味着ttime将是相同的,即,

float t = time; //time ranges from 0 to 1 and so does t

如果你坚持你的结是ints(其中,恕我直言,导致你的困惑)使用

int[] knots = new int[] { 0, 1, 2, 3, 4, 5, 6 };
float t = time * 6; //time ranges from 0 to 1 and t from 0 to 6

注意交换顺序:t必须是time缩放以覆盖整个结范围。

评估

De Boor的算法评估一个B样条,即基函数之一(有些人使用冲突的命名法,并使用单词B样条作为整个样条函数而不仅仅是基函数之一;这有时会导致混淆) 。

非正式地说,对于给定的t,你的deBoor函数给出了i-th控制点的系数,这是一个标量。因此,deBoor的返回值必须是floatdouble或类似的东西,当然不是point

对于每个t,您需要对这些系数缩放的控制点求和。所以你的最终结果将是这样的

point value = deBoor(0, 2, t, knots) * points[0]
  + deBoor(1, 2, t, knots) * points[1]
  + deBoor(2, 2, t, knots) * points[2]
  + deBoor(3, 2, t, knots) * points[3];

请注意,*表示floatpoint的乘法(您可能需要重载此类运算符)并且+表示两个points的总和(同样,可能需要运算符重载)。我不是很熟悉C#,所以可能有更优雅的写作方式;例如,我邀请你使用for循环。

如果你仍然感到困惑,我建议先熟悉Bézier曲线,然后再进行B样条曲线。可以找到相对简洁的介绍,例如here。这些图片有点90年代风格,但这些想法仍然有效,并以可理解和简洁的方式呈现。

© www.soinside.com 2019 - 2024. All rights reserved.