当前位置: 首页 > news >正文

次函数图像工厂:用 SymPy 自动生成 y=kx+b 对比动画合集 - manim动画(43)

痛点场景还原

假设我们想做一个简单的对比动画,在坐标系里同时画出:

  • y=2x+1
  • y=−12x+3

如果纯用Manim手写,我们一般会这样写(只画其中一条的片段):

from manim import * class ManualLinear(Scene): def construct(self): ax = Axes( x_range=[-5, 5], y_range=[-5, 5], axis_config={"include_numbers": True} ) # 手动计算两个点的坐标,以保证线段能覆盖整个画面 # y = 2x + 1,当 x=-5 时 y=-9,当 x=5 时 y=11 line1 = Line(ax.c2p(-5, -9), ax.c2p(5, 11), color=RED) # 手动计算与 y 轴的交点 (0, 1) intercept_dot = Dot(ax.c2p(0, 1), color=YELLOW) self.add(ax, line1, intercept_dot)

这里的问题很明显:端点坐标、截距坐标都是我“算出来写死”的

如果想把k改成-0.7b改成2.5,上面所有数字都得重新算一遍。

更难受的是,如果想让线段刚好卡在坐标轴的边框上(既不超出也不短),还需要解方程求直线与矩形边框的交点——手动做实在太低效了。

这还只是一条线,如果要一次性展示k-22的多条直线,手动计算根本不可能。

2. SymPy 解决方案:把计算“外包”出去

解决思路非常直接:用SymPy负责符号运算,根据给定的参数自动求出我们需要的所有坐标。

核心任务有三个:

  • 给定kb和坐标系可视范围,自动生成直线的两个端点(正好落在边框上)
  • 自动求出直线与坐标轴的交点(截距)
  • 判断两条直线是否平行(系数比较)

先看纯SymPy的运算逻辑,不需要Manim

import sympy as sp x, y = sp.symbols('x y') k, b = sp.symbols('k b') expr = k * x + b # y = kx + b # 示例:取 k=2, b=1,x 范围 [-5, 5],y 范围 [-5, 5] x_min, x_max = -5, 5 y_min, y_max = -5, 5 # 1. 求与坐标轴的交点 x_intercept = sp.solve(expr.subs({k: 2, b: 1}), x) # 令 y=0 # x_intercept = [-1/2] 即 (-0.5, 0) y_intercept = expr.subs({k: 2, b: 1, x: 0}) # 令 x=0 # y_intercept = 1 即 (0, 1) # 2. 自动求边框端点:解直线与 x=x_min, x=x_max, y=y_min, y=y_max 的交点, # 保留落在矩形范围且是“极值方向”的两个点 points_on_border = [] for x_val in (x_min, x_max): y_val = expr.subs({k: 2, b: 1, x: x_val}) if y_min <= y_val <= y_max: points_on_border.append((x_val, y_val)) for y_val in (y_min, y_max): sol_x = sp.solve(expr.subs({k: 2, b: 1}) - y_val, x) for x_sol in sol_x: if x_min <= x_sol <= x_max: points_on_border.append((x_sol, y_val)) # 取两个端点(按 x 排序即可) points_on_border = sorted(points_on_border, key=lambda p: p[0]) endpoints = [points_on_border[0], points_on_border[-1]] # 3. 判断平行:比较化简后的系数(注意避免浮点精度问题) k1, k2 = sp.sympify('2'), sp.sympify('-0.5') parallel = sp.simplify(k1 - k2) == 0 # 完全相等才平行

上面的计算过程被封装成一个工具函数后,接下来Manim只需要拿着这些坐标画图就行了。

3. Manim 联动实战:完整可运行代码

下面给出完整的场景代码,一次运行自动生成 y=kx+b 多条直线的对比图,带截距高亮和平行判断。

