Python 开放均匀 B 样条曲线中的 ZeroDivisionError

Python ZeroDivisionError in Open Uniform B-Spline Curve

所以我正在 python 中实现 B 样条曲线(我知道有现有的库,但我想自己做)并且它适用于非开放式统一 B-样条曲线如下所示:

右侧显示基函数(Cox de Boor 的递归公式)计算的图表与该视频中显示的内容非常吻合,唯一的区别是控制点的数量。 (https://www.youtube.com/watch?v=qhQrRCJ-mVg&t=2136):

一旦我尝试将其设为开放的均匀 B 样条曲线。我期待这样的图表 (https://www.youtube.com/watch?v=qhQrRCJ-mVg&t=2501):

但是,我得到了一个 ZeroDivisionError:

a = ((t - knotVector[i]) / (knotVector[i + j] - knotVector[i]) * CoxDeBoorRecursion(i, j - 1, t, knotVector))
ZeroDivisionError: float division by zero

其中结向量定义为 [0, 0, 0, 1, 2, 3, 3, 3],如视频 https://www.youtube.com/watch?v=qhQrRCJ-mVg&t=2460 中所示。

这是我的代码:

def UniformBSpline(t, controlPoints, open=False):
    sumX, sumY = 0, 0
    degree = 2
    knotVector = [x for x in range(len(controlPoints) + 2 + degree)]

    if open:
        knotVector = knotVector[0:len(controlPoints)]
        for _ in range(degree):
            knotVector.insert(0, knotVector[0])
            knotVector.insert(-1, knotVector[-1])

    for i in range(len(controlPoints)):
        sumX += CoxDeBoorRecursion(i, degree, t, knotVector) * controlPoints[i].x
        sumY += CoxDeBoorRecursion(i, degree, t, knotVector) * controlPoints[i].y

    return sumX, sumY

def CoxDeBoorRecursion(i, j, t, knotVector):
    if j == 0:
        return 1 if knotVector[i] <= t < knotVector[i+1] else 0

    a = ((t - knotVector[i]) / (knotVector[i + j] - knotVector[i]) * CoxDeBoorRecursion(i, j - 1, t, knotVector))
    b = (((knotVector[i + j + 1] - t)/(knotVector[i + j + 1] - knotVector[i + 1])) * CoxDeBoorRecursion(i + 1, j - 1, t, knotVector))
    return a + b

这似乎是我正在使用的函数中的一个问题,该函数定义为:

我该如何解决这个问题?对于 Math StackExchange,这可能是一个更好的问题吗?

您的代码中有两个错误:

均匀闭合 B 样条曲线的结向量应该计算到段数,而不是控制点数。段数是控制点数减去度数。也就是说,如果你有一个 2 阶样条的三个点(即,一个简单的贝塞尔曲线),你会得到一个单一的线段。对于每一个额外的点,你会得到一个更多的部分。因此,结向量必须是:

if open:
    knotVector = [x for x in range(len(controlPoints) - degree + 1)]    
    for _ in range(degree):
        knotVector.insert(0, knotVector[0])
        knotVector.insert(-1, knotVector[-1])

对于四个控制点,这会给你

[0, 0, 0, 1, 2, 2, 2]

您对关闭的案例有类似的问题。这就是为什么样条曲线的角位于 (0, 0)。不过还没有具体检查过。

第二个问题:如果节点重复,权重的除数可以为零。如果是这种情况,就忽略那部分权重:

d1 = (knotVector[i + j] - knotVector[i])
a = ((t - knotVector[i]) / d1 * CoxDeBoorRecursion(i, j - 1, t, knotVector)) if d1 > 0 else 0

d2 = (knotVector[i + j + 1] - knotVector[i + 1])
b = (((knotVector[i + j + 1] - t)/d2) * CoxDeBoorRecursion(i + 1, j - 1, t, knotVector)) if d2 > 0 else 0

您也可以将该部分提取到一个单独的函数中,就像 Wikipedia article 那样(函数 ω)。