double_integral_concept.py
1from manim import *2 3class DoubleIntegralConcept(ThreeDScene):4 def construct(self):5 # 先显示标题6 title = Text("曲顶柱体的体积计算", font="SimSun").scale(0.8)7 title.to_corner(UR, buff=0.5)8 self.add_fixed_in_frame_mobjects(title)9 self.play(Write(title))10 11 self.wait(1) # 给观众时间阅读标题12 13 # 创建3D坐标系14 axes = ThreeDAxes(15 x_range=[0, 2, 0.5],16 y_range=[0, 2, 0.5],17 z_range=[0, 4, 1],18 x_length=6,19 y_length=6,20 z_length=421 ).shift(RIGHT* 4) # 将坐标系向下平移2个单位22 23 # 定义函数 z = f(x, y) = x^2 + y^224 def func(x, y):25 return x**2 + y**226 27 # 创建曲面28 surface = Surface(29 lambda u, v: axes.c2p(u, v, func(u, v)),30 u_range=[0, 2],31 v_range=[0, 2],32 resolution=(20, 20),33 fill_color=BLUE,34 fill_opacity=0.6,35 should_make_jagged=True36 )37 38 # 创建底部投影区域39 projection = Surface(40 lambda u, v: axes.c2p(u, v, 0),41 u_range=[0, 2],42 v_range=[0, 2],43 fill_color=GREEN,44 fill_opacity=0.3,45 should_make_jagged=True46 )47 48 # 设置网格参数49 dx = dy = 0.2 # 网格大小改为0.250 nx = ny = int(2 / dx) # 计算网格数量51 52 # 创建网格线53 grid_lines = VGroup()54 55 # x方向的网格线56 for i in range(nx + 1):57 x = i * dx58 line = Line(59 start=axes.c2p(x, 0, 0),60 end=axes.c2p(x, 2, 0),61 color=WHITE,62 stroke_width=263 )64 grid_lines.add(line)65 66 # y方向的网格线67 for j in range(ny + 1):68 y = j * dy69 line = Line(70 start=axes.c2p(0, y, 0),71 end=axes.c2p(2, y, 0),72 color=WHITE,73 stroke_width=274 )75 grid_lines.add(line)76 77 # 创建坐标轴标签78 x_label = axes.get_x_axis_label("x")79 y_label = axes.get_y_axis_label("y")80 z_label = axes.get_z_axis_label("z")81 labels = VGroup(x_label, y_label, z_label)82 83 # 设置相机角度84 self.set_camera_orientation(phi=60 * DEGREES, theta=45 * DEGREES)85 86 # 显示坐标系和标签87 self.play(88 Create(axes),89 Create(labels),90 run_time=191 )92 93 # 显示曲面和投影94 self.play(95 Create(surface),96 Create(projection),97 Create(grid_lines),98 run_time=299 )100 self.wait(1)101 102 # 创建完整的曲顶柱体103 full_prism = VGroup()104 105 # 创建四个竖直面106 vertices = [107 [0, 0, 0],108 [2, 0, 0],109 [2, 2, 0],110 [0, 2, 0]111 ]112 for i in range(4):113 start = vertices[i]114 end = vertices[(i+1)%4]115 points = []116 # 底边两点117 points.append(axes.c2p(*start))118 points.append(axes.c2p(*end))119 # 上边两点(对应曲面上的点)120 points.append(axes.c2p(end[0], end[1], func(end[0], end[1])))121 points.append(axes.c2p(start[0], start[1], func(start[0], start[1])))122 123 side = Polygon(124 *[np.array(p) for p in points],125 fill_color=YELLOW,126 fill_opacity=0.3,127 stroke_width=1,128 stroke_color=WHITE129 )130 full_prism.add(side)131 132 # 显示完整曲顶柱体133 self.play(134 Create(full_prism),135 run_time=1136 )137 self.wait(1)138 139 # 淡出完整曲顶柱体,准备显示分割的小柱体140 self.play(141 FadeOut(full_prism),142 run_time=1143 )144 145 # 创建并显示小柱体146 columns = VGroup()147 148 for i in range(nx):149 for j in range(ny):150 x = i * dx151 y = j * dy152 153 # 计算底面中心点的高度154 center_x = x + dx/2155 center_y = y + dy/2156 height = func(center_x, center_y) # 使用中心点的函数值157 158 # 创建柱体159 column = Prism(160 dimensions=[0.6, 0.6, height],161 fill_color=YELLOW,162 fill_opacity=0.1,163 stroke_width=1,164 stroke_color=WHITE165 )166 167 # 调整柱体位置168 column.move_to(169 axes.c2p(x + dx/2, y + dy/2, height/2),170 aligned_edge=ORIGIN171 )172 columns.add(column)173 174 # 添加顶面 - 使用中心点高度175 top_vertices = [176 axes.c2p(x, y, height),177 axes.c2p(x + dx, y, height),178 axes.c2p(x + dx, y + dy, height),179 axes.c2p(x, y + dy, height)180 ]181 top_face = Polygon(182 *[np.array(v) for v in top_vertices],183 fill_color=BLUE,184 fill_opacity=0.4,185 stroke_width=1186 )187 columns.add(top_face)188 189 # 分批显示小柱体 - 减少批次以使显示更连续190 n_batches = 3 # 减少批次数量191 columns_list = [columns[i::n_batches] for i in range(n_batches)]192 for batch in columns_list:193 self.play(194 Create(batch),195 run_time=0.8 # 增加运行时间使动画更流畅196 )197 198 # 在右侧创建一个单独的示例柱体199 # 选择一个合适的位置(比如x=1, y=1附近的柱体)200 sample_x = dx * 5201 sample_y = dy * 5202 sample_center_x = sample_x + dx/2203 sample_center_y = sample_y + dy/2204 sample_height = func(sample_center_x, sample_center_y)205 206 # 创建一个新的坐标系用于示例 - 位置调整到y轴延长线上207 sample_axes = ThreeDAxes(208 x_range=[-0.5, 1.5, 0.5],209 y_range=[-0.5, 1.5, 0.5],210 z_range=[0, 4, 1],211 x_length=3,212 y_length=3,213 z_length=4214 ).shift(LEFT * 1 + UP * 4) # 调整位置到y轴延长线上215 216 # 创建示例柱体217 sample_column = Prism(218 dimensions=[0.6, 0.6, sample_height],219 fill_color=YELLOW,220 fill_opacity=0.3,221 stroke_width=2, # 示例柱体的边界线可以稍粗一些222 stroke_color=WHITE223 ).move_to(sample_axes.c2p(0.5, 0.5, sample_height/2))224 225 # 创建穿过柱体的曲面部分226 sample_surface = Surface(227 lambda u, v: sample_axes.c2p(228 u, v, 229 func(sample_x + u*dx, sample_y + v*dy)230 ),231 u_range=[0.3, 0.7],232 v_range=[0.3, 0.7],233 resolution=(20, 20),234 fill_color=BLUE,235 fill_opacity=0.8236 )237 238 # 添加连接线,表示这是放大的部分239 start_point = axes.c2p(sample_x, sample_y, func(sample_x, sample_y))240 end_point = sample_axes.c2p(0.6, 0.6, 0)241 connection_line = DashedLine(242 start_point,243 end_point,244 dash_length=0.1,245 color=RED # 改为红色246 )247 self.play(Create(connection_line))248 249 # 添加示例图形250 self.play(251 Create(sample_axes),252 run_time=1253 )254 self.play(255 Create(sample_column),256 Create(sample_surface),257 run_time=1258 )259 260 # 添加说明标签261 sample_label = Text("局部放大", font="SimSun").scale(0.6)262 sample_label.next_to(sample_axes, UP, buff=0.2)263 self.add_fixed_in_frame_mobjects(sample_label)264 self.play(Write(sample_label))265 266 # 在示例坐标系底面添加dσ符号267 d_sigma = MathTex(r"d\sigma_i").scale(0.6)268 d_sigma.move_to(269 sample_axes.c2p(0.5, 0.5, 0) # 放在xy平面上270 )271 272 # 添加小柱体体积计算公式 - 放在xoy平面上273 volume_formula = MathTex(274 r"dV = f(\xi_i, \zeta_i)d\sigma"275 ).scale(0.9)276 277 # 将公式放在xoy平面上,不再旋转278 volume_formula.move_to(279 sample_axes.c2p(0.8, 0.3, 0) # 放在xoy平面上,调整位置避免重叠280 )281 282 # 显示公式283 self.play(284 Write(d_sigma),285 Write(volume_formula),286 run_time=1287 )288 289 self.wait(1) # 给观众一些时间阅读公式290 291 # 开始相机旋转292 self.begin_ambient_camera_rotation(rate=0.2)293 self.wait(8)294 self.stop_ambient_camera_rotation()295 296 self.wait(2) 297 298 # 移除之前的fixed_in_frame设置299 # self.add_fixed_in_frame_mobjects(d_sigma, volume_formula) 300 301 # 停止相机旋转后,显示二重积分定义式302 integral_formula = MathTex(303 r"V = \iint_D f(x,y)\,d\sigma = \lim_{||T||\to 0} \sum_{i=1}^n f(\xi_i, \zeta_i)\Delta \sigma_i",304 ).scale(0.7)305 306 # 将公式放在屏幕底部并向右平移307 integral_formula.to_edge(DOWN, buff=0.5).shift(RIGHT * 3)308 309 # 添加为固定在屏幕上的元素310 self.add_fixed_in_frame_mobjects(integral_formula)311 312 # 渐变显示公式313 self.play(314 FadeIn(integral_formula),315 run_time=2316 )317 318 self.wait(2) 讲解
这个视频围绕「曲顶柱体的体积计算」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
画面中出现的文字「曲顶柱体的体积计算」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。
随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。
核心公式可以写成:
这类公式可以和画面中的符号一一对应。
观察路径
观察路径可以分三步:先锁定「曲顶柱体的体积计算」中的核心对象,尤其留意二重积分、曲顶柱体体积、曲顶柱体之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从二重积分、曲顶柱体体积、曲顶柱体、黎曼和极限这些概念进入,继续沿相邻问题观察同一类数学结构。