from manim import * import sympy as sp class AutoLinearComparison(Scene): def construct(self): # 坐标轴及范围 ax = Axes( x_range=[-4, 4, 1], y_range=[-4, 4, 1], x_length=8, y_length=6, axis_config={"include_numbers": True, "font_size": 18}, tips=False, ).add_coordinates() self.add(ax) # 需要对比的参数列表:(k, b, 颜色) params = [ (2, 1, RED), (-0.5, 3, BLUE), (1, -2, GREEN), (-0.5, -1, ORANGE), ] lines_vg = VGroup() dots_vg = VGroup() labels_vg = VGroup() x_min, x_max = ax.x_range[0], ax.x_range[1] # -6, 6 y_min, y_max = ax.y_range[0], ax.y_range[1] # -4, 4 x, y = sp.symbols("x y") k_sym, b_sym = sp.symbols("k b") expr_template = k_sym * x + b_sym # 符号模板 for k_val, b_val, color in params: # ---- SymPy 计算 ---- expr = expr_template.subs({k_sym: k_val, b_sym: b_val}) # 代入具体参数 # 1. 求直线与坐标轴交点(截距) x_int = sp.solve(expr, x) # 令 y=0 x_int = float(x_int[0]) if x_int else None y_int = float(expr.subs(x, 0)) # 令 x=0 # 2. 求直线与矩形边框的合理端点 border_pts = [] for x_val in (x_min, x_max): y_val = float(expr.subs(x, x_val)) if y_min <= y_val <= y_max: border_pts.append((x_val, y_val)) for y_val in (y_min, y_max): sol_x = sp.solve(expr - y_val, x) for sx in sol_x: sx_f = float(sx) if x_min <= sx_f <= x_max: border_pts.append((sx_f, y_val)) border_pts = sorted(border_pts, key=lambda p: p[0]) # 取首尾作为线段端点 p1, p2 = border_pts[0], border_pts[-1] # ---- Manim 绘制 ---- line = Line(ax.c2p(*p1), ax.c2p(*p2), color=color, stroke_width=4) lines_vg.add(line) # 截距点(如果落在坐标轴范围内) if x_int is not None and y_min <= 0 <= y_max: dot_x = Dot(ax.c2p(x_int, 0), color=color, radius=0.08) dots_vg.add(dot_x) # 标注 x 截距坐标 label_x = MathTex( f"({x_int:.1f},0)", font_size=20, color=color ).next_to(dot_x, DOWN) labels_vg.add(label_x) if y_int is not None and x_min <= 0 <= x_max: dot_y = Dot(ax.c2p(0, y_int), color=color, radius=0.08) dots_vg.add(dot_y) label_y = MathTex( f"(0,{y_int:.1f})", font_size=20, color=color ).next_to(dot_y, LEFT) labels_vg.add(label_y) # 播放动画 self.play(Create(lines_vg), run_time=3) self.play(FadeIn(dots_vg, scale=0.5), Write(labels_vg), run_time=2) self.wait(2)

http://www.gsyq.cn/news/1605518.html

相关文章:

  • 30家商家实证:数字人直播90%的钱都白花了?2026全周期选型白皮书
  • 雷达编程实战之FFT的窗函数与补零策略
  • 2026年下半年量化工具选择,先说清交易规则
  • app_power.c 学习笔记:从端口状态机到 DCDC 调压链路
  • 防爆电气工程选型 不同供应商产品线定位与场景适配参考
  • 字节跳动Seedance:从“卖Token”到“卖生产力”,多赛道试水开启商业化新征程
  • bilibili视频解析:3分钟学会获取B站高清播放地址的实用指南
  • MSC许可管理系统的选择与使用:优化软件资源管理新途径
  • 城中村出入口改造,让居住更有秩序
  • 人才公寓智慧通行,让安居更安心
  • 2026年跨境电商新机遇:避开这5个坑,中小卖家如何用AI选品月入10万?附最新平台政策解读
  • 实战:从水色到纸币——彩色图像识别模型的双场景应用
  • Claude 4 Opus 评测 2026:200K 上下文与中文创作之王
  • CTF实战:巧用文件结构修复图片宽高
  • Android中App电量优化
  • 防止 iOS 应用被二次打包 代码混淆 和 签名校验的防篡改方案
  • Ryujinx:在PC上免费体验Nintendo Switch游戏的全能模拟器
  • 元器件为什么会失效?
  • 一颗芯片撬动48款爆款产品:杰理2026最新矩阵与尚凌科技供应链布局揭秘
  • 企业微信API开发会话数据进入业务系统时,需要注意哪些边界
  • 《电工学》核心解题思路精讲:从电路定理到暂态分析
  • LoadRunner 11.0 在 Windows 11 上的完整部署与本地化实战
  • 从单线程到多线程 IO,Redis 7.2 到底快了多少?
  • 从0开始学梯形图:10个经典案例,一次讲透!
  • C/C++ 堆与栈的区别——面试完整知识体系
  • 怎么知道供应商在不在行业黑名单里
  • 密码学 | 数字签名进阶:Schnorr签名的线性之美与密钥聚合
  • 为什么 CPU/内存指标不足以支撑真实业务伸缩
  • 软硬一体销售会话分析软硬件一体方案选型与落地参考
  • vitest + vue3 踩坑记录