1. 前言
关于 Manim 的安装,github 上有多种版本,使用起来各不相同(运行方式、API等等)踩了一些坑,最终用的 Manim 社区版,里面有详细的教程和API介绍。
2. 绘制动态函数切线
from manim import *
def func(x):
return np.sin(x*PI)
class PlotTangent(MovingCameraScene):
def construct(self):
self.camera.frame.save_state()
# 标题函数公式, 放置在左上角
title = MathTex("f(x) = sin(x)")
title.to_corner(UL)
# 绘制坐标系
ax = Axes(x_range = [-2.5, 2.5, 0.5],
y_range = [-1.5, 1.5],
x_length = 10,
axis_config={
"include_numbers": True},
)
graph = ax.get_graph(func, color=BLUE) # 函数曲线
x_space = np.linspace(-1.5, 1.5, 200) # 设定切线的 x 取值范围
''' 绘制切线, get_secant_slope_group 是用来绘制割线的, 这里 dx 设的足够小看起来就像切线 这里的 slopes 里面有3条线, 第三条是需要的切线, 另外两条是 dx 和 dy '''
slopes = ax.get_secant_slope_group(
x = x_space[0],
graph = graph,
dx = 0.0001,
secant_line_length = 5,
secant_line_color = RED_D,
)
dot = Dot(point=[ax.coords_to_point(x_space[0], func(x_space[0]))]) # 初始切点
# 切点处标记的信息, 用 VGroup() 把公式和动态斜率数值绑定成一组
text, decimal = label = VGroup(
MathTex("f'(x) = "),
DecimalNumber(
0,
num_decimal_places=3,
include_sign=True
)
)
label.arrange(RIGHT) # 没有这行的话公式和数值会重合在一起
label.scale(0.6) # 比例缩放
label.set_color(YELLOW_C) # 颜色
label.add_updater(lambda d: d.next_to(dot, UL)) # label 在 dot 的左上角
# decimal 的数值设定为 dot 点的斜率
decimal.add_updater(lambda d: d.set_value(
np.cos(ax.point_to_coords(dot.get_center())[0] * PI)
))
self.add(title, ax, graph, dot) # 标题、坐标系、函数曲线、初始切点
# 切点的xy轴垂线
h_line = always_redraw(lambda: ax.get_lines_to_point(dot.get_center()))
self.play(Create(h_line), Create(slopes[2]), Write(label)) # 切点垂线、切线、标签
self.play(self.camera.frame.animate.scale(0.8).move_to(dot)) # 镜头跟踪切点位置
def update_curve(mob):
mob.move_to(dot.get_center())
''' 这里用循环来变换切点坐标和切线, 感觉不是最优的方法 其实点可以通过 dot.add_updater(lambda x: x.move_to()) 来动态更新 但是不知道切线怎么用 add_updater 的方式来变换, 只好先用循环实现 '''
for x in x_space[1:]:
self.camera.frame.add_updater(update_curve)
slopes_d = ax.get_secant_slope_group(
x = x,
graph = graph,
dx = 0.0001,
secant_line_length = 5,
secant_line_color = RED_D,
)
dot_d = Dot(point=[ax.coords_to_point(x, func(x))])
self.play(
ReplacementTransform(slopes[2], slopes_d[2]),
ReplacementTransform(dot, dot_d),
run_time=0.015
)
slopes = slopes_d
dot = dot_d
self.camera.frame.remove_updater(update_curve)
self.play(Restore(self.camera.frame))
self.wait()
# jupyter notebook 的魔法函数运行方式
%manim -ql -v WARNING -i PlotTangent
文章评论