02
topic
证明7x5x2=7x(5x2)
场景提示
好的,下面给出 7 x 5 x 2 = 7 x (5 x 2) 的证明,以及对证明过程的解释,便于你理解:
**证明:**
根据乘法结合律,对于任意实数 a, b, 和 c,都有:
a x b x c = a x (b x c) = (a x b) x c
在本例中,a = 7,b = 5,c = 2。因此,我们可以直接应用乘法结合律来证明:
7 x 5 x 2 = 7 x (5 x 2)
**解释:**
* **乘法结合律:** 这个定律是证明的关键。它表明,在只有乘法的运算中,改变运算顺序(通过添加括号)不会改变结果。
* **应用:** 我们直接将乘法结合律应用于给定的表达式。左侧是三个数的连续乘积,而右侧是先计算后两个数的乘积,然后再乘以第一个数。根据结合律,这两个表达式是等价的。
**结论:**
因为乘法满足结合律,所以 7 x 5 x 2 必然等于 7 x (5 x 2)。这个等式成立,无需进行具体的数值计算。 你可以计算一下,两边都等于70,验证这个结果。
代码
# -*- coding: utf-8 -*-
import os
import numpy as np
import requests
from contextlib import contextmanager
from manim import *
import hashlib
import manimpango # For font checking
from moviepy import AudioFileClip # Correct import
# --- Font Check ---
DEFAULT_FONT = "Noto Sans CJK SC" # Example desired font for Chinese
available_fonts = manimpango.list_fonts()
final_font = None
if DEFAULT_FONT in available_fonts:
print(f"Font '{DEFAULT_FONT}' found.")
final_font = DEFAULT_FONT
else:
print(f"Warning: Font '{DEFAULT_FONT}' not found. Trying fallback fonts...")
fallback_fonts = ["PingFang SC", "Microsoft YaHei", "SimHei", "Arial Unicode MS"]
found_fallback = False
for font in fallback_fonts:
if font in available_fonts:
print(f"Switched to fallback font: '{font}'")
final_font = font
found_fallback = True
break
if not found_fallback:
print(f"Warning: Neither '{DEFAULT_FONT}' nor fallbacks found. Using Manim default. Chinese characters may not display correctly.")
# final_font remains None
# --- Custom Colors ---
MY_DARK_BLUE = "#1E3A8A" # 深蓝色
MY_LIGHT_GRAY = "#F3F4F6" # 浅灰色
MY_MEDIUM_GRAY = "#D1D5DB" # 中灰色
MY_GOLD = "#F59E0B" # 金色
MY_ORANGE = "#F97316" # 橙色
MY_RED = "#DC2626" # 红色
MY_GREEN = "#10B981" # 绿色
MY_WHITE = "#FFFFFF" # 白色
MY_BLACK = "#000000" # 黑色
# --- TTS Caching Setup ---
CACHE_DIR = "tts_cache"
os.makedirs(CACHE_DIR, exist_ok=True)
class CustomVoiceoverTracker:
"""Tracks audio path and duration for TTS."""
def __init__(self, audio_path, duration):
self.audio_path = audio_path
self.duration = duration
def get_cache_filename(text):
"""Generates a unique filename based on the text hash."""
# Use a hash of the text for a unique filename
text_hash = hashlib.md5(text.encode('utf-8')).hexdigest()
return os.path.join(CACHE_DIR, f"{text_hash}.mp3")
@contextmanager
def custom_voiceover_tts(text, token="123456", base_url="https://uni-ai.fly.dev/api/manim/tts"):
"""
Fetches TTS audio, caches it, and provides path and duration.
Usage: with custom_voiceover_tts("text") as tracker: ...
"""
cache_file = get_cache_filename(text)
audio_file = cache_file # Initialize audio_file
if os.path.exists(cache_file):
# print(f"Using cached TTS for: {text[:30]}...")
audio_file = cache_file
else:
# print(f"Requesting TTS for: {text[:30]}...")
try:
# URL encode the input text to handle special characters
input_text_encoded = requests.utils.quote(text)
url = f"{base_url}?token={token}&input={input_text_encoded}"
response = requests.get(url, stream=True, timeout=60) # Added timeout
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
with open(cache_file, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk: # filter out keep-alive new chunks
f.write(chunk)
audio_file = cache_file
# print("TTS downloaded and cached.")
except requests.exceptions.RequestException as e:
print(f"TTS API request failed: {e}")
# Fallback: create a dummy tracker with zero duration
tracker = CustomVoiceoverTracker(None, 0)
yield tracker
return # Exit context manager
except Exception as e:
# Clean up potentially incomplete cache file on error
if os.path.exists(cache_file):
os.remove(cache_file)
print(f"An error occurred during TTS processing: {e}")
tracker = CustomVoiceoverTracker(None, 0)
yield tracker
return # Exit context manager
# Ensure audio file exists before processing with MoviePy
if audio_file and os.path.exists(audio_file):
try:
# Use context manager for AudioFileClip
with AudioFileClip(audio_file) as clip:
duration = clip.duration
# print(f"Audio duration: {duration:.2f}s")
tracker = CustomVoiceoverTracker(audio_file, duration)
except Exception as e:
print(f"Error processing audio file {audio_file}: {e}")
# Fallback if audio file is corrupted or invalid
tracker = CustomVoiceoverTracker(None, 0)
else:
# Fallback if audio file was not created or found
print(f"TTS audio file not found or not created: {audio_file}")
tracker = CustomVoiceoverTracker(None, 0)
try:
yield tracker
finally:
# No cleanup needed here as we are caching
pass
# --- Custom TeX Template for HTML Colors ---
HTML_COLOR_TEMPLATE = TexTemplate(
preamble=r"""
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage[HTML]{xcolor} %% <<< MUST INCLUDE THIS LINE
\usepackage{graphicx}
% Add other necessary packages like fontspec, ctex if needed
"""
)
# -----------------------------
# CombinedScene: Integrates all scenes for the proof
# -----------------------------
class CombinedScene(MovingCameraScene):
"""
Combines scenes to prove and explain 7 x 5 x 2 = 7 x (5 x 2)
using the associative property of multiplication.
"""
def setup(self):
MovingCameraScene.setup(self)
# Set default font if found
if final_font:
Text.set_default(font=final_font)
def construct(self):
# --- Play Scenes Sequentially ---
self.play_scene_01()
self.clear_and_reset()
self.play_scene_02()
self.clear_and_reset()
self.play_scene_03()
self.clear_and_reset()
self.play_scene_04()
self.clear_and_reset()
# End of animation message
final_message = Text("证明完毕,感谢观看! 😄", font_size=48, color=MY_WHITE)
bg_final = Rectangle(width=config.frame_width, height=config.frame_height, fill_color=MY_BLACK, fill_opacity=1,
stroke_width=0).set_z_index(-10)
self.add(bg_final)
self.play(FadeIn(final_message))
self.wait(2)
def get_scene_number(self, number_str):
"""Creates and positions the scene number."""
scene_num = Text(number_str, font_size=24, color=MY_WHITE)
scene_num.to_corner(UR, buff=0.3)
scene_num.set_z_index(10)
return scene_num
def clear_and_reset(self):
"""Clears all objects and resets the camera."""
# Clear updaters explicitly
for mob in self.mobjects:
if mob is not None and hasattr(mob, 'get_updaters') and mob.get_updaters():
mob.clear_updaters()
valid_mobjects = [m for m in self.mobjects if m is not None]
all_mobjects = Group(*valid_mobjects)
if all_mobjects:
self.play(FadeOut(all_mobjects, shift=DOWN * 0.5), run_time=0.5)
self.clear()
self.camera.frame.move_to(ORIGIN)
self.camera.frame.set(width=config.frame_width, height=config.frame_height)
self.wait(0.1)
# --- Scene 1: Introduction & Associative Law ---
def play_scene_01(self):
"""Scene 1: Introduce the equation and the associative law."""
# Background
bg1 = Rectangle(width=config.frame_width, height=config.frame_height, fill_color=MY_DARK_BLUE, fill_opacity=1.0, stroke_width=0).set_z_index(-10)
self.add(bg1)
scene_num_01 = self.get_scene_number("01")
self.add(scene_num_01)
# Title: Combine Text and MathTex
title_text = Text("证明:", font_size=48, color=MY_WHITE)
title_math = MathTex("7 \\times 5 \\times 2 = 7 \\times (5 \\times 2)", font_size=52, color=MY_ORANGE)
title_group = VGroup(title_text, title_math).arrange(RIGHT, buff=0.2)
title_group.to_edge(UP, buff=1.5)
# Associative Law Formula
law_text = Text("基于乘法结合律:", font_size=36, color=MY_WHITE)
law_formula = MathTex("a \\times b \\times c = a \\times (b \\times c) = (a \\times b) \\times c", font_size=40, color=MY_GOLD)
law_group = VGroup(law_text, law_formula).arrange(DOWN, buff=0.4)
law_group.next_to(title_group, DOWN, buff=1.0)
# --- TTS Integration ---
voice_text_01 = "我们将证明 7 乘以 5 乘以 2 等于 7 乘以 括号 5 乘以 2 括号。这个证明基于乘法的一个基本定律:结合律。"
with custom_voiceover_tts(voice_text_01) as tracker:
if tracker.audio_path and tracker.duration > 0:
self.add_sound(tracker.audio_path, time_offset=0)
else:
print("Warning: Scene 1 TTS audio failed or has zero duration.")
subtitle_voice = Text(
voice_text_01, font_size=32, color=MY_WHITE,
width=config.frame_width - 2, should_center=True
).to_edge(DOWN, buff=0.5)
# Animations
anim_runtime_title = 2.0
anim_runtime_law = 2.5
fade_out_duration = 1.0
total_initial_anim_time = max(anim_runtime_title, 0.5) # Subtitle fadein is fast
self.play(
AnimationGroup(
FadeIn(subtitle_voice, run_time=0.5),
FadeIn(title_group, shift=DOWN*0.2, run_time=anim_runtime_title),
lag_ratio=0.0
),
run_time=total_initial_anim_time
)
self.play(FadeIn(law_group, shift=UP*0.2, run_time=anim_runtime_law))
# Calculate wait time
elapsed_time = total_initial_anim_time + anim_runtime_law
if tracker.duration > 0:
remaining_time = tracker.duration - elapsed_time - fade_out_duration
if remaining_time > 0:
self.wait(remaining_time)
else:
self.wait(1.0)
self.play(FadeOut(subtitle_voice), run_time=fade_out_duration)
self.wait(1)
# --- Scene 2: Applying the Law ---
def play_scene_02(self):
"""Scene 2: Show how the law applies to the specific numbers."""
# Background
bg2 = Rectangle(width=config.frame_width, height=config.frame_height, fill_color=MY_LIGHT_GRAY, fill_opacity=1.0, stroke_width=0).set_z_index(-10)
self.add(bg2)
scene_num_02 = self.get_scene_number("02")
self.add(scene_num_02)
# Explanation Text
explanation1 = Text("根据乘法结合律,对于任意实数 a, b, 和 c,都有:", font_size=36, color=MY_BLACK)
explanation1.to_edge(UP, buff=1.0)
# General Law (again for reference)
law_formula_ref = MathTex("a \\times b \\times c = a \\times (b \\times c)", font_size=40, color=MY_DARK_BLUE)
law_formula_ref.next_to(explanation1, DOWN, buff=0.5)
# Specific Case Explanation
explanation2 = Text("在本例中,我们令:", font_size=36, color=MY_BLACK)
explanation2.next_to(law_formula_ref, DOWN, buff=1.0)
# Identify a, b, c
var_a = MathTex("a = 7", font_size=40, color=MY_RED)
var_b = MathTex("b = 5", font_size=40, color=MY_GREEN)
var_c = MathTex("c = 2", font_size=40, color=MY_ORANGE)
vars_group = VGroup(var_a, var_b, var_c).arrange(RIGHT, buff=0.8)
vars_group.next_to(explanation2, DOWN, buff=0.5)
# Apply the law text
explanation3 = Text("因此,我们可以直接应用此定律得到:", font_size=36, color=MY_BLACK)
explanation3.next_to(vars_group, DOWN, buff=1.0)
# Specific Equation
specific_eq = MathTex("7 \\times 5 \\times 2 = 7 \\times (5 \\times 2)", font_size=48, color=MY_DARK_BLUE)
specific_eq.next_to(explanation3, DOWN, buff=0.5)
# --- TTS Integration ---
voice_text_02 = "根据乘法结合律,对于任意实数 a, b, c,都有 a 乘以 b 乘以 c 等于 a 乘以 括号 b 乘以 c 括号。 在本例中,我们让 a 等于 7,b 等于 5,c 等于 2。 因此,我们可以直接应用这个定律,得到 7 乘以 5 乘以 2 等于 7 乘以 括号 5 乘以 2 括号。"
with custom_voiceover_tts(voice_text_02) as tracker:
if tracker.audio_path and tracker.duration > 0:
self.add_sound(tracker.audio_path, time_offset=0)
else:
print("Warning: Scene 2 TTS audio failed or has zero duration.")
subtitle_voice = Text(
voice_text_02, font_size=32, color=MY_BLACK,
width=config.frame_width - 2, should_center=True
).to_edge(DOWN, buff=0.5)
# Animations
anim_exp1 = 1.5
anim_law = 1.5
anim_exp2 = 1.0
anim_vars = 1.5
anim_exp3 = 1.0
anim_eq = 2.0
fade_out_duration = 1.0
self.play(
AnimationGroup(
FadeIn(subtitle_voice, run_time=0.5),
FadeIn(explanation1, run_time=anim_exp1),
lag_ratio=0.0
),
run_time=anim_exp1
)
self.play(Write(law_formula_ref), run_time=anim_law)
self.play(FadeIn(explanation2), run_time=anim_exp2)
self.play(LaggedStart(FadeIn(var_a), FadeIn(var_b), FadeIn(var_c), lag_ratio=0.3), run_time=anim_vars)
self.play(FadeIn(explanation3), run_time=anim_exp3)
self.play(Write(specific_eq), run_time=anim_eq)
# Calculate wait time
elapsed_time = anim_exp1 + anim_law + anim_exp2 + anim_vars + anim_exp3 + anim_eq
if tracker.duration > 0:
remaining_time = tracker.duration - elapsed_time - fade_out_duration
if remaining_time > 0:
self.wait(remaining_time)
else:
self.wait(1.0)
self.play(FadeOut(subtitle_voice), run_time=fade_out_duration)
self.wait(1)
# --- Scene 3: Verification Calculation ---
def play_scene_03(self):
"""Scene 3: Calculate both sides to verify they are equal."""
# Background
bg3 = Rectangle(width=config.frame_width, height=config.frame_height, fill_color=MY_BLACK, fill_opacity=1.0, stroke_width=0).set_z_index(-10)
self.add(bg3)
scene_num_03 = self.get_scene_number("03")
self.add(scene_num_03)
# Title
verify_title = Text("验证计算:", font_size=40, color=MY_WHITE)
verify_title.to_edge(UP, buff=1.0)
# Left Hand Side (LHS) Calculation
lhs_title = Text("左侧 (LHS):", font_size=36, color=MY_WHITE).next_to(verify_title, DOWN, buff=0.8).align_to(verify_title, LEFT).shift(LEFT*3)
lhs_calc1 = MathTex("LHS = 7 \\times 5 \\times 2", font_size=40, color=MY_WHITE).next_to(lhs_title, DOWN, buff=0.4, aligned_edge=LEFT)
lhs_calc2 = MathTex("= 35 \\times 2", font_size=40, color=MY_WHITE).next_to(lhs_calc1, DOWN, buff=0.4, aligned_edge=LEFT)
lhs_calc3 = MathTex("= 70", font_size=40, color=MY_GREEN).next_to(lhs_calc2, DOWN, buff=0.4, aligned_edge=LEFT)
lhs_group = VGroup(lhs_title, lhs_calc1, lhs_calc2, lhs_calc3)
# Right Hand Side (RHS) Calculation
rhs_title = Text("右侧 (RHS):", font_size=36, color=MY_WHITE).align_to(lhs_title, UP).align_to(verify_title, RIGHT).shift(RIGHT*3)
rhs_calc1 = MathTex("RHS = 7 \\times (5 \\times 2)", font_size=40, color=MY_WHITE).next_to(rhs_title, DOWN, buff=0.4, aligned_edge=LEFT)
rhs_calc2 = MathTex("= 7 \\times 10", font_size=40, color=MY_WHITE).next_to(rhs_calc1, DOWN, buff=0.4, aligned_edge=LEFT)
rhs_calc3 = MathTex("= 70", font_size=40, color=MY_GREEN).next_to(rhs_calc2, DOWN, buff=0.4, aligned_edge=LEFT)
rhs_group = VGroup(rhs_title, rhs_calc1, rhs_calc2, rhs_calc3)
# Result Comparison
result_comp = MathTex("LHS = RHS = 70", font_size=48, color=MY_GOLD)
result_comp.next_to(VGroup(lhs_group, rhs_group), DOWN, buff=1.0)
# --- TTS Integration ---
voice_text_03 = "我们可以分别计算等式两边的值来验证。首先看左侧:7 乘以 5 等于 35,然后 35 乘以 2 等于 70。 接着看右侧:括号里的 5 乘以 2 等于 10,然后 7 乘以 10 等于 70。 可以看到,两边的计算结果都是 70。"
with custom_voiceover_tts(voice_text_03) as tracker:
if tracker.audio_path and tracker.duration > 0:
self.add_sound(tracker.audio_path, time_offset=0)
else:
print("Warning: Scene 3 TTS audio failed or has zero duration.")
subtitle_voice = Text(
voice_text_03, font_size=32, color=MY_WHITE,
width=config.frame_width - 2, should_center=True
).to_edge(DOWN, buff=0.5)
# Animations
anim_title = 1.0
anim_lhs_step = 1.5
anim_rhs_step = 1.5
anim_result = 1.5
fade_out_duration = 1.0
self.play(
AnimationGroup(
FadeIn(subtitle_voice, run_time=0.5),
FadeIn(verify_title, run_time=anim_title),
lag_ratio=0.0
),
run_time=anim_title
)
# LHS Calculation Steps
self.play(FadeIn(lhs_title), Write(lhs_calc1), run_time=anim_lhs_step)
# Highlight 7x5
rect_lhs1 = SurroundingRectangle(lhs_calc1[0][4:7], color=MY_ORANGE, buff=0.05) # Indices for 7x5
self.play(Create(rect_lhs1))
self.play(TransformMatchingTex(lhs_calc1.copy(), lhs_calc2, path_arc=PI/2), FadeOut(rect_lhs1), run_time=anim_lhs_step)
# Highlight 35x2
rect_lhs2 = SurroundingRectangle(lhs_calc2[0][2:], color=MY_ORANGE, buff=0.05) # Indices for 35x2
self.play(Create(rect_lhs2))
self.play(TransformMatchingTex(lhs_calc2.copy(), lhs_calc3, path_arc=PI/2), FadeOut(rect_lhs2), run_time=anim_lhs_step)
self.wait(0.5)
# RHS Calculation Steps
self.play(FadeIn(rhs_title), Write(rhs_calc1), run_time=anim_rhs_step)
# Highlight (5x2)
rect_rhs1 = SurroundingRectangle(rhs_calc1[0][5:10], color=MY_ORANGE, buff=0.05) # Indices for (5x2)
self.play(Create(rect_rhs1))
self.play(TransformMatchingTex(rhs_calc1.copy(), rhs_calc2, path_arc=-PI/2), FadeOut(rect_rhs1), run_time=anim_rhs_step)
# Highlight 7x10
rect_rhs2 = SurroundingRectangle(rhs_calc2[0][2:], color=MY_ORANGE, buff=0.05) # Indices for 7x10
self.play(Create(rect_rhs2))
self.play(TransformMatchingTex(rhs_calc2.copy(), rhs_calc3, path_arc=-PI/2), FadeOut(rect_rhs2), run_time=anim_rhs_step)
self.wait(0.5)
# Show final comparison
self.play(Write(result_comp), run_time=anim_result)
# Calculate wait time
elapsed_time = anim_title + 3*anim_lhs_step + 0.5 + 3*anim_rhs_step + 0.5 + anim_result
if tracker.duration > 0:
remaining_time = tracker.duration - elapsed_time - fade_out_duration
if remaining_time > 0:
self.wait(remaining_time)
else:
self.wait(1.0)
self.play(FadeOut(subtitle_voice), run_time=fade_out_duration)
self.wait(1)
# --- Scene 4: Conclusion ---
def play_scene_04(self):
"""Scene 4: State the conclusion based on the associative law."""
# Background
bg4 = Rectangle(width=config.frame_width, height=config.frame_height, fill_color=MY_DARK_BLUE, fill_opacity=1.0, stroke_width=0).set_z_index(-10)
self.add(bg4)
scene_num_04 = self.get_scene_number("04")
self.add(scene_num_04)
# Conclusion Text
conclusion_text1 = Text("结论:", font_size=48, color=MY_WHITE, weight=BOLD)
conclusion_text1.move_to(UP * 2.0)
conclusion_text2 = Text("因为乘法满足结合律,", font_size=40, color=MY_WHITE)
conclusion_text3 = Text("所以", font_size=40, color=MY_WHITE)
conclusion_math = MathTex("7 \\times 5 \\times 2", font_size=44, color=MY_ORANGE)
conclusion_text4 = Text("必然等于", font_size=40, color=MY_WHITE)
conclusion_math2 = MathTex("7 \\times (5 \\times 2)", font_size=44, color=MY_ORANGE)
conclusion_text5 = Text("。", font_size=40, color=MY_WHITE)
line1 = VGroup(conclusion_text2).arrange(RIGHT, buff=0.2)
line2 = VGroup(conclusion_text3, conclusion_math, conclusion_text4, conclusion_math2, conclusion_text5).arrange(RIGHT, buff=0.2)
conclusion_group = VGroup(line1, line2).arrange(DOWN, buff=0.5, aligned_edge=LEFT)
conclusion_group.next_to(conclusion_text1, DOWN, buff=0.8)
# Final Statement
final_statement = Text("这个等式成立。", font_size=40, color=MY_GREEN)
final_statement.next_to(conclusion_group, DOWN, buff=1.0)
# --- TTS Integration ---
voice_text_04 = "结论:因为乘法满足结合律,所以 7 乘以 5 乘以 2 必然等于 7 乘以 括号 5 乘以 2 括号。 这个等式成立,无需进行具体的数值计算,定律本身保证了结果的正确性。"
with custom_voiceover_tts(voice_text_04) as tracker:
if tracker.audio_path and tracker.duration > 0:
self.add_sound(tracker.audio_path, time_offset=0)
else:
print("Warning: Scene 4 TTS audio failed or has zero duration.")
subtitle_voice = Text(
voice_text_04, font_size=32, color=MY_WHITE,
width=config.frame_width - 2, should_center=True
).to_edge(DOWN, buff=0.5)
# Animations
anim_conc1 = 1.0
anim_conc_lines = 3.0 # Time to reveal the main conclusion lines
anim_final = 1.5
fade_out_duration = 1.0
self.play(
AnimationGroup(
FadeIn(subtitle_voice, run_time=0.5),
FadeIn(conclusion_text1, run_time=anim_conc1),
lag_ratio=0.0
),
run_time=anim_conc1
)
# Use LaggedStart for the conclusion lines
self.play(
LaggedStart(
FadeIn(line1),
# Animate parts of line2
AnimationGroup(
FadeIn(conclusion_text3), Write(conclusion_math), FadeIn(conclusion_text4),
Write(conclusion_math2), FadeIn(conclusion_text5), lag_ratio=0.1
),
lag_ratio=0.5 # Lag between line1 and line2 appearing
),
run_time=anim_conc_lines
)
self.play(FadeIn(final_statement, shift=UP*0.2), run_time=anim_final)
# Calculate wait time
elapsed_time = anim_conc1 + anim_conc_lines + anim_final
if tracker.duration > 0:
remaining_time = tracker.duration - elapsed_time - fade_out_duration
if remaining_time > 0:
self.wait(remaining_time)
else:
self.wait(1.0)
self.play(FadeOut(subtitle_voice), run_time=fade_out_duration)
self.wait(1)
# --- Main execution block ---
if __name__ == "__main__":
# Basic configuration
config.pixel_height = 1080 # Set resolution height
config.pixel_width = 1920 # Set resolution width
config.frame_rate = 30 # Set frame rate
config.output_file = "CombinedScene" # Specify output filename
config.disable_caching = True # Disable caching
# Set output directory using placeholder for Java replacement
config.media_dir = r"#(output_path)" # IMPORTANT: Use the placeholder
# Create and render the scene
scene = CombinedScene()
scene.render()
print(f"Scene rendering finished. Output in: {config.media_dir}")
Video
https://manim.collegebot.ai/cache/499192080877916160/videos/1080p30/CombinedScene.mp4