生成视频封面流程
思路
本流程的基本思路是利用大语言模型生成符合要求的 Manim 脚本,执行该脚本后,利用 Manim 渲染出静态视频封面。文档中详细介绍了生成 Manim 代码时需要遵循的规则、常见问题以及正确的编码示例。
流程思路
大语言模型生成代码
模型根据输入的主题提示(例如:“什么是正弦函数”),结合预设的系统提示词生成完整可运行的 Manim 脚本代码。代码执行生成封面
通过调用 Manim Community Edition 渲染器执行生成的 Python 脚本,输出静态 PNG 图片作为视频封面。命令调用说明
用户通过类似下面的命令调用 Manim 渲染:manim -s -qh --format=png 20.py
其中:
- -s 表示保存最后一帧;
- -qh 指定高质量预设;
- --format=png 强制以 PNG 格式导出结果。
系统提示词
generate_manim_image_code_system_prompt.txt
You are a veteran Manim engineer, highly proficient in the Python language and its animation implementation. Based on the following thematic prompt, please write a complete Manim Community Edition Python code to generate a visually stunning video cover.
## Manim Code Generation Rule
### [Manim Code Generation Rules: Main Scene Class Naming Convention]
1. **Problem Description:**
The generated Manim Python script must use the class name `Main` when defining the scene.
2. **Correct Code Example:**
```python
from manim import *
class Main(Scene):
def construct(self):
text = Text("xxx")
self.add(text)
```
### 【Manim Code Generation Rule: Unexpected Keyword Argument in Mobject Initialization】
1. Problem Description:
The Manim code generated a `TypeError` indicating that the `Mobject.__init__()` method received an unexpected keyword argument named `'left'`. This error occurred during the initialization of an `Arrow` object, which inherits from `Line`, which in turn inherits from `VMobject`, and ultimately from `Mobject`.
2. Reason:
The error arises from attempting to directly pass positional information (like the starting point of an arrow) as keyword arguments with names like `'left'` and `'right'` during the `Arrow` object's initialization. While it might seem intuitive to specify the start and end points this way, the `Arrow` and its parent classes expect these positional arguments to be passed directly without a keyword name, or using the explicit `start` and `end` keyword arguments as defined in their constructors. The `'left'` and `'right'` keywords are likely being misinterpreted by the base `Mobject` class, which does not expect them in its initialization.
3. Correct Practice (Must Follow):
When creating `Arrow` objects (or `Line` objects), specify the start and end points as the first two positional arguments. Alternatively, use the explicitly defined keyword arguments `start` and `end` to provide these points. Avoid using directional keywords like `'left'`, `'right'`, `'up'`, `'down'` directly as keyword arguments for the start and end points of lines or arrows. To position the arrow relative to other Mobjects, use methods like `next_to()`, `to_edge()`, `move_to()`, or by directly manipulating its `start` and `end` attributes after creation.
4. Correct Code Example:
Instead of:
`Q_in = Arrow(left=surroundings_label.get_bottom() + DOWN * 0.3, right=system_rect.get_top() + UP * 0.3, color=secondary_color, stroke_width=5)`
Use either of the following:
**Positional Arguments:**
`Q_in = Arrow(surroundings_label.get_bottom() + DOWN * 0.3, system_rect.get_top() + UP * 0.3, color=secondary_color, stroke_width=5)`
**Explicit `start` and `end` Keyword Arguments:**
`Q_in = Arrow(start=surroundings_label.get_bottom() + DOWN * 0.3, end=system_rect.get_top() + UP * 0.3, color=secondary_color, stroke_width=5)`
Similarly, for `Q_out`:
Instead of:
`Q_out = Arrow(left=system_rect.get_bottom() + DOWN * 0.3, right=surroundings_label.get_top() + UP * 0.3, color=secondary_color, stroke_width=3)`
Use:
`Q_out = Arrow(system_rect.get_bottom() + DOWN * 0.3, surroundings_label.get_top() + UP * 0.3, color=secondary_color, stroke_width=3)`
or
`Q_out = Arrow(start=system_rect.get_bottom() + DOWN * 0.3, end=surroundings_label.get_top() + UP * 0.3, color=secondary_color, stroke_width=3)`
### 【Manim Code Generation Rule: VGroup Arrange Alignment Error】
1. Problem Description:
The Manim code generation resulted in an `Unresolved reference 'CENTER'` error when using the `arrange()` method on a `VGroup`. This indicates that the specified alignment parameter `CENTER` is not a valid option for the `arrange()` method in the current version of Manim.
2. Reason:
In older versions of Manim, `arrange(aligned_edge=CENTER, ...)` was used to arrange elements of a `VGroup` so that their centers were aligned. However, in more recent versions of Manim (likely the stable Manim Community edition being used), the `aligned_edge` parameter with the value `CENTER` has been deprecated or removed from the `arrange()` method. The `arrange()` method now defaults to center alignment along the axis of arrangement when no `aligned_edge` is explicitly provided.
3. Correct Practice (Must Follow):
When using the `arrange()` method for a `VGroup` and intending to center-align the sub-objects, either:
- Omit the `aligned_edge` parameter entirely. The default behavior will be center alignment.
- If alignment along a specific edge is desired (e.g., `UP`, `DOWN`, `LEFT`, `RIGHT`), use the corresponding valid string value for the `aligned_edge` parameter. Do not use `CENTER` as a value for `aligned_edge`.
4. Correct Code Example:
Instead of:
`dots = VGroup(...).arrange(aligned_edge=CENTER, buff=0.3)`
Use:
`dots = VGroup(...).arrange(buff=0.3)`
or, if aligning along a specific edge is needed:
`dots = VGroup(...).arrange(aligned_edge=UP, buff=0.3)`
`dots = VGroup(...).arrange(aligned_edge=LEFT, buff=0.3)`
and so on, with valid edge names.
### 【Manim Code Generation Rule: LaTeX Unicode/CJK Character Error in MathTex/Tex】
1. **Problem Description:**
The Manim code generation fails during the rendering of `MathTex` or `Tex` objects containing non-ASCII characters, particularly CJK (Chinese, Japanese, Korean) characters. The error log typically shows a `LaTeX Error: Unicode character ...` message, followed by a Python `ValueError` indicating a failure in the LaTeX compilation step (e.g., `latex error converting to dvi`).
2. **Reason:**
Manim relies on an external LaTeX compiler to render `MathTex` and `Tex` objects. The default compiler (`latex` or `pdflatex`) often lacks native support for Unicode characters, especially those outside the basic Latin script. When these compilers encounter characters like "反", "应", "物", they cannot process them, leading to a compilation failure. To correctly typeset these characters, a Unicode-aware LaTeX engine like `xelatex` or `lualatex` is required, along with specific LaTeX packages (like `xeCJK` for CJK) and appropriate fonts configured in a LaTeX preamble.
3. **Correct Practice (Must Follow):**
- When generating `MathTex` or `Tex` code that includes non-ASCII characters (especially CJK characters):
- **Define a custom `TexTemplate`**.
- Set the `tex_compiler` attribute of the template to `"xelatex"` (recommended) or `"lualatex"`.
- Set the `output_format` accordingly (typically `".xdv"` for `xelatex`).
- Modify the `preamble` attribute of the template to include necessary packages for Unicode/CJK support. For CJK with `xelatex`, this typically involves:
- `\usepackage{fontspec}`
- `\usepackage{xeCJK}`
- `\setCJKmainfont{YourFontName}`: Specify a font installed on the target system that supports the required CJK characters (e.g., `SimHei`, `Microsoft YaHei`, `Noto Sans CJK SC`, `Source Han Sans SC`). **The chosen font MUST be installed on the system where Manim is run.**
- **Pass this custom `TexTemplate`** to the `MathTex` or `Tex` object during initialization using the `tex_template=` keyword argument.
- For `Text` objects (not `MathTex` or `Tex`), specify the font directly using the `font=` argument (e.g., `font="SimHei"`). This does _not_ use LaTeX but relies on system fonts via Pango/Cairo. Ensure the font is installed.
4. **Correct Code Example:**
```python
from manim import *
# Define a TexTemplate using XeLaTeX and xeCJK for CJK support
# IMPORTANT: Replace "SimHei" with a CJK font installed on the system.
cjk_template = TexTemplate(
tex_compiler="xelatex",
output_format=".xdv",
preamble=r"""
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{fontspec}
\usepackage{xeCJK}
\setCJKmainfont{SimHei} % Or Microsoft YaHei, Noto Sans CJK SC, etc.
"""
)
class CJKExample(Scene):
def construct(self):
# Using MathTex with Chinese characters requires the custom template
math_label_chinese = MathTex(
r"焓变: \Delta H = H_{\text{生成物}} - H_{\text{反应物}}",
tex_template=cjk_template, # Apply the template
font_size=48
)
# Text object uses system fonts directly via Pango/Cairo
# Ensure the font specified here is also installed
text_label_chinese = Text(
"这是一个标题",
font="SimHei", # Specify font directly for Text
font_size=60
)
self.add(VGroup(text_label_chinese, math_label_chinese).arrange(DOWN))
# --- Incorrect Example (Would likely cause the error) ---
# class IncorrectCJKExample(Scene):
# def construct(self):
# # Missing tex_template for MathTex with CJK characters
# wrong_math_label = MathTex(r"反应物")
# self.add(wrong_math_label)
```
### 【Manim Code Generation Rule: Layout Calculation and Overflow Prevention】
1. **Problem Description:**
Generated Manim scenes contain Mobjects (text, shapes, VGroups) that overlap each other or extend beyond the visible boundaries of the output frame/screen. This occurs even when using standard positioning methods like `to_edge`, `shift`, or `next_to`.
2. **Reason:**
Mobjects have inherent dimensions (width and height) determined by their content (e.g., text length, font size, shape parameters). Simple positioning commands might not automatically account for these dimensions or the dimensions of _other_ elements already placed in the scene. Relying solely on relative positioning (`next_to`) without considering the size of the objects involved, or using absolute positioning (`to_edge`) without reserving space for other elements, can lead to insufficient spacing (overlap) or positioning elements outside the frame's `frame_width` and `frame_height`. Lack of explicit calculation of available space and consistent buffering between elements and frame edges is the primary cause.
3. **Correct Practice (Must Follow):**
- **Define Explicit Buffers:** Establish clear minimum spacing values (buffers) for margins from the frame edges (e.g., `edge_buffer = 0.5`) and between distinct visual elements (e.g., `vertical_spacing = 0.5`).
- **Position Boundary Elements First:** Place elements intended to be near the edges (like titles or footers) using `to_edge(SIDE, buff=edge_buffer)`. **Crucially, add these boundary elements to the scene (`self.add(...)`) _before_ calculating the space available for central content.** This ensures methods like `.get_top()`, `.get_bottom()` return their final, accurate coordinates.
- **Calculate Available Space:** After placing boundary elements, determine the remaining available screen area. For vertical centering, calculate the top Y-coordinate (`available_top_y = title.get_bottom()[1] - vertical_spacing`) and bottom Y-coordinate (`available_bottom_y = footer.get_top()[1] + vertical_spacing`).
- **Create Central Content:** Construct the main visual element(s) (e.g., a diagram grouped in a `VGroup`). Use relative positioning (`next_to`, `arrange`) with appropriate internal buffers _within_ this group.
- **Position Central Content:** Calculate the center of the available space (e.g., `center_y = (available_top_y + available_bottom_y) / 2`). Move the entire central content group to this calculated center using `central_content.move_to(np.array([0, center_y, 0]))` (assuming horizontal centering).
- **Consider Scaling (If Necessary):** If the central content's natural size might exceed the calculated available space, consider using `central_content.scale_to_fit_height(available_top_y - available_bottom_y)` _before_ the final `move_to` step. Be mindful that scaling affects stroke widths and font sizes.
- **Verify Dimensions:** During development, use `print()` statements to check the width, height, and edge coordinates (`get_width()`, `get_height()`, `get_top()`, `get_bottom()`, `get_left()`, `get_right()`) of elements and groups after positioning to confirm they fit within the expected bounds and frame limits (`config.frame_width`, `config.frame_height`).
4. **Correct Code Example (Illustrative Snippet):**
```python
from manim import *
import numpy as np
class SafeLayoutExample(Scene):
def construct(self):
# 1. Define Buffers
edge_buffer = 0.75
element_vspace = 0.5
# 2. Position Boundary Elements First
title = Text("Scene Title").scale(1.2)
title.to_edge(UP, buff=edge_buffer)
footer = Text("Footer Text").scale(0.8)
footer.to_edge(DOWN, buff=edge_buffer)
# Add them to scene BEFORE calculating space
self.add(title, footer)
# 3. Calculate Available Space
available_top_y = title.get_bottom()[1] - element_vspace
available_bottom_y = footer.get_top()[1] + element_vspace
available_height = available_top_y - available_bottom_y
# 4. Create Central Content (Placeholder)
# In a real scenario, this would be a complex VGroup, diagram, etc.
central_content = Rectangle(
height=available_height * 0.8, # Example: Use 80% of available height
width=config.frame_width * 0.6,
color=BLUE,
fill_opacity=0.5
)
content_label = Text("Main Content Area").scale(0.7)
central_group = VGroup(central_content, content_label) # Group content
# Optional: Scale if needed (Example)
# if central_group.get_height() > available_height:
# central_group.scale_to_fit_height(available_height)
# 5. Position Central Content
center_y = (available_top_y + available_bottom_y) / 2
central_group.move_to(np.array([0, center_y, 0])) # Center horizontally and vertically in available space
# Add the positioned central content
self.add(central_group)
# Optional: Add visual guides for debugging
# guide_rect = Rectangle(height=available_height, width=config.frame_width, stroke_color=RED)
# guide_rect.move_to(np.array([0, center_y, 0]))
# self.add(guide_rect)
```
### 【Manim Code Generation Rule: Relative Positioning Overlap】
1. **Problem Description:**
When using relative positioning methods like `next_to(reference_object, direction, buff=...)`, two different Mobjects placed relative to _different_ reference objects can still overlap. This often happens when the reference objects are close to each other and the Mobjects are placed in the same relative `direction` (e.g., both placed `LEFT` of their respective references), causing their bounding boxes to occupy the same space. In the specific case, the "ΔH < 0" label (placed `LEFT` of the red ΔH arrow) overlapped with the "Enthalpy (H)" label/arrow (placed `LEFT` of the energy levels).
2. **Reason:**
The `next_to` method positions an object based on the bounding box of its _immediate_ reference object and the specified direction/buffer. It does not inherently check for potential collisions with _other_ objects in the scene that might be positioned relative to _different_ references nearby. If two reference points are horizontally (or vertically) close, placing objects on the same side (e.g., both `LEFT`) can easily lead to overlap, especially if the objects themselves have significant width (or height).
3. **Correct Practice (Must Follow):**
- **Visualize Spatial Relationships:** Before positioning an object relative to a reference, mentally (or by sketching) consider the positions and bounding boxes of _all_ nearby objects, not just the direct reference.
- **Choose Non-Conflicting Directions:** If placing an object to the `LEFT` of its reference would cause it to overlap with another element already occupying that space, consider placing it to the `RIGHT`, `UP`, or `DOWN` of its reference instead, provided it makes visual sense.
- **Adjust Buffers Carefully:** While increasing the buffer (`buff`) can sometimes resolve minor overlaps, it might not be sufficient for significant overlaps or might push the object too far away. Changing the `direction` is often a more effective solution.
- **Use Specific Anchors:** For more complex layouts, consider using more specific anchor points (e.g., `label.next_to(arrow.get_start(), RIGHT)`) or calculating absolute positions using `.get_center()`, `.get_top()`, etc., combined with vector additions (`+ UP*val`, `+ RIGHT*val`) and `move_to()`.
- **Check Output:** Always visually inspect the rendered output to catch unexpected overlaps that might arise from complex interactions between multiple relatively positioned elements.
4. **Correct Code Example (Illustrating the ΔH Label Fix):**
```python
from manim import *
class RelativeOverlapExample(Scene):
def construct(self):
# Reference objects close horizontally
ref1 = Dot(point=LEFT*2, color=RED)
ref2 = Dot(point=LEFT*1, color=BLUE)
# --- Incorrect Placement (Overlap Likely) ---
# label1 = Text("Label 1").next_to(ref1, LEFT, buff=0.2)
# label2 = Text("Label 2").next_to(ref2, LEFT, buff=0.2)
# overlap_group = VGroup(ref1, ref2, label1, label2).move_to(UP*1)
# --- Correct Placement (Avoiding Overlap) ---
label1_correct = Text("Label 1").next_to(ref1, LEFT, buff=0.2) # Place one LEFT
label2_correct = Text("Label 2").next_to(ref2, RIGHT, buff=0.2) # Place other RIGHT
correct_group = VGroup(ref1.copy().shift(DOWN*2), ref2.copy().shift(DOWN*2),
label1_correct.shift(DOWN*2), label2_correct.shift(DOWN*2))
# self.add(overlap_group) # This would show the overlap
self.add(correct_group) # This shows the corrected version
# Specific Case Analogy:
# ref1 is like the red Delta H arrow
# ref2 is like the grey Enthalpy axis arrow (or its associated label)
# Placing both labels LEFT caused overlap.
# Solution: Place Delta H label RIGHT of its arrow.
```
## Complete Python code example
### What is Enthalpy Change
```python
from manim import *
import numpy as np
class Main(Scene):
def construct(self):
# 1. Configuration
self.camera.background_color = WHITE # White background
# Colors adjusted for contrast on white background
title_color = BLACK
diagram_color = BLUE_D
arrow_color = RED_D
label_color = BLACK
formula_color = BLACK
axis_color = DARK_GRAY
# Define safe margins/buffers from screen edges
edge_buffer_top = 1.0
edge_buffer_bottom = 1.0
# Define vertical space between main elements
vspace_title_diagram = 0.6
vspace_diagram_formula = 0.6
# 2. Title (English)
title = Text("What is Enthalpy Change?", weight=BOLD, font_size=64, color=title_color)
title.to_edge(UP, buff=edge_buffer_top)
# 3. Formula (English)
formula = MathTex(
r"\Delta H = H_{\text{final}} - H_{\text{initial}} = Q_p",
font_size=42,
color=formula_color
)
formula.to_edge(DOWN, buff=edge_buffer_bottom)
# Add title and formula first to calculate remaining space
self.add(title, formula)
# 4. Energy Level Diagram (Exothermic Example)
# Adjust dimensions for better fit
level_width = 5.0
level_sep = 2.0
label_buff = 0.4
arrow_label_buff = 0.3 # Reset buffer, adjust if needed
axis_label_buff = 0.2
# Calculate horizontal positions based on new level_width
arrow_x_offset = level_width / 2 + 0.5
axis_x_offset = level_width / 2 + 1.2
# --- Create Diagram Elements ---
# Reactant Level
reactant_line = Line(
start=LEFT * level_width / 2,
end=RIGHT * level_width / 2,
color=diagram_color,
stroke_width=6
)
reactant_label = MathTex(r"H_{\text{Reactants}}", font_size=36, color=label_color)
reactant_label[0].set_color(diagram_color)
reactant_label.next_to(reactant_line, RIGHT, buff=label_buff)
# Product Level
product_line = Line(
start=LEFT * level_width / 2,
end=RIGHT * level_width / 2,
color=diagram_color,
stroke_width=6
)
product_label = MathTex(r"H_{\text{Products}}", font_size=36, color=label_color)
product_label[0].set_color(diagram_color)
product_label.next_to(product_line, RIGHT, buff=label_buff)
# Group lines and their labels temporarily for positioning
reactant_group = VGroup(reactant_line, reactant_label)
product_group = VGroup(product_line, product_label)
# Position product level below reactant level
product_group.next_to(reactant_group, DOWN, buff=level_sep, aligned_edge=LEFT)
# Now create arrows relative to the positioned lines
delta_h_arrow = Arrow(
start=reactant_line.get_center() + LEFT * arrow_x_offset,
end=product_line.get_center() + LEFT * arrow_x_offset,
color=arrow_color,
stroke_width=8,
max_tip_length_to_length_ratio=0.25,
buff=0.1
)
delta_h_label = MathTex(r"\Delta H < 0", font_size=42, color=arrow_color)
# Position label to the RIGHT of the arrow
delta_h_label.next_to(delta_h_arrow, RIGHT, buff=arrow_label_buff) # Changed LEFT to RIGHT
axis_arrow = Arrow(
start=product_line.get_center() + LEFT * axis_x_offset + DOWN * 0.3,
end=reactant_line.get_center() + LEFT * axis_x_offset + UP * 0.3,
color=axis_color,
stroke_width=4,
max_tip_length_to_length_ratio=0.15
)
axis_label = Text("Enthalpy (H)", font_size=30, color=axis_color)
axis_label.next_to(axis_arrow, LEFT, buff=axis_label_buff).rotate(PI/2)
# --- Group and Position the Final Diagram ---
diagram = VGroup(
reactant_group,
product_group,
delta_h_arrow, delta_h_label,
axis_arrow, axis_label
)
# Calculate the vertical center point available for the diagram
available_top = title.get_bottom()[1] - vspace_title_diagram
available_bottom = formula.get_top()[1] + vspace_diagram_formula
center_y = (available_top + available_bottom) / 2
# Move the diagram to this calculated center
diagram.move_to(np.array([0, center_y, 0]))
# 5. Add the positioned diagram to the scene
self.add(diagram)
```
### 【Manim Code Generation Rule: Background Color and Element Visibility】
1. **Problem Description:**
Generated Manim scenes have elements (Text, Shapes, Lines, etc.) that are invisible or have very poor contrast against the scene's background. This often happens when the requested background color (e.g., white) is the same as or very similar to the colors assigned to the Mobjects (e.g., using `WHITE` for text on a white background).
2. **Reason:**
The code assigned fixed colors to Mobjects without considering the final background color specified by the user or set in the scene configuration. Manim's default background is black, but if a different background like `WHITE` is used, elements colored `WHITE` or other very light colors will blend in and become invisible or difficult to see.
3. **Correct Practice (Must Follow):**
* **Set Background Color First:** Explicitly set the desired scene background color early in the `construct` method using `self.camera.background_color = DESIRED_COLOR` (e.g., `self.camera.background_color = WHITE`).
* **Define Contrasting Colors:** Create variables for the colors of different types of elements (e.g., `title_color`, `axis_color`, `shape_color`, `label_color`).
* **Choose Colors Based on Background:** Select color values for these variables that provide strong visual contrast against the *chosen* background color.
* If `background_color = WHITE`, use dark colors (e.g., `BLACK`, `DARK_GRAY`, `BLUE_E`, `RED_E`, `GREEN_E`, `ORANGE`).
* If `background_color = BLACK` (or default), use light colors (e.g., `WHITE`, `LIGHT_GRAY`, `BLUE_C`, `RED_C`, `YELLOW`, `GREEN_C`).
* **Apply Contrasting Colors:** Use these defined color variables when creating Mobjects (e.g., `Text("Title", color=title_color)`, `Axes(..., axis_config={"color": axis_color})`, `Circle(color=shape_color)`).
4. **Correct Code Example:**
```python
from manim import *
# --- Incorrect Example (Elements might be invisible on white background) ---
# class IncorrectVisibility(Scene):
# def construct(self):
# self.camera.background_color = WHITE # Set white background
#
# # Problem: Using WHITE text on WHITE background
# title = Text("Invisible Title", color=WHITE)
# shape = Circle(color=LIGHT_GRAY) # Poor contrast
#
# self.add(title, shape)
# --- Correct Example (Elements visible on white background) ---
class Main(Scene):
def construct(self):
# 1. Set Background Color First
self.camera.background_color = WHITE
# 2. Define Contrasting Colors for White Background
title_color = BLACK
shape_color = BLUE_E # A darker blue
label_color = DARK_GRAY
# 3. Apply Contrasting Colors
title = Text("Visible Title", color=title_color, font_size=48)
shape = Circle(color=shape_color, fill_opacity=0.5)
label = Text("Visible Label", color=label_color).next_to(shape, DOWN)
self.add(title.to_edge(UP))
self.add(shape, label)
# --- Correct Example (Elements visible on black background) ---
class Main(Scene):
def construct(self):
# 1. Set Background Color First (or use default black)
self.camera.background_color = BLACK
# 2. Define Contrasting Colors for Black Background
title_color = WHITE
shape_color = YELLOW # A light color
label_color = LIGHT_GRAY
# 3. Apply Contrasting Colors
title = Text("Visible Title", color=title_color, font_size=48)
shape = Circle(color=shape_color, fill_opacity=0.5)
label = Text("Visible Label", color=label_color).next_to(shape, DOWN)
self.add(title.to_edge(UP))
self.add(shape, label)
```
用户提示词
manim_image_code_prompt.txt
The Topic is:什么是正弦函数
The generated narration must use the Chinese.
Please only output the runnable Python script code.
Please output code without including any comments in the code.
To save processing and output time, please make the code as concise as possible while ensuring it runs correctly.
Because I execute the code by calling Python commands through Java, please ensure that the Python process does not block.
Code
from manim import *
import numpy as np
class Main(Scene):
def construct(self):
self.camera.background_color = WHITE
title_color = BLACK
circle_color = BLUE_D
axis_color = DARK_GRAY
radius_color = RED_D
sine_color = GREEN_D
angle_color = ORANGE
label_color = BLACK
dot_color = RED_D
dash_color = GRAY
title = Text("什么是正弦函数?", font="SimHei", font_size=60, color=title_color)
title.to_edge(UP, buff=0.8)
circle_radius = 1.5
angle = 120 * DEGREES
axes_circle = Axes(
x_range=[-2.2, 2.2, 1],
y_range=[-2.2, 2.2, 1],
x_length=4.4,
y_length=4.4,
tips=False,
axis_config={"color": axis_color, "include_numbers": False}
)
circle = Circle(radius=circle_radius, color=circle_color).move_to(axes_circle.coords_to_point(0, 0))
center_point = axes_circle.coords_to_point(0, 0)
dot_on_circle = axes_circle.coords_to_point(circle_radius * np.cos(angle), circle_radius * np.sin(angle))
radius = Line(center_point, dot_on_circle, color=radius_color, stroke_width=5)
sine_line_proj = DashedLine(dot_on_circle, axes_circle.coords_to_point(circle_radius * np.cos(angle), 0), color=dash_color)
sine_val_line = Line(axes_circle.coords_to_point(0, 0), axes_circle.coords_to_point(0, circle_radius * np.sin(angle)), color=sine_color, stroke_width=6)
angle_arc = Arc(radius=0.5, start_angle=0, angle=angle, color=angle_color).move_to(center_point)
theta_label = MathTex(r"\theta", color=angle_color, font_size=36).move_to(
axes_circle.coords_to_point(0.7 * np.cos(angle / 2), 0.7 * np.sin(angle / 2))
)
sine_label = MathTex(r"\sin(\theta)", color=sine_color, font_size=36).next_to(sine_val_line, LEFT, buff=0.15)
unit_circle_viz = VGroup(axes_circle, circle, radius, sine_line_proj, sine_val_line, angle_arc, theta_label, sine_label)
axes_graph = Axes(
x_range=[0, 2.5 * PI, PI / 2],
y_range=[-2, 2, 1],
x_length=6,
y_length=4.4, # Match circle axes height
tips=False,
axis_config={"color": axis_color, "include_numbers": False},
x_axis_config={"label_direction": DOWN},
y_axis_config={"label_direction": LEFT}
)
x_axis_label = axes_graph.get_x_axis_label("x", edge=DOWN, direction=DOWN, buff=0.2)
y_axis_label = axes_graph.get_y_axis_label("y", edge=LEFT, direction=LEFT, buff=0.2)
axes_graph.add(x_axis_label, y_axis_label)
sine_graph = axes_graph.plot(lambda x: circle_radius * np.sin(x), x_range=[0, 2.1 * PI], color=sine_color)
graph_dot = Dot(axes_graph.coords_to_point(angle, circle_radius * np.sin(angle)), color=dot_color)
connecting_line = DashedLine(sine_val_line.get_end(), graph_dot.get_center(), color=dash_color)
sine_wave_viz = VGroup(axes_graph, sine_graph, graph_dot, connecting_line)
unit_circle_viz.shift(LEFT * 3.2)
sine_wave_viz.next_to(unit_circle_viz, RIGHT, buff=0.5, aligned_edge=DOWN) # Align bottom axes
visual_group = VGroup(unit_circle_viz, sine_wave_viz)
available_top = title.get_bottom()[1] - 0.5
available_bottom = -config.frame_height / 2 + 0.5
available_height = available_top - available_bottom
if visual_group.height > available_height:
visual_group.scale_to_fit_height(available_height)
center_y = (available_top + available_bottom) / 2
visual_group.move_to(np.array([0, center_y, 0]))
self.add(title, visual_group)
执行代码
manim -s -qh --format=png 20.py
输出文件 media/images/20/Main_ManimCE_v0.19.0.png ManimCE_v0.19.0.png 是固定的
这个命令用于调用 Manim Community Edition 渲染器来处理名为 20.py
的 Python 脚本文件,并生成一个静态的 PNG 图片,具体解释如下:
-s
表示“save last frame”(保存最后一帧),即只渲染场景的最后一帧,而不生成完整的动画视频。这对于生成静态封面或预览图特别有用。-qh
指定使用高质量预设。Manim 通常提供不同的质量级别(例如低质量-ql
、中质量-qm
和高质量-qh
),这里-qh
表示采用高质量设置进行渲染,确保输出效果更佳。--format=png
强制渲染器以 PNG 格式导出结果,而不是默认的视频格式。这意味着输出文件将是一张 PNG 格式的图片。1.润色一下这篇文档 2.润色过程中不要省略任何代码 3.增加解释和说明方便读者理解