使用DeBoor算法发布均匀B样条

时间:2018-12-14 12:48:41

标签: c# recursion bezier curve spline

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

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

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);
    }

我试图从deBoor方法中得到这样的点:

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,它们似乎都执行相同的工作,只是编码方式不同。我试图复制他们的解决方案,但结果却更糟。所有这些使我觉得我缺少明显痛苦的东西。

1 个答案:

答案 0 :(得分: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

如果您坚持使用int(IMHO,这是造成您混乱的原因)

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个控制点的系数,它是一个标量。因此,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的乘法(您可能需要重载这样的运算符),而+表示两个{{ 1}} s(同样,可能需要运算符重载)。我不是很用C#精通,所以有可能把它写下来的更优雅的方式;例如,我邀请您使用point循环。

如果仍然感到困惑,我建议您先熟悉贝塞尔曲线,然后再进行B样条曲线。例如,here可以找到相对简洁的介绍。这些照片是有点90年代的风格,但思想仍然有效和易于理解和简洁的方式呈现。