贝塞尔曲线的动画演示

程序代码如下

复制代码
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation


class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def as_array(self):
        return np.array([self.x, self.y])

# 给定的三个点坐标:P0, P1, P2
P0 = Point(0, 0)        # 起点
P1 = Point(2.5, 5)      # 控制点
P2 = Point(6, 0)        # 终点

# 初始化图形和轴
fig, ax = plt.subplots()
ax.set_xlim(-1, 7)
ax.set_ylim(-1, 6)
ax.set_aspect('equal')

# 初始化曲线
x, y = [], []
line, = ax.plot([], [], 'r-', lw=2)  # 贝塞尔曲线
line_1, = ax.plot([], [], 'g', label='Control Line P0 to P1')  # 控制线段 P0 到 P1
line_2, = ax.plot([], [], 'g', label='Control Line P1 to P2')  # 控制线段 P1 到 P2
ctrl_line_1, = ax.plot([], [], 'b--', label='Control Line Between P0 and P2')  # 控制线段

# 添加点的标记
point1_marker, = ax.plot([], [], 'o', markerfacecolor='none', markeredgecolor='k', markersize=8, label='Bezier Point')  # 空心圆标记
point2_marker, = ax.plot([], [], 'o', markerfacecolor='none', markeredgecolor='k', markersize=8, label='Bezier Point')  # 空心圆标记
point_marker, = ax.plot([], [], 'o', markerfacecolor='none', markeredgecolor='k', markersize=8, label='Bezier Point')  # 空心圆标记
# ax.legend()

# 初始化函数
def init():
    line.set_data([], [])
    line_1.set_data([P0.x, P1.x], [P0.y, P1.y])
    line_2.set_data([P1.x, P2.x], [P1.y, P2.y])
    ctrl_line_1.set_data([], [])
    return line, line_1, line_2, ctrl_line_1

# 更新函数
def update(frame):
    t = frame  # 当前帧对应的t值
    # 计算贝塞尔曲线的点
    point = (1-t)**2 * P0.as_array() + 2*(1-t)*t * P1.as_array() + t**2 * P2.as_array()
    x.append(point[0])
    y.append(point[1])
    line.set_data(x, y)

    # 更新标记点位置
    point_marker.set_data(point[0], point[1])

    # 计算P0和P1之间的中间点
    p0_p1 = (1-t) * P0.as_array() + t * P1.as_array()
    # 计算P1和P2之间的中间点
    p1_p2 = (1-t) * P1.as_array() + t * P2.as_array()

    # 更新控制线段
    ctrl_line_1.set_data([p0_p1[0], p1_p2[0]], [p0_p1[1], p1_p2[1]])
    point1_marker.set_data(p0_p1[0], p0_p1[1])
    point2_marker.set_data(p1_p2[0], p1_p2[1])

    # 在最后一帧时清空贝塞尔曲线并重置
    if t >= 1:
        x.clear()  # 清空 x 数据
        y.clear()  # 清空 y 数据
        line.set_data([], [])  # 清空线条
        # point_marker.set_data([], [])  # 清空点标记

    return line, ctrl_line_1, line_1, line_2,point_marker,point1_marker,point2_marker

# 创建动画
ani = FuncAnimation(fig, update, frames=np.linspace(0, 1, 100),
                    init_func=init, blit=True, interval=20)

# 显示动画
plt.show()
复制代码

 这是更新之后的代码

复制代码
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def as_array(self):
        return np.array([self.x, self.y])

    @staticmethod
    def get_xylim(points):
        x_min = min(p.x for p in points)
        x_max = max(p.x for p in points)
        y_min = min(p.y for p in points)
        y_max = max(p.y for p in points)
        return x_min, x_max, y_min, y_max

    @staticmethod
    def get_bezier_control_points(t, pnts):
        def get_mid_point(t, pnt1, pnt2):
            x = (1 - t) * pnt1.x + t * pnt2.x
            y = (1 - t) * pnt1.y + t * pnt2.y
            return Point(x, y)

        def calculate_control_points(t, pnts):
            control_points = []
            for i in range(len(pnts) - 1):
                control_point = get_mid_point(t, pnts[i], pnts[i + 1])
                control_points.append(control_point)
            return control_points

        levels = {}
        current_points = pnts
        level = 0

        while len(current_points) > 1:
            levels[level] = calculate_control_points(t, current_points)
            current_points = levels[level]
            level += 1

        return levels  # 字典,键为级别,值为控制点列表

    @staticmethod
    def plot_base_points(ax, points, color='k', markersize=8):
        for point in points:
            ax.plot(point.x, point.y, color=color, marker='o', markersize=markersize)

    @staticmethod
    def plot_ctrl_points(ax, points, markersize=8):
        for i in range(len(points)):
            for point in points[i]:
                color = 'r' if len(points[i]) == 1 else 'b'
                ax.plot(point.x, point.y, color=color, marker='o', markersize=markersize)

