total_differential.py
1# -*- coding: utf-8 -*-2from manim import *3import numpy as np4 5class TotalDifferential(ThreeDScene):6 def construct(self):7 # 创建3D坐标系并向右下方移动,增加刻度间距8 axes = ThreeDAxes(9 x_range=[-2, 2, 0.5], # 增加刻度间距10 y_range=[-2, 2, 0.5],11 z_range=[-1, 3, 0.5],12 x_length=12, # 增加坐标轴长度以放大图像13 y_length=12,14 z_length=815 ).shift(2 * DOWN + RIGHT) # 向右下方移动更多16 17 # 设置初始相机角度18 self.set_camera_orientation(phi=60 * DEGREES, theta=-45 * DEGREES)19 20 # 添加坐标轴标签21 x_label = MathTex("x").next_to(axes.x_axis.get_end(), RIGHT)22 y_label = MathTex("y").next_to(axes.y_axis.get_end(), UP)23 z_label = MathTex("z").next_to(axes.z_axis.get_end(), OUT)24 labels = VGroup(x_label, y_label, z_label)25 26 # 创建函数z = x^2 + y^2的曲面,缩小范围27 def func(x, y):28 return x**2 + y**229 30 surface = Surface(31 lambda u, v: axes.c2p(u, v, func(u, v)),32 u_range=[-1.5, 1.5], # 缩小范围33 v_range=[-1.5, 1.5],34 resolution=(50, 50), # 增加网格线密度35 fill_color=PURPLE, # 更改曲面颜色为紫色36 fill_opacity=0.6 # 设置不透明度为0.637 )38 39 # 显示坐标系和曲面40 self.play(Create(axes), Write(labels))41 self.play(Create(surface))42 self.wait(1)43 44 # 选择一个点(x,y)并显示45 x0, y0 = 0.5, 0.546 point = Dot3D(axes.c2p(x0, y0, func(x0, y0)), color=YELLOW, radius=0.1)47 self.play(Create(point))48 49 # 创建垂直于z轴的水平平面并显示50 horizontal_plane = Surface(51 lambda u, v: axes.c2p(u, v, func(x0, y0)),52 u_range=[-1.5, 1.5],53 v_range=[-1.5, 1.5],54 resolution=(20, 20), # 增加网格线密度55 fill_opacity=0.3, # 设置不透明度为0.356 color=BLUE57 )58 self.play(Create(horizontal_plane))59 60 # 在水平面上选取新点并创建垂线61 dx, dy = 0.3, 0.362 new_point = axes.c2p(x0 + dx, y0 + dy, func(x0, y0))63 new_dot = Dot3D(new_point, color=WHITE, radius=0.05)64 self.play(Create(new_dot))65 66 # 垂线与实际增量相交67 actual_intersection = axes.c2p(x0 + dx, y0 + dy, func(x0 + dx, y0 + dy))68 actual_line = DashedLine(new_point, actual_intersection, color=YELLOW_D)69 self.play(Create(actual_line))70 71 # 创建切平面72 def get_tangent_plane(x0, y0, delta=1):73 # 计算偏导数74 fx = 2 * x075 fy = 2 * y076 f_val = func(x0, y0)77 78 # 切平面方程:z = f(x0,y0) + fx(x-x0) + fy(y-y0)79 def plane_func(x, y):80 return f_val + fx*(x-x0) + fy*(y-y0)81 82 plane = Surface(83 lambda u, v: axes.c2p(u, v, plane_func(u, v)),84 u_range=[x0-delta, x0+delta],85 v_range=[y0-delta, y0+delta],86 resolution=(40, 40), # 增加网格线密度87 fill_opacity=0.6,88 color=ORANGE # 更改切平面颜色为橙色89 )90 return plane91 92 # 创建并显示切平面93 tangent_plane = get_tangent_plane(x0, y0)94 self.play(Create(tangent_plane))95 96 # 垂线与线性近似相交97 approx_intersection = axes.c2p(x0 + dx, y0 + dy, func(x0, y0) + 2*x0*dx + 2*y0*dy)98 approx_line = DashedLine(new_point, approx_intersection, color=GREEN_D)99 self.play(Create(approx_line))100 101 # 显示误差线102 error_line = Line(actual_intersection, approx_intersection, color=RED_B, stroke_width=2)103 self.play(Create(error_line))104 105 # 添加文字说明106 explanation_text = VGroup(107 Text("点(x0+Δx, y0+Δy)到曲面距离为Δz", font="SimSun", color=YELLOW_D).scale(0.4),108 Text("点(x0+Δx, y0+Δy)到切平面距离为dz", font="SimSun", color=GREEN_D).scale(0.4),109 # Text("切平面到曲面的距离为误差", font="SimSun", color=RED_B).scale(0.4)110 ).arrange(DOWN, aligned_edge=LEFT)111 explanation_text.to_corner(DR) # 移动到右下角112 self.add_fixed_in_frame_mobjects(explanation_text)113 self.play(Write(explanation_text))114 115 # 调整相机运动116 self.move_camera(phi=70 * DEGREES, theta=-30 * DEGREES, run_time=2)117 self.begin_ambient_camera_rotation(rate=0.15)118 self.wait(6)119 self.stop_ambient_camera_rotation()120 121 # 最后移动到另一个角度以更好地观察122 self.move_camera(phi=45 * DEGREES, theta=60 * DEGREES, run_time=2)123 self.wait(2) 讲解
这个视频围绕「全微分的几何意义」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
画面中出现的文字「点(x0+Δx, y0+Δy)到曲面距离为Δz」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。
随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。
观察路径
观察路径可以分三步:先锁定「全微分的几何意义」中的核心对象,尤其留意全微分、切平面、偏导数之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从全微分、切平面、偏导数、局部线性化这些概念进入,继续沿相邻问题观察同一类数学结构。