多元微积分可视化

三重积分计算

围绕三重积分计算,观察三重积分、累次积分、空间区域之间的关系与推理路径。

打开原视频

triple_integral_manim.py
1from manim import *2import numpy as np3 4class TripleIntegralScene(ThreeDScene):5    def construct(self):6        # 设置更好的相机角度和更美观的外观7        self.set_camera_orientation(phi=70 * DEGREES, theta=20 * DEGREES, zoom=0.8)8        9        # 创建更美观的坐标轴10        axes = ThreeDAxes(11            x_range=[-2.5, 2.5, 1],12            y_range=[-2.5, 2.5, 1],13            z_range=[0, 4.5, 1],14            x_length=5,15            y_length=5,16            z_length=5,17            axis_config={"color": BLUE_B}18        )19        labels = axes.get_axis_labels(20            x_label=MathTex("x", color=BLUE_B),21            y_label=MathTex("y", color=BLUE_B),22            z_label=MathTex("z", color=BLUE_B)23        )24        25        # 调整布局 - 顶部标题和说明更靠上26        problem_title = Text("三重积分计算(先二后一)", font_size=36, color=YELLOW).to_edge(UP, buff=0.1)27        28        # 将中文和数学公式分开显示,调整位置 - 修正Ω为立体区域29        calc_text = Text("计算:", font_size=24, color=WHITE)30        calc_formula = MathTex(r"\iiint_{\Omega} (x^2 + y^2) \, dV", font_size=24, color=WHITE)31        calc_group = VGroup(calc_text, calc_formula).arrange(RIGHT, buff=0.1)32        33        # 全部使用Text显示中文内容,避免中文在LaTeX中的问题34        domain_text = Text("其中 Ω 为旋转抛物面 z = x² + y²", font_size=24, color=WHITE)35        domain_formula = Text("与平面 z = 4 围成的立体区域", font_size=24, color=WHITE)36        domain_group = VGroup(domain_text, domain_formula).arrange(DOWN, buff=0.1, aligned_edge=LEFT)37        38        # 将描述分组放在左上方区域39        problem_desc = VGroup(calc_group, domain_group).arrange(DOWN, buff=0.2).next_to(problem_title, DOWN, buff=0.1).to_edge(LEFT, buff=0.5)40        41        # 显示坐标轴42        self.play(Create(axes), Create(labels), run_time=1.5)43        44        # 添加题目和方程45        self.add_fixed_in_frame_mobjects(problem_title, problem_desc)46        self.play(Write(problem_title), Write(problem_desc), run_time=2)47        48        # 旋转抛物面 z = r^2, 0 ≤ r ≤ 2, 0 ≤ z ≤ 449        def paraboloid(u, v):50            r = u51            theta = v52            x = r * np.cos(theta)53            y = r * np.sin(theta)54            z = r**255            return np.array([x, y, z])56        57        # 创建更美观的表面58        surface = Surface(59            paraboloid,60            u_range=[0, 2],61            v_range=[0, TAU],62            fill_opacity=0.3,63            resolution=(30, 30),64            stroke_width=0.5,65            stroke_color=BLUE_E,66            checkerboard_colors=[BLUE_D, "#3070B0"]67        )68        69        # 显示旋转抛物面70        self.play(Create(surface, run_time=2))71        72        # 旋转相机视角73        self.move_camera(phi=65 * DEGREES, theta=50 * DEGREES)74        self.wait(0.5)75        76        # 创建所有场景元素,但暂不显示77        78        # 定义z_tracker 79        self.z_tracker = ValueTracker(0.1)80        81        # 创建一个随z值变化的半透明截面平面82        section_plane = always_redraw(83            lambda: Surface(84                lambda u, v: np.array([u, v, self.z_tracker.get_value()]),85                u_range=[-2.5, 2.5],86                v_range=[-2.5, 2.5],87                resolution=(2, 2),88                fill_opacity=0.2,89                stroke_width=0.5,90                stroke_color=YELLOW,91                fill_color=YELLOW92            )93        )94        95        # 动态截面 - 使用填充和更突出的边缘96        cross_section = always_redraw(97            lambda: ParametricFunction(98                lambda t: np.array([99                    np.sqrt(self.z_tracker.get_value()) * np.cos(t),100                    np.sqrt(self.z_tracker.get_value()) * np.sin(t),101                    self.z_tracker.get_value()102                ]),103                t_range=[0, TAU],104                color=RED_B,105                stroke_width=4106            )107        )108        109        # 在截面内绘制积分区域 - 使用更美观的填充110        integral_region = always_redraw(111            lambda: Surface(112                lambda u, v: np.array([113                    u * np.cos(v),114                    u * np.sin(v),115                    self.z_tracker.get_value()116                ]),117                u_range=[0, np.sqrt(self.z_tracker.get_value())],118                v_range=[0, TAU],119                fill_opacity=0.4,120                resolution=(20, 20),121                stroke_width=0,122                checkerboard_colors=[RED_D, RED_A]123            )124        )125        126        # 创建左下角的2D截面变化示意图 - 坐标轴更长127        section_frame = Rectangle(height=3.5, width=3.5, color=WHITE)128        section_frame.to_corner(DL, buff=0.2)129        section_axes = Axes(130            x_range=[-2.0, 2.0, 1],131            y_range=[-2.0, 2.0, 1],132            x_length=3.0,  # 增加坐标轴长度133            y_length=3.0,  # 增加坐标轴长度134            axis_config={"color": GREY_B, "include_ticks": True},  # 包含刻度135        ).move_to(section_frame.get_center())136        137        # 添加坐标轴标签138        section_x_label = MathTex("x", font_size=16, color=GREY_B).next_to(section_axes.get_x_axis(), RIGHT)139        section_y_label = MathTex("y", font_size=16, color=GREY_B).next_to(section_axes.get_y_axis(), UP)140        141        # 调整标题位置,确保有足够空间放置截面方程142        section_title = Text("截面示意图", font_size=20, color=YELLOW)143        section_title.next_to(section_frame, UP, buff=0.1)144        section_title.align_to(section_frame, LEFT)145        146        # 截面方程(截面方程)放在标题上方,确保在框内147        section_text = Text("截面方程:z = x² + y² = ", font_size=24, color=YELLOW)148        149        # 将z值单独设置为动态更新的元素150        z_value_label = always_redraw(151            lambda: Text(152                f"{round(self.z_tracker.get_value(), 1)}",153                font_size=24,154                color=YELLOW155            )156        )157        158        # 将文字和动态z值组合在一起159        cross_eq = VGroup(section_text, z_value_label).arrange(RIGHT, buff=0.05)160        cross_eq.next_to(section_title, UP, buff=0.1)161        cross_eq.align_to(section_frame, LEFT)162        163        # 固定z值标签在文字后面164        z_value_label.add_updater(lambda m: m.next_to(section_text, RIGHT, buff=0.05))165        166        # 在小图右侧添加截面范围D(z)的显示167        range_text_title = Text("截面范围D(z):", font_size=20, color=YELLOW)168        range_text_theta = MathTex(r"0 \leq \theta \leq 2\pi", font_size=20, color=WHITE)169        range_text_r = MathTex(r"0 \leq r \leq \sqrt{z}", font_size=20, color=WHITE)170        171        # 将范围说明组合并垂直排列172        range_group = VGroup(range_text_title, range_text_theta, range_text_r).arrange(DOWN, buff=0.1, aligned_edge=LEFT)173        range_group.next_to(section_frame, RIGHT, buff=0.3)174        range_group.align_to(section_frame, UP)175        176        # 创建动态更新的2D截面圆177        section_circle = always_redraw(178            lambda: Circle(179                radius=np.sqrt(self.z_tracker.get_value()) * 0.75,  # 缩放以适应框架,但比例更大180                color=RED,181                fill_opacity=0.3,182                fill_color=RED_A183            ).move_to(section_axes.get_center())184        )185        186        # 提示文字 - 放在右侧上方位置187        explanation = Text("先二后一计算法", font_size=28, color=YELLOW).to_edge(RIGHT, buff=0.5).shift(UP * 2)188        189        # 截面变化提示 - 放在右侧说明文字下方190        section_desc = Text("截面随z值变化而变化", font_size=24, color=WHITE).next_to(explanation, DOWN, buff=0.3)191        192        # 积分公式 - 放在"先二后一计算法"下方,但先不显示193        integral_steps = VGroup(194            MathTex(195                r"\begin{aligned}"196                r"\iiint_{\Omega} (x^2 + y^2) \, dV &= \int_{0}^{4} \left( \iint_{D(z)} (x^2 + y^2) \, dxdy \right) dz \\"197                r"&= \int_{0}^{4} \left( \int_{0}^{2\pi} \int_{0}^{\sqrt{z}} r^2 \cdot r \, drd\theta \right) dz \\"198                r"&= \int_{0}^{4} \left( 2\pi \cdot \frac{r^4}{4} \Big|_{0}^{\sqrt{z}} \right) dz \\"199                r"&= \int_{0}^{4} \frac{\pi}{2} \cdot z^2 \, dz \\"200                r"&= \frac{\pi}{2} \cdot \frac{z^3}{3} \Big|_{0}^{4} \\"201                r"&= \frac{\pi}{2} \cdot \frac{64}{3} = \frac{32\pi}{3}"202                r"\end{aligned}",203                font_size=24,204                color=WHITE205            )206        ).next_to(section_desc, DOWN, buff=0.3).to_edge(RIGHT, buff=0.5)207        208        # 最终结果 - 放在中央底部,先不显示209        final_result = MathTex(210            r"\iiint_{\Omega} (x^2 + y^2) \, dV = \frac{32\pi}{3}",211            font_size=36,212            color=YELLOW213        ).to_edge(DOWN, buff=0.3)214        215        # 按照逻辑顺序播放动画216        # 1. 方法介绍217        self.add_fixed_in_frame_mobjects(explanation)218        self.play(Write(explanation), run_time=1)219        self.wait(0.2)220        221        # 2. 截面平面和截面描述222        self.add_fixed_in_frame_mobjects(section_desc)223        self.play(224            Create(section_plane),225            Write(section_desc),226            run_time=1.5227        )228        self.wait(0.2)229        230        # 3. 截面圆和积分区域231        self.play(232            Create(cross_section),233            Create(integral_region),234            run_time=1.5235        )236        self.wait(0.2)237        238        # 4. 2D截面示意图和相关说明 - 修复闪现问题239        # 先只添加框架,不添加动态更新的元素240        self.add_fixed_in_frame_mobjects(section_frame)241        self.play(FadeIn(section_frame), run_time=0.5)242        243        # 添加坐标轴和标签244        self.add_fixed_in_frame_mobjects(section_axes, section_x_label, section_y_label)245        self.play(246            FadeIn(section_axes),247            FadeIn(section_x_label),248            FadeIn(section_y_label),249            run_time=0.5250        )251        252        # 添加标题253        self.add_fixed_in_frame_mobjects(section_title)254        self.play(FadeIn(section_title), run_time=0.5)255        256        # 添加截面方程257        self.add_fixed_in_frame_mobjects(cross_eq)258        self.play(FadeIn(cross_eq), run_time=0.5)259        260        # 添加范围说明261        self.add_fixed_in_frame_mobjects(range_group)262        self.play(FadeIn(range_group), run_time=0.5)263        264        # 最后添加动态更新的圆 - 避免闪现265        self.add_fixed_in_frame_mobjects(section_circle)266        self.play(FadeIn(section_circle), run_time=0.5)267        self.wait(0.3)268        269        # 5. 动态变化z值,显示截面变化270        self.play(271            self.z_tracker.animate.set_value(4),272            run_time=5273        )274        self.wait(0.5)275        276        # 6. 旋转相机角度,准备显示积分计算277        self.move_camera(phi=70 * DEGREES, theta=80 * DEGREES)278        self.wait(0.3)279        280        # 7. 积分计算步骤281        self.add_fixed_in_frame_mobjects(integral_steps)282        self.play(Write(integral_steps), run_time=2.5)283        self.wait(1)284        285        # 8. 旋转展示最终结果286        self.begin_ambient_camera_rotation(rate=0.15)287        self.wait(4)288        self.stop_ambient_camera_rotation()289        290        # 9. 显示最终结果291        self.add_fixed_in_frame_mobjects(final_result)292        self.play(293            FadeOut(integral_steps),294            FadeIn(final_result),295            run_time=1.5296        )297        self.wait(2)298 299if __name__ == "__main__":300    # 使用更好的渲染设置301    config.frame_rate = 30302    config.pixel_width = 1280303    config.pixel_height = 720304    scene = TripleIntegralScene()305    scene.render()

讲解

这个视频围绕「三重积分计算」展开。画面把问题、图像和公式放在同一条理解路径中,让抽象关系先被看见。

开头先建立问题背景和主要对象,让观察从标题、坐标或第一组关系进入。

画面中出现的文字「三重积分计算(先二后一)」给出视觉入口。随后画面通过对象创建、移动、淡入淡出和高亮,把抽象命题拆成可以跟随的步骤。

随后出现更具体的图像或公式提示,动画会把局部对象和整体结论联系起来。这里可以重点观察变量、曲线、区域或向量如何随镜头推进而变化。

核心公式可以写成:

Ω(x2+y2)dV\iiint_{\Omega} (x^2 + y^2) \, dV

这类公式可以和画面中的符号一一对应。

观察路径

观察路径可以分三步:先锁定「三重积分计算」中的核心对象,尤其留意三重积分、累次积分、空间区域之间的联系;再跟随画面中变量、图像或向量的变化;最后回到公式或结论,判断局部变化如何支撑整体关系。

本页可以从三重积分、累次积分、空间区域、积分求体积这些概念进入,继续沿相邻问题观察同一类数学结构。