用Manim实现动态交点计算--从一个动点问题说起

大家好,今天想和大家分享一个在制作Manim动画时非常实用的话题:如何动态计算两条直线的交点

对于动点问题,比如初中数学中经典的**"时钟模型""将军饮马"**及其变种等等,硬编码坐标肯定不行,因为交点坐标是随动点变化的。

下面,我们结合 Python 的符号计算库 SymPyManim 的更新器(Updater),来实现真正的动态交点计算。

1. 从一个初中题目说起

先来看一道经典的初中动点问题:

这个题目中,当点 E E E和 F F F在线段 A D AD AD上移动时,点 H H H和 G G G都随之变换。

题目如何解答我们不用管,我们的重点是如何实现点 E E E和 F F F移动时,实时的更新点 H H H和 G G G。

2. 解决方案:用Sympy解方程组

这里简单说明下,Sympy是一个Python的符号计算库,可以像数学课本那样进行代数运算。

它能解方程求导积分化简表达式 ,最重要的是能精确求解方程组,返回的是精确的数学解而不是近似值。

我们实现这个动画效果时,是根据两个直线的方程来求解它的交点的,如果没有Sympy,我们要手动推导直线方程、联立求解,代码会非常冗长且容易出错。

从后面的代码你可以看出,Sympy让我们只需"翻译"数学表达式,就能得到精确结果,大大简化了代码逻辑。

我们的思路很简单:

  • 已知两个点 的坐标,可以求出直线方程
  • 已知两条直线方程 ,联立求解得到交点坐标

Python中,用Sympy这个符号计算库来实现再合适不过了。

2.1. 第一步:定义求直线方程的函数

python 复制代码
from sympy import Symbol, solve

def get_line(p1, p2):
    """已知两点,求直线y = kx + b的k和b"""
    k = Symbol("k")
    b = Symbol("b")
    
    expr1 = p1[0] * k + b - p1[1]
    expr2 = p2[0] * k + b - p2[1]
    
    ret = solve((expr1, expr2), dict=True)
    return {"k": ret[0][k], "b": ret[0][b]}

2.2. 第二步:定义求交点的函数

python 复制代码
def cross_points(l1, l2):
    """已知两条直线方程,求交点坐标"""
    x = Symbol("x")
    y = Symbol("y")
    
    expr1 = l1["k"] * x + l1["b"] - y
    expr2 = l2["k"] * x + l2["b"] - y
    ret = solve((expr1, expr2), dict=True)
    
    return np.array((float(ret[0][x]), float(ret[0][y]), 0))

有了这两个函数,我们就可以在Manim中动态更新交点了。

2.3. 完整的Manim动画实现

python 复制代码
from manim import *
import numpy as np
from sympy import Symbol, solve

class DynamicCrossPoint(Scene):
    def construct(self):
        # 定义矩形的顶点坐标
        points = {
            "A": np.array([-2.5, 2, 0]),
            "B": np.array([-2.5, -3, 0]),
            "C": np.array([2.5, -3, 0]),
            "D": np.array([2.5, 2, 0]),
        }
        
        # 初始动点的位置
        points["E"] = np.array([-0.52, 2, 0])   # E在AB上
        points["F"] = np.array([0.52, 2, 0])    # F在CD上,且AE=CF
        
        # 画矩形
        rectangle = Polygon(
            points["A"], points["B"], 
            points["C"], points["D"],
            stroke_width=3, color=GREEN
        )
        self.play(Create(rectangle))
        
        # 创建初始的点和线
        d_e = Dot(points["E"], radius=0.05, color=BLUE)
        d_f = Dot(points["F"], radius=0.05, color=BLUE)
        d_h = Dot(points["A"], radius=0.05, color=YELLOW)  # 初始随便放
        
        l_bf = Line(points["B"], points["E"], color=BLUE, stroke_width=2)
        l_ce = Line(points["C"], points["F"], color=BLUE, stroke_width=2)
        l_bd = Line(points["B"], points["D"], color=GREEN, stroke_width=2)
        
        self.play(Create(VGroup(l_bf, l_ce, l_bd, d_e, d_f, d_h)))
        
        # 核心部分:设置更新器
        # F点随着E点移动(保持AE=CF的关系)
        d_f.add_updater(
            lambda z: z.become(
                Dot(points["D"] - (d_e.get_center() - points["A"]), 
                    radius=0.05, color=BLUE)
            )
        )
        
        # H点是BF和CE的交点
        d_h.add_updater(
            lambda z: z.become(
                Dot(
                    cross_points(
                        get_line(points["B"], d_e.get_center()),  # BF
                        get_line(points["C"], d_f.get_center()),  # CE
                    ),
                    radius=0.05, color=YELLOW
                )
            )
        )
        
        # 更新线段
        l_bf.add_updater(
            lambda z: z.become(
                Line(points["B"], d_e.get_center(), color=BLUE, stroke_width=2)
            )
        )
        
        l_ce.add_updater(
            lambda z: z.become(
                Line(points["C"], d_f.get_center(), color=BLUE, stroke_width=2)
            )
        )
        
        # 让E点动起来
        self.play(d_e.animate.shift(LEFT * 1.5), run_time=3)
        self.play(d_e.animate.shift(RIGHT * 3), run_time=6)
        self.play(d_e.animate.shift(LEFT * 1.5), run_time=3)
        
        # 清理更新器
        for mob in [d_f, d_h, l_bf, l_ce]:
            mob.clear_updaters()
        
        self.wait()

2.4. 为什么不直接用Manim的几何变换?

可能有朋友会问:Manim不是有.move_to().shift()这些方法吗?为什么非要用Sympy算交点?

答案是:当动点之间没有简单的几何变换关系时,比如,在这个例子中,交点的位置是由两条动态直线决定的,没有办法通过简单的位移或旋转得到。

这时,数学计算就是最直接的方法。

2.5. 进阶思考

上面介绍的方法,通用性很强:

  • 可以用来求直线与圆的交点
  • 可以用来求两条曲线的交点
  • 甚至可以用来求动点轨迹的方程

只要你能写出方程,Sympy就能帮你解出来。

3. 结语

今天这个例子,我们学会了:

  1. Sympy求解直线交点
  2. Manim中动态更新交点
  3. 处理多个相互关联的动点

希望这篇文章能帮助你在制作Manim动画时,更自如地处理动态几何问题。

相关推荐
笨鸟先飞,勤能补拙20 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
长和信泰光伏储能21 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂21 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study1 天前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境1 天前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境1 天前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd1231 天前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy12393102161 天前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
bamb001 天前
一个项目带你入门AI应用开发01
python
0566461 天前
Python康复训练——常用标准库
开发语言·python·学习