class Line:
    @staticmethod
    def get_bezier_control_lines_endpoint(pnts):
        ctrl_lines = []
        for i in range(len(pnts) - 1):
            dim_pnts = pnts[i]
            if len(dim_pnts) > 1:
                for j in range(len(dim_pnts) - 1):
                    ctrl_line = (dim_pnts[j], dim_pnts[j + 1])
                    ctrl_lines.append(ctrl_line)
        return ctrl_lines

    @staticmethod
    def get_bezier_base_lines_endpoint(pnts):
        base_lines = []
        if len(pnts) > 1:
            for i in range(len(pnts) - 1):
                base_line = (pnts[i], pnts[i + 1])
                base_lines.append(base_line)
        return base_lines

    @staticmethod
    def plot_lines(ax, lines, color='b', linestyle='--'):
        artists = []
        for line in lines:
            artist, = ax.plot([line[0].x, line[1].x], [line[0].y, line[1].y], color=color, linestyle=linestyle)
            artists.append(artist)
        return artists

# 贝塞尔曲线计算函数
def bezier_curve(t, points):
    n = len(points)
    if n == 2:  # 线性
        return (1 - t) * points[0].as_array() + t * points[1].as_array()
    elif n == 3:  # 二次
        return (1 - t) ** 2 * points[0].as_array() + 2 * (1 - t) * t * points[1].as_array() + t ** 2 * points[2].as_array()
    elif n == 4:  # 三次
        return (1 - t) ** 3 * points[0].as_array() + 3 * (1 - t) ** 2 * t * points[1].as_array() + 3 * (1 - t) * t ** 2 * points[2].as_array() + t ** 3 * points[3].as_array()
    else:
        raise ValueError("Bezier curves of degree higher than 3 are not supported.")

def init():
    # 清空之前的贝塞尔曲线数据
    bezier_spline.set_data([], [])
    arr_ctrl_lines = []  # 存储控制线的 Artist 对象
    return bezier_spline, *arr_ctrl_lines

# 更新函数
def update(frame):
    t = frame
    point = bezier_curve(t, points)

    # 更新贝塞尔曲线数据
    bezier_spline.set_data(np.append(bezier_spline.get_xdata(), point[0]),
                            np.append(bezier_spline.get_ydata(), point[1]))

    # 更新贝塞尔曲线数据
    bezier_spline.set_data(np.append(bezier_spline.get_xdata(), point[0]),
                            np.append(bezier_spline.get_ydata(), point[1]))

    # 计算控制点
    ctrl_points = Point.get_bezier_control_points(t, points)
    ctrl_lines = Line.get_bezier_control_lines_endpoint(ctrl_points)

    # 清空之前的控制线
    for line in arr_ctrl_lines:
        line.remove()
    arr_ctrl_lines.clear()

    # 绘制控制线
    arr_ctrl_lines.extend(Line.plot_lines(ax, ctrl_lines, color='b', linestyle='--'))

    # 返回当前需要更新的 Artist 对象
    return bezier_spline, *arr_ctrl_lines

