C# 使用deBoor和#x27的均匀B样条;s算法问题

C# 使用deBoor和#x27的均匀B样条;s算法问题,c#,recursion,bezier,curve,spline,C#,Recursion,Bezier,Curve,Spline,我有一个非常简单的例子,我需要画一个2次均匀B样条线段(4个控制点),我正试图用C#()实现deBoor的算法,但我遇到了一个问题,没有多少阅读和研究帮助我找出到底发生了什么 在我的例子中,一个Point[]数组中只定义了4个控制点(p1、p2、p3和p4)。所以我只需要点p2和p3之间的曲线段。正因为如此,我构造了一个均匀的结数组,没有前导和尾随结[0,1,2,3]——基本上我可以在这个例子中使用I,但我这样做是为了坚持公式。 我从维基百科构建了递归公式的实现: 看起来是这样的:

我有一个非常简单的例子,我需要画一个2次均匀B样条线段(4个控制点),我正试图用C#()实现deBoor的算法,但我遇到了一个问题,没有多少阅读和研究帮助我找出到底发生了什么

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

看起来是这样的:

    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);
    }
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);
我试着从deBoor方法中得到点,如下所示:

    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);
    }
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);
不幸的是,我得到的结果是不正确的。此图显示了我的控制点的外观、我期望得到的以及我实际得到的:


我看了其他的实现,比如这个:它们似乎都做同样的事情,只是编码不同。我试图模仿他们的解决方案,但结果更糟。所有这些让我觉得我错过了一些非常明显的东西。

你似乎混淆了节点和控制点。我们需要在您的解决方案中改进几点,以使其正常工作

零度函数 正如@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;
}
请注意,结现在介于0和1之间;这意味着
t
time
将是相同的,即

float t = time; //time ranges from 0 to 1 and so does t
如果你坚持你的结是
int
s(IMHO,这导致了你的困惑),使用

注意交换顺序:
t
必须按
time
比例缩放,以覆盖整个绳结范围

评价 De Boor的算法计算一个B样条,即一个基函数(有些人使用一个相互冲突的术语,对整个样条函数使用B样条,而不仅仅是一个基函数;这有时会导致混淆)

非正式地说,对于给定的
t
,您的
deBoor
函数将为您提供第
i
-个控制点的系数,这是一个标量。因此,
deBoor
的返回值必须是
float
double
或类似的值,当然不是

对于每个
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];
请注意,
*
表示一个
浮点
和一个
(您可能需要重载这样的运算符)的乘积,
+
表示两个
的和(同样,可能需要重载运算符)。我对C#不是很精通,所以可能有一种更优雅的方式来记录它;例如,我邀请您使用
for
循环


如果您仍然感到困惑,我建议您首先熟悉Bézier曲线,然后继续学习B样条曲线。例如,可以找到相对简洁的介绍。这些图片有点90年代的风格,但是这些想法仍然有效,并且以一种可以理解和简洁的方式呈现出来。

您能否将其详细说明为一个最小的、完整的示例,以便我们进行调试?例如,
点的定义和运行时值
,以及使用调用deBoor的结果的方法/循环。@dlatikay当然可以。这里它使用的是来自图像的相同控制点:结向量似乎不正确。对于具有4个控制点的2阶B样条曲线,节点向量应具有7个节点值,例如[0、0、0、1、2、2]。此外,短语“p2和p3之间的曲线段”没有太多意义,因为p2和p3不会位于曲线上,除非您指的是p2和p3投影点所绑定的曲线段。谢谢,@fang我以前尝试过这组结-0,0,0,1,2,2,2,但不幸的是,这甚至没有生成曲线。当我为t(0-(points.Length-1))的任何值调用deBoor(0,2,t,knots)时,它生成第一个控制点的位置。这就是我构建当前使用的结数组的原因。我对节点向量的理解是,它提供了一种方法,可以获得由任意控制点集约束的曲线段,在我的情况下,我只需要由p2和p3约束的曲线段,并且永远不需要除此之外的任何东西。我不久前写过这篇文章,在这里可能有用吗?