惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
C
CERT Recently Published Vulnerability Notes
P
Proofpoint News Feed
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
云风的 BLOG
云风的 BLOG
H
Help Net Security
Recorded Future
Recorded Future
The Register - Security
The Register - Security
F
Full Disclosure
N
Netflix TechBlog - Medium
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hackread – Cybersecurity News, Data Breaches, AI and More
爱范儿
爱范儿
Security Archives - TechRepublic
Security Archives - TechRepublic
Simon Willison's Weblog
Simon Willison's Weblog
Cisco Talos Blog
Cisco Talos Blog
I
InfoQ
T
Tenable Blog
T
Tor Project blog
人人都是产品经理
人人都是产品经理
D
DataBreaches.Net
NISL@THU
NISL@THU
Google DeepMind News
Google DeepMind News
博客园 - 叶小钗
B
Blog
V
V2EX
Jina AI
Jina AI
L
LangChain Blog
月光博客
月光博客
W
WeLiveSecurity
U
Unit 42
AWS News Blog
AWS News Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
博客园 - 聂微东
V
Visual Studio Blog
A
Arctic Wolf
T
Tailwind CSS Blog
The Cloudflare Blog
SecWiki News
SecWiki News
S
SegmentFault 最新的问题
Hacker News - Newest:
Hacker News - Newest: "LLM"
宝玉的分享
宝玉的分享
MyScale Blog
MyScale Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
S
Securelist
www.infosecurity-magazine.com
www.infosecurity-magazine.com
腾讯CDC
雷峰网
雷峰网

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
用SymPy自动求解三角形构造与全等条件验证
wang_yb · 2026-06-08 · via 博客园_首页

Manim 动画演示三角形全等判定定理时,我需要根据给定的边长或角度条件,在坐标系中精确放置三角形的三个顶点。

手动调整点的位置来凑 SSSSAS 这些条件,反复试错、坐标对不齐,根本没法精确展示“给定条件后三角形唯一确定”这个核心结论。

这篇文章用 SymPy 把几何约束转化为代数方程,自动解出顶点坐标,让动画精准且高效。

1. 痛点场景还原

假设我要做一个 SSS 全等判定的演示:给定三边长度 AB=4, BC=3, AC=5,在坐标系中画出这个三角形,然后改变边长观察三角形是否唯一确定。

如果纯手动操作,我会这样写:

from manim import *
import numpy as np


class PainfulSSSDemo(Scene):
    def construct(self):
        # 手动指定顶点坐标 —— 怎么知道这三个坐标能满足边长条件?
        A = np.array([0, 0, 0])  # 固定 A 在原点
        B = np.array([4, 0, 0])  # 固定 B 在 (4,0),这样 AB=4
        # C 的位置?需要同时满足 AC=5, BC=3
        # 只能手动解方程:x²+y²=25, (x-4)²+y²=9
        # 手算得 x=4, y=3,于是 C=(4,3) —— 但这是直角三角形特例
        # 换一组边长又要重新手算,而且每次都要判断镜像解
        C = np.array([4, 3, 0])

        triangle = Polygon(A, B, C)
        self.add(triangle)

核心痛点:

  • 给 A、B 定好位置后,C 的坐标必须同时满足 AC 和 BC 的距离约束,手算就是解二元二次方程组,换个边长就要重算一次。
  • SASASA 更麻烦,涉及角度条件,需要把“$ \angle A = 60^{\circ} $”转化为向量点积方程,手算极易出错。
  • 方程组通常有两组解(镜像三角形),需要判断哪个是合理的朝向。
  • 手动算出来的坐标是近似值,动画中顶点位置不够精准。

这些计算本质上就是几何约束求解,完全可以交给 SymPy 自动完成。

2. SymPy 解决方案介绍

SymPy 可以将几何条件转化为代数方程,然后自动求解顶点坐标。

2.1 SSS 全等:已知三边求顶点

已知 $ A=(0,0) $, $ B=(c,0) $,求 $ C=(x,y) $ 满足 $ AC=b $, $ BC=a $。

import sympy as sp

x, y = sp.symbols('x y', real=True)
a, b, c = 3, 5, 4  # BC, AC, AB

