使用Manim画分段函数的图像

使用Manim难免会遇到渲染函数图像的问题,特别是初中数学使用Manim制作课件,今天我们就来简单的总结一下,使用Manim制作分段函数的图像该如何制实现这个过程,做一个记录分析,主要是帮助自己记住一些简单的知识,高手滑过。

1.画一个分段函数

我们先来制作一个分段函数的图像,来看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
from manim import *
import numpy as np

class PiecewiseFunctionPlot(Scene):
def construct(self):
# 设置坐标系
axes = Axes(
x_range=[-3.5, 3.5, 1],
y_range=[-1.5, 3.5, 1],
axis_config={"color": BLUE},
x_length=7,
y_length=5,
)

# 添加坐标轴标签
axes_labels = axes.get_axis_labels(x_label="x", y_label="y")

# 定义分段函数
# f(x) = x², x < 0
# f(x) = 1, x = 0
# f(x) = 2x + 1, x > 0
def piecewise_func(x):
if x < 0:
return x**2
elif x == 0:
return 1
else:
return 2*x + 1

# 绘制第一段:x < 0 的部分 (x²)
graph1 = axes.plot(lambda x: x**2, x_range=[-1.5, 0], color=RED)
# 空心圆点表示开区间
dot_open = Dot(axes.c2p(0, 0), color=RED, radius=0.08)
dot_open.set_fill(opacity=0) # 空心

# 绘制第二段:x > 0 的部分 (2x+1)
graph2 = axes.plot(lambda x: 2*x + 1, x_range=[0, 1], color=GREEN)
# 实心圆点表示闭区间
dot_closed = Dot(axes.c2p(0, 1), color=GREEN, radius=0.08)

# 特殊点:x=0 时的函数值
special_point = Dot(axes.c2p(0, 1), color=YELLOW, radius=0.1)

# 添加函数表达式标签
func_label1 = MathTex("f(x) = x^2,\\ x < 0", color=RED).scale(0.8)
func_label1.next_to(axes.c2p(-2, 2.25), LEFT,buff=0.5)

func_label2 = MathTex("f(x) = 2x + 1,\\ x > 0", color=GREEN).scale(0.8)
func_label2.next_to(axes.c2p(1, 3), RIGHT)

special_label = MathTex("f(0) = 1", color=YELLOW).scale(0.8)
special_label.next_to(axes.c2p(0, 1), UP)

# 添加动画
self.play(Create(axes), Write(axes_labels))
self.wait(0.5)

# 绘制第一段函数
self.play(Create(graph1), run_time=2)
self.play(FadeIn(dot_open))
self.play(Write(func_label1))
self.wait(0.5)

# 绘制特殊点
self.play(FadeIn(special_point))
self.play(Write(special_label))
self.wait(0.5)

# 绘制第二段函数
self.play(Create(graph2), run_time=2)
self.play(FadeIn(dot_closed))
self.play(Write(func_label2))
self.wait(1)

# 添加标题
title = Text("分段函数示例", font_size=36, color=WHITE)
title.to_edge(UP)
self.play(Write(title))
self.wait(2)

2.分析坐标系

Manim渲染坐标系最容易遇到的问题,就是坐标轴的单位长度不一致,为了避免这个问题,我们分别调整了两个参数

1
2
3
4
5
6
7
8
9
10
11
# 设置坐标系
axes = Axes(
x_range=[-3.5, 3.5, 1],
y_range=[-1.5, 3.5, 1],
axis_config={"color": BLUE},
x_length=7,
y_length=5,
)

# 添加坐标轴标签
axes_labels = axes.get_axis_labels(x_label="x", y_label="y")

注意看这里

1
2
x_length=7,
y_length=5,

这两个参数的数值不是凭空产生的,依据是

1
3.5-(-3.5)=7
1
3.5-(-1.5)=5

这样一来,7➗️7=5➗️5,就保持了单位长度的一致。详细的分析如下

1
2
3
4
5
6
7
axes = Axes(
x_range=[-3.5, 3.5, 1], # 总跨度7
y_range=[-1.5, 3.5, 1], # 总跨度5
x_length=7, # 比例 7/7 = 1
y_length=5, # 比例 5/5 = 1,单位长度相同!
tips=False,
)

3.定义函数

Manim的函数定义有个特殊点儿,需要注意这个地方。先来看代码

1
2
3
4
5
6
7
8
9
10
11
# 定义分段函数
# f(x) = x², x < 0
# f(x) = 1, x = 0
# f(x) = 2x + 1, x > 0
def piecewise_func(x):
if x < 0:
return x**2
elif x == 0:
return 1
else:
return 2*x + 1

piecewise_func(x)仅仅是随意的一个自变量定义名,没有任何的特殊含义,但是指定了自变量是 x

1
2
3
4
5
6
if 条件:
return 结果
elif 另一个条件:
return 另一个结果
else 剩余的条件:
return 剩余的结果

Python变成之中没有 else if,只有 elif,很多有编程基础的朋友会犯这个错误。

4.函数图像绘制

我们仔细观察代码之中函数图像的绘制,会发现如下代码

1
graph = axes.plot(lambda x: x**2, x_range=[-1.5, 0], color=RED)

graph并没有特殊的含义,英语之中就是分段的意思。其中绘制函数图像最终的函数,就是 axes.plot()函数,我们来看相关参数

1
2
3
4
5
axes.plot(
function, # 要绘制的函数
x_range, # x的取值范围
**kwargs # 其他样式参数
)

其他的样式参数不限于

1
2
3
4
5
6
7
8
9
10
11
12
# 线条样式
color=RED, # 颜色
stroke_width=3, # 线宽,默认4
stroke_opacity=0.8, # 透明度,0-1

# 曲线质量
use_smoothing=True, # 是否平滑曲线(默认True)
discontinuities=[], # 不连续点列表(分段函数时很有用)
dt=0.01, # 采样步长,越小曲线越精细

# 其他
use_vectorized=True, # 是否使用向量化计算(默认True)

5.坐标转换

发现一个小小的惊喜,就是笛卡尔坐标转换的问题

1
axes.c2p()

他可以直接把屏幕坐标转换成当前屏幕中设定的坐标系坐标。

1
func_label1.next_to(axes.c2p(-2, 2.25), LEFT,buff=0.5)

也就是我们输入的坐标,就是当前坐标系的坐标。这为其他对象提供了准确的参照物。

6.坐标系平移

添加的坐标系默认原点中心在屏幕中心,如果需要将坐标系平移,我们可以修改坐标系代码

1
2
3
4
5
6
7
8
# 设置坐标系
axes = Axes(
x_range=[-3.5, 3.5, 1],
y_range=[-1.5, 3.5, 1],
axis_config={"color": BLUE},
x_length=7,
y_length=5,
)

将上面的坐标系添加 shift

1
2
3
4
5
6
7
8
# 设置坐标系
axes = Axes(
x_range=[-3.5, 3.5, 1],
y_range=[-1.5, 3.5, 1],
axis_config={"color": BLUE},
x_length=7,
y_length=5,
).shift(Up*2) # 将坐标系向上平移两个单位。

大家注意平移的真是距离,需要适当的调整。另外记录shift的用法

1
2
3
4
5
6
7
8
# 设置坐标系
axes = Axes(
x_range=[-3.5, 3.5, 1],
y_range=[-1.5, 3.5, 1],
axis_config={"color": BLUE},
x_length=7,
y_length=5,
).shift(RIGHT * 2 + UP * 1) # 向右平移2个单位,向上平移1各单位

好了,今天的学习就到这里。来看最终的代码展示效果