Python ZeroDivisionError в открытой однородной кривой B-сплайна

Итак, я работал с реализацией кривых B-сплайнов в python (и я знаю, что существуют существующие библиотеки, но я хотел сделать это сам), и это хорошо сработало для неоткрытых однородных кривых B-сплайнов, как показано здесь:

введите описание изображения здесь

График справа, на котором показаны расчеты базовой функции (формула рекурсии Кокса де Бура), очень хорошо соответствует тому, что показано в этом видео, с той лишь разницей, что количество контрольных точек. (https://www.youtube.com/watch?v=qhQrRCJ-mVg&t=2136):

введите описание изображения здесь

Как только я попытаюсь сделать это открытой однородной кривой B-Spline. Я ожидаю такой диаграммы (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?


person Bhavye Mathur    schedule 05.07.2020    source источник


Ответы (1)


В вашем коде есть две ошибки:

Узловой вектор для однородных замкнутых 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

Вы также можете выделить эту часть в отдельную функцию, например, в статье Википедии ( функция ω).

person Nico Schertler    schedule 05.07.2020