You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

封闭B-Spline Python实现异常:不改动节点与控制点的修复需求

问题:封闭2D B-Spline曲线异常修复

我编写了一段实现封闭2D B-Spline的Python代码,运行后生成的曲线不符合预期,需要在不改变节点向量(knots)和控制点规模的前提下修复问题。当前代码尝试用模运算处理节点与控制点的索引越界,但仍存在错误。

原代码:

import numpy as np
import matplotlib.pyplot as plt

class BSpline2D:
    def __init__(self, degree, knots, control_points):
        self.degree = degree
        self.knots = np.array(knots)
        self.control_points = np.array(control_points)

    def get_knot_at(self, i):
        n = len(self.knots)
        return self.knots[(i % n + n) % n]

    def evaluate(self, t):
        span = self.find_span(t)
        coeff = self.basis_functions(span, t)
        point = np.zeros(2)  # Assuming 2D control points
        for i in range(self.degree + 1):
            point += coeff[i] * self.get_control_point_at(span - self.degree + i)
        return point

    def find_span(self, u):
        return np.searchsorted(self.knots, u) - 1

    def basis_functions(self, i, u):
        coeff = [1.0]
        left = [0.0] * (self.degree + 1)
        right = [0.0] * (self.degree + 1)

        for j in range(1, self.degree + 1):
            left[j] = u - self.get_knot_at(i + 1 - j)
            right[j] = self.get_knot_at(i + j) - u
            saved = 0.0
            for r in range(j):
                temp = coeff[r] / (right[r + 1] + left[j - r])
                coeff[r] = saved + temp * right[r + 1]
                saved = temp * left[j - r]
            coeff.append(saved)
        return coeff

    def get_control_point_at(self, i):
        n = len(self.control_points)
        return self.control_points[(i % n + n) % n]

def plot_bspline_2d(spline, resolution=100):
    t_values = np.linspace(spline.knots[0], spline.knots[-1], resolution)
    curve_points = [spline.evaluate(t) for t in t_values]

    # Extract x, y for plotting
    curve_points = np.array(curve_points)
    x, y = curve_points[:, 0], curve_points[:, 1]

    # Extract control points
    control_points = np.array(spline.control_points)
    cx, cy = control_points[:, 0], control_points[:, 1]

    # Plot
    plt.figure()
    plt.plot(x, y, label='B-Spline Curve', color='blue')
    plt.scatter(cx, cy, color='red', label='Control Points')
    plt.plot(cx, cy, linestyle='dotted', color='red', label='Control Polygon')
    plt.legend()
    plt.xlabel("X")
    plt.ylabel("Y")
    plt.title("2D B-Spline Curve")
    plt.axis("equal")
    plt.show()

# Example usage:
degree = 2
knots = [1, 2, 3, 4, 5]
control_points = [
    [0.0, 0.0],
    [1.0, 0.0],
    [1.0, 1.0],
    [0.0, 1.0]
]

spline = BSpline2D(degree, knots, control_points)
plot_bspline_2d(spline)

错误分析

  1. 节点周期处理错误:原get_knot_at单纯用索引模运算,忽略了节点是递增的周期序列(周期为knots[-1] - knots[0]),导致计算基函数时节点差值错误。
  2. Span查找逻辑错误:原find_span未将参数t映射到基础周期区间,导致接近节点末尾时span定位错误。
  3. 参数范围错误:绘制时t_values取了整个节点区间,而封闭B样条的有效参数范围应为[knots[degree], knots[-degree]],否则会出现曲线首尾不闭合或异常段。

修复后的代码

import numpy as np
import matplotlib.pyplot as plt

class BSpline2D:
    def __init__(self, degree, knots, control_points):
        self.degree = degree
        self.knots = np.array(knots)
        self.control_points = np.array(control_points)
        self.n_knots = len(self.knots)
        self.n_ctrl = len(self.control_points)
        # 计算节点周期
        self.knot_period = self.knots[-1] - self.knots[0]

    def get_knot_at(self, i):
        # 计算周期偏移量,返回周期化后的节点值
        idx = i % self.n_knots
        cycles = i // self.n_knots
        return self.knots[idx] + cycles * self.knot_period

    def evaluate(self, t):
        # 将t映射到基础周期区间 [knots[0], knots[-1])
        t_normalized = t - self.knot_period * np.floor((t - self.knots[0]) / self.knot_period)
        span = self.find_span(t_normalized)
        coeff = self.basis_functions(span, t)
        point = np.zeros(2)
        for i in range(self.degree + 1):
            ctrl_idx = span - self.degree + i
            point += coeff[i] * self.get_control_point_at(ctrl_idx)
        return point

    def find_span(self, u):
        # 在基础节点区间内查找span
        span = np.searchsorted(self.knots, u) - 1
        # 处理u等于最后一个节点的情况
        if span >= self.n_knots - 1:
            span = self.n_knots - 2
        return span

    def basis_functions(self, i, u):
        coeff = [1.0]
        left = [0.0] * (self.degree + 1)
        right = [0.0] * (self.degree + 1)

        for j in range(1, self.degree + 1):
            left[j] = u - self.get_knot_at(i + 1 - j)
            right[j] = self.get_knot_at(i + j) - u
            saved = 0.0
            for r in range(j):
                temp = coeff[r] / (right[r + 1] + left[j - r])
                coeff[r] = saved + temp * right[r + 1]
                saved = temp * left[j - r]
            coeff.append(saved)
        return coeff

    def get_control_point_at(self, i):
        # 控制点循环索引,保持原逻辑但确保索引正确
        return self.control_points[i % self.n_ctrl]

def plot_bspline_2d(spline, resolution=100):
    # 封闭B样条的有效参数范围是 [knots[degree], knots[-degree]]
    t_start = spline.knots[spline.degree]
    t_end = spline.knots[-spline.degree]
    t_values = np.linspace(t_start, t_end, resolution)
    curve_points = [spline.evaluate(t) for t in t_values]

    # 提取曲线点坐标
    curve_points = np.array(curve_points)
    x, y = curve_points[:, 0], curve_points[:, 1]

    # 提取控制点坐标
    cx, cy = spline.control_points[:, 0], spline.control_points[:, 1]

    # 绘图
    plt.figure()
    plt.plot(x, y, label='封闭B-Spline曲线', color='blue')
    plt.scatter(cx, cy, color='red', label='控制点')
    plt.plot(np.append(cx, cx[0]), np.append(cy, cy[0]), linestyle='dotted', color='red', label='控制多边形')
    plt.legend()
    plt.xlabel("X")
    plt.ylabel("Y")
    plt.title("2D封闭B-Spline曲线")
    plt.axis("equal")
    plt.show()

# 示例使用:保持原节点和控制点不变
degree = 2
knots = [1, 2, 3, 4, 5]
control_points = [
    [0.0, 0.0],
    [1.0, 0.0],
    [1.0, 1.0],
    [0.0, 1.0]
]

spline = BSpline2D(degree, knots, control_points)
plot_bspline_2d(spline)

关键修改说明

  • 节点周期化:get_knot_at不再单纯模索引,而是计算周期偏移,返回符合递增周期的节点值,确保基函数计算时的节点差值正确。
  • 参数归一化:evaluate中将参数t映射到基础周期区间,避免span查找越界。
  • 有效参数范围:绘图时使用封闭B样条的有效参数区间[knots[degree], knots[-degree]],确保曲线首尾闭合且无异常段。
  • 控制多边形闭合:绘图时将控制多边形首尾相连,更直观展示封闭特性。

内容的提问来源于stack exchange,提问作者Jakab Martin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 05:04:51