manim 0.17.0__py3-none-any.whl → 0.19.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- manim/__init__.py +11 -6
- manim/__main__.py +62 -19
- manim/_config/__init__.py +10 -9
- manim/_config/cli_colors.py +26 -9
- manim/_config/default.cfg +1 -3
- manim/_config/logger_utils.py +23 -13
- manim/_config/utils.py +662 -468
- manim/animation/animation.py +164 -18
- manim/animation/changing.py +34 -23
- manim/animation/composition.py +265 -67
- manim/animation/creation.py +208 -26
- manim/animation/fading.py +16 -18
- manim/animation/growing.py +35 -15
- manim/animation/indication.py +150 -76
- manim/animation/movement.py +56 -22
- manim/animation/numbers.py +64 -6
- manim/animation/rotation.py +78 -7
- manim/animation/specialized.py +6 -7
- manim/animation/speedmodifier.py +13 -10
- manim/animation/transform.py +14 -11
- manim/animation/transform_matching_parts.py +3 -4
- manim/animation/updaters/mobject_update_utils.py +152 -30
- manim/animation/updaters/update.py +10 -7
- manim/camera/camera.py +182 -118
- manim/camera/mapping_camera.py +34 -3
- manim/camera/moving_camera.py +95 -74
- manim/camera/multi_camera.py +23 -15
- manim/camera/three_d_camera.py +70 -52
- manim/cli/__init__.py +17 -0
- manim/cli/cfg/group.py +76 -44
- manim/cli/checkhealth/checks.py +192 -0
- manim/cli/checkhealth/commands.py +90 -0
- manim/cli/default_group.py +158 -25
- manim/cli/init/commands.py +33 -25
- manim/cli/plugins/commands.py +16 -3
- manim/cli/render/commands.py +72 -60
- manim/cli/render/ease_of_access_options.py +4 -3
- manim/cli/render/global_options.py +59 -17
- manim/cli/render/output_options.py +6 -5
- manim/cli/render/render_options.py +98 -33
- manim/constants.py +109 -59
- manim/data_structures.py +31 -0
- manim/mobject/frame.py +8 -5
- manim/mobject/geometry/__init__.py +1 -0
- manim/mobject/geometry/arc.py +277 -135
- manim/mobject/geometry/boolean_ops.py +32 -31
- manim/mobject/geometry/labeled.py +376 -0
- manim/mobject/geometry/line.py +192 -87
- manim/mobject/geometry/polygram.py +224 -58
- manim/mobject/geometry/shape_matchers.py +61 -25
- manim/mobject/geometry/tips.py +122 -48
- manim/mobject/graph.py +1027 -419
- manim/mobject/graphing/coordinate_systems.py +533 -278
- manim/mobject/graphing/functions.py +53 -32
- manim/mobject/graphing/number_line.py +123 -65
- manim/mobject/graphing/probability.py +88 -62
- manim/mobject/graphing/scale.py +33 -19
- manim/mobject/logo.py +118 -28
- manim/mobject/matrix.py +87 -83
- manim/mobject/mobject.py +912 -442
- manim/mobject/opengl/dot_cloud.py +16 -5
- manim/mobject/opengl/opengl_compatibility.py +4 -2
- manim/mobject/opengl/opengl_geometry.py +254 -153
- manim/mobject/opengl/opengl_image_mobject.py +3 -1
- manim/mobject/opengl/opengl_mobject.py +779 -482
- manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
- manim/mobject/opengl/opengl_surface.py +14 -92
- manim/mobject/opengl/opengl_three_dimensions.py +12 -8
- manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
- manim/mobject/svg/brace.py +173 -41
- manim/mobject/svg/svg_mobject.py +139 -53
- manim/mobject/table.py +61 -68
- manim/mobject/text/code_mobject.py +193 -539
- manim/mobject/text/numbers.py +81 -34
- manim/mobject/text/tex_mobject.py +130 -78
- manim/mobject/text/text_mobject.py +288 -164
- manim/mobject/three_d/polyhedra.py +111 -13
- manim/mobject/three_d/three_d_utils.py +17 -8
- manim/mobject/three_d/three_dimensions.py +239 -106
- manim/mobject/types/image_mobject.py +50 -30
- manim/mobject/types/point_cloud_mobject.py +120 -75
- manim/mobject/types/vectorized_mobject.py +841 -408
- manim/mobject/value_tracker.py +105 -38
- manim/mobject/vector_field.py +50 -31
- manim/opengl/__init__.py +3 -3
- manim/plugins/__init__.py +14 -1
- manim/plugins/plugins_flags.py +10 -14
- manim/renderer/cairo_renderer.py +65 -50
- manim/renderer/opengl_renderer.py +89 -69
- manim/renderer/opengl_renderer_window.py +39 -18
- manim/renderer/shader.py +123 -87
- manim/renderer/shader_wrapper.py +44 -28
- manim/renderer/vectorized_mobject_rendering.py +38 -10
- manim/scene/moving_camera_scene.py +32 -3
- manim/scene/scene.py +507 -242
- manim/scene/scene_file_writer.py +371 -220
- manim/scene/section.py +20 -16
- manim/scene/three_d_scene.py +14 -22
- manim/scene/vector_space_scene.py +223 -129
- manim/scene/zoomed_scene.py +46 -41
- manim/typing.py +990 -0
- manim/utils/bezier.py +1823 -371
- manim/utils/caching.py +12 -5
- manim/utils/color/AS2700.py +236 -0
- manim/utils/color/BS381.py +318 -0
- manim/utils/color/DVIPSNAMES.py +96 -0
- manim/utils/color/SVGNAMES.py +179 -0
- manim/utils/color/X11.py +533 -0
- manim/utils/color/XKCD.py +952 -0
- manim/utils/color/__init__.py +61 -0
- manim/utils/color/core.py +1667 -0
- manim/utils/color/manim_colors.py +218 -0
- manim/utils/commands.py +48 -20
- manim/utils/config_ops.py +39 -19
- manim/utils/debug.py +8 -7
- manim/utils/deprecation.py +86 -39
- manim/utils/docbuild/__init__.py +17 -0
- manim/utils/docbuild/autoaliasattr_directive.py +236 -0
- manim/utils/docbuild/autocolor_directive.py +99 -0
- manim/utils/docbuild/manim_directive.py +94 -41
- manim/utils/docbuild/module_parsing.py +245 -0
- manim/utils/exceptions.py +6 -0
- manim/utils/family.py +5 -3
- manim/utils/family_ops.py +17 -4
- manim/utils/file_ops.py +27 -17
- manim/utils/hashing.py +55 -45
- manim/utils/images.py +13 -7
- manim/utils/ipython_magic.py +13 -7
- manim/utils/iterables.py +163 -120
- manim/utils/module_ops.py +66 -24
- manim/utils/opengl.py +77 -24
- manim/utils/parameter_parsing.py +32 -0
- manim/utils/paths.py +30 -33
- manim/utils/polylabel.py +235 -0
- manim/utils/qhull.py +218 -0
- manim/utils/rate_functions.py +98 -32
- manim/utils/simple_functions.py +25 -33
- manim/utils/sounds.py +7 -1
- manim/utils/space_ops.py +188 -115
- manim/utils/testing/__init__.py +17 -0
- manim/utils/testing/_frames_testers.py +13 -8
- manim/utils/testing/_show_diff.py +5 -3
- manim/utils/testing/_test_class_makers.py +34 -18
- manim/utils/testing/frames_comparison.py +37 -19
- manim/utils/tex.py +130 -198
- manim/utils/tex_file_writing.py +77 -47
- manim/utils/tex_templates.py +2 -1
- manim/utils/unit.py +6 -5
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
- manim-0.19.1.dist-info/RECORD +220 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
- manim-0.19.1.dist-info/entry_points.txt +3 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
- manim/cli/new/group.py +0 -189
- manim/communitycolors.py +0 -9
- manim/gui/__init__.py +0 -0
- manim/gui/gui.py +0 -82
- manim/plugins/import_plugins.py +0 -43
- manim/utils/color.py +0 -552
- manim-0.17.0.dist-info/RECORD +0 -206
- manim-0.17.0.dist-info/entry_points.txt +0 -4
- /manim/cli/{new → checkhealth}/__init__.py +0 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE +0 -0
|
@@ -6,50 +6,28 @@ __all__ = [
|
|
|
6
6
|
"Code",
|
|
7
7
|
]
|
|
8
8
|
|
|
9
|
-
import html
|
|
10
|
-
import os
|
|
11
9
|
import re
|
|
12
10
|
from pathlib import Path
|
|
11
|
+
from typing import Any, Literal
|
|
13
12
|
|
|
14
|
-
import
|
|
13
|
+
from bs4 import BeautifulSoup, Tag
|
|
15
14
|
from pygments import highlight
|
|
16
15
|
from pygments.formatters.html import HtmlFormatter
|
|
17
|
-
from pygments.lexers import get_lexer_by_name, guess_lexer_for_filename
|
|
16
|
+
from pygments.lexers import get_lexer_by_name, guess_lexer, guess_lexer_for_filename
|
|
18
17
|
from pygments.styles import get_all_styles
|
|
19
18
|
|
|
20
19
|
from manim.constants import *
|
|
21
20
|
from manim.mobject.geometry.arc import Dot
|
|
22
|
-
from manim.mobject.geometry.polygram import RoundedRectangle
|
|
23
21
|
from manim.mobject.geometry.shape_matchers import SurroundingRectangle
|
|
24
|
-
from manim.mobject.
|
|
25
|
-
from manim.mobject.types.vectorized_mobject import VGroup
|
|
26
|
-
from manim.
|
|
22
|
+
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
|
|
23
|
+
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
|
|
24
|
+
from manim.typing import StrPath
|
|
25
|
+
from manim.utils.color import WHITE, ManimColor
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
class Code(
|
|
28
|
+
class Code(VMobject, metaclass=ConvertToOpenGL):
|
|
30
29
|
"""A highlighted source code listing.
|
|
31
30
|
|
|
32
|
-
An object ``listing`` of :class:`.Code` is a :class:`.VGroup` consisting
|
|
33
|
-
of three objects:
|
|
34
|
-
|
|
35
|
-
- The background, ``listing.background_mobject``. This is either
|
|
36
|
-
a :class:`.Rectangle` (if the listing has been initialized with
|
|
37
|
-
``background="rectangle"``, the default option) or a :class:`.VGroup`
|
|
38
|
-
resembling a window (if ``background="window"`` has been passed).
|
|
39
|
-
|
|
40
|
-
- The line numbers, ``listing.line_numbers`` (a :class:`.Paragraph`
|
|
41
|
-
object).
|
|
42
|
-
|
|
43
|
-
- The highlighted code itself, ``listing.code`` (a :class:`.Paragraph`
|
|
44
|
-
object).
|
|
45
|
-
|
|
46
|
-
.. WARNING::
|
|
47
|
-
|
|
48
|
-
Using a :class:`.Transform` on text with leading whitespace (and in
|
|
49
|
-
this particular case: code) can look
|
|
50
|
-
`weird <https://github.com/3b1b/manim/issues/1067>`_. Consider using
|
|
51
|
-
:meth:`remove_invisible_chars` to resolve this issue.
|
|
52
|
-
|
|
53
31
|
Examples
|
|
54
32
|
--------
|
|
55
33
|
|
|
@@ -58,16 +36,16 @@ class Code(VGroup):
|
|
|
58
36
|
listing = Code(
|
|
59
37
|
"helloworldcpp.cpp",
|
|
60
38
|
tab_width=4,
|
|
61
|
-
|
|
62
|
-
background_stroke_color=WHITE,
|
|
63
|
-
insert_line_no=True,
|
|
64
|
-
style=Code.styles_list[15],
|
|
39
|
+
formatter_style="emacs",
|
|
65
40
|
background="window",
|
|
66
41
|
language="cpp",
|
|
42
|
+
background_config={"stroke_color": WHITE},
|
|
43
|
+
paragraph_config={"font": "Noto Sans Mono"},
|
|
67
44
|
)
|
|
68
45
|
|
|
69
|
-
We can also render code passed as a string
|
|
70
|
-
|
|
46
|
+
We can also render code passed as a string. As the automatic language
|
|
47
|
+
detection can be a bit flaky, it is recommended to specify the language
|
|
48
|
+
explicitly:
|
|
71
49
|
|
|
72
50
|
.. manim:: CodeFromString
|
|
73
51
|
:save_last_frame:
|
|
@@ -81,532 +59,208 @@ class Code(VGroup):
|
|
|
81
59
|
s = Square()
|
|
82
60
|
self.play(FadeIn(s))
|
|
83
61
|
self.play(s.animate.scale(2))
|
|
84
|
-
self.wait()
|
|
85
|
-
|
|
86
|
-
rendered_code = Code(
|
|
87
|
-
|
|
62
|
+
self.wait()'''
|
|
63
|
+
|
|
64
|
+
rendered_code = Code(
|
|
65
|
+
code_string=code,
|
|
66
|
+
language="python",
|
|
67
|
+
background="window",
|
|
68
|
+
background_config={"stroke_color": "maroon"},
|
|
69
|
+
)
|
|
88
70
|
self.add(rendered_code)
|
|
89
71
|
|
|
90
72
|
Parameters
|
|
91
73
|
----------
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
74
|
+
code_file
|
|
75
|
+
The path to the code file to display.
|
|
76
|
+
code_string
|
|
77
|
+
Alternatively, the code string to display.
|
|
78
|
+
language
|
|
79
|
+
The programming language of the code. If not specified, it will be
|
|
80
|
+
guessed from the file extension or the code itself.
|
|
81
|
+
formatter_style
|
|
82
|
+
The style to use for the code highlighting. Defaults to ``"vim"``.
|
|
83
|
+
A list of all available styles can be obtained by calling
|
|
84
|
+
:meth:`.Code.get_styles_list`.
|
|
97
85
|
tab_width
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
font
|
|
104
|
-
The name of the text font to be used. Defaults to ``"Monospac821 BT"``.
|
|
105
|
-
stroke_width
|
|
106
|
-
Stroke width for text. 0 is recommended, and the default.
|
|
107
|
-
margin
|
|
108
|
-
Inner margin of text from the background. Defaults to 0.3.
|
|
109
|
-
indentation_chars
|
|
110
|
-
"Indentation chars" refers to the spaces/tabs at the beginning of a given code line. Defaults to ``" "`` (spaces).
|
|
86
|
+
The width of a tab character in spaces. Defaults to 4.
|
|
87
|
+
add_line_numbers
|
|
88
|
+
Whether to display line numbers. Defaults to ``True``.
|
|
89
|
+
line_numbers_from
|
|
90
|
+
The first line number to display. Defaults to 1.
|
|
111
91
|
background
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
style
|
|
126
|
-
Defines the style type of displayed code. You can see possible names of styles in with :attr:`styles_list`. Defaults to ``"vim"``.
|
|
127
|
-
language
|
|
128
|
-
Specifies the programming language the given code was written in. If ``None``
|
|
129
|
-
(the default), the language will be automatically detected. For the list of
|
|
130
|
-
possible options, visit https://pygments.org/docs/lexers/ and look for
|
|
131
|
-
'aliases or short names'.
|
|
132
|
-
generate_html_file
|
|
133
|
-
Defines whether to generate highlighted html code to the folder `assets/codes/generated_html_files`. Defaults to `False`.
|
|
134
|
-
|
|
135
|
-
Attributes
|
|
136
|
-
----------
|
|
137
|
-
background_mobject : :class:`~.VGroup`
|
|
138
|
-
The background of the code listing.
|
|
139
|
-
line_numbers : :class:`~.Paragraph`
|
|
140
|
-
The line numbers for the code listing. Empty, if
|
|
141
|
-
``insert_line_no=False`` has been specified.
|
|
142
|
-
code : :class:`~.Paragraph`
|
|
143
|
-
The highlighted code.
|
|
144
|
-
|
|
92
|
+
The type of background to use. Can be either ``"rectangle"`` (the
|
|
93
|
+
default) or ``"window"``.
|
|
94
|
+
background_config
|
|
95
|
+
Keyword arguments passed to the background constructor. Default
|
|
96
|
+
settings are stored in the class attribute
|
|
97
|
+
:attr:`.default_background_config` (which can also be modified
|
|
98
|
+
directly).
|
|
99
|
+
paragraph_config
|
|
100
|
+
Keyword arguments passed to the constructor of the
|
|
101
|
+
:class:`.Paragraph` objects holding the code, and the line
|
|
102
|
+
numbers. Default settings are stored in the class attribute
|
|
103
|
+
:attr:`.default_paragraph_config` (which can also be modified
|
|
104
|
+
directly).
|
|
145
105
|
"""
|
|
146
106
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
107
|
+
_styles_list_cache: list[str] | None = None
|
|
108
|
+
default_background_config: dict[str, Any] = {
|
|
109
|
+
"buff": 0.3,
|
|
110
|
+
"fill_color": ManimColor("#222"),
|
|
111
|
+
"stroke_color": WHITE,
|
|
112
|
+
"corner_radius": 0.2,
|
|
113
|
+
"stroke_width": 1,
|
|
114
|
+
"fill_opacity": 1,
|
|
115
|
+
}
|
|
116
|
+
default_paragraph_config: dict[str, Any] = {
|
|
117
|
+
"font": "Monospace",
|
|
118
|
+
"font_size": 24,
|
|
119
|
+
"line_spacing": 0.5,
|
|
120
|
+
"disable_ligatures": True,
|
|
121
|
+
}
|
|
122
|
+
code: VMobject
|
|
154
123
|
|
|
155
124
|
def __init__(
|
|
156
125
|
self,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
tab_width: int = 3,
|
|
160
|
-
line_spacing: float = 0.3,
|
|
161
|
-
font_size: float = 24,
|
|
162
|
-
font: str = "Monospac821 BT",
|
|
163
|
-
stroke_width: float = 0,
|
|
164
|
-
margin: float = 0.3,
|
|
165
|
-
indentation_chars: str = " ",
|
|
166
|
-
background: str = "rectangle", # or window
|
|
167
|
-
background_stroke_width: float = 1,
|
|
168
|
-
background_stroke_color: str = WHITE,
|
|
169
|
-
corner_radius: float = 0.2,
|
|
170
|
-
insert_line_no: bool = True,
|
|
171
|
-
line_no_from: int = 1,
|
|
172
|
-
line_no_buff: float = 0.4,
|
|
173
|
-
style: str = "vim",
|
|
126
|
+
code_file: StrPath | None = None,
|
|
127
|
+
code_string: str | None = None,
|
|
174
128
|
language: str | None = None,
|
|
175
|
-
|
|
176
|
-
|
|
129
|
+
formatter_style: str = "vim",
|
|
130
|
+
tab_width: int = 4,
|
|
131
|
+
add_line_numbers: bool = True,
|
|
132
|
+
line_numbers_from: int = 1,
|
|
133
|
+
background: Literal["rectangle", "window"] = "rectangle",
|
|
134
|
+
background_config: dict[str, Any] | None = None,
|
|
135
|
+
paragraph_config: dict[str, Any] | None = None,
|
|
177
136
|
):
|
|
178
|
-
super().__init__(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
self.font_size = font_size
|
|
188
|
-
self.margin = margin
|
|
189
|
-
self.indentation_chars = indentation_chars
|
|
190
|
-
self.background = background
|
|
191
|
-
self.corner_radius = corner_radius
|
|
192
|
-
self.insert_line_no = insert_line_no
|
|
193
|
-
self.line_no_from = line_no_from
|
|
194
|
-
self.line_no_buff = line_no_buff
|
|
195
|
-
self.style = style
|
|
196
|
-
self.language = language
|
|
197
|
-
self.generate_html_file = generate_html_file
|
|
198
|
-
|
|
199
|
-
self.file_path = None
|
|
200
|
-
self.file_name = file_name
|
|
201
|
-
if self.file_name:
|
|
202
|
-
self._ensure_valid_file()
|
|
203
|
-
self.code_string = self.file_path.read_text(encoding="utf-8")
|
|
204
|
-
elif code:
|
|
205
|
-
self.code_string = code
|
|
206
|
-
else:
|
|
207
|
-
raise ValueError(
|
|
208
|
-
"Neither a code file nor a code string have been specified.",
|
|
209
|
-
)
|
|
210
|
-
if isinstance(self.style, str):
|
|
211
|
-
self.style = self.style.lower()
|
|
212
|
-
self._gen_html_string()
|
|
213
|
-
strati = self.html_string.find("background:")
|
|
214
|
-
self.background_color = self.html_string[strati + 12 : strati + 19]
|
|
215
|
-
self._gen_code_json()
|
|
216
|
-
|
|
217
|
-
self.code = self._gen_colored_lines()
|
|
218
|
-
if self.insert_line_no:
|
|
219
|
-
self.line_numbers = self._gen_line_numbers()
|
|
220
|
-
self.line_numbers.next_to(self.code, direction=LEFT, buff=self.line_no_buff)
|
|
221
|
-
if self.background == "rectangle":
|
|
222
|
-
if self.insert_line_no:
|
|
223
|
-
foreground = VGroup(self.code, self.line_numbers)
|
|
137
|
+
super().__init__()
|
|
138
|
+
|
|
139
|
+
if code_file is not None:
|
|
140
|
+
code_file = Path(code_file)
|
|
141
|
+
code_string = code_file.read_text(encoding="utf-8")
|
|
142
|
+
lexer = guess_lexer_for_filename(code_file.name, code_string)
|
|
143
|
+
elif code_string is not None:
|
|
144
|
+
if language is not None:
|
|
145
|
+
lexer = get_lexer_by_name(language)
|
|
224
146
|
else:
|
|
225
|
-
|
|
226
|
-
rect = SurroundingRectangle(
|
|
227
|
-
foreground,
|
|
228
|
-
buff=self.margin,
|
|
229
|
-
color=self.background_color,
|
|
230
|
-
fill_color=self.background_color,
|
|
231
|
-
stroke_width=self.background_stroke_width,
|
|
232
|
-
stroke_color=self.background_stroke_color,
|
|
233
|
-
fill_opacity=1,
|
|
234
|
-
)
|
|
235
|
-
rect.round_corners(self.corner_radius)
|
|
236
|
-
self.background_mobject = rect
|
|
147
|
+
lexer = guess_lexer(code_string)
|
|
237
148
|
else:
|
|
238
|
-
|
|
239
|
-
foreground = VGroup(self.code, self.line_numbers)
|
|
240
|
-
else:
|
|
241
|
-
foreground = self.code
|
|
242
|
-
height = foreground.height + 0.1 * 3 + 2 * self.margin
|
|
243
|
-
width = foreground.width + 0.1 * 3 + 2 * self.margin
|
|
244
|
-
|
|
245
|
-
rect = RoundedRectangle(
|
|
246
|
-
corner_radius=self.corner_radius,
|
|
247
|
-
height=height,
|
|
248
|
-
width=width,
|
|
249
|
-
stroke_width=self.background_stroke_width,
|
|
250
|
-
stroke_color=self.background_stroke_color,
|
|
251
|
-
color=self.background_color,
|
|
252
|
-
fill_opacity=1,
|
|
253
|
-
)
|
|
254
|
-
red_button = Dot(radius=0.1, stroke_width=0, color="#ff5f56")
|
|
255
|
-
red_button.shift(LEFT * 0.1 * 3)
|
|
256
|
-
yellow_button = Dot(radius=0.1, stroke_width=0, color="#ffbd2e")
|
|
257
|
-
green_button = Dot(radius=0.1, stroke_width=0, color="#27c93f")
|
|
258
|
-
green_button.shift(RIGHT * 0.1 * 3)
|
|
259
|
-
buttons = VGroup(red_button, yellow_button, green_button)
|
|
260
|
-
buttons.shift(
|
|
261
|
-
UP * (height / 2 - 0.1 * 2 - 0.05)
|
|
262
|
-
+ LEFT * (width / 2 - 0.1 * 5 - self.corner_radius / 2 - 0.05),
|
|
263
|
-
)
|
|
149
|
+
raise ValueError("Either a code file or a code string must be specified.")
|
|
264
150
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
self.background_mobject, self.line_numbers, self.code, **kwargs
|
|
272
|
-
)
|
|
273
|
-
else:
|
|
274
|
-
super().__init__(
|
|
275
|
-
self.background_mobject,
|
|
276
|
-
Dot(fill_opacity=0, stroke_opacity=0),
|
|
277
|
-
self.code,
|
|
278
|
-
**kwargs,
|
|
279
|
-
)
|
|
280
|
-
self.move_to(np.array([0, 0, 0]))
|
|
281
|
-
|
|
282
|
-
def _ensure_valid_file(self):
|
|
283
|
-
"""Function to validate file."""
|
|
284
|
-
if self.file_name is None:
|
|
285
|
-
raise Exception("Must specify file for Code")
|
|
286
|
-
possible_paths = [
|
|
287
|
-
Path() / "assets" / "codes" / self.file_name,
|
|
288
|
-
Path(self.file_name).expanduser(),
|
|
289
|
-
]
|
|
290
|
-
for path in possible_paths:
|
|
291
|
-
if path.exists():
|
|
292
|
-
self.file_path = path
|
|
293
|
-
return
|
|
294
|
-
error = (
|
|
295
|
-
f"From: {Path.cwd()}, could not find {self.file_name} at either "
|
|
296
|
-
+ f"of these locations: {list(map(str, possible_paths))}"
|
|
297
|
-
)
|
|
298
|
-
raise OSError(error)
|
|
299
|
-
|
|
300
|
-
def _gen_line_numbers(self):
|
|
301
|
-
"""Function to generate line_numbers.
|
|
302
|
-
|
|
303
|
-
Returns
|
|
304
|
-
-------
|
|
305
|
-
:class:`~.Paragraph`
|
|
306
|
-
The generated line_numbers according to parameters.
|
|
307
|
-
"""
|
|
308
|
-
line_numbers_array = []
|
|
309
|
-
for line_no in range(0, self.code_json.__len__()):
|
|
310
|
-
number = str(self.line_no_from + line_no)
|
|
311
|
-
line_numbers_array.append(number)
|
|
312
|
-
line_numbers = Paragraph(
|
|
313
|
-
*list(line_numbers_array),
|
|
314
|
-
line_spacing=self.line_spacing,
|
|
315
|
-
alignment="right",
|
|
316
|
-
font_size=self.font_size,
|
|
317
|
-
font=self.font,
|
|
318
|
-
disable_ligatures=True,
|
|
319
|
-
stroke_width=self.stroke_width,
|
|
320
|
-
)
|
|
321
|
-
for i in line_numbers:
|
|
322
|
-
i.set_color(self.default_color)
|
|
323
|
-
return line_numbers
|
|
324
|
-
|
|
325
|
-
def _gen_colored_lines(self):
|
|
326
|
-
"""Function to generate code.
|
|
327
|
-
|
|
328
|
-
Returns
|
|
329
|
-
-------
|
|
330
|
-
:class:`~.Paragraph`
|
|
331
|
-
The generated code according to parameters.
|
|
332
|
-
"""
|
|
333
|
-
lines_text = []
|
|
334
|
-
for line_no in range(0, self.code_json.__len__()):
|
|
335
|
-
line_str = ""
|
|
336
|
-
for word_index in range(self.code_json[line_no].__len__()):
|
|
337
|
-
line_str = line_str + self.code_json[line_no][word_index][0]
|
|
338
|
-
lines_text.append(self.tab_spaces[line_no] * "\t" + line_str)
|
|
339
|
-
code = Paragraph(
|
|
340
|
-
*list(lines_text),
|
|
341
|
-
line_spacing=self.line_spacing,
|
|
342
|
-
tab_width=self.tab_width,
|
|
343
|
-
font_size=self.font_size,
|
|
344
|
-
font=self.font,
|
|
345
|
-
disable_ligatures=True,
|
|
346
|
-
stroke_width=self.stroke_width,
|
|
151
|
+
code_string = code_string.expandtabs(tabsize=tab_width)
|
|
152
|
+
|
|
153
|
+
formatter = HtmlFormatter(
|
|
154
|
+
style=formatter_style,
|
|
155
|
+
noclasses=True,
|
|
156
|
+
cssclasses="",
|
|
347
157
|
)
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
line_char_index = self.tab_spaces[line_no]
|
|
351
|
-
for word_index in range(self.code_json[line_no].__len__()):
|
|
352
|
-
line[
|
|
353
|
-
line_char_index : line_char_index
|
|
354
|
-
+ self.code_json[line_no][word_index][0].__len__()
|
|
355
|
-
].set_color(self.code_json[line_no][word_index][1])
|
|
356
|
-
line_char_index += self.code_json[line_no][word_index][0].__len__()
|
|
357
|
-
return code
|
|
358
|
-
|
|
359
|
-
def _gen_html_string(self):
|
|
360
|
-
"""Function to generate html string with code highlighted and stores in variable html_string."""
|
|
361
|
-
self.html_string = _hilite_me(
|
|
362
|
-
self.code_string,
|
|
363
|
-
self.language,
|
|
364
|
-
self.style,
|
|
365
|
-
self.insert_line_no,
|
|
366
|
-
"border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;",
|
|
367
|
-
self.file_path,
|
|
368
|
-
self.line_no_from,
|
|
158
|
+
soup = BeautifulSoup(
|
|
159
|
+
highlight(code_string, lexer, formatter), features="html.parser"
|
|
369
160
|
)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (
|
|
385
|
-
self.background_color == "#111111"
|
|
386
|
-
or self.background_color == "#272822"
|
|
387
|
-
or self.background_color == "#202020"
|
|
388
|
-
or self.background_color == "#000000"
|
|
389
|
-
):
|
|
390
|
-
self.default_color = "#ffffff"
|
|
391
|
-
else:
|
|
392
|
-
self.default_color = "#000000"
|
|
393
|
-
# print(self.default_color,self.background_color)
|
|
394
|
-
for i in range(3, -1, -1):
|
|
395
|
-
self.html_string = self.html_string.replace("</" + " " * i, "</")
|
|
396
|
-
|
|
397
|
-
# handle pygments bug
|
|
398
|
-
# https://github.com/pygments/pygments/issues/961
|
|
399
|
-
self.html_string = self.html_string.replace("<span></span>", "")
|
|
400
|
-
|
|
401
|
-
for i in range(10, -1, -1):
|
|
402
|
-
self.html_string = self.html_string.replace(
|
|
403
|
-
"</span>" + " " * i,
|
|
404
|
-
" " * i + "</span>",
|
|
405
|
-
)
|
|
406
|
-
self.html_string = self.html_string.replace("background-color:", "background:")
|
|
407
|
-
|
|
408
|
-
if self.insert_line_no:
|
|
409
|
-
start_point = self.html_string.find("</td><td><pre")
|
|
410
|
-
start_point = start_point + 9
|
|
411
|
-
else:
|
|
412
|
-
start_point = self.html_string.find("<pre")
|
|
413
|
-
self.html_string = self.html_string[start_point:]
|
|
414
|
-
# print(self.html_string)
|
|
415
|
-
lines = self.html_string.split("\n")
|
|
416
|
-
lines = lines[0 : lines.__len__() - 2]
|
|
417
|
-
start_point = lines[0].find(">")
|
|
418
|
-
lines[0] = lines[0][start_point + 1 :]
|
|
419
|
-
# print(lines)
|
|
420
|
-
self.code_json = []
|
|
421
|
-
self.tab_spaces = []
|
|
422
|
-
code_json_line_index = -1
|
|
423
|
-
for line_index in range(0, lines.__len__()):
|
|
424
|
-
# print(lines[line_index])
|
|
425
|
-
self.code_json.append([])
|
|
426
|
-
code_json_line_index = code_json_line_index + 1
|
|
427
|
-
if lines[line_index].startswith(self.indentation_chars):
|
|
428
|
-
start_point = lines[line_index].find("<")
|
|
429
|
-
starting_string = lines[line_index][:start_point]
|
|
430
|
-
indentation_chars_count = lines[line_index][:start_point].count(
|
|
431
|
-
self.indentation_chars,
|
|
432
|
-
)
|
|
433
|
-
if (
|
|
434
|
-
starting_string.__len__()
|
|
435
|
-
!= indentation_chars_count * self.indentation_chars.__len__()
|
|
436
|
-
):
|
|
437
|
-
lines[line_index] = (
|
|
438
|
-
"\t" * indentation_chars_count
|
|
439
|
-
+ starting_string[
|
|
440
|
-
starting_string.rfind(self.indentation_chars)
|
|
441
|
-
+ self.indentation_chars.__len__() :
|
|
442
|
-
]
|
|
443
|
-
+ lines[line_index][start_point:]
|
|
161
|
+
self._code_html = soup.find("pre")
|
|
162
|
+
assert isinstance(self._code_html, Tag)
|
|
163
|
+
|
|
164
|
+
# as we are using Paragraph to render the text, we need to find the character indices
|
|
165
|
+
# of the segments of changed color in the HTML code
|
|
166
|
+
color_ranges = []
|
|
167
|
+
current_line_color_ranges = []
|
|
168
|
+
current_line_char_index = 0
|
|
169
|
+
for child in self._code_html.children:
|
|
170
|
+
if child.name == "span":
|
|
171
|
+
try:
|
|
172
|
+
child_style = child["style"]
|
|
173
|
+
match_ = re.match(
|
|
174
|
+
r"color: (#[A-Fa-f0-9]{6}|#[A-Fa-f0-9]{3})", child_style
|
|
444
175
|
)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
176
|
+
color = None if match_ is None else match_.group(1)
|
|
177
|
+
except KeyError:
|
|
178
|
+
color = None
|
|
179
|
+
current_line_color_ranges.append(
|
|
180
|
+
(
|
|
181
|
+
current_line_char_index,
|
|
182
|
+
current_line_char_index + len(child.text),
|
|
183
|
+
color,
|
|
448
184
|
)
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
while lines[line_index][indentation_chars_count] == "\t":
|
|
452
|
-
indentation_chars_count = indentation_chars_count + 1
|
|
453
|
-
self.tab_spaces.append(indentation_chars_count)
|
|
454
|
-
# print(lines[line_index])
|
|
455
|
-
lines[line_index] = self._correct_non_span(lines[line_index])
|
|
456
|
-
# print(lines[line_index])
|
|
457
|
-
words = lines[line_index].split("<span")
|
|
458
|
-
for word_index in range(1, words.__len__()):
|
|
459
|
-
color_index = words[word_index].find("color:")
|
|
460
|
-
if color_index == -1:
|
|
461
|
-
color = self.default_color
|
|
462
|
-
else:
|
|
463
|
-
starti = words[word_index][color_index:].find("#")
|
|
464
|
-
color = words[word_index][
|
|
465
|
-
color_index + starti : color_index + starti + 7
|
|
466
|
-
]
|
|
467
|
-
start_point = words[word_index].find(">")
|
|
468
|
-
end_point = words[word_index].find("</span>")
|
|
469
|
-
text = words[word_index][start_point + 1 : end_point]
|
|
470
|
-
text = html.unescape(text)
|
|
471
|
-
if text != "":
|
|
472
|
-
# print(text, "'" + color + "'")
|
|
473
|
-
self.code_json[code_json_line_index].append([text, color])
|
|
474
|
-
# print(self.code_json)
|
|
475
|
-
|
|
476
|
-
def _correct_non_span(self, line_str: str):
|
|
477
|
-
"""Function put text color to those strings that don't have one according to background_color of displayed code.
|
|
478
|
-
|
|
479
|
-
Parameters
|
|
480
|
-
---------
|
|
481
|
-
line_str
|
|
482
|
-
Takes a html element's string to put color to it according to background_color of displayed code.
|
|
483
|
-
|
|
484
|
-
Returns
|
|
485
|
-
-------
|
|
486
|
-
:class:`str`
|
|
487
|
-
The generated html element's string with having color attributes.
|
|
488
|
-
"""
|
|
489
|
-
words = line_str.split("</span>")
|
|
490
|
-
line_str = ""
|
|
491
|
-
for i in range(0, words.__len__()):
|
|
492
|
-
if i != words.__len__() - 1:
|
|
493
|
-
j = words[i].find("<span")
|
|
185
|
+
)
|
|
186
|
+
current_line_char_index += len(child.text)
|
|
494
187
|
else:
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
188
|
+
for char in child.text:
|
|
189
|
+
if char == "\n":
|
|
190
|
+
color_ranges.append(current_line_color_ranges)
|
|
191
|
+
current_line_color_ranges = []
|
|
192
|
+
current_line_char_index = 0
|
|
193
|
+
else:
|
|
194
|
+
current_line_char_index += 1
|
|
195
|
+
|
|
196
|
+
color_ranges.append(current_line_color_ranges)
|
|
197
|
+
code_lines = self._code_html.get_text().removesuffix("\n").split("\n")
|
|
198
|
+
|
|
199
|
+
if paragraph_config is None:
|
|
200
|
+
paragraph_config = {}
|
|
201
|
+
base_paragraph_config = self.default_paragraph_config.copy()
|
|
202
|
+
base_paragraph_config.update(paragraph_config)
|
|
203
|
+
|
|
204
|
+
from manim.mobject.text.text_mobject import Paragraph
|
|
205
|
+
|
|
206
|
+
self.code_lines = Paragraph(
|
|
207
|
+
*code_lines,
|
|
208
|
+
**base_paragraph_config,
|
|
209
|
+
)
|
|
210
|
+
for line, color_range in zip(self.code_lines, color_ranges):
|
|
211
|
+
for start, end, color in color_range:
|
|
212
|
+
line[start:end].set_color(color)
|
|
213
|
+
|
|
214
|
+
if add_line_numbers:
|
|
215
|
+
base_paragraph_config.update({"alignment": "right"})
|
|
216
|
+
self.line_numbers = Paragraph(
|
|
217
|
+
*[
|
|
218
|
+
str(i)
|
|
219
|
+
for i in range(
|
|
220
|
+
line_numbers_from, line_numbers_from + len(self.code_lines)
|
|
520
221
|
)
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
def _hilite_me(
|
|
529
|
-
code: str,
|
|
530
|
-
language: str,
|
|
531
|
-
style: str,
|
|
532
|
-
insert_line_no: bool,
|
|
533
|
-
divstyles: str,
|
|
534
|
-
file_path: Path,
|
|
535
|
-
line_no_from: int,
|
|
536
|
-
):
|
|
537
|
-
"""Function to highlight code from string to html.
|
|
222
|
+
],
|
|
223
|
+
**base_paragraph_config,
|
|
224
|
+
)
|
|
225
|
+
self.line_numbers.next_to(self.code_lines, direction=LEFT).align_to(
|
|
226
|
+
self.code_lines, UP
|
|
227
|
+
)
|
|
228
|
+
self.add(self.line_numbers)
|
|
538
229
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
Code string.
|
|
543
|
-
language
|
|
544
|
-
The name of the programming language the given code was written in.
|
|
545
|
-
style
|
|
546
|
-
Code style name.
|
|
547
|
-
insert_line_no
|
|
548
|
-
Defines whether line numbers should be inserted in the html file.
|
|
549
|
-
divstyles
|
|
550
|
-
Some html css styles.
|
|
551
|
-
file_path
|
|
552
|
-
Path of code file.
|
|
553
|
-
line_no_from
|
|
554
|
-
Defines the first line's number in the line count.
|
|
555
|
-
"""
|
|
556
|
-
style = style or "colorful"
|
|
557
|
-
defstyles = "overflow:auto;width:auto;"
|
|
558
|
-
|
|
559
|
-
formatter = HtmlFormatter(
|
|
560
|
-
style=style,
|
|
561
|
-
linenos=False,
|
|
562
|
-
noclasses=True,
|
|
563
|
-
cssclass="",
|
|
564
|
-
cssstyles=defstyles + divstyles,
|
|
565
|
-
prestyles="margin: 0",
|
|
566
|
-
)
|
|
567
|
-
if language is None and file_path:
|
|
568
|
-
lexer = guess_lexer_for_filename(file_path, code)
|
|
569
|
-
html = highlight(code, lexer, formatter)
|
|
570
|
-
elif language is None:
|
|
571
|
-
raise ValueError(
|
|
572
|
-
"The code language has to be specified when rendering a code string",
|
|
573
|
-
)
|
|
574
|
-
else:
|
|
575
|
-
html = highlight(code, get_lexer_by_name(language, **{}), formatter)
|
|
576
|
-
if insert_line_no:
|
|
577
|
-
html = _insert_line_numbers_in_html(html, line_no_from)
|
|
578
|
-
html = "<!-- HTML generated by Code() -->" + html
|
|
579
|
-
return html
|
|
230
|
+
for line in self.code_lines:
|
|
231
|
+
line.submobjects = [c for c in line if not isinstance(c, Dot)]
|
|
232
|
+
self.add(self.code_lines)
|
|
580
233
|
|
|
234
|
+
if background_config is None:
|
|
235
|
+
background_config = {}
|
|
236
|
+
background_config_base = self.default_background_config.copy()
|
|
237
|
+
background_config_base.update(background_config)
|
|
581
238
|
|
|
582
|
-
|
|
583
|
-
|
|
239
|
+
if background == "rectangle":
|
|
240
|
+
self.background = SurroundingRectangle(
|
|
241
|
+
self,
|
|
242
|
+
**background_config_base,
|
|
243
|
+
)
|
|
244
|
+
elif background == "window":
|
|
245
|
+
buttons = VGroup(
|
|
246
|
+
Dot(radius=0.1, stroke_width=0, color=button_color)
|
|
247
|
+
for button_color in ["#ff5f56", "#ffbd2e", "#27c93f"]
|
|
248
|
+
).arrange(RIGHT, buff=0.1)
|
|
249
|
+
buttons.next_to(self, UP, buff=0.1).align_to(self, LEFT).shift(LEFT * 0.1)
|
|
250
|
+
self.background = SurroundingRectangle(
|
|
251
|
+
VGroup(self, buttons),
|
|
252
|
+
**background_config_base,
|
|
253
|
+
)
|
|
254
|
+
buttons.shift(UP * 0.1 + LEFT * 0.1)
|
|
255
|
+
self.background.add(buttons)
|
|
256
|
+
else:
|
|
257
|
+
raise ValueError(f"Unknown background type: {background}")
|
|
584
258
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
-------
|
|
594
|
-
:class:`str`
|
|
595
|
-
The generated html string with having line numbers.
|
|
596
|
-
"""
|
|
597
|
-
match = re.search("(<pre[^>]*>)(.*)(</pre>)", html, re.DOTALL)
|
|
598
|
-
if not match:
|
|
599
|
-
return html
|
|
600
|
-
pre_open = match.group(1)
|
|
601
|
-
pre = match.group(2)
|
|
602
|
-
pre_close = match.group(3)
|
|
603
|
-
|
|
604
|
-
html = html.replace(pre_close, "</pre></td></tr></table>")
|
|
605
|
-
numbers = range(line_no_from, line_no_from + pre.count("\n") + 1)
|
|
606
|
-
format_lines = "%" + str(len(str(numbers[-1]))) + "i"
|
|
607
|
-
lines = "\n".join(format_lines % i for i in numbers)
|
|
608
|
-
html = html.replace(
|
|
609
|
-
pre_open,
|
|
610
|
-
"<table><tr><td>" + pre_open + lines + "</pre></td><td>" + pre_open,
|
|
611
|
-
)
|
|
612
|
-
return html
|
|
259
|
+
self.add_to_back(self.background)
|
|
260
|
+
|
|
261
|
+
@classmethod
|
|
262
|
+
def get_styles_list(cls) -> list[str]:
|
|
263
|
+
"""Get the list of all available formatter styles."""
|
|
264
|
+
if cls._styles_list_cache is None:
|
|
265
|
+
cls._styles_list_cache = list(get_all_styles())
|
|
266
|
+
return cls._styles_list_cache
|