OpenMontage 数学动画坐标系实战:基于 ManimCE 的 Axes、NumberPlane 与坐标转换系统
【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage
在数学类视频的自动化生产中,一切函数曲线、向量、复数点与几何变换都必须先落在正确的坐标系上。本文以 OpenMontage 仓库中的 ManimCE 最佳实践技能文档 .agents/skills/manimce-best-practices/rules/axes.md 为核心骨架,系统讲解Axes、NumberPlane、ComplexPlane、NumberLine与ThreeDAxes的构造、定制与坐标换算方法,并结合仓库中math_animate工具的源码与可运行示例,说明如何写出可以直接渲染的坐标系场景。读完本文,你将能够独立编写带坐标轴、网格、刻度、标签与复数标注的数学动画 Scene,并掌握两套坐标系统(数学坐标 ↔ 屏幕坐标)互转的全部 API。
坐标系在 OpenMontage 数学动画管线中的位置
OpenMontage 是一个把 AI 编码助手变成"完整视频制作工作室"的智能体视频生产系统,其中数学/科学讲解类视频由本地免费的math_animate工具完成渲染(见 tools/graphics/math_animate.py:Mathematical animation tool via ManimCE ... Free, local, no API key required)。该工具底层执行 Manim Community Edition 场景代码,并在其agent_skills = ["manimce-best-practices", "manim-composer"]中声明依赖本文所在的技能目录——也就是说,.agents/skills/manimce-best-practices/下的规则文件正是 Agent 编写数学场景时会被调取的知识库。
坐标系是这一切的地基:函数绘图、参数曲线、复数平面、黎曼和、微分几何几乎全部依赖Axes一族对象。技能的入口文档 .agents/skills/manimce-best-practices/SKILL.md 将本文档与 graphing.md(函数绘制)和 3d.md(3D 坐标)并列归入 "Coordinate Systems & Graphing" 板块,三者共同构成一套从"坐标骨架"到"图形内容"的完整写作规范。
Axes:二维坐标轴
Axes是 ManimCE 中最常用的二维坐标容器:它本身是 Mobject,被self.add()或self.play(Create(axes))加入场景后即可作为所有曲线、点的参照系。最基本的使用方式如下:
from manim import * class AxesExample(Scene): def construct(self): # Default axes axes = Axes() self.add(axes)默认坐标轴会按 ManimCE 内置的x_range/y_range范围铺满画面,但工程实践中几乎总是需要显式定制,以匹配被绘制的函数定义域与值域。
定制 Axes:范围、物理长度与样式
class CustomAxes(Scene): def construct(self): axes = Axes( x_range=[-5, 5, 1], # [min, max, step] y_range=[-3, 3, 1], x_length=10, # Physical length on screen y_length=6, axis_config={ "color": BLUE, "include_tip": True, "include_numbers": True, }, x_axis_config={ "numbers_to_include": [-4, -2, 0, 2, 4], }, y_axis_config={ "numbers_to_include": [-2, 0, 2], }, ) self.add(axes)关键参数语义如下表:
| 参数 | 取值示例 | 含义 |
|---|---|---|
x_range/y_range | [-5, 5, 1] | 数学坐标系范围,三元组为[min, max, step],step决定刻度间距 |
x_length/y_length | 10/6 | 坐标轴在屏幕上占据的物理长度(单位是 Manim 场景单位),决定网格与曲线的缩放比例 |
axis_config | {"color": BLUE, ...} | 同时作用于 x、y 两条轴的通用样式与行为 |
x_axis_config | {"numbers_to_include": [...]} | 仅作用于 x 轴的覆盖项,优先级高于axis_config |
y_axis_config | {"numbers_to_include": [...]} | 仅作用于 y 轴的覆盖项 |
axis_config中最常用的键包括:color(轴线与刻度颜色)、include_tip(是否在正方向末端加箭头)、include_numbers(是否在每个刻度旁渲染数字标签)、stroke_width(线宽)、tip_shape(箭头形状)等。numbers_to_include用于精挑哪些刻度显示数字——这是避免画面过密的直接手段,与下文 Best Practices 中"数字宁缺毋滥"的原则一脉相承。
为轴添加标签
class AxesLabels(Scene): def construct(self): axes = Axes(x_range=[-5, 5], y_range=[-3, 3]) # Add axis labels x_label = axes.get_x_axis_label("x") y_label = axes.get_y_axis_label("y") # Custom labels x_label = axes.get_x_axis_label(MathTex(r"\theta")) y_label = axes.get_y_axis_label(MathTex(r"f(\theta)")) self.add(axes, x_label, y_label)get_x_axis_label/get_y_axis_label接受纯字符串(如"x"、"t")或任意 Mobject(最常见的做法是传入MathTex排版 LaTeX 数学符号),并会自动把标签附着在对应轴的正方向末端。示例中的第二个赋值覆盖了第一个,说明该方法可以反复调用——实际场景中通常直接传入最终想要的MathTex。这一用法与仓库内可运行的示例 graph_plotting.py 中BasicAxes场景完全一致,该场景还示范了用self.play(Create(axes), Write(x_label), Write(y_label))让轴与标签逐帧"生长"出来。
NumberPlane:带网格线的坐标平面
NumberPlane在坐标轴之上叠加了背景网格,是展示坐标线与几何变换的核心容器——当图形发生拉伸、剪切、旋转等线性变换时,变形的网格能直观地暴露变换效果。
class NumberPlaneExample(Scene): def construct(self): # Default plane plane = NumberPlane() self.add(plane)定制 NumberPlane 网格样式
class CustomPlane(Scene): def construct(self): plane = NumberPlane( x_range=[-4, 4, 1], y_range=[-3, 3, 1], x_length=8, y_length=6, background_line_style={ "stroke_color": BLUE_D, "stroke_width": 1, "stroke_opacity": 0.5, }, axis_config={ "color": WHITE, }, ) self.add(plane)与Axes的差异在于多了一个background_line_style参数,它统一控制所有背景网格线的观感,常用键包括stroke_color(推荐使用深色系如BLUE_D、GREY以与白色轴线区分)、stroke_width(细线如1更显克制)、stroke_opacity(半透明0.5让网格"退居幕后")。同时仍可通过axis_config单独加亮两条主轴。
仓库示例 graph_plotting.py 中的NumberPlaneExample场景给出了网格上叠加元素的完整范式:用plane.c2p(2, 3)定位Dot、用Arrow(plane.c2p(0, 0), plane.c2p(2, 3), buff=0)画从原点指向该点的向量,配合GrowArrow、Write等动画完成一个"点在平面上的表示"镜头。
ComplexPlane:复数平面
ComplexPlane是NumberPlane的复数语义封装,用于把复数可视化为平面上的点。
class ComplexPlaneExample(Scene): def construct(self): plane = ComplexPlane() # Plot complex number z = complex(2, 1) # 2 + i dot = Dot(plane.n2p(z), color=YELLOW) label = MathTex("2+i").next_to(dot, UR) self.add(plane, dot, label)关键转换方法是plane.n2p(z):输入一个 Python 内置complex(如complex(2, 1)即2+i),返回该复数在屏幕坐标系上的位置,随后即可用Dot落点、用next_to放置标注。如需在屏幕上叠加两个复数进行加减的几何演示,只需对n2p返回的点做向量运算即可。
NumberLine:一维数轴
当内容只涉及单变量(如区间、单调性、数列)时,数轴比二维平面更简洁清晰。
class NumberLineExample(Scene): def construct(self): line = NumberLine( x_range=[-5, 5, 1], length=10, include_numbers=True, include_tip=True, ) self.add(line)注意此处参数名为x_range与length:length等价于二维情形下的x_length,include_numbers=True表示在x_range指定间隔的位置渲染刻度数字。
坐标系 ↔ 屏幕坐标的双向转换
ManimCE 场景坐标系与"屏幕上实际摆放物体的坐标"是两套系统:Axes(x_range=[-5,5], ...)说的是数学定义域,而Dot、MathTex的位置参数用的是以画面中心为原点的场景坐标。所有坐标对象都提供成对的方法完成互转,规则文档强调:一切坐标换算都应使用这些内置方法,绝不要手工换算。
class CoordinateConversion(Scene): def construct(self): axes = Axes(x_range=[-5, 5], y_range=[-3, 3]) # Convert coordinates to screen position point = axes.c2p(2, 1) # coords_to_point: (2, 1) -> screen position # Convert screen position to coordinates coords = axes.p2c(point) # point_to_coords: screen -> (x, y) dot = Dot(point, color=RED) self.add(axes, dot)简写方法汇总
axes = Axes() # c2p = coords_to_point axes.c2p(x, y) # p2c = point_to_coords axes.p2c(point) # i2gp = input_to_graph_point (for graphs) axes.i2gp(x, graph) # For NumberPlane/ComplexPlane plane.n2p(complex_number) # number_to_point plane.p2n(point) # point_to_number把这些方法与 OpenMontage 场景编排习惯结合使用时,几个高频模式值得记录:
| 需求 | 推荐写法 |
|---|---|
| 在坐标 (2, 1) 处放一个点 | Dot(axes.c2p(2, 1), color=YELLOW) |
| 让动点沿曲线滑行 | Dot(axes.i2gp(tracker.get_value(), graph))(配合always_redraw) |
| 在坐标原点画向量到 (2,3) | Arrow(axes.c2p(0,0), axes.c2p(2,3), buff=0) |
| 标注横坐标刻度位置 | MathTex("2").next_to(axes.c2p(x_val, 0), DOWN) |
其中i2gp系列在配套的 graphing.md 中有更完整的应用(如Moving Point on Graph中用ValueTracker驱动点在正弦曲线上往返),并与示例 graph_plotting.py 的切线标注场景互相印证。
在坐标轴上绘制点与集合点
构造好坐标骨架后,最直接的"填内容"方式就是用c2p把一组数据点投影到轴上:
class PlotPoints(Scene): def construct(self): axes = Axes(x_range=[-5, 5], y_range=[-3, 3]) points = [(1, 2), (-2, 1), (3, -1), (0, 2)] dots = VGroup(*[ Dot(axes.c2p(x, y), color=YELLOW) for x, y in points ]) self.add(axes, dots)这里使用列表推导把(x, y)元组批量转换为Dot,再用VGroup聚合以便统一管理或整体动画化——点的颜色统一为YELLOW能在深色背景下形成强视觉焦点。对离散数据的进一步处理(散点、轨迹连线)只需把收集到的c2p结果喂给Polygon、Line或dashed_line即可。
ThreeDAxes:三维可视化坐标
当概念本质上是三维空间关系(曲面z=f(x,y)、向量场、体积)时,应切换为ThreeDScene+ThreeDAxes。坐标对象层面它与 2D 用法对称,只是多出z_range/z_length两个维度:
class ThreeDAxesExample(ThreeDScene): def construct(self): axes = ThreeDAxes( x_range=[-4, 4, 1], y_range=[-4, 4, 1], z_range=[-4, 4, 1], x_length=8, y_length=8, z_length=6, ) self.set_camera_orientation(phi=75 * DEGREES, theta=-45 * DEGREES) self.add(axes)两条 3D 专属注意点:其一,必须显式调用set_camera_orientation(phi, theta)设置相机俯仰角与水平转角,否则默认视角难以体现立体感(phi为相对 z 轴的仰角,75 * DEGREES是接近斜俯视的常用值);其二,ThreeDAxes还额外提供get_z_axis_label()用于标注 z 轴(见 3d.md 的 ThreeDAxes 小节)。3D 场景相关细节请直接查阅 3d.md 与可运行示例 3d_visualization.py。
渲染、验证与自动化:从坐标场景到成片
命令行直接渲染
坐标场景写好后,用 ManimCE CLI 渲染。开发期推荐用低质量快速预览,出片用高质量,全套质量档位如下(见 cli.md):
# Low Quality: 854x480, 15fps(快速迭代) manim -ql scene.py AxesExample # Medium Quality: 1280x720, 30fps manim -qm scene.py AxesExample # High Quality: 1920x1080, 60fps manim -qh scene.py AxesExample # 2K / 4K manim -qp scene.py AxesExample manim -qk scene.py AxesExample # 预览(渲染后自动打开) manim -pql scene.py AxesExample通过 math_animate 工具渲染
在 OpenMontage 内部,推荐路径是把Scene代码交给 tools/graphics/math_animate.py 中的math_animate工具:它会自动补齐from manim import *(若代码缺失),自动从Scene|ThreeDScene|MovingCameraScene|ZoomedScene子类中识别场景名,并把quality参数映射到上述 CLI 质量标志(其QUALITY_PRESETS定义的low/medium/high/4k/preview与-ql/-qm/-qh/-qk一一对应)。该工具还支持background_color(如深色'#1a1a2e')、transparent(透明背景)、format(mp4/gif/webm/png)等参数,完全覆盖坐标场景的导出需求。
需特别注意:math_animate会在宿主机上执行调用方提供的 Python 代码,属本地代码执行边界(issue #219)。因此工具默认用_scan_scene_code做静态安全扫描,拦截os、subprocess、socket等危险 import、eval/open等内建名及 dunder 属性访问;对应守卫逻辑可参见 tests/tools/test_math_animate_safety.py 中的安全用例。坐标场景本身只使用manim命名空间与numpy,不会触碰任何被禁构造,可以放心通过默认扫描。
画质与叙事节奏建议
结合 OpenMontage 的数学动画规范 skills/creative/manim-usage.md:
- 正式成片用
-qh(1080p),草稿用-qm,便于在脚本/旁白对白推进中快速评审; - 默认使用深色背景(
BLACK或#1a1a2e),与axis_config、网格线的配色保持一致; - 单个 Scene 只承载一个概念,坐标轴
Create之后给1.0-2.0s停留,再让曲线、点与标注逐层进入,避免同时亮出超过 3-4 个新元素。
坐标场景 Best Practices 汇总
综合规则文档axes.md的结论与实践经验,落地为 5 条可直接执行的原则:
- 设置合理的坐标范围——
x_range/y_range要紧贴内容,不要遗留大块无用空白,画面聚焦远胜铺满。 - 让物理长度与范围比例匹配——
x_length / y_length应约等于(x_max-x_min) / (y_max-y_min),否则圆会变椭圆、正方形会变矩形,造成视觉失真。例如 x 范围跨度 10、y 范围跨度 6 时,取x_length=10, y_length=6即保持等比。 - 变换演示优先用 NumberPlane——网格变形能把平移、缩放、线性变换的形变细节清清楚楚暴露出来,比裸轴更有说服力。
- 一切坐标换算走 c2p/p2c/i2gp/n2p/p2n——绝不手工换算;这些内置方法同时处理了刻度间隔、物理长度与原点偏移,手工换算一旦改轴参数就会整体错位。
- 刻度数字宁缺毋滥——用
numbers_to_include只保留必要刻度,密集数字是画面噪点的最大来源。
若要在坐标骨架上继续绘制函数曲线、参数曲线、面积与黎曼矩形,请直接阅读同目录的 graphing.md;需要快速验证整套写法的完整可运行参考,仓库内提供了覆盖坐标轴标注、函数绘制、参数曲线、切线、动点与黎曼和等场景的 graph_plotting.py,一条命令即可渲染:
manim -pql graph_plotting.py BasicAxes【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考