贝塞尔曲线的动画演示
程序代码如下
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()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!