# 距离约束转化为方程
eq1 = (x - 0)**2 + (y - 0)**2 - b**2  # AC = 5
eq2 = (x - c)**2 + (y - 0)**2 - a**2  # BC = 3

solutions = sp.solve([eq1, eq2], (x, y))
# 输出两组解:[(4, -3), (4, 3)]  —— 对应镜像三角形

对于 SASASA,只需把角度条件用向量点积或余弦定理表示,同样构建方程组求解。

2.2 SAS 全等:已知两边和夹角求第三顶点

已知 $ AB=c $, $ AC=b $, $ \angle A=\theta \(,\) A $ 在原点,$ B $ 在 $ (c,0) \(,\) C $ 满足到 $ A $ 的距离为 $ b $、到 $ B $ 的距离用余弦定理求:

import sympy as sp

x, y = sp.symbols('x y', real=True)
b, c, theta = 4, 5, sp.rad(60)  # AC=4, AB=5, ∠A=60°

# C 到 A 的距离
eq1 = x**2 + y**2 - b**2
# 用余弦定理:BC² = AB² + AC² - 2·AB·AC·cosθ
bc_sq = c**2 + b**2 - 2*c*b*sp.cos(theta)
eq2 = (x - c)**2 + y**2 - bc_sq

solutions = sp.solve([eq1, eq2], (x, y))

2.3 筛选合理解

SymPy 会返回两组解(关于 AB 所在直线对称),通过指定 y 的正负号可以筛选:

# 筛选 y >= 0 的解(取上方三角形)
valid_solution = [sol for sol in solutions if sol[1] >= 0][0]

这样就能得到唯一确定的三角形顶点坐标,完美支撑全等判定定理的可视化。

3. Manim 联动实战

下面是一个完整的动画场景,用 ValueTracker 控制边长,动态展示 SSS 全等下三角形的唯一确定性。

from manim import *
import sympy as sp
import numpy as np


class SSSCongruenceDemo(Scene):
    def construct(self):
        # 固定两个顶点
        A = np.array([-1, 0, 0])
        B = np.array([1, 0, 0])

        # 可调边长
        a_tracker = ValueTracker(2)  # BC
        b_tracker = ValueTracker(2)  # AC

        # 用 always_redraw 动态更新三角形(两个镜像三角形)
        triangles = always_redraw(
            lambda: self.get_triangles(
                A, B, a_tracker.get_value(), b_tracker.get_value()
            )
        )
        self.add(triangles)

        # 顶点标签
        labels = always_redraw(
            lambda: self.get_labels(A, B, a_tracker.get_value(), b_tracker.get_value())
        )
        self.add(labels)

        # 边长标注
        side_labels = always_redraw(
            lambda: self.get_side_labels(
                A, B, a_tracker.get_value(), b_tracker.get_value()
            )
        )
        self.add(side_labels)

        # 动画:改变 BC 和 AC 的长度
        self.play(a_tracker.animate.set_value(3), run_time=2)
        self.play(b_tracker.animate.set_value(1), run_time=2)
        self.play(
            a_tracker.animate.set_value(2), b_tracker.animate.set_value(2), run_time=2
        )
        self.wait()

    def solve_vertex_C(self, A, B, a, b):
        """用 SymPy 求解顶点 C,返回两个镜像点的 np.array 坐标列表"""
        x, y = sp.symbols("x y", real=True)
        # AB 的长度
        c = np.linalg.norm(B - A)

        # 距离约束方程
        eq1 = (x - 0) ** 2 + (y - 0) ** 2 - b**2  # 以 A 为原点
        eq2 = (x - c) ** 2 + (y - 0) ** 2 - a**2  # 以 B 为原点

        solutions = sp.solve([eq1, eq2], (x, y), dict=True)
        if not solutions:
            return []

        # 转换到实际坐标系
        AB_vec = B - A
        x_axis = AB_vec / c
        y_axis = np.array([-x_axis[1], x_axis[0], 0])

        C_points = []
        for sol in solutions:
            sol_x = float(sp.N(sol[x]))
            sol_y = float(sp.N(sol[y]))
            # 局部坐标转全局坐标
            C = A + sol_x * x_axis + sol_y * y_axis
            C_points.append(C)
        return C_points

    def get_triangles(self, A, B, a, b):
        C_points = self.solve_vertex_C(A, B, a, b)
        if not C_points:
            return VGroup()  # 无法构成三角形时返回空

        triangles = VGroup()
        colors = [BLUE, GREEN]
        for i, C in enumerate(C_points):
            triangle = Polygon(
                A, B, C, color=colors[i % 2], fill_opacity=0.3, stroke_width=2
            )
            triangles.add(triangle)
        return triangles

    def get_labels(self, A, B, a, b):
        C_points = self.solve_vertex_C(A, B, a, b)
        if not C_points:
            return VGroup()

        label_A = MathTex("A", color=WHITE, font_size=28).next_to(A, DL, buff=0.15)
        label_B = MathTex("B", color=WHITE, font_size=28).next_to(B, DR, buff=0.15)

        labels = VGroup(label_A, label_B)
        for i, C in enumerate(C_points):
            direction = UP if C[1] >= A[1] else DOWN
            label_C = MathTex(f"C_{i+1}", color=WHITE, font_size=28).next_to(
                C, direction, buff=0.15
            )
            labels.add(label_C)
        return labels

    def get_side_labels(self, A, B, a, b):
        C_points = self.solve_vertex_C(A, B, a, b)
        if not C_points:
            return VGroup()

        c = np.linalg.norm(B - A)
        labels = VGroup()

        # AB 边长标注(共用)
        label_AB = MathTex(f"{c:.1f}", font_size=24, color=YELLOW).move_to(
            (A + B) / 2 + DOWN * 0.3
        )
        labels.add(label_AB)

        # 每个三角形的 AC 和 BC 边长标注
        for i, C in enumerate(C_points):
            direction = LEFT if C[0] < (A[0] + B[0]) / 2 else RIGHT
            label_AC = MathTex(f"{b:.1f}", font_size=24, color=RED).move_to(
                (A + C) / 2 + direction * 0.3
            )
            label_BC = MathTex(f"{a:.1f}", font_size=24, color=RED).move_to(
                (B + C) / 2 + (-direction) * 0.3
            )
            labels.add(label_AC, label_BC)

        return labels

