torus_volume_integral.py
1from manim import *2import numpy as np3 4class TorusVolumeIntegral(ThreeDScene):5 def construct(self):6 # 在开始添加标题7 title = Text("直角坐标计算二重积分", font="SimSun", font_size=36)8 title.to_edge(UP)9 self.add_fixed_in_frame_mobjects(title)10 self.play(Write(title))11 self.wait(1)12 13 # 设置相机角度(初始视角旋转180度)14 self.set_camera_orientation(15 phi=65 * DEGREES, 16 theta=(30 + 260) * DEGREES # 增加180度17 )18 19 # 创建3D坐标系并向左移动20 axes = ThreeDAxes(21 x_range=[0, 4],22 y_range=[0, 2],23 z_range=[0, 4],24 x_length=6,25 y_length=6,26 z_length=4,27 axis_config={28 "color": GREY,29 "include_numbers": False # 移除数字标识30 }31 ).shift(LEFT * 2) # 向左移动3个单位32 33 # 添加坐标轴标签34 x_label = MathTex("x").next_to(axes.x_axis.get_end(), RIGHT)35 y_label = MathTex("y").next_to(axes.y_axis.get_end(), UP)36 z_label = MathTex("z").next_to(axes.z_axis.get_end(), OUT)37 axes_labels = VGroup(x_label, y_label, z_label)38 39 # 显示坐标系和标签40 self.play(Create(axes))41 self.play(Write(axes_labels))42 self.wait(1)43 44 # 创建底面区域的边界曲线45 def curve1(x): return np.sqrt(x) # y = √x46 def curve2(x): return (1/5)*(x-1)**2 # y = (1/5)(x-1)²47 48 x_vals = np.linspace(1, 3, 100)49 curve1_points = [[x, curve1(x), 0] for x in x_vals]50 curve2_points = [[x, curve2(x), 0] for x in x_vals]51 52 # 创建完整的底面区域轮廓53 region_points = [54 *curve1_points, # 上边界:y = √x55 [3, curve2(3), 0], # 右边界:x = 356 *curve2_points[::-1], # 下边界:y = (1/5)(x-1)²57 [1, curve2(1), 0], # 左边界:x = 158 ]59 60 # 创建底面61 region = Polygon(62 *[axes.c2p(*p) for p in region_points],63 color=BLUE,64 fill_opacity=0.365 )66 67 # 创建高度函数68 def height_func(x, y):69 return 2 - (1/10)*(x**2 + y**2)70 71 # 创建立体图形(包括顶面和侧面)72 # 顶面73 def param_surface(u, v):74 x = u75 y = v * curve1(x) + (1-v) * curve2(x)76 return axes.c2p(x, y, height_func(x, y))77 78 top_surface = Surface(79 param_surface,80 u_range=[1, 3],81 v_range=[0, 1],82 resolution=(30, 30),83 fill_opacity=0.7,84 fill_color=BLUE,85 stroke_color=WHITE,86 stroke_width=0.587 )88 89 # 添加曲面方程标注90 surface_equation = MathTex(91 r"z=f(x,y)",92 font_size=32,93 color=BLUE94 ).move_to(95 axes.c2p(2, 1, height_func(2, 1) + 0.5) # 将标注放在曲面上方96 )97 self.play(Write(surface_equation))98 99 # 创建所有侧面100 side_surfaces = VGroup()101 102 # 前侧面(x=1)103 front_surface = Surface(104 lambda u, v: axes.c2p(105 1, # x固定为1106 u, # y从下边界到上边界107 v * height_func(1, u) # z从0到对应高度108 ),109 u_range=[curve2(1), curve1(1)],110 v_range=[0, 1], # 使用参数v来控制高度111 fill_opacity=0, # 完全透明112 fill_color=BLUE_E,113 stroke_color=BLUE_E,114 stroke_width=2115 )116 117 # 后侧面(x=3)118 back_surface = Surface(119 lambda u, v: axes.c2p(120 3, # x固定为3121 u, # y从下边界到上边界122 v * height_func(3, u) # z从0到对应高度123 ),124 u_range=[curve2(3), curve1(3)],125 v_range=[0, 1], # 使用参数v来控制高度126 fill_opacity=0, # 完全透明127 fill_color=BLUE_E,128 stroke_color=BLUE_E,129 stroke_width=2130 )131 132 # 上边界曲面(y = √x)133 upper_surface = Surface(134 lambda u, v: axes.c2p(135 u,136 curve1(u),137 v * height_func(u, curve1(u))138 ),139 u_range=[1, 3],140 v_range=[0, 1],141 fill_opacity=0, # 完全透明142 fill_color=BLUE_E,143 stroke_color=BLUE_E,144 stroke_width=2145 )146 147 # 下边界曲面(y = (1/5)(x-1)²)148 lower_surface = Surface(149 lambda u, v: axes.c2p(150 u,151 curve2(u),152 v * height_func(u, curve2(u))153 ),154 u_range=[1, 3],155 v_range=[0, 1],156 fill_opacity=0, # 完全透明157 fill_color=BLUE_E,158 stroke_color=BLUE_E,159 stroke_width=2160 )161 162 # 底面163 bottom_surface = Surface(164 lambda u, v: axes.c2p(165 u,166 v * curve1(u) + (1-v) * curve2(u),167 0168 ),169 u_range=[1, 3],170 v_range=[0, 1],171 fill_opacity=0, # 完全透明172 fill_color=BLUE,173 stroke_color=BLUE,174 stroke_width=2175 )176 177 side_surfaces.add(front_surface, back_surface, upper_surface, lower_surface, bottom_surface)178 179 # 添加边界函数标注180 boundary_labels = VGroup(181 # 上边界标注182 MathTex(r"y=\varphi_2(x)", font_size=32).next_to(183 axes.c2p(2, curve1(2), 0),184 UP,185 buff=0.2186 ),187 # 下边界标注188 MathTex(r"y=\varphi_1(x)", font_size=32).next_to(189 axes.c2p(2, curve2(2), 0),190 DOWN,191 buff=0.2192 )193 )194 195 # 添加x=a和x=b的边界标注196 x_boundary_labels = VGroup(197 MathTex(r"x=a", font_size=32).next_to(198 axes.c2p(1, 0, 0),199 DOWN,200 buff=0.2201 ),202 MathTex(r"x=b", font_size=32).next_to(203 axes.c2p(3, 0, 0),204 DOWN,205 buff=0.2206 )207 )208 209 # 修改显示顺序210 # 1. 显示顶面211 self.play(Create(top_surface))212 self.wait(1)213 214 # 2. 显示底面215 self.play(Create(bottom_surface))216 self.wait(1)217 218 # 3. 显示边界函数标注219 self.play(220 Write(boundary_labels),221 Write(x_boundary_labels)222 )223 self.wait(1)224 225 # 4. 显示侧面226 self.play(227 Create(front_surface),228 Create(back_surface),229 Create(upper_surface),230 Create(lower_surface)231 )232 self.wait(1)233 234 # 创建切片平面(x=2和x=2.1)235 def create_slice_plane(x_val):236 y_min = curve2(x_val)237 y_max = curve1(x_val)238 239 # 创建填充面240 slice_plane = Surface(241 lambda u, v: axes.c2p(242 x_val,243 u,244 v * height_func(x_val, u)245 ),246 u_range=[y_min, y_max],247 v_range=[0, 1],248 fill_opacity=0.8,249 stroke_width=0250 )251 252 # 创建完整边界253 slice_border = VGroup(254 # 上边界曲线255 ParametricFunction(256 lambda t: axes.c2p(257 x_val,258 t,259 height_func(x_val, t)260 ),261 t_range=[y_min, y_max],262 color=YELLOW,263 stroke_width=2264 ),265 # 下边界(底部)266 ParametricFunction(267 lambda t: axes.c2p(x_val, t, 0),268 t_range=[y_min, y_max],269 color=YELLOW,270 stroke_width=2271 ),272 # 左边界273 Line(274 axes.c2p(x_val, y_min, 0),275 axes.c2p(x_val, y_min, height_func(x_val, y_min)),276 color=YELLOW,277 stroke_width=2278 ),279 # 右边界280 Line(281 axes.c2p(x_val, y_max, 0),282 axes.c2p(x_val, y_max, height_func(x_val, y_max)),283 color=YELLOW,284 stroke_width=2285 )286 )287 288 return VGroup(slice_plane, slice_border)289 290 # 创建两个切片291 slice_1 = create_slice_plane(2.0)292 slice_1[0].set_fill(color=YELLOW) # 设置填充颜色293 slice_2 = create_slice_plane(2.1)294 slice_2[0].set_fill(color=YELLOW) # 设置填充颜色295 296 # 显示立体图形和切片297 # self.play(298 # Create(top_surface),299 # Create(side_surfaces)300 # )301 #self#.wait(1)302 303 self.play(304 Create(slice_1),305 Create(slice_2)306 )307 self.wait(1)308 309 # 修改右侧2D截面显示310 section_axes = Axes(311 x_range=[0, 4],312 y_range=[0, 4],313 x_length=3,314 y_length=4,315 axis_config={316 "color": GREY,317 "include_numbers": False # 移除数字标识318 }319 ).shift(RIGHT * 4)320 321 # 添加2D坐标轴标签322 section_x_label = MathTex("y").next_to(section_axes.x_axis.get_end(), RIGHT)323 section_y_label = MathTex("z").next_to(section_axes.y_axis.get_end(), UP)324 section_labels = VGroup(section_x_label, section_y_label)325 326 # 固定2D坐标系、标签和截面在屏幕上327 self.add_fixed_in_frame_mobjects(section_axes, section_labels)328 329 # 显示2D坐标系和标签330 self.play(331 Create(section_axes),332 Write(section_labels)333 )334 335 # 在新坐标系中绘制完整截面336 def create_section_curve(x_val):337 y_min = curve2(x_val)338 y_max = curve1(x_val)339 340 # 计算z的范围341 z_max = height_func(x_val, y_min) # 计算最大高度342 343 return VGroup(344 # 主曲线(y-z平面上的曲线)345 ParametricFunction(346 lambda t: section_axes.c2p(347 t, # y坐标作为横坐标348 height_func(x_val, t) # z坐标作为纵坐标349 ),350 t_range=[y_min, y_max],351 color=YELLOW,352 stroke_width=2353 ),354 # 底边(y轴上的线段)355 Line(356 section_axes.c2p(y_min, 0), # 从最小y值开始357 section_axes.c2p(y_max, 0), # 到最大y值结束358 color=YELLOW,359 stroke_width=2360 ),361 # 左边界(垂直线)362 Line(363 section_axes.c2p(y_min, 0),364 section_axes.c2p(y_min, height_func(x_val, y_min)),365 color=YELLOW,366 stroke_width=2367 ),368 # 右边界(垂直线)369 Line(370 section_axes.c2p(y_max, 0),371 section_axes.c2p(y_max, height_func(x_val, y_max)),372 color=YELLOW,373 stroke_width=2374 )375 )376 377 # 定义切片位置和边界378 x_val = 2.0379 y_min = curve2(x_val) # φ₁(x₀)380 y_max = curve1(x_val) # φ₂(x₀)381 382 # 创建x=2处的截面曲线383 section_curve = create_section_curve(2.0)384 self.add_fixed_in_frame_mobjects(section_curve)385 386 # 显示截面387 self.play(Create(section_curve))388 389 # 添加z=f(x₀,y)标注390 section_equation = MathTex(391 r"z=f(x_0,y)",392 font_size=28,393 color=YELLOW394 ).move_to(395 section_axes.c2p(396 (y_min + y_max)/2, # y坐标取中点397 height_func(x_val, (y_min + y_max)/2) + 0.5 # 曲线上方0.5个单位398 )399 )400 self.add_fixed_in_frame_mobjects(section_equation)401 self.play(Write(section_equation))402 403 # 添加φ₁(x₀)和φ₂(x₀)标注404 phi_labels = VGroup(405 # φ₁(x₀)标注406 MathTex(r"\varphi_1(x_0)", font_size=20).next_to(407 section_axes.c2p(y_min, 0),408 LEFT,409 buff=0.1410 ),411 # φ₂(x₀)标注412 MathTex(r"\varphi_2(x_0)", font_size=20).next_to(413 section_axes.c2p(y_max, 0),414 LEFT,415 buff=0.1416 )417 )418 self.add_fixed_in_frame_mobjects(phi_labels)419 self.play(Write(phi_labels))420 421 # 修改标注文字422 section_label = VGroup(423 MathTex(r"x=x_0", font_size=24),424 Text("处的截面", font="SimSun", font_size=24)425 ).arrange(RIGHT, buff=0.1).next_to(section_axes, UP)426 self.add_fixed_in_frame_mobjects(section_label)427 self.play(Write(section_label))428 429 # 在切片内添加A(x₀)标注430 area_label = MathTex(431 r"A(x_0)",432 font_size=28,433 color=YELLOW434 ).move_to(435 section_axes.c2p(436 (y_min + y_max)/2, # y坐标取中点437 height_func(x_val, (y_min + y_max)/2)/2 # z坐标取高度的一半438 )439 )440 self.add_fixed_in_frame_mobjects(area_label)441 self.play(Write(area_label))442 443 # 添加面积计算公式444 area_formula = MathTex(445 r"A(x_0) = \int_{\varphi_1(x_0)}^{\varphi_2(x_0)} f(x_0,y)dy",446 font_size=28447 ).next_to(section_axes, DOWN, buff=0.5) # 在坐标系下方显示448 self.add_fixed_in_frame_mobjects(area_formula)449 self.play(Write(area_formula))450 451 # 修改积分公式为两行452 conversion_formulas = VGroup(453 # 第一行:二重积分454 MathTex(455 r"\iint_D f(x,y)dxdy",456 font_size=32457 ),458 # 第二行:等号和累次积分459 VGroup(460 MathTex(r"=", font_size=32),461 MathTex(462 r"\int_a^b \left(\int_{\varphi_1(x)}^{\varphi_2(x)} f(x,y)dy\right)dx",463 font_size=32464 )465 ).arrange(RIGHT, buff=0.3)466 ).arrange(DOWN, buff=0.3).to_corner(DL)467 468 # 显示转换公式469 self.add_fixed_in_frame_mobjects(conversion_formulas)470 self.play(Write(conversion_formulas[0]))471 self.wait(0.5)472 self.play(Write(conversion_formulas[1]))473 self.wait(1)474 475 # 最后等待几秒476 self.wait(3) 讲解
这个视频围绕「直角坐标计算二重积分」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
画面中出现的文字「直角坐标计算二重积分」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。
随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。
核心公式可以写成:
这类公式可以和画面中的符号一一对应。
观察路径
观察路径可以分三步:先锁定「直角坐标计算二重积分」中的核心对象,尤其留意二重积分、累次积分、曲顶柱体之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从二重积分、累次积分、曲顶柱体、坐标网格这些概念进入,继续沿相邻问题观察同一类数学结构。