space_curve_demo.py
1from manim import *2import numpy as np3 4class SpaceCurveDemo(ThreeDScene):5 def construct(self):6 # 设置3D场景7 self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)8 9 # 创建标题10 title = Text("空间曲线的切线和法平面", font="PingFang SC", font_size=32)11 title.to_corner(UL, buff=0.5)12 self.add_fixed_in_frame_mobjects(title)13 self.play(Write(title))14 self.wait()15 16 # 创建3D坐标系17 axes = ThreeDAxes(18 x_range=[-3, 3, 1],19 y_range=[-3, 3, 1],20 z_range=[-1, 2, 1],21 axis_config={"color": GRAY}22 )23 self.add(axes)24 self.wait()25 26 # 定义螺旋线参数方程27 def helix(t):28 return np.array([29 1.5 * np.cos(t),30 1.5 * np.sin(t),31 t/632 ])33 34 # 定义螺旋线的导数(切向量)35 def helix_derivative(t):36 return np.array([37 -1.5 * np.sin(t),38 1.5 * np.cos(t),39 1/640 ])41 42 # 创建螺旋线43 curve = ParametricFunction(44 lambda t: helix(t),45 t_range=[0, 4*PI],46 color=BLUE47 )48 self.play(Create(curve))49 self.wait()50 51 # 显示参数方程52 param_eq = VGroup(53 MathTex(r"x = x(t)", color=BLUE, font_size=24),54 MathTex(r"y = y(t)", color=BLUE, font_size=24),55 MathTex(r"z = z(t)", color=BLUE, font_size=24)56 )57 param_eq.arrange(DOWN, aligned_edge=LEFT, buff=0.2)58 param_eq.to_corner(UR, buff=0.5)59 param_eq.shift(LEFT) # 向左移动一个单位60 self.add_fixed_in_frame_mobjects(param_eq)61 self.play(Write(param_eq))62 self.wait()63 64 # 创建固定点和动点65 fixed_point = helix(1)66 fixed_dot = Dot3D(point=fixed_point, color=RED)67 self.add(fixed_dot)68 self.wait()69 70 # 创建割线71 def create_secant_line(t):72 point = helix(t)73 direction = point - fixed_point74 # 创建无限延伸的直线75 return Line(76 start=fixed_point - 2*direction,77 end=fixed_point + 2*direction,78 color=YELLOW79 )80 81 # 创建割线点82 secant_dot = Dot3D(point=helix(3), color=GREEN)83 secant_line = create_secant_line(3)84 self.add(secant_dot, secant_line)85 self.wait()86 87 # 显示割线方程标注88 secant_label = Text("割线方程", font="PingFang SC", font_size=16, color=YELLOW)89 secant_label.next_to(param_eq, DOWN, buff=0.2)90 self.add_fixed_in_frame_mobjects(secant_label)91 self.play(Write(secant_label))92 self.wait()93 94 # 显示割线方程95 secant_eq = MathTex(96 r"\frac{x-x(t_0)}{x(t)-x(t_0)} = \frac{y-y(t_0)}{y(t)-y(t_0)} = \frac{z-z(t_0)}{z(t)-z(t_0)}",97 color=YELLOW,98 font_size=2099 )100 secant_eq.next_to(secant_label, DOWN, buff=0.2)101 self.add_fixed_in_frame_mobjects(secant_eq)102 self.play(Write(secant_eq))103 self.wait()104 105 # 演示割线趋近于切线的过程106 def update_secant(mob, alpha):107 t = 3 - 2*alpha # 从3趋近于1108 point = helix(t)109 # 更新割线110 direction = point - fixed_point111 if alpha < 0.99: # 在接近极限之前保持割线112 mob.become(Line(113 start=fixed_point - 2*direction,114 end=fixed_point + 2*direction,115 color=YELLOW116 ))117 # 更新动点位置118 secant_dot.move_to(point)119 else: # 在最后直接变成切线120 derivative = helix_derivative(1)121 mob.become(Line(122 start=fixed_point - 2*derivative,123 end=fixed_point + 2*derivative,124 color=YELLOW125 ))126 127 # 创建动画128 self.play(129 UpdateFromAlphaFunc(secant_line, update_secant),130 UpdateFromAlphaFunc(secant_dot, lambda m, a: m.move_to(helix(3 - 2*a))),131 run_time=3132 )133 self.wait()134 135 # 移除割线点136 self.remove(secant_dot)137 self.wait()138 139 # 显示极限表示140 limit_eq = MathTex(141 r"\lim_{t \to t_0} \frac{x(t)-x(t_0)}{t-t_0} = x'(t_0)",142 color=YELLOW,143 font_size=20144 )145 limit_eq.next_to(secant_eq, DOWN, buff=0.2)146 self.add_fixed_in_frame_mobjects(limit_eq)147 self.play(Write(limit_eq))148 self.wait()149 150 # 显示切线方程标注151 tangent_label = Text("切线方程", font="PingFang SC", font_size=16, color=YELLOW)152 tangent_label.next_to(limit_eq, DOWN, buff=0.2)153 self.add_fixed_in_frame_mobjects(tangent_label)154 self.play(Write(tangent_label))155 self.wait()156 157 # 显示切线方程158 tangent_eq = MathTex(159 r"\frac{x-x(t_0)}{x'(t_0)} = \frac{y-y(t_0)}{y'(t_0)} = \frac{z-z(t_0)}{z'(t_0)}",160 color=YELLOW,161 font_size=20162 )163 tangent_eq.next_to(tangent_label, DOWN, buff=0.2)164 self.add_fixed_in_frame_mobjects(tangent_eq)165 self.play(Write(tangent_eq))166 self.wait()167 168 # 清理残影169 self.clear()170 self.add(axes, curve, fixed_dot, secant_line, title, param_eq, secant_label, secant_eq, limit_eq, tangent_label, tangent_eq)171 self.wait()172 173 # 改变视角以更好地观察174 self.move_camera(phi=60 * DEGREES, theta=60 * DEGREES, run_time=2)175 self.wait()176 177 # 旋转3D视图178 self.begin_ambient_camera_rotation(rate=0.2)179 self.wait(4)180 self.stop_ambient_camera_rotation()181 self.wait()182 183 # 总结184 summary = Text(185 "空间曲线的切线是割线的极限",186 font="PingFang SC",187 color=WHITE,188 font_size=24189 )190 summary.to_edge(DOWN, buff=0.3)191 self.add_fixed_in_frame_mobjects(summary)192 self.play(Write(summary))193 self.wait(2)194 195 # 淡出所有元素196 self.play(197 *[FadeOut(mob) for mob in self.mobjects],198 run_time=2199 ) 讲解
这个视频围绕「空间曲线的切线和法平面」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
画面中出现的文字「空间曲线的切线和法平面」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。
随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。
核心公式可以写成:
这类公式可以和画面中的符号一一对应。
观察路径
观察路径可以分三步:先锁定「空间曲线的切线和法平面」中的核心对象,尤其留意空间曲线、切向量、法平面之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从空间曲线、切向量、法平面、参数曲线这些概念进入,继续沿相邻问题观察同一类数学结构。