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

寻求将B样条/三次样条拆分为多段Bezier曲线的方法

平滑样条转多段Bezier曲线的问题与需求

背景与现有实现

本人正在自学游戏开发,研究路径设计方向。现有一组随机点,需要构建最优拟合的平滑路径(无需经过所有点)。通过Scipy的平滑样条插值解决了常规样条必须经过所有点的问题,生成的B样条满足平滑需求,代码如下:

from scipy.interpolate import BSpline, CubicSpline
from scipy.interpolate import splrep
from scipy.interpolate import BPoly
import numpy as np
# x, y are the given points
x = np.array([ 0. ,  1.2,  1.9,  3.2,  5, 6.5])
y = np.array([ 0. ,  2.3,  3. ,  4.3,  2.9, 3.1])
t, c, k = splrep(x, y, s=0.8, k=3) # 通过设置s=0.8实现平滑

spl = BSpline(t, c, k) # 创建B样条

转换为多段Bezier曲线的尝试与问题

需要将上述B样条拆分为多段Bezier曲线,尝试两种方法均遇到问题:

方法1:参考代码转换的困惑

参考相关代码进行B样条转Bezier曲线,代码如下:

import aggdraw
import numpy as np
import scipy.interpolate as si
from PIL import Image

def scipy_bspline(cv, degree=3):
    """ cv:       控制点数组
        degree:   曲线阶数
    """
    count = cv.shape[0]

    degree = np.clip(degree, 1, count-1)
    kv = np.clip(np.arange(count+degree+1)-degree, 0, count-degree)

    max_param = count - (degree * (1-0))  # 原代码中periodic未定义,此处修正为0
    spline = si.BSpline(kv, cv, degree)
    return spline, max_param

# 基于数学方法转换
def bspline_to_bezier(cv):
    cv_len = cv.shape[0]
    assert cv_len >= 4, "至少提供4个控制点"
    spline, max_param = scipy_bspline(cv, degree=3)
    for i in range(1, max_param):
        spline = si.insert(i, spline, 2)
    return spline.c[:3 * max_param + 1]

def draw_bezier(d, bezier):
    path = aggdraw.Path()
    path.moveto(*bezier[0])
    for i in range(1, len(bezier) - 1, 3):
        v1, v2, v = bezier[i:i+3]
        path.curveto(*v1, *v2, *v)
    d.path(path, aggdraw.Pen("black", 2))

cv = np.array([[ 40., 148.], [ 40.,  48.],
               [244.,  24.], [160., 120.],
               [240., 144.], [210., 260.],
               [110., 250.]])

im = Image.fromarray(np.ones((400, 400, 3), dtype=np.uint8) * 255)
bezier = bspline_to_bezier(cv)
d = aggdraw.Draw(im)
draw_bezier(d, bezier)
d.flush()
# 显示/保存图像

无法理解代码中v1, v2, v = bezier[i:i+3]的含义,误将其当作二次Bezier曲线的控制点绘图,得到异常结果,验证代码如下:

for i in range(1, len(bezier) - 1, 3):
        v1, v2, v = bezier[i:i+3]
        cc = np.array([(v1[0], v1[1]), (v2[0], v2[1]), (v[0], v[1])])
        curve = BPoly(cc[:, None, :], [0,1])
        X = np.linspace(0, 1, 20)
        p = curve(X)
        plt.gca().set_aspect('equal')
        plt.plot(*p.T)

方法2:CubicSpline分段的问题

改用CubicSpline获取分段多项式,代码如下:

from scipy.interpolate import BSpline, CubicSpline
from scipy.interpolate import splrep
from scipy.interpolate import BPoly
import numpy as np
x = np.array([ 0. ,  1.2,  1.9,  3.2,  5, 6.5])
y = np.array([ 0. ,  2.3,  3. ,  4.3,  2.9, 3.1])
t, c, k = splrep(x, y, s=0.8, k=3)
spl = BSpline(t, c, k)

import matplotlib.pyplot as plt
fig, ax = plt.subplots()
xx = np.linspace(0, 6, 500)
ax.plot(xx, spl(xx), 'b-', lw=4, alpha=0.7, label='BSpline')
ax.plot(x, y, 'rs')

xx = x
yy = spl(xx)

cu = CubicSpline(xx, yy)
plt.plot(xx, yy, 'ko')
for i in range(len(cu.x)-1):
    xs = np.linspace(cu.x[i], cu.x[i+1], 100)
    plt.plot(xs, np.polyval(cu.c[:,i], xs - cu.x[i]))

该方法可生成分段曲线,但分段数量随采样点密度变化,希望以最少分段数逼近样条曲线。

求可行解决思路与指导

恳请提供将平滑B样条转换为最少分段数Bezier曲线的可行方法与指导。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 06:25:21