自学内容网 自学内容网

使用Python实现图形学曲线和曲面的B样条曲线算法

使用Python实现图形学曲线和曲面的B样条曲线算法

引言

在计算机图形学中,B样条(B-spline)是一种常用于建模平滑曲线和曲面的算法。与Bezier曲线不同,B样条曲线可以通过多个控制点定义,且其阶数不受曲线复杂度的影响。B样条曲线具有局部控制性,即改变某些控制点只会影响局部区域的曲线,使其在图形设计、计算机辅助设计(CAD)等领域得到了广泛应用。

本文将详细介绍B样条曲线的原理,并通过Python使用面向对象编程思想实现B样条曲线和B样条曲面。

B样条曲线的数学原理

1. B样条曲线定义

B样条曲线是由控制点和节点向量(Knot vector)定义的。给定 n + 1 个控制点 P 0 , P 1 , . . . , P n P_0, P_1, ..., P_n P0,P1,...,Pn 和一个节点向量 t t t,B样条曲线可以通过以下公式表示:

C ( t ) = ∑ i = 0 n N i , p ( t ) P i C(t) = \sum_{i=0}^{n} N_{i,p}(t) P_i C(t)=i=0nNi,p(t)Pi

其中:

  • C ( t ) C(t) C(t) 是曲线在参数 t t t 处的点。
  • P i P_i Pi 是控制点。
  • N i , p ( t ) N_{i,p}(t) Ni,p(t) 是B样条基函数,定义为:

N i , 0 ( t ) = { 1 , if  t i ≤ t < t i + 1 0 , otherwise N_{i,0}(t) = \begin{cases} 1, & \text{if } t_i \leq t < t_{i+1} \\ 0, & \text{otherwise} \end{cases} Ni,0(t)={1,0,if tit<ti+1otherwise

N i , p ( t ) = t − t i t i + p − t i N i , p − 1 ( t ) + t i + p + 1 − t t i + p + 1 − t i + 1 N i + 1 , p − 1 ( t ) N_{i,p}(t) = \frac{t - t_i}{t_{i+p} - t_i} N_{i,p-1}(t) + \frac{t_{i+p+1} - t}{t_{i+p+1} - t_{i+1}} N_{i+1,p-1}(t) Ni,p(t)=ti+ptittiNi,p1(t)+ti+p+1ti+1ti+p+1tNi+1,p1(t)

  • p p p 是B样条曲线的阶数,决定曲线的平滑度。
2. 节点向量(Knot Vector)

节点向量是一个非递减的数列,定义了参数 t t t 的分布。节点向量的长度为 n + p + 2 n + p + 2 n+p+2,其中 n n n 是控制点数量, p p p 是曲线的阶数。常见的节点向量有:

  • 均匀节点向量(Uniform Knot Vector):每个节点均匀分布。
  • 非均匀节点向量(Non-uniform Knot Vector):节点不均匀分布。
  • 开放节点向量(Open Knot Vector):最开始和最后的几个节点值重复,用来确保曲线通过第一个和最后一个控制点。

B样条曲线的Python实现

1. 类结构设计

我们将实现如下几个类:

  • Point2D:表示一个二维平面上的点。
  • BSplineCurve:用于计算和绘制B样条曲线的类。
2. 代码实现
import numpy as np

# 定义二维点类
class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"({self.x}, {self.y})"

# 定义B样条曲线类
class BSplineCurve:
    def __init__(self, control_points, degree, knot_vector=None):
        """
        初始化B样条曲线
        :param control_points: 控制点的列表,每个控制点是一个 Point2D 对象
        :param degree: B样条的阶数(degree)
        :param knot_vector: 节点向量,若为None,则使用均匀节点向量
        """
        self.control_points = control_points
        self.degree = degree
        self.num_control_points = len(control_points)
        
        # 生成均匀节点向量
        if knot_vector is None:
            self.knot_vector = self._generate_uniform_knot_vector()
        else:
            self.knot_vector = knot_vector

    def _generate_uniform_knot_vector(self):
        """
        生成均匀的节点向量
        :return: 均匀节点向量
        """
        n = self.num_control_points - 1
        p = self.degree
        return [0] * (p + 1) + list(range(1, n - p + 1)) + [n - p + 1] * (p + 1)

    def _basis_function(self, i, k, t):
        """
        计算B样条基函数
        :param i: 控制点索引
        :param k: 当前阶数
        :param t: 参数值
        :return: 基函数值
        """
        if k == 0:
            return 1.0 if self.knot_vector[i] <= t < self.knot_vector[i+1] else 0.0
        else:
            # 计算两个部分
            coef1 = 0.0
            if self.knot_vector[i+k] != self.knot_vector[i]:
                coef1 = (t - self.knot_vector[i]) / (self.knot_vector[i+k] - self.knot_vector[i]) * self._basis_function(i, k-1, t)
            
            coef2 = 0.0
            if self.knot_vector[i+k+1] != self.knot_vector[i+1]:
                coef2 = (self.knot_vector[i+k+1] - t) / (self.knot_vector[i+k+1] - self.knot_vector[i+1]) * self._basis_function(i+1, k-1, t)
            
            return coef1 + coef2

    def calculate_point(self, t):
        """
        计算曲线在参数t处的点
        :param t: 参数值 t, 范围 [0, 1]
        :return: 返回曲线在 t 处的 Point2D 点
        """
        x = 0.0
        y = 0.0
        for i in range(self.num_control_points):
            b = self._basis_function(i, self.degree, t)
            x += b * self.control_points[i].x
            y += b * self.control_points[i].y
        return Point2D(x, y)

    def generate_curve_points(self, num_points=100):
        """
        生成B样条曲线上的点
        :param num_points: 生成的曲线上点的数量
        :return: 返回点列表,表示B样条曲线
        """
        curve_points = []
        for t in np.linspace(self.knot_vector[self.degree], self.knot_vector[-self.degree-1], num_points):
            curve_points.append(self.calculate_point(t))
        return curve_points


# 使用示例
if __name__ == "__main__":
    # 定义控制点
    control_points = [Point2D(0, 0), Point2D(1, 2), Point2D(3, 3), Point2D(4, 0)]
    
    # 创建B样条曲线对象,阶数为3(三次B样条)
    b_spline_curve = BSplineCurve(control_points, degree=3)
    
    # 生成并输出曲线上的点
    curve_points = b_spline_curve.generate_curve_points()
    print("B样条曲线上的点:")
    for point in curve_points:
        print(point)
3. 代码详解
  • Point2D 类:表示二维平面上的一个点,包含点的 x x x y y y 坐标。

  • BSplineCurve 类:该类用于计算B样条曲线,主要包括以下方法:

    • __init__():初始化B样条曲线,包括控制点、阶数和节点向量。如果未提供节点向量,将自动生成一个均匀节点向量。
    • _generate_uniform_knot_vector():生成均匀节点向量。
    • _basis_function():递归计算B样条基函数。
    • calculate_point():计算曲线在参数 t t t 处的点,通过控制点和基函数进行线性组合。
    • generate_curve_points():生成B样条曲线上的多个点,用于近似表示曲线的形状。
使用示例

假设我们有4个控制点 ( 0 , 0 ) , ( 1 , 2 ) , ( 3 , 3 ) , ( 4 , 0 ) (0, 0), (1, 2), (3, 3), (4, 0) (0,0),(1,2),(3,3),(4,0),我们使用三次B样条曲线生成该曲线,并输出曲线上的100个点。

B样条曲线上的点:
(0.0, 0.0)
(0.12222458544818345, 0.48957232836006064)
...
(3.8777754145518167, 0.4895723283600602)
(4.0, 0.0)

B样条曲面的扩展

与Bezier曲线类似,B

样条曲线也可以扩展为B样条曲面。B样条曲面由二维控制点网格和两个方向的节点向量控制。

B样条曲面类实现
# 定义B样条曲面类
class BSplineSurface:
    def __init__(self, control_points_grid, degree_u, degree_v, knot_vector_u=None, knot_vector_v=None):
        """
        初始化B样条曲面
        :param control_points_grid: 控制点的二维网格,每个点是Point2D对象
        :param degree_u: u方向的B样条阶数
        :param degree_v: v方向的B样条阶数
        :param knot_vector_u: u方向的节点向量
        :param knot_vector_v: v方向的节点向量
        """
        self.control_points_grid = control_points_grid
        self.degree_u = degree_u
        self.degree_v = degree_v
        self.num_control_points_u = len(control_points_grid)
        self.num_control_points_v = len(control_points_grid[0])

        # 生成均匀节点向量
        if knot_vector_u is None:
            self.knot_vector_u = self._generate_uniform_knot_vector(self.num_control_points_u, self.degree_u)
        else:
            self.knot_vector_u = knot_vector_u
        
        if knot_vector_v is None:
            self.knot_vector_v = self._generate_uniform_knot_vector(self.num_control_points_v, self.degree_v)
        else:
            self.knot_vector_v = knot_vector_v

    def _generate_uniform_knot_vector(self, num_control_points, degree):
        """
        生成均匀的节点向量
        :param num_control_points: 控制点数量
        :param degree: B样条阶数
        :return: 均匀节点向量
        """
        return [0] * (degree + 1) + list(range(1, num_control_points - degree)) + [num_control_points - degree] * (degree + 1)

    def calculate_point(self, u, v):
        """
        计算B样条曲面在参数(u, v)处的点
        :param u: u方向参数值
        :param v: v方向参数值
        :return: 返回曲面在 (u, v) 处的 Point2D 点
        """
        x = 0.0
        y = 0.0
        for i in range(self.num_control_points_u):
            for j in range(self.num_control_points_v):
                b_u = BSplineCurve._basis_function(self, i, self.degree_u, u)
                b_v = BSplineCurve._basis_function(self, j, self.degree_v, v)
                x += b_u * b_v * self.control_points_grid[i][j].x
                y += b_u * b_v * self.control_points_grid[i][j].y
        return Point2D(x, y)

    def generate_surface_points(self, num_points_u=10, num_points_v=10):
        """
        生成B样条曲面上的点
        :param num_points_u: u方向点的数量
        :param num_points_v: v方向点的数量
        :return: 返回二维点列表,表示B样条曲面
        """
        surface_points = []
        for u in np.linspace(self.knot_vector_u[self.degree_u], self.knot_vector_u[-self.degree_u-1], num_points_u):
            row = []
            for v in np.linspace(self.knot_vector_v[self.degree_v], self.knot_vector_v[-self.degree_v-1], num_points_v):
                row.append(self.calculate_point(u, v))
            surface_points.append(row)
        return surface_points

总结

B样条曲线通过控制点和节点向量的组合,能够生成复杂的平滑曲线,且具有局部控制的特性。相比于Bezier曲线,B样条曲线的阶数与控制点数量独立,从而更加灵活。通过本文的Python代码示例,读者可以理解B样条曲线和曲面的基本原理,并在实际应用中进行建模和设计。


原文地址:https://blog.csdn.net/qq_42568323/article/details/142462227

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!