码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 一次函数图像工厂:用 SymPy 自动生成 y=kx+b 对比动画


    合集 - manim动画(43)
    1.【manim动画教程】-- 安装2023-03-282.【manim动画教程】-- 基本图形2023-03-293.【manim动画教程】-- 坐标系2023-04-104.【manim动画教程】-- 文本样式2023-04-075.【manim动画教程】-- 文字和公式2023-04-046.【manim动画教程】-- 图形样式2023-03-317.【manim动画教程】--相机2023-04-198.【manim动画教程】--高级动画效果2023-04-149.【manim动画教程】--常用动画效果2023-04-1210.【manim】之滚动字幕2022-12-0611.【manim】之圆规动画2023-01-3112.【manim】之目录动画2023-02-2813.Manim:动画制作背后的魔法2025-05-1814.manim 动画效果总结2025-05-2015.manim变换效果总结2025-05-2116.Manim动画渲染:从代码到屏幕的幕后故事2025-05-2217.轻松掌握Manim的.animate语法:让动画编程更简单2025-05-2518.Manim中三种函数图像类的比较2025-06-0519.掌握ChangeSpeed类:让数学动画速度随心而动!2025-06-1520.把数学对象画出来:Manim Mobject类库速查手册2025-08-1621.「亲手画出动态数学」:让数学可视化触手可及的Manim入门课2025-11-0722.让你的动画“活”过来:Manim 节奏控制指南 (Rate Functions)2025-11-2323.Manim进阶:用背景图片让你的数学视频脱颖而出2025-11-2624.Manim v0.19.1 发布啦!三大新特性让动画制作更丝滑2025-12-0225.🚀 Manim CE v0.20.0 发布:动画构建更丝滑,随机性终于“可控”了!02-2126.ManimCE v0.20.1 发布:LaTeX 渲染修复与动画稳定性提升03-0227.探索视觉的边界:用 Manim 重现有趣的知觉错觉03-0728.用Manim实现动态交点计算--从一个动点问题说起04-1629.Manim如何在数学公式中完美显示中文?04-1930.如何灵活设置公式中各个部分的颜色?04-2231.如何让多个动画“齐步走”?04-2632.告别手动计算,SymPy 初识与 Manim 联动05-1633.让数学公式自动推导05-1734.切线的魔法:用 SymPy 和 Manim 轻松搞定导数动画05-1935.填充与积累:积分与面积的可视化05-2136.轨迹的蓝图:方程求解与交点计算05-2337.Manim物理模拟:别自己写欧拉了!05-2538.用 SymPy 解决 Manim 曲线绘制速度不均的问题06-01
    39.一次函数图像工厂:用 SymPy 自动生成 y=kx+b 对比动画06-03
    40.用SymPy自动计算抛物线求根、判别式与顶点06-0541.用SymPy自动求解三角形构造与全等条件验证06-0842.用SymPy自动求解追及问题的方程06-1043.用SymPy自动因式分解:从面积拼图到代数恒等式06-13
    收起

    你在用 Manim 制作一次函数图像的对比动画时,是不是也遇到过这种麻烦:想直观展示不同斜率 k 和截距 b 对直线的影响,但每改一个参数,都得重新手算两端点坐标、重新算与坐标轴的交点,甚至要凭感觉“拉长”线段保证它贯穿画面。

    改三组参数,工作量就翻三倍。

    今天这篇文章,就是要彻底解决这个体力活。我会带你用 SymPy 把计算交给代码,让 Manim 只负责“画”,实现一次函数图像的自动化生成与对比。

    1. 痛点场景还原

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

    • y=2x+1" role="presentation">y=2x+1y=2x+1
    • y=−12x+3" role="presentation">y=−12x+3y=−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.7,b 改成 2.5,上面所有数字都得重新算一遍。

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

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

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

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

    核心任务有三个:

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

    先看纯 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" role="presentation">y=kx+by=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)
    

    说明几点关键设计:

    • x_range[0], x_range[1] 直接读取坐标轴的数值范围,后续所有计算都以此为基准,修改范围再也不用手动改端点计算。
    • 与边框求交时,遍历了四条边界线 x=min, x=max, y=min, y=max,并筛选落在范围内的点,确保线段两端刚好“顶”到边框,不多不少。
    • 截距点用了 sp.solve(expr, x) 求 x 截距(即 y=0 时 x 的值),用 .subs(x,0) 求 y 截距。这些值可直接传给 ax.c2p 完成坐标转换。
    • 平行判断在这个例子里没显示,但你可以轻松加入:用 sp.simplify(k1 - k2) == 0 比较两条直线的斜率,如果平行就给特殊标注。

    4. 效果展示说明

    运行上述代码后,你会看到:

    • 坐标系先出现,随后四条不同颜色的直线同时生长出来。
    • 每条直线的长度恰好贯穿整个画面,没有任何线段伸到坐标轴之外或中途截断,视觉效果干净利落。
    • 紧接着,每个颜色对应的截距点(与 x 轴、y 轴的交点)以圆点浮现,旁边自动标注坐标数值,像是“( -0.5 , 0 )”、“( 0 , 3 )” 这样的形式。
    • 如果两条直线的 k 值相等(比如再补一条平行线),你还可以添加文字提示“这两条直线平行”,彻底不用人工判断。

    更棒的是,如果你想换成另外一组 k、b 组合,只需要改动 params 列表,其余一切自动计算、自动适应。比如演示“k 逐渐增大时直线越来越陡”,直接写个循环生成 10 条线,瞬间得到教学需要的对比图。

    5. 小结

    这一期我们解决了一个非常具体的教学动画痛点:手工计算直线端点与截距。通过引入 SymPy,我们实现了:

    • 表达式符号化:y = kx + b 作为模板,替换参数即可得到具体表达式。
    • 端点自动生成:解直线与坐标轴矩形的交点,再也不用担心线段太长或太短。
    • 截距自动标注:solve 和 subs 精确求出与轴的交点,无手动误差。
    • 易于扩展:可以轻松加入平行/相交判断、动态改变 k 或 b 的动画等。

    当你把繁琐的计算全部外包给 SymPy 后,Manim 就回归了它最擅长的角色:一个纯粹的视觉表达工具。

    你的创意不再被重复的算术打断,这才是代码动画应有的样子。

  • 相关阅读:
    小程序bindtap 和 catchtap 的区别以及如何使用
    AGI概念与实现
    如何在.Net Framework应用中请求HTTP2站点
    iOS UWB——Neaby Interaction框架(一)
    Selenium启动常用浏览器
    [数据分析与可视化] Python绘制数据地图2-GeoPandas地图可视化
    java中优雅的判空之Optional和其基本用法
    洛谷 P1135 奇怪的电梯 P1135 Java
    【广州华锐互动】钢厂铸锻部VR沉浸式实训系统
    Java从入门到精通
  • 原文地址:https://www.cnblogs.com/wang_yb/p/20285897
  • 最新文章
  • 沪漂五周年了:我越来越迷茫了
    Agentic Skill Routing 实战:别再把所有 Skill 塞进 AI Agent 上下文
    MySQL-Seconds_behind_master的精度误差
    [MAF预定义ChatClient中间件-03]CachingChatClient——利用缓存省钱省时间
    AI的至暗历史:从万众期待到被政府撤资,AI的两次死亡徘徊
    Agent OS :五种驯服不确定性的范式
    PortSwigger SQL注入LAB11
    数据库即时编译JIT
    [Begin]AI Learn Data Day 0
    深度学习进阶(二十七)现代 LLM 的核心架构设计其二:SwiGLU
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
小工具 小游戏
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1

京公网安备 11010502049817号