if __name__ == '__main__':
    # 定义贝塞尔曲线节点
    num_knots = 4  # 可以更改为2、3或4
    if num_knots == 3:
        points = [Point(0, 0), Point(2.5, 8), Point(10, -2)]
    elif num_knots == 4:
        points = [Point(0, 0), Point(2.5, 3), Point(5, -2), Point(10, 5)]
    elif num_knots == 2:
        points = [Point(0, 0), Point(6, 3)]

    # 设置坐标轴
    x_min, x_max, y_min, y_max = Point.get_xylim(points)
    fig, ax = plt.subplots()
    ax.set_xlim(x_min, x_max)
    ax.set_ylim(y_min, y_max)
    ax.set_aspect('equal')

    # # 初始化曲线和标记
    # bezier_spline, = ax.plot([], [], 'r-', lw=2)
    # arr_ctrl_lines = []  # 存储控制线的 Artist 对象

    # 绘制基础点
    Point.plot_base_points(ax, points, color='k', markersize=8)
    Line.plot_lines(ax, Line.get_bezier_base_lines_endpoint(points), color='b', linestyle='--')

    bezier_spline, = ax.plot([], [], 'r-', lw=2)
    # bezier_spline.set_data([], [])
    arr_ctrl_lines = []  # 存储控制线的 Artist 对象
    # 创建动画
    ani = FuncAnimation(fig, update, frames=np.linspace(0, 1, 100), init_func=init,blit=True, interval=20)

    # 显示动画
    plt.show()

    print('done')
复制代码

 继续升级

复制代码
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def as_array(self):
        return np.array([self.x, self.y])

    def get_coords(self):
        return [self.x, self.y]

    @staticmethod
    def get_xylim(points):
        x_min = min(p.x for p in points)
        x_max = max(p.x for p in points)
        y_min = min(p.y for p in points)
        y_max = max(p.y for p in points)
        return x_min, x_max, y_min, y_max

    @staticmethod
    def get_bezier_control_points(t, pnts):
        def get_mid_point(t, pnt1, pnt2):
            x = (1 - t) * pnt1.x + t * pnt2.x
            y = (1 - t) * pnt1.y + t * pnt2.y
            return Point(x, y)

        def calculate_control_points(t, pnts):
            control_points = []
            for i in range(len(pnts) - 1):
                control_point = get_mid_point(t, pnts[i], pnts[i + 1])
                control_points.append(control_point)
            return control_points

        levels = {}
        current_points = pnts
        level = 0

        while len(current_points) > 1:
            levels[level] = calculate_control_points(t, current_points)
            current_points = levels[level]
            level += 1

        return levels  # 字典,键为级别,值为控制点列表

    @staticmethod
    def plot_base_points(ax, points, color='k', markersize=8):
        for point in points:
            ax.plot(point.x, point.y, color=color, marker='o', markersize=markersize)

    @staticmethod
    def plot_ctrl_points(ax, points, markersize=8):
        for i in range(len(points)):
            for point in points[i]:
                color = 'r' if len(points[i]) == 1 else 'b'
                ax.plot(point.x, point.y, color=color, marker='o', markersize=markersize)

class Line:
    @staticmethod
    def get_bezier_control_lines_endpoint(pnts):
        ctrl_lines = []
        for i in range(len(pnts) - 1):
            dim_pnts = pnts[i]
            if len(dim_pnts) > 1:
                for j in range(len(dim_pnts) - 1):
                    ctrl_line = (dim_pnts[j], dim_pnts[j + 1])
                    ctrl_lines.append(ctrl_line)
        return ctrl_lines

    @staticmethod
    def get_bezier_base_lines_endpoint(pnts):
        base_lines = []
        if len(pnts) > 1:
            for i in range(len(pnts) - 1):
                base_line = (pnts[i], pnts[i + 1])
                base_lines.append(base_line)
        return base_lines

    @staticmethod
    def plot_lines(ax, lines, color='b', linestyle='-'):
        artists = []
        for line in lines:
            artist, = ax.plot([line[0].x, line[1].x], [line[0].y, line[1].y], color=color, linestyle=linestyle)
            artists.append(artist)
        return artists

def de_casteljau(points, t):
    n = len(points)
    if n == 1:
        return points[0].as_array()  # 如果只有一个点,直接返回该点的坐标
    else:
        # 计算新的点集,每个点是两个相邻点的线性插值
        new_points = []
        for i in range(n - 1):
            # 将 Point 对象转换为 numpy 数组
            pnt0 = points[i].as_array()
            pnt1 = points[i + 1].as_array()
            # 进行线性插值
            new_point_xy = pnt0 * (1 - t) + pnt1 * t
            # 创建新的 Point 实例
            new_point = Point(new_point_xy[0], new_point_xy[1])
            new_points.append(new_point)

        # 递归调用de Casteljau算法并返回结果
        return de_casteljau(new_points, t)  # 返回最终计算出的点

