1. 自由曲面基础参数化建模

任务说明

本案例使用非均匀有理B样条(NURBS)方法建立自由曲面模型。给定一张曲面的控制点网格、各方向节点向量及权重,通过NURBS曲面求值公式计算曲面上任意参数坐标(u,v)处的三维坐标点,并输出曲面在若干采样点处的坐标值,为后续自由曲面光学设计提供基础参数化模型。

NURBS曲面数学定义

一张p×q次NURBS曲面定义为:

S(u,v) = Σᵢ₌₀ⁿ Σⱼ₌₀ᵐ Nᵢ,ₚ(u) Nⱼ,𝒒(v) wᵢ,ⱼ Pᵢ,ⱼ / Σᵢ₌₀ⁿ Σⱼ₌₀ᵐ Nᵢ,ₚ(u) Nⱼ,𝒒(v) wᵢ,ⱼ

其中Pᵢ,ⱼ为控制点(三维坐标),wᵢ,ⱼ为对应权重,Nᵢ,ₚ(u)为定义在节点向量U上的B样条基函数。本案例实现完整的NURBS曲面求值:包括B样条基函数递归计算、节点向量构建、有理加权求值,并提供一个4×4控制点网格的曲面实例进行验证。

代码实现


import numpy as np

def find_span(n, p, u, U):
    """找到参数u所在节点区间下标(针对非均匀节点向量)"""
    if u >= U[n+1]:
        return n
    if u <= U[p]:
        return p
    low, high = p, n+1
    mid = (low + high) // 2
    while (u < U[mid] or u >= U[mid+1]) and low < high:
        if u < U[mid]:
            high = mid
        else:
            low = mid
        mid = (low + high) // 2
    return mid

def basis_funcs(i, u, p, U):
    """计算第i个p次B样条基函数在u处的值N[i-p]...N[i]"""
    N = np.zeros(p+1)
    N[0] = 1.0
    left = np.zeros(p+1)
    right = np.zeros(p+1)
    for j in range(1, p+1):
        left[j] = u - U[i+1-j]
        right[j] = U[i+j] - u
        saved = 0.0
        for r in range(j):
            temp = N[r] / (right[r+1] + left[j-r])
            N[r] = saved + right[r+1] * temp
            saved = left[j-r] * temp
        N[j] = saved
    return N

def nurbs_surface_point(n, p, U, m, q, V, P, W, u, v):
    """计算NURBS曲面上参数(u,v)处的点坐标
    参数:
        n, p, U: u方向控制点数量-1、次数、节点向量
        m, q, V: v方向控制点数量-1、次数、节点向量
        P: 控制点数组,形状(n+1, m+1, 3)
        W: 权重数组,形状(n+1, m+1)
        u, v: 曲面参数坐标
    返回:
        曲面上的三维点 (x, y, z)
    """
    # u方向基函数
    iu = find_span(n, p, u, U)
    Nu = basis_funcs(iu, u, p, U)
    # v方向基函数
    iv = find_span(m, q, v, V)
    Nv = basis_funcs(iv, v, q, V)

    # 有理加权求和
    S = np.zeros(3)
    w_sum = 0.0
    for k in range(p+1):
        for l in range(q+1):
            i = iu - p + k
            j = iv - q + l
            weight = Nu[k] * Nv[l] * W[i][j]
            S += weight * P[i][j]
            w_sum += weight
    return S / w_sum

def uniform_knot_vector(n, p):
    """生成均匀 clamped 节点向量(首尾重复p+1次)"""
    m = n + p + 1
    U = np.zeros(m+1)
    for i in range(m+1):
        if i <= p:
            U[i] = 0.0
        elif i >= m - p:
            U[i] = 1.0
        else:
            U[i] = (i - p) / (m - 2*p)
    return U

# ========== 实例:4x4控制点网格,3x3次NURBS曲面 ==========
n, p = 3, 3   # u方向:4个控制点,3次
m, q = 3, 3   # v方向:4个控制点,3次

# 生成均匀clamped节点向量
U = uniform_knot_vector(n, p)
V = uniform_knot_vector(m, q)

# 定义控制点:4x4网格,z坐标构成一个凸曲面形状
P = np.zeros((4, 4, 3))
for i in range(4):
    for j in range(4):
        P[i][j][0] = i * 10.0          # x: 0,10,20,30
        P[i][j][1] = j * 10.0          # y: 0,10,20,30
        # z: 边缘低、中心高的自由曲面形状
        P[i][j][2] = 5.0 * (1 - abs(i-1.5)/1.5) * (1 - abs(j-1.5)/1.5) + 2.0

# 定义权重:全部为1.0(退化为B样条曲面)
W = np.ones((4, 4))

# 采样参数网格(u,v ∈ [0,1])
print("NURBS曲面采样点坐标(u,v → x,y,z):")
for u in [0.0, 0.33, 0.67, 1.0]:
    for v in [0.0, 0.33, 0.67, 1.0]:
        pt = nurbs_surface_point(n, p, U, m, q, V, P, W, u, v)
        print(f"u={u:.2f}, v={v:.2f} → ({pt[0]:.4f}, {pt[1]:.4f}, {pt[2]:.4f})")

落地与验证

运行方式:将代码保存为nurbs_surface.py,在装有NumPy的Python环境中执行python nurbs_surface.py。

验证方法:观察输出结果,应满足以下判定标准:

  • 曲面四角点(u=0/1, v=0/1)处坐标与控制点网格四角坐标完全一致,即(0,0,2.0)、(30,0,2.0)、(0,30,2.0)、(30,30,2.0)——因为clamped节点向量保证NURBS曲面插值边界控制点;
  • 曲面中心(u=0.5, v=0.5)处z坐标应接近控制点中心高度(约7.0),体现曲面凸起形状;
  • 所有输出点的x坐标在[0,30]区间、y坐标在[0,30]区间内单调分布。

若输出满足上述条件,说明NURBS曲面参数化建模实现正确,该模型可作为后续自由曲面光学设计的基底曲面。

NURBS自由曲面参数化建模结构 控制点网格 (4×4) x → y → P₀₀(0,0,2) P₃₀(30,0,2) P₀₃(0,30,2) P₃₃(30,30,2) 节点向量与参数设置 U方向节点向量 (p=3) U = [0,0,0,0, 0.33, 0.67, 1,1,1,1] V方向节点向量 (q=3) V = [0,0,0,0, 0.33, 0.67, 1,1,1,1] 权重矩阵 W (4×4) 全部为 1.0(退化为B样条) NURBS曲面求值 S(u,v) = ΣᵢΣⱼ Nᵢ(u)Nⱼ(v)wᵢⱼPᵢⱼ / ΣᵢΣⱼ Nᵢ(u)Nⱼ(v)wᵢⱼ 曲面形状(侧视示意) 中心凸起区域(z≈7.0) 边缘z=2.0 边缘z=2.0 验证:角点与控制点重合,中心z≈7.0