surface_flux.py
1from manim import *2import numpy as np3 4config.tex_template = TexTemplateLibrary.ctex5config.tex_template.add_to_preamble(r"\setCJKmainfont{STSong}")6 7class SurfaceFlux(ThreeDScene):8 def construct(self):9 # 创建标题10 title = Text("向量场通量的计算", font="STSong", font_size=48)11 self.add_fixed_in_frame_mobjects(title)12 self.play(Write(title))13 self.wait(1)14 self.play(FadeOut(title))15 16 # 创建3D坐标系17 left_axes = ThreeDAxes(18 x_range=[-2, 2, 1],19 y_range=[-2, 2, 1],20 z_range=[0, 2, 1],21 x_length=4, # 缩小左侧坐标系22 y_length=4,23 z_length=424 ).shift(LEFT * 3) # 向左移动25 26 right_axes = ThreeDAxes(27 x_range=[-2, 2, 1],28 y_range=[-2, 2, 1],29 z_range=[0, 2, 1],30 x_length=4, # 右侧坐标系大小相同31 y_length=4,32 z_length=433 ).shift(RIGHT * 3) # 向右移动34 35 # 设置一个稍微倾斜的固定视角,便于同时观察平面和曲面36 self.set_camera_orientation(37 phi=75*DEGREES, # 稍微抬高视角38 theta=270*DEGREES # 旋转270度(相当于-90度),从另一侧观察39 )40 41 # 显示坐标系42 self.play(Create(left_axes), Create(right_axes))43 self.wait(2)44 45 # 第一部分:平面通量演示46 # 创建平面 z = 147 plane = Surface(48 lambda u, v: left_axes.c2p(u, v, 0), # 改为z=0平面49 u_range=[-2, 2],50 v_range=[-2, 2],51 resolution=(10, 10),52 fill_opacity=0.5,53 color=BLUE54 )55 56 self.play(Create(plane))57 self.wait(1)58 59 # 创建平面上的法向量场(只显示几个代表性向量)60 normal_vectors = VGroup()61 for u, v in [(-0.8, -0.8), (-0.8, 0.8), (0.8, -0.8), (0.8, 0.8), (0, 0)]:62 point = left_axes.c2p(u, v, 0) # 改为z=0平面上的点63 normal = np.array([0, 0, 1])64 vector = Arrow3D(65 start=point,66 end=point + normal * 0.5,67 color=YELLOW,68 thickness=0.0269 )70 normal_vectors.add(vector)71 72 # 显示法向量场73 self.play(74 Create(normal_vectors),75 run_time=176 )77 self.wait(2)78 79 # 显示法向量标注80 normal_text = MathTex(81 "\\text{法向量}\\,\\vec{n}",82 color=YELLOW83 ).scale(0.6).to_corner(UL)84 self.add_fixed_in_frame_mobjects(normal_text)85 self.play(Write(normal_text))86 self.wait(1)87 88 # 创建向量场 F = (0, 0, z)89 def get_vector_field(u, v, z):90 return np.array([1, 1, 1]) # 统一方向的向量场,指向(1,1,1)91 92 field_vectors = VGroup()93 for u, v in [(-0.8, -0.8), (-0.8, 0.8), (0.8, -0.8), (0.8, 0.8), (0, 0)]:94 point = left_axes.c2p(u, v, 0)95 field = get_vector_field(u, v, 0)96 # 归一化向量场97 norm = np.linalg.norm(field)98 if norm > 0:99 field = field / norm * 0.5100 vector = Arrow3D(101 start=point,102 end=point + field,103 color=RED,104 thickness=0.03105 )106 vector.set_fill(RED, opacity=0.8)107 field_vectors.add(vector)108 109 # 显示向量场110 self.play(111 *[Create(vec) for vec in field_vectors],112 run_time=1113 )114 self.wait(2)115 116 # 显示向量场标注117 field_desc = MathTex(118 "\\text{向量场}\\,\\vec{F}",119 color=RED120 ).scale(0.6).next_to(normal_text, DOWN, buff=0.3)121 self.add_fixed_in_frame_mobjects(field_desc)122 self.play(Write(field_desc))123 self.wait(1)124 125 # 显示平面通量计算公式126 flux_formula = MathTex(127 "\\text{通量}\\Phi = \\vec{F} \\cdot \\vec{n} \\, S"128 ).scale(0.6).next_to(field_desc, DOWN, buff=0.3)129 self.add_fixed_in_frame_mobjects(flux_formula)130 self.play(Write(flux_formula))131 self.wait(1)132 133 # 第二部分:曲面通量演示134 # 创建抛物面 z = 0.1(x² + y²)135 surface = Surface(136 lambda u, v: right_axes.c2p(u, v, 0.1*(u**2 + v**2)),137 u_range=[-2, 2],138 v_range=[-2, 2],139 resolution=(10, 10),140 checkerboard_colors=[BLUE_D, BLUE_E],141 fill_opacity=0.7142 )143 self.play(Create(surface))144 self.wait(1)145 146 # 定义法向量计算函数147 def get_normal_vector(u, v):148 # 计算曲面在点(u,v)处的法向量149 tangent_u = np.array([1, 0, 0.2*u]) # 修改系数150 tangent_v = np.array([0, 1, 0.2*v])151 normal = np.cross(tangent_u, tangent_v)152 normal = normal / np.linalg.norm(normal)153 return normal154 155 # 定义向量场函数156 def get_vector_field(u, v):157 point = np.array([u, v, 0.1*(u**2 + v**2)]) # 修改系数158 return point159 160 # 创建法向量场161 normal_vectors = VGroup()162 for u, v in [(-0.8, -0.8), (-0.8, 0.8), (0.8, -0.8), (0.8, 0.8), (0, 0)]:163 point = right_axes.c2p(u, v, 0.1*(u**2 + v**2))164 normal = get_normal_vector(u, v)165 vector = Arrow3D(166 start=point,167 end=point + normal * 0.5,168 color=YELLOW,169 thickness=0.03170 )171 vector.set_fill(YELLOW, opacity=0.8)172 normal_vectors.add(vector)173 174 # 修改显示法向量场的方式175 self.play(176 *[Create(vec) for vec in normal_vectors],177 run_time=1178 )179 self.wait(0.3)180 181 # 创建向量场(同样减少向量数量)182 field_vectors = VGroup()183 for u, v in [(-0.8, -0.8), (-0.8, 0.8), (0.8, -0.8), (0.8, 0.8), (0, 0)]:184 point = right_axes.c2p(u, v, 0.1*(u**2 + v**2))185 field = get_vector_field(u, v)186 # 归一化向量场,避免太长187 norm = np.linalg.norm(field)188 if norm > 0:189 field = field / norm * 0.5190 vector = Arrow3D(191 start=point,192 end=point + field,193 color=RED,194 thickness=0.03195 )196 # 设置向量的填充不透明度197 vector.set_fill(RED, opacity=0.8)198 field_vectors.add(vector)199 200 # 修改显示向量场的方式201 self.play(202 *[Create(vec) for vec in field_vectors],203 run_time=1204 )205 self.wait(2)206 207 # 添加向量场说明208 field_desc = MathTex(209 "\\text{法向量}\\,\\vec{n}",210 color=YELLOW211 ).scale(0.7).to_corner(UR)212 self.add_fixed_in_frame_mobjects(field_desc)213 214 field_desc2 = MathTex(215 "\\text{向量场}\\,\\vec{F}",216 color=RED217 ).scale(0.7).next_to(field_desc, DOWN, buff=0.3)218 self.add_fixed_in_frame_mobjects(field_desc2)219 220 self.play(221 Write(field_desc),222 Write(field_desc2)223 )224 self.wait(1)225 226 # 添加微元演示227 micro_u, micro_v = 1.0, 1.0 # 选择曲面上更倾斜的位置228 du, dv = 0.2, 0.2 # 微元大小229 230 # 创建放大的微元框架231 micro_frame = ThreeDAxes(232 x_range=[-0.5, 0.5, 0.2],233 y_range=[-0.5, 0.5, 0.2],234 z_range=[0, 0.5, 0.1],235 x_length=2,236 y_length=2,237 z_length=1238 ).shift(DOWN * 6).set_opacity(0)239 240 # 获取原曲面上微元中心点的坐标241 original_point = right_axes.c2p(micro_u, micro_v, 0.1*(micro_u**2 + micro_v**2))242 243 # 创建并显示放大框和连接线244 zoom_box = Rectangle(245 height=1,246 width=1,247 color=YELLOW248 ).set_fill(BLACK, opacity=0.1)249 zoom_box.move_to(original_point)250 251 zoom_lines = VGroup(252 Line(253 original_point,254 micro_frame.get_origin(),255 color=YELLOW,256 stroke_width=0.5257 )258 )259 260 # 计算微元处的法向量,用于确定微元的倾斜角度261 normal = get_normal_vector(micro_u, micro_v)262 # 计算微元平面的旋转矩阵263 z_axis = np.array([0, 0, 1])264 rotation_axis = np.cross(z_axis, normal)265 rotation_angle = np.arccos(np.dot(z_axis, normal))266 267 # 旋转微元框架268 if np.linalg.norm(rotation_axis) > 1e-6: # 避免零向量269 rotation_axis = rotation_axis / np.linalg.norm(rotation_axis)270 micro_frame.rotate(angle=rotation_angle, axis=rotation_axis)271 272 # 在原曲面上标记微元区域273 highlight_box = Surface(274 lambda u, v: right_axes.c2p(275 micro_u + u*du, micro_v + v*du, 276 0.1*((micro_u + u*du)**2 + (micro_v + v*du)**2)277 ),278 u_range=[-0.5, 0.5],279 v_range=[-0.5, 0.5],280 resolution=(2, 2),281 fill_opacity=0.3,282 color=YELLOW283 )284 285 self.play(286 Create(zoom_box),287 Create(zoom_lines),288 Create(highlight_box)289 )290 self.wait(1)291 292 # 创建微元区域(保持与原曲面相同的倾斜角度)293 micro_surface = Surface(294 lambda u, v: micro_frame.c2p(295 u, v, 296 0 # 在旋转后的坐标系中,微元近似为平面297 ),298 u_range=[-0.5, 0.5],299 v_range=[-0.5, 0.5],300 resolution=(2, 2),301 fill_opacity=0.8,302 color=YELLOW_A303 )304 305 self.play(306 Create(micro_surface)307 )308 self.wait(1)309 310 # 在微元上显示向量场和法向量311 center_point = micro_frame.c2p(0, 0, 0)312 micro_normal = Arrow3D(313 start=center_point,314 end=center_point + normal * 1,315 color=YELLOW,316 thickness=0.03317 )318 319 field = get_vector_field(micro_u, micro_v)320 field = field / np.linalg.norm(field) * 1321 micro_field = Arrow3D(322 start=center_point,323 end=center_point + field,324 color=RED,325 thickness=0.03326 )327 328 self.play(329 Create(micro_normal),330 Create(micro_field)331 )332 self.wait(2)333 334 # 添加说明文字335 micro_text = Text(336 "曲面微元可近似为平面",337 font="STSong",338 font_size=24339 ).shift(DOWN * 2.5+RIGHT*3.5)340 self.add_fixed_in_frame_mobjects(micro_text)341 342 # 显示平面近似343 self.play(344 Write(micro_text),345 run_time=1346 )347 self.wait(2)348 349 # 显示微元通量计算公式和最后的结论(在同一行)350 micro_flux_formula = MathTex(351 "d\\Phi = \\vec{F} \\cdot \\vec{n} \\, dS"352 ).scale(0.6).shift(DOWN * 3.5).shift(LEFT * 2) # 放在左侧353 self.add_fixed_in_frame_mobjects(micro_flux_formula)354 self.play(Write(micro_flux_formula))355 self.wait(2)356 357 358 359 # 最后的结论360 conclusion = MathTex(361 "\\text{通量} = \\iint_S \\vec{F} \\cdot \\vec{n} \\, dS"362 ).scale(0.6).shift(DOWN * 3.5).shift(RIGHT * 2) # 放在右侧363 self.add_fixed_in_frame_mobjects(conclusion)364 self.play(Write(conclusion))365 self.wait(3)366 367 # 最后多等待一会,让观众有时间观察368 self.wait(1)369 370def main():371 import os372 os.system("manim -pqh surface_flux.py SurfaceFlux")373 374if __name__ == "__main__":375 main() 讲解
这个视频围绕「向量场通量的计算」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
画面中出现的文字「向量场通量的计算」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。
观察路径
观察路径可以分三步:先锁定「向量场通量的计算」中的核心对象,尤其留意通量、曲面积分、向量场之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从通量、曲面积分、向量场、法向量、微元法这些概念进入,继续沿相邻问题观察同一类数学结构。