# 使用de Casteljau算法计算贝塞尔曲线上的点
def bezier_curve(t, points):
    if len(points) < 2:
        raise ValueError("At least 2 points are required for a Bezier curve.")
    return de_casteljau(points, t)

# 贝塞尔曲线计算函数
def bezier_curve2(t, points):
    n = len(points)
    if n>=2:
        pnt0 = points[0].as_array()
        pnt1 = points[1].as_array()
        if n == 2:  # 一次贝塞尔曲线
            return (1-t) * pnt0+ t * pnt1
        elif n == 3:  # 二次贝塞尔曲线
            pnt2= points[2].as_array()
            return (1-t)**2 * pnt0 + 2*(1-t)*t * pnt1 + t**2 * pnt2
        elif n == 4:  # 三次贝塞尔曲线
            pnt2 = points[2].as_array()
            pnt3 = points[3].as_array()
            return (1-t)**3 * pnt0 + 3*(1-t)**2*t * pnt1 + 3*(1-t)*t**2 * pnt2 + t**3 * pnt3
        elif n == 5:  # 四次贝塞尔曲线
            pnt2 = points[2].as_array()
            pnt3 = points[3].as_array()
            pnt4 = points[4].as_array()
            return (1-t)**4 * pnt0 + 4*(1-t)**3*t * pnt1 + 6*(1-t)**2*t**2 * pnt2 + 4*(1-t)*t**3 * pnt3 + t**4 * pnt4
        else:
            raise ValueError("Bezier curves of degree higher than 3 are not supported.")
    else:
        raise ValueError("At least 2 points are required for a Bezier curve.")

def init():
    # 清空之前的贝塞尔曲线数据
    bezier_spline.set_data([], [])
    arr_ctrl_lines = []  # 存储控制线的 Artist 对象
    return bezier_spline, *arr_ctrl_lines

# 更新函数
def update(frame):
    t = frame
    point = bezier_curve(t, points)

    # 更新贝塞尔曲线数据
    bezier_spline.set_data(np.append(bezier_spline.get_xdata(), point[0]),
                            np.append(bezier_spline.get_ydata(), point[1]))

    # 更新贝塞尔曲线数据
    bezier_spline.set_data(np.append(bezier_spline.get_xdata(), point[0]),
                            np.append(bezier_spline.get_ydata(), point[1]))

    # 计算控制点
    ctrl_points = Point.get_bezier_control_points(t, points)
    ctrl_lines = Line.get_bezier_control_lines_endpoint(ctrl_points)

    # 清空之前的控制线
    for line in arr_ctrl_lines:
        line.remove()
    arr_ctrl_lines.clear()

    # 绘制控制线
    arr_ctrl_lines.extend(Line.plot_lines(ax, ctrl_lines, color='b', linestyle='--'))

    # 返回当前需要更新的 Artist 对象
    return bezier_spline, *arr_ctrl_lines

if __name__ == '__main__':
    # 定义贝塞尔曲线节点
    num_knots = 5  # 可以更改为2、3或4
    if num_knots == 3:
        points = [Point(-16, 0), Point(0, 20), Point(16, 20)]
    elif num_knots == 4:
        points = [Point(-16, 0), Point(0,20) , Point(16,20), Point(16,0)]
    elif num_knots == 5:
        points = [Point(-16, 0), Point(0,15) , Point(16,20), Point(30,0),Point(50,10)]
    elif num_knots == 2:
        points = [Point(-16, 0), Point(0,20)]

    # 设置坐标轴
    x_min, x_max, y_min, y_max = Point.get_xylim(points)
    fig, ax = plt.subplots()
    ax.set_xlim(x_min-1, x_max+1)
    ax.set_ylim(y_min-1, y_max+1)
    ax.set_aspect('equal')

    # # 初始化曲线和标记
    # bezier_spline, = ax.plot([], [], 'r-', lw=2)
    # arr_ctrl_lines = []  # 存储控制线的 Artist 对象

    # 绘制基础点
    Point.plot_base_points(ax, points, color='k', markersize=8)
    Line.plot_lines(ax, Line.get_bezier_base_lines_endpoint(points), color='r', linestyle='-')

    bezier_spline, = ax.plot([], [], 'r-', lw=2)
    # bezier_spline.set_data([], [])
    arr_ctrl_lines = []  # 存储控制线的 Artist 对象
    # 创建动画
    ani = FuncAnimation(fig, update, frames=np.linspace(0, 1, 100), init_func=init,blit=True, interval=20)

    # 显示动画
    plt.show()

    print('done')