关键点解释:

  • solve_vertex_C 是核心:将 A、B 固定后,以 A 为原点、AB 为 x 轴建立局部坐标系,用 SymPy 解二元二次方程组求 C 的局部坐标,再通过向量旋转平移到全局坐标系。
  • 同时显示 C 点两个解的镜像,上下两个全等三角形在视觉上稳定一致。
  • always_redraw 保证边长改变时三角形实时更新,顶点坐标由 SymPy 自动重新计算,完全不用手动干预。
  • 当给定三边长度不满足三角形不等式时,solve 无实解,函数返回 None,动画自动隐藏三角形,自然展示了“不是任意三边都能构成三角形”。

4. 效果展示说明

运行这个场景,你会看到:

  • 坐标系中,$ A $ 和 $ B $ 两个顶点固定不变,边长标注清晰显示当前的 $ AB \(、\) BC \(、\) AC $ 长度。
  • **改变 **$ BC \(** 的长度**:\) C $ 点沿一条弧线移动,三角形的形状随之变化,但始终满足给定的三边长度约束。你能直观看到“改变一边,三角形形状唯一确定”。
  • **改变 **$ AC \(** 的长度**:类似地,\) C $ 点在另一条弧线上移动,三角形的三个顶点自动重新定位。
  • 同时改变两边:三角形平滑过渡到新的形状,整个过程顶点坐标精确、无任何手动调整的痕迹。
  • 如果输入的边长无法构成三角形(如 $ a+b \leqslant c $),三角形消失,自然展示了三角形不等式这个隐含条件。

以此为基础,只需修改方程组,就可以扩展为 SASASAAAS 等其他全等判定的可视化演示。

5. 小结

SymPyManim 几何动画中的作用可以概括为:将几何约束转化为代数方程,让计算机解出精确的顶点坐标。

你不再需要手动凑坐标、手算方程组、处理镜像解,只需要描述“已知条件是什么”,SymPy 就会返回“点应该在哪里”。

这个思路不仅适用于全等三角形,也适用于任何需要精确几何构造的场景:尺规作图、动点轨迹、最值问题等等。

把数学计算交给 SymPy,把视觉表达留给 Manim,两者配合才能做出真正精准而优雅的数学动画。