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