复制代码

 以下是n阶贝塞尔曲线多项式系数曲线的动画演示

复制代码
# import matplotlib.pyplot as plt
# import numpy as np
# from matplotlib.animation import FuncAnimation


# # 贝塞尔曲线的多项式系数
# def bezier_coefficients(t):
#     return np.array([
#         -t**3 + 3 * t**2 - 3 * t + 1,        # P0 的系数
#         3*t**3 - 6 * t**2 + 3 * t, # P1 的系数
#         -3 * t**3 + 3 * t**2, # P2 的系数
#         t ** 3                # P3 的系数
#     ])

# # 设置图形
# fig, ax = plt.subplots(figsize=(12, 8))
# ax.set_xlim(0, 1)
# ax.set_ylim(0, 1)
# ax.set_xlabel("t")
# ax.set_ylabel("Coefficient Value")
# ax.set_title("Bézier Curve Coefficients Animation")

# # t 值
# t_values = np.linspace(0, 1, 100)

# # 系数曲线
# coefficients_points = np.array([bezier_coefficients(t) for t in t_values])

# # 绘制系数曲线
# coefficients_lines = [ax.plot(t_values, coefficients_points[:, i], label=f'Coefficient {i}')[0] for i in range(4)]

# # 动画中移动的竖线和交点
# vertical_line = ax.axvline(x=0, color='grey', linestyle='--')
# marker, = ax.plot([], [], 'ro', markersize=8, label='Intersection Point')

# # 动画更新函数
# def update(frame):
#     t = frame / 100
#     # 更新竖线位置
#     vertical_line.set_xdata(t)

#     # 更新交点
#     coefficients = bezier_coefficients(t)
#     marker.set_data(t, coefficients)  # 设置交点的坐标

#     return vertical_line, marker

# # 创建动画
# ani = FuncAnimation(fig, update, frames=np.arange(0, 101), blit=True, interval=100)

import matplotlib.pyplot as plt
# # 添加图例
# ax.legend()
# plt.show()
import numpy as np
from matplotlib.animation import FuncAnimation


# 计算伯恩斯坦基多项式
def bernstein_poly(n, i, t):
    return (np.math.factorial(n) / (np.math.factorial(i) * np.math.factorial(n - i))) * (t ** i) * ((1 - t) ** (n - i))

# 贝塞尔曲线的多项式系数
def bezier_coefficients(n, t):
    return np.array([bernstein_poly(n, i, t) for i in range(n + 1)])

# 设置控制点数量和控制点
n = 10  # 控制点数量(可更改为任意值)
# control_points = np.array([[0, 0], [2, 3], [4, 2], [5, 5], [7, 0]])[:n + 1]  # 生成控制点

# 设置图形
fig, ax = plt.subplots(figsize=(12, 8))
ax.set_xlim(0, 1)
ax.set_ylim(0,1)
ax.set_xlabel("t")
ax.set_ylabel("Coefficient Value")
ax.set_title(f"Bézier Curve Coefficients Animation (n={n})")

# t 值
t_values = np.linspace(0, 1, 100)

# 系数曲线
coefficients_points = np.array([bezier_coefficients(n, t) for t in t_values])

# 绘制系数曲线
coefficients_lines = [ax.plot(t_values, coefficients_points[:, i], label=f'Coefficient {i}')[0] for i in range(n + 1)]

# 动画中移动的竖线和交点
vertical_line = ax.axvline(x=0, color='grey', linestyle='--')
marker, = ax.plot([], [], 'ro', markersize=8, label='Intersection Point')

# 动画更新函数
def update(frame):
    t = frame
    # 更新竖线位置
    vertical_line.set_xdata(t)

    # 更新交点
    coefficients = bezier_coefficients(n, t)
    marker.set_data(t, coefficients)  # 设置交点的坐标

    return vertical_line, marker

# 创建动画
ani = FuncAnimation(fig, update, frames=np.linspace(0,1,100), blit=True, interval=100)

# 添加图例
ax.legend()
plt.show()
复制代码

 

posted on   风中狂笑  阅读(33)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示