volume_calculation_demo.py
1from manim import *2import numpy as np3 4class VolumeCalculationDemo(ThreeDScene):5 def construct(self):6 # 第一步:清屏显示题目7 problem_title = Text("求立体体积", font_size=32, color=YELLOW)8 problem_desc = Text("求由以下两曲面围成的立体体积", font_size=20, color=WHITE)9 surface_eq1 = MathTex("z = x^2 + y^2", color=BLUE).scale(1.2)10 surface_eq2 = MathTex("z = x + y", color=RED).scale(1.2)11 12 # 居中显示题目13 problem_title.move_to(ORIGIN + UP * 1.5)14 problem_desc.next_to(problem_title, DOWN, buff=0.5)15 surface_eq1.next_to(problem_desc, DOWN, buff=0.8)16 surface_eq2.next_to(surface_eq1, DOWN, buff=0.4)17 18 # 动画1:显示题目和曲面方程19 self.play(Write(problem_title))20 self.wait(1)21 self.play(Write(problem_desc))22 self.wait(1)23 self.play(Write(surface_eq1))24 self.wait(0.8)25 self.play(Write(surface_eq2))26 self.wait(2)27 28 # 动画2:将题目移到右上角保持显示(放大字体)29 title_corner = Text("求立体体积", font_size=24, color=YELLOW).to_corner(UR).shift(LEFT * 0.5 + DOWN * 0.5)30 desc_corner = Text("z=x²+y²与z=x+y围成的立体", font_size=16, color=WHITE).next_to(title_corner, DOWN, buff=0.2)31 32 self.play(33 Transform(problem_title, title_corner),34 Transform(problem_desc, desc_corner),35 FadeOut(surface_eq1),36 FadeOut(surface_eq2)37 )38 self.wait(0.5)39 40 # 设置3D相机41 self.set_camera_orientation(phi=75 * DEGREES, theta=45 * DEGREES)42 43 # 固定题目在屏幕上44 self.add_fixed_in_frame_mobjects(problem_title, problem_desc)45 46 # 创建坐标轴(全屏居中)47 axes = ThreeDAxes(48 x_range=[-2, 2, 0.5],49 y_range=[-2, 2, 0.5],50 z_range=[-1, 3, 0.5],51 x_length=6,52 y_length=6,53 z_length=654 )55 56 # 坐标轴标签57 x_label = axes.get_x_axis_label("x").scale(0.8)58 y_label = axes.get_y_axis_label("y").scale(0.8)59 z_label = axes.get_z_axis_label("z").scale(0.8)60 61 # 动画3:创建坐标轴62 self.play(Create(axes), Write(x_label), Write(y_label), Write(z_label))63 self.wait(1)64 65 # 定义曲面函数66 def paraboloid(u, v):67 return np.array([u, v, u**2 + v**2])68 69 def plane(u, v):70 return np.array([u, v, u + v])71 72 # 创建抛物面 z = x² + y²73 paraboloid_surface = Surface(74 lambda u, v: paraboloid(u, v),75 u_range=[-1.5, 1.5],76 v_range=[-1.5, 1.5],77 resolution=(20, 20),78 fill_color=BLUE,79 fill_opacity=0.7,80 stroke_color=BLUE,81 stroke_width=0.582 )83 84 # 创建平面 z = x + y85 plane_surface = Surface(86 lambda u, v: plane(u, v),87 u_range=[-1.5, 1.5],88 v_range=[-1.5, 1.5],89 resolution=(15, 15),90 fill_color=RED,91 fill_opacity=0.6,92 stroke_color=RED,93 stroke_width=0.594 )95 96 # 创建角落显示的公式(左上角,放大字体)97 surface_eq1_corner = MathTex("z = x^2 + y^2", color=BLUE).scale(0.9).to_corner(UL).shift(DOWN * 0.3 + RIGHT * 0.2)98 surface_eq2_corner = MathTex("z = x + y", color=RED).scale(0.9).next_to(surface_eq1_corner, DOWN, buff=0.2)99 100 # 动画4:显示抛物面和公式101 self.add_fixed_in_frame_mobjects(surface_eq1_corner)102 self.play(Create(paraboloid_surface), Write(surface_eq1_corner))103 self.wait(1)104 105 # 动画5:显示平面和公式106 self.add_fixed_in_frame_mobjects(surface_eq2_corner)107 self.play(Create(plane_surface), Write(surface_eq2_corner))108 self.wait(1)109 110 # 动画6:旋转视角查看两个曲面111 self.move_camera(phi=60 * DEGREES, theta=120 * DEGREES, run_time=3)112 self.wait(1)113 114 # 计算交线:x² + y² = x + y115 # 即 x² - x + y² - y = 0116 # 完成平方:(x - 1/2)² + (y - 1/2)² = 1/2117 118 # 创建交线119 intersection_curve = ParametricFunction(120 lambda t: np.array([121 0.5 + np.sqrt(0.5) * np.cos(t),122 0.5 + np.sqrt(0.5) * np.sin(t),123 0.5 + np.sqrt(0.5) * np.cos(t) + 0.5 + np.sqrt(0.5) * np.sin(t)124 ]),125 t_range=[0, 2*PI],126 color=YELLOW,127 stroke_width=4128 )129 130 # 创建角落显示的交线公式(移动到右边投影区域下面)131 intersection_eq_corner = MathTex("x^2 + y^2 = x + y", color=YELLOW).scale(0.8).to_corner(UR).shift(LEFT * 1.2 + DOWN * 2.0)132 intersection_simplified_corner = MathTex(r"(x-\frac{1}{2})^2 + (y-\frac{1}{2})^2 = \frac{1}{2}", color=YELLOW).scale(0.7).next_to(intersection_eq_corner, DOWN, buff=0.15)133 134 # 动画7:显示交线135 self.play(Create(intersection_curve))136 self.wait(1)137 138 # 动画8:着重显示围成的立体区域139 # 突出显示两个曲面围成的立体140 self.play(141 paraboloid_surface.animate.set_fill_opacity(0.8),142 plane_surface.animate.set_fill_opacity(0.7),143 intersection_curve.animate.set_stroke(width=6),144 run_time=2145 )146 self.wait(1)147 148 # 动画9:先显示投影到xy平面149 # 创建投影圆150 projection_circle = Circle(151 radius=np.sqrt(0.5),152 color=GREEN,153 fill_opacity=0.4,154 stroke_color=GREEN,155 stroke_width=3156 ).shift([0.5, 0.5, 0])157 158 # 投影说明放在题目正下方(左移)159 projection_label = Text("投影区域边界", font_size=20, color=GREEN).to_corner(UR).shift(LEFT * 1.2 + DOWN * 1.5)160 self.add_fixed_in_frame_mobjects(projection_label)161 162 self.play(Create(projection_circle), Write(projection_label))163 self.wait(2)164 165 # 动画10:然后显示交线方程166 self.add_fixed_in_frame_mobjects(intersection_eq_corner)167 self.play(Write(intersection_eq_corner))168 self.wait(1)169 170 # 显示交线简化形式171 self.add_fixed_in_frame_mobjects(intersection_simplified_corner)172 self.play(Write(intersection_simplified_corner))173 self.wait(1)174 175 # 动画11:然后演示z上下限的确定方法176 # 创建一个垂直的线段来显示z的范围177 sample_point = [0.3, 0.3, 0] # 在积分区域内选择一个点178 z_lower = sample_point[0]**2 + sample_point[1]**2 # 抛物面的z值179 z_upper = sample_point[0] + sample_point[1] # 平面的z值180 181 # 创建垂直线段182 z_line = Line(183 [sample_point[0], sample_point[1], z_lower],184 [sample_point[0], sample_point[1], z_upper],185 color=ORANGE,186 stroke_width=6187 )188 189 # 标记点190 lower_point = Dot([sample_point[0], sample_point[1], z_lower], color=BLUE, radius=0.1)191 upper_point = Dot([sample_point[0], sample_point[1], z_upper], color=RED, radius=0.1)192 193 # 说明文字(在投影区域下方显示,左移)194 z_demo_text = Text("z的上下限演示", font_size=18, color=ORANGE).to_corner(UR).shift(LEFT * 1.2 + DOWN * 3.5)195 self.add_fixed_in_frame_mobjects(z_demo_text)196 197 self.play(198 Write(z_demo_text),199 Create(z_line),200 Create(lower_point),201 Create(upper_point)202 )203 self.wait(2)204 205 # z的上下限说明(在z演示下方显示,左移)206 z_limit_text = Text("确定z的上下限:", font_size=18, color=ORANGE).to_corner(UR).shift(LEFT * 1.2 + DOWN * 4.5)207 z_limit_label1 = Text("下限:", font_size=16, color=BLUE).next_to(z_limit_text, DOWN, buff=0.2)208 z_limit_eq = MathTex(r"z = x^2 + y^2").scale(0.8).next_to(z_limit_label1, RIGHT, buff=0.2)209 z_limit_label2 = Text("上限:", font_size=16, color=RED).next_to(z_limit_label1, DOWN, buff=0.15)210 z_limit_eq2 = MathTex(r"z = x + y").scale(0.8).next_to(z_limit_label2, RIGHT, buff=0.2)211 212 # 动画12:显示z上下限说明(右边)213 self.add_fixed_in_frame_mobjects(z_limit_text)214 self.play(Write(z_limit_text))215 self.wait(1)216 217 # 动画13:显示z下限218 self.add_fixed_in_frame_mobjects(z_limit_label1, z_limit_eq)219 self.play(Write(z_limit_label1), Write(z_limit_eq))220 self.wait(1)221 222 # 动画14:显示z上限223 self.add_fixed_in_frame_mobjects(z_limit_label2, z_limit_eq2)224 self.play(Write(z_limit_label2), Write(z_limit_eq2))225 self.wait(1)226 227 # 创建详细的计算过程公式(左下角上移并右移0.5个单位)228 step1 = Text("计算步骤:", font_size=18, color=YELLOW).to_corner(DL).shift(UP * 4.5 + RIGHT * 1.5)229 step2 = MathTex(r"V = \iint_D \int_{x^2+y^2}^{x+y} dz \, dx \, dy").scale(0.6).next_to(step1, DOWN, buff=0.15)230 step3 = MathTex(r"D: (x-\frac{1}{2})^2 + (y-\frac{1}{2})^2 \leq \frac{1}{2}").scale(0.55).next_to(step2, DOWN, buff=0.1)231 step4 = MathTex(r"V = \iint_D (x+y-x^2-y^2) \, dx \, dy").scale(0.6).next_to(step3, DOWN, buff=0.1)232 step5 = MathTex(r"x = \frac{1}{2} + r\cos\theta, y = \frac{1}{2} + r\sin\theta").scale(0.55).next_to(step4, DOWN, buff=0.1)233 step6 = MathTex(r"V = \int_0^{2\pi} \int_0^{\sqrt{1/2}} r \cdot (\frac{1}{2} - r^2) \, dr \, d\theta").scale(0.6).next_to(step5, DOWN, buff=0.1)234 step7 = MathTex(r"V = \frac{\pi}{8}", color=GREEN).scale(0.9).next_to(step6, DOWN, buff=0.15)235 236 # 动画15:显示积分设置237 self.move_camera(phi=30 * DEGREES, theta=0 * DEGREES, run_time=2)238 239 # 动画16:显示计算步骤标题(左下角)240 self.add_fixed_in_frame_mobjects(step1)241 self.play(Write(step1))242 self.wait(0.8)243 244 # 动画17:显示积分表达式245 self.add_fixed_in_frame_mobjects(step2)246 self.play(Write(step2))247 self.wait(1)248 249 # 动画18:显示积分区域250 self.add_fixed_in_frame_mobjects(step3)251 self.play(Write(step3))252 self.wait(1)253 254 # 动画19:显示简化的积分255 self.add_fixed_in_frame_mobjects(step4)256 self.play(Write(step4))257 self.wait(1)258 259 # 动画20:显示极坐标变换260 self.add_fixed_in_frame_mobjects(step5)261 self.play(Write(step5))262 self.wait(1.5)263 264 # 动画21:显示极坐标积分(包含具体的f表达式)265 self.add_fixed_in_frame_mobjects(step6)266 self.play(Write(step6))267 self.wait(2)268 269 # 动画22:显示最终结果270 self.add_fixed_in_frame_mobjects(step7)271 self.play(Write(step7))272 self.wait(1)273 274 # 动画23:最终旋转展示275 self.move_camera(phi=75 * DEGREES, theta=360 * DEGREES, run_time=4)276 277 self.wait(2)278 279 # 动画24:最终突出显示(避免放大造成重叠)280 self.play(281 projection_circle.animate.set_fill_opacity(0.6),282 projection_label.animate.scale(1.1),283 step7.animate.scale(1.1),284 run_time=2285 )286 287 self.wait(3) 讲解
这个视频围绕「求立体图形体积」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。
开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。
画面中出现的文字「求立体体积」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。
随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。
核心公式可以写成:
这类公式可以和画面中的符号一一对应。
观察路径
观察路径可以分三步:先锁定「求立体图形体积」中的核心对象,尤其留意积分求体积、二重积分、空间区域之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。
本页可以从积分求体积、二重积分、空间区域、曲面围成区域这些概念进入,继续沿相邻问题观察同一类数学结构。