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.
Files changed (163) hide show
  1. manim/__init__.py +11 -6
  2. manim/__main__.py +62 -19
  3. manim/_config/__init__.py +10 -9
  4. manim/_config/cli_colors.py +26 -9
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +23 -13
  7. manim/_config/utils.py +662 -468
  8. manim/animation/animation.py +164 -18
  9. manim/animation/changing.py +34 -23
  10. manim/animation/composition.py +265 -67
  11. manim/animation/creation.py +208 -26
  12. manim/animation/fading.py +16 -18
  13. manim/animation/growing.py +35 -15
  14. manim/animation/indication.py +150 -76
  15. manim/animation/movement.py +56 -22
  16. manim/animation/numbers.py +64 -6
  17. manim/animation/rotation.py +78 -7
  18. manim/animation/specialized.py +6 -7
  19. manim/animation/speedmodifier.py +13 -10
  20. manim/animation/transform.py +14 -11
  21. manim/animation/transform_matching_parts.py +3 -4
  22. manim/animation/updaters/mobject_update_utils.py +152 -30
  23. manim/animation/updaters/update.py +10 -7
  24. manim/camera/camera.py +182 -118
  25. manim/camera/mapping_camera.py +34 -3
  26. manim/camera/moving_camera.py +95 -74
  27. manim/camera/multi_camera.py +23 -15
  28. manim/camera/three_d_camera.py +70 -52
  29. manim/cli/__init__.py +17 -0
  30. manim/cli/cfg/group.py +76 -44
  31. manim/cli/checkhealth/checks.py +192 -0
  32. manim/cli/checkhealth/commands.py +90 -0
  33. manim/cli/default_group.py +158 -25
  34. manim/cli/init/commands.py +33 -25
  35. manim/cli/plugins/commands.py +16 -3
  36. manim/cli/render/commands.py +72 -60
  37. manim/cli/render/ease_of_access_options.py +4 -3
  38. manim/cli/render/global_options.py +59 -17
  39. manim/cli/render/output_options.py +6 -5
  40. manim/cli/render/render_options.py +98 -33
  41. manim/constants.py +109 -59
  42. manim/data_structures.py +31 -0
  43. manim/mobject/frame.py +8 -5
  44. manim/mobject/geometry/__init__.py +1 -0
  45. manim/mobject/geometry/arc.py +277 -135
  46. manim/mobject/geometry/boolean_ops.py +32 -31
  47. manim/mobject/geometry/labeled.py +376 -0
  48. manim/mobject/geometry/line.py +192 -87
  49. manim/mobject/geometry/polygram.py +224 -58
  50. manim/mobject/geometry/shape_matchers.py +61 -25
  51. manim/mobject/geometry/tips.py +122 -48
  52. manim/mobject/graph.py +1027 -419
  53. manim/mobject/graphing/coordinate_systems.py +533 -278
  54. manim/mobject/graphing/functions.py +53 -32
  55. manim/mobject/graphing/number_line.py +123 -65
  56. manim/mobject/graphing/probability.py +88 -62
  57. manim/mobject/graphing/scale.py +33 -19
  58. manim/mobject/logo.py +118 -28
  59. manim/mobject/matrix.py +87 -83
  60. manim/mobject/mobject.py +912 -442
  61. manim/mobject/opengl/dot_cloud.py +16 -5
  62. manim/mobject/opengl/opengl_compatibility.py +4 -2
  63. manim/mobject/opengl/opengl_geometry.py +254 -153
  64. manim/mobject/opengl/opengl_image_mobject.py +3 -1
  65. manim/mobject/opengl/opengl_mobject.py +779 -482
  66. manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
  67. manim/mobject/opengl/opengl_surface.py +14 -92
  68. manim/mobject/opengl/opengl_three_dimensions.py +12 -8
  69. manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
  70. manim/mobject/svg/brace.py +173 -41
  71. manim/mobject/svg/svg_mobject.py +139 -53
  72. manim/mobject/table.py +61 -68
  73. manim/mobject/text/code_mobject.py +193 -539
  74. manim/mobject/text/numbers.py +81 -34
  75. manim/mobject/text/tex_mobject.py +130 -78
  76. manim/mobject/text/text_mobject.py +288 -164
  77. manim/mobject/three_d/polyhedra.py +111 -13
  78. manim/mobject/three_d/three_d_utils.py +17 -8
  79. manim/mobject/three_d/three_dimensions.py +239 -106
  80. manim/mobject/types/image_mobject.py +50 -30
  81. manim/mobject/types/point_cloud_mobject.py +120 -75
  82. manim/mobject/types/vectorized_mobject.py +841 -408
  83. manim/mobject/value_tracker.py +105 -38
  84. manim/mobject/vector_field.py +50 -31
  85. manim/opengl/__init__.py +3 -3
  86. manim/plugins/__init__.py +14 -1
  87. manim/plugins/plugins_flags.py +10 -14
  88. manim/renderer/cairo_renderer.py +65 -50
  89. manim/renderer/opengl_renderer.py +89 -69
  90. manim/renderer/opengl_renderer_window.py +39 -18
  91. manim/renderer/shader.py +123 -87
  92. manim/renderer/shader_wrapper.py +44 -28
  93. manim/renderer/vectorized_mobject_rendering.py +38 -10
  94. manim/scene/moving_camera_scene.py +32 -3
  95. manim/scene/scene.py +507 -242
  96. manim/scene/scene_file_writer.py +371 -220
  97. manim/scene/section.py +20 -16
  98. manim/scene/three_d_scene.py +14 -22
  99. manim/scene/vector_space_scene.py +223 -129
  100. manim/scene/zoomed_scene.py +46 -41
  101. manim/typing.py +990 -0
  102. manim/utils/bezier.py +1823 -371
  103. manim/utils/caching.py +12 -5
  104. manim/utils/color/AS2700.py +236 -0
  105. manim/utils/color/BS381.py +318 -0
  106. manim/utils/color/DVIPSNAMES.py +96 -0
  107. manim/utils/color/SVGNAMES.py +179 -0
  108. manim/utils/color/X11.py +533 -0
  109. manim/utils/color/XKCD.py +952 -0
  110. manim/utils/color/__init__.py +61 -0
  111. manim/utils/color/core.py +1667 -0
  112. manim/utils/color/manim_colors.py +218 -0
  113. manim/utils/commands.py +48 -20
  114. manim/utils/config_ops.py +39 -19
  115. manim/utils/debug.py +8 -7
  116. manim/utils/deprecation.py +86 -39
  117. manim/utils/docbuild/__init__.py +17 -0
  118. manim/utils/docbuild/autoaliasattr_directive.py +236 -0
  119. manim/utils/docbuild/autocolor_directive.py +99 -0
  120. manim/utils/docbuild/manim_directive.py +94 -41
  121. manim/utils/docbuild/module_parsing.py +245 -0
  122. manim/utils/exceptions.py +6 -0
  123. manim/utils/family.py +5 -3
  124. manim/utils/family_ops.py +17 -4
  125. manim/utils/file_ops.py +27 -17
  126. manim/utils/hashing.py +55 -45
  127. manim/utils/images.py +13 -7
  128. manim/utils/ipython_magic.py +13 -7
  129. manim/utils/iterables.py +163 -120
  130. manim/utils/module_ops.py +66 -24
  131. manim/utils/opengl.py +77 -24
  132. manim/utils/parameter_parsing.py +32 -0
  133. manim/utils/paths.py +30 -33
  134. manim/utils/polylabel.py +235 -0
  135. manim/utils/qhull.py +218 -0
  136. manim/utils/rate_functions.py +98 -32
  137. manim/utils/simple_functions.py +25 -33
  138. manim/utils/sounds.py +7 -1
  139. manim/utils/space_ops.py +188 -115
  140. manim/utils/testing/__init__.py +17 -0
  141. manim/utils/testing/_frames_testers.py +13 -8
  142. manim/utils/testing/_show_diff.py +5 -3
  143. manim/utils/testing/_test_class_makers.py +34 -18
  144. manim/utils/testing/frames_comparison.py +37 -19
  145. manim/utils/tex.py +130 -198
  146. manim/utils/tex_file_writing.py +77 -47
  147. manim/utils/tex_templates.py +2 -1
  148. manim/utils/unit.py +6 -5
  149. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
  150. manim-0.19.1.dist-info/RECORD +220 -0
  151. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
  152. manim-0.19.1.dist-info/entry_points.txt +3 -0
  153. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
  154. manim/cli/new/group.py +0 -189
  155. manim/communitycolors.py +0 -9
  156. manim/gui/__init__.py +0 -0
  157. manim/gui/gui.py +0 -82
  158. manim/plugins/import_plugins.py +0 -43
  159. manim/utils/color.py +0 -552
  160. manim-0.17.0.dist-info/RECORD +0 -206
  161. manim-0.17.0.dist-info/entry_points.txt +0 -4
  162. /manim/cli/{new → checkhealth}/__init__.py +0 -0
  163. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE +0 -0
@@ -12,6 +12,8 @@ r"""Mobjects representing text rendered using LaTeX.
12
12
 
13
13
  from __future__ import annotations
14
14
 
15
+ from manim.utils.color import BLACK, ManimColor, ParsableManimColor
16
+
15
17
  __all__ = [
16
18
  "SingleStringMathTex",
17
19
  "MathTex",
@@ -24,24 +26,21 @@ __all__ = [
24
26
  import itertools as it
25
27
  import operator as op
26
28
  import re
29
+ from collections.abc import Iterable, Sequence
27
30
  from functools import reduce
28
31
  from textwrap import dedent
29
- from typing import Dict, Iterable, Optional
32
+ from typing import Any
30
33
 
31
- from colour import Color
34
+ from typing_extensions import Self
32
35
 
33
36
  from manim import config, logger
34
37
  from manim.constants import *
35
38
  from manim.mobject.geometry.line import Line
36
39
  from manim.mobject.svg.svg_mobject import SVGMobject
37
- from manim.mobject.types.vectorized_mobject import VectorizedPoint, VGroup, VMobject
40
+ from manim.mobject.types.vectorized_mobject import VGroup, VMobject
38
41
  from manim.utils.tex import TexTemplate
39
42
  from manim.utils.tex_file_writing import tex_to_svg_file
40
43
 
41
- SCALE_FACTOR_PER_FONT_POINT = 1 / 960
42
-
43
- tex_string_to_mob_map = {}
44
-
45
44
 
46
45
  class SingleStringMathTex(SVGMobject):
47
46
  """Elementary building block for rendering text with LaTeX.
@@ -61,25 +60,22 @@ class SingleStringMathTex(SVGMobject):
61
60
  should_center: bool = True,
62
61
  height: float | None = None,
63
62
  organize_left_to_right: bool = False,
64
- tex_environment: str = "align*",
63
+ tex_environment: str | None = "align*",
65
64
  tex_template: TexTemplate | None = None,
66
65
  font_size: float = DEFAULT_FONT_SIZE,
67
- **kwargs,
66
+ color: ParsableManimColor | None = None,
67
+ **kwargs: Any,
68
68
  ):
69
-
70
- if kwargs.get("color") is None:
71
- # makes it so that color isn't explicitly passed for these mobs,
72
- # and can instead inherit from the parent
73
- kwargs["color"] = VMobject().color
69
+ if color is None:
70
+ color = VMobject().color
74
71
 
75
72
  self._font_size = font_size
76
73
  self.organize_left_to_right = organize_left_to_right
77
74
  self.tex_environment = tex_environment
78
75
  if tex_template is None:
79
76
  tex_template = config["tex_template"]
80
- self.tex_template = tex_template
77
+ self.tex_template: TexTemplate = tex_template
81
78
 
82
- assert isinstance(tex_string, str)
83
79
  self.tex_string = tex_string
84
80
  file_name = tex_to_svg_file(
85
81
  self._get_modified_expression(tex_string),
@@ -91,6 +87,7 @@ class SingleStringMathTex(SVGMobject):
91
87
  should_center=should_center,
92
88
  stroke_width=stroke_width,
93
89
  height=height,
90
+ color=color,
94
91
  path_string_config={
95
92
  "should_subdivide_sharp_curves": True,
96
93
  "should_remove_null_curves": True,
@@ -108,16 +105,16 @@ class SingleStringMathTex(SVGMobject):
108
105
  if self.organize_left_to_right:
109
106
  self._organize_submobjects_left_to_right()
110
107
 
111
- def __repr__(self):
108
+ def __repr__(self) -> str:
112
109
  return f"{type(self).__name__}({repr(self.tex_string)})"
113
110
 
114
111
  @property
115
- def font_size(self):
112
+ def font_size(self) -> float:
116
113
  """The font size of the tex mobject."""
117
114
  return self.height / self.initial_height / SCALE_FACTOR_PER_FONT_POINT
118
115
 
119
116
  @font_size.setter
120
- def font_size(self, font_val):
117
+ def font_size(self, font_val: float) -> None:
121
118
  if font_val <= 0:
122
119
  raise ValueError("font_size must be greater than 0.")
123
120
  elif self.height > 0:
@@ -128,13 +125,13 @@ class SingleStringMathTex(SVGMobject):
128
125
  # font_size does not depend on current size.
129
126
  self.scale(font_val / self.font_size)
130
127
 
131
- def _get_modified_expression(self, tex_string):
128
+ def _get_modified_expression(self, tex_string: str) -> str:
132
129
  result = tex_string
133
130
  result = result.strip()
134
131
  result = self._modify_special_strings(result)
135
132
  return result
136
133
 
137
- def _modify_special_strings(self, tex):
134
+ def _modify_special_strings(self, tex: str) -> str:
138
135
  tex = tex.strip()
139
136
  should_add_filler = reduce(
140
137
  op.or_,
@@ -178,8 +175,8 @@ class SingleStringMathTex(SVGMobject):
178
175
  tex = self._remove_stray_braces(tex)
179
176
 
180
177
  for context in ["array"]:
181
- begin_in = ("\\begin{%s}" % context) in tex
182
- end_in = ("\\end{%s}" % context) in tex
178
+ begin_in = ("\\begin{%s}" % context) in tex # noqa: UP031
179
+ end_in = ("\\end{%s}" % context) in tex # noqa: UP031
183
180
  if begin_in ^ end_in:
184
181
  # Just turn this into a blank string,
185
182
  # which means caller should leave a
@@ -187,14 +184,13 @@ class SingleStringMathTex(SVGMobject):
187
184
  tex = ""
188
185
  return tex
189
186
 
190
- def _remove_stray_braces(self, tex):
187
+ def _remove_stray_braces(self, tex: str) -> str:
191
188
  r"""
192
189
  Makes :class:`~.MathTex` resilient to unmatched braces.
193
190
 
194
191
  This is important when the braces in the TeX code are spread over
195
192
  multiple arguments as in, e.g., ``MathTex(r"e^{i", r"\tau} = 1")``.
196
193
  """
197
-
198
194
  # "\{" does not count (it's a brace literal), but "\\{" counts (it's a new line and then brace)
199
195
  num_lefts = tex.count("{") - tex.count("\\{") + tex.count("\\\\{")
200
196
  num_rights = tex.count("}") - tex.count("\\}") + tex.count("\\\\}")
@@ -206,18 +202,25 @@ class SingleStringMathTex(SVGMobject):
206
202
  num_rights += 1
207
203
  return tex
208
204
 
209
- def _organize_submobjects_left_to_right(self):
205
+ def _organize_submobjects_left_to_right(self) -> Self:
210
206
  self.sort(lambda p: p[0])
211
207
  return self
212
208
 
213
- def get_tex_string(self):
209
+ def get_tex_string(self) -> str:
214
210
  return self.tex_string
215
211
 
216
- def init_colors(self, propagate_colors=True):
217
- if config.renderer == RendererType.OPENGL:
218
- super().init_colors()
219
- elif config.renderer == RendererType.CAIRO:
220
- super().init_colors(propagate_colors=propagate_colors)
212
+ def init_colors(self, propagate_colors: bool = True) -> Self:
213
+ for submobject in self.submobjects:
214
+ # needed to preserve original (non-black)
215
+ # TeX colors of individual submobjects
216
+ if submobject.color != BLACK:
217
+ continue
218
+ submobject.color = self.color
219
+ if config.renderer == RendererType.OPENGL:
220
+ submobject.init_colors()
221
+ elif config.renderer == RendererType.CAIRO:
222
+ submobject.init_colors(propagate_colors=propagate_colors)
223
+ return self
221
224
 
222
225
 
223
226
  class MathTex(SingleStringMathTex):
@@ -253,21 +256,22 @@ class MathTex(SingleStringMathTex):
253
256
 
254
257
  def __init__(
255
258
  self,
256
- *tex_strings,
259
+ *tex_strings: str,
257
260
  arg_separator: str = " ",
258
261
  substrings_to_isolate: Iterable[str] | None = None,
259
- tex_to_color_map: dict[str, Color] = None,
260
- tex_environment: str = "align*",
261
- **kwargs,
262
+ tex_to_color_map: dict[str, ParsableManimColor] | None = None,
263
+ tex_environment: str | None = "align*",
264
+ **kwargs: Any,
262
265
  ):
263
266
  self.tex_template = kwargs.pop("tex_template", config["tex_template"])
264
267
  self.arg_separator = arg_separator
265
268
  self.substrings_to_isolate = (
266
269
  [] if substrings_to_isolate is None else substrings_to_isolate
267
270
  )
268
- self.tex_to_color_map = tex_to_color_map
269
- if self.tex_to_color_map is None:
270
- self.tex_to_color_map = {}
271
+ if tex_to_color_map is None:
272
+ self.tex_to_color_map: dict[str, ParsableManimColor] = {}
273
+ else:
274
+ self.tex_to_color_map = tex_to_color_map
271
275
  self.tex_environment = tex_environment
272
276
  self.brace_notation_split_occurred = False
273
277
  self.tex_strings = self._break_up_tex_strings(tex_strings)
@@ -299,12 +303,14 @@ class MathTex(SingleStringMathTex):
299
303
  if self.organize_left_to_right:
300
304
  self._organize_submobjects_left_to_right()
301
305
 
302
- def _break_up_tex_strings(self, tex_strings):
306
+ def _break_up_tex_strings(self, tex_strings: Sequence[str]) -> list[str]:
303
307
  # Separate out anything surrounded in double braces
304
308
  pre_split_length = len(tex_strings)
305
- tex_strings = [re.split("{{(.*?)}}", str(t)) for t in tex_strings]
306
- tex_strings = sum(tex_strings, [])
307
- if len(tex_strings) > pre_split_length:
309
+ tex_strings_brace_splitted = [
310
+ re.split("{{(.*?)}}", str(t)) for t in tex_strings
311
+ ]
312
+ tex_strings_combined = sum(tex_strings_brace_splitted, [])
313
+ if len(tex_strings_combined) > pre_split_length:
308
314
  self.brace_notation_split_occurred = True
309
315
 
310
316
  # Separate out any strings specified in the isolate
@@ -322,19 +328,19 @@ class MathTex(SingleStringMathTex):
322
328
  pattern = "|".join(patterns)
323
329
  if pattern:
324
330
  pieces = []
325
- for s in tex_strings:
331
+ for s in tex_strings_combined:
326
332
  pieces.extend(re.split(pattern, s))
327
333
  else:
328
- pieces = tex_strings
334
+ pieces = tex_strings_combined
329
335
  return [p for p in pieces if p]
330
336
 
331
- def _break_up_by_substrings(self):
337
+ def _break_up_by_substrings(self) -> Self:
332
338
  """
333
339
  Reorganize existing submobjects one layer
334
340
  deeper based on the structure of tex_strings (as a list
335
341
  of tex_strings)
336
342
  """
337
- new_submobjects = []
343
+ new_submobjects: list[VMobject] = []
338
344
  curr_index = 0
339
345
  for tex_string in self.tex_strings:
340
346
  sub_tex_mob = SingleStringMathTex(
@@ -347,10 +353,6 @@ class MathTex(SingleStringMathTex):
347
353
  curr_index + num_submobs + len("".join(self.arg_separator.split()))
348
354
  )
349
355
  if num_submobs == 0:
350
- # For cases like empty tex_strings, we want the corresponding
351
- # part of the whole MathTex to be a VectorizedPoint
352
- # positioned in the right part of the MathTex
353
- sub_tex_mob.submobjects = [VectorizedPoint()]
354
356
  last_submob_index = min(curr_index, len(self.submobjects) - 1)
355
357
  sub_tex_mob.move_to(self.submobjects[last_submob_index], RIGHT)
356
358
  else:
@@ -360,8 +362,10 @@ class MathTex(SingleStringMathTex):
360
362
  self.submobjects = new_submobjects
361
363
  return self
362
364
 
363
- def get_parts_by_tex(self, tex, substring=True, case_sensitive=True):
364
- def test(tex1, tex2):
365
+ def get_parts_by_tex(
366
+ self, tex: str, substring: bool = True, case_sensitive: bool = True
367
+ ) -> VGroup:
368
+ def test(tex1: str, tex2: str) -> bool:
365
369
  if not case_sensitive:
366
370
  tex1 = tex1.lower()
367
371
  tex2 = tex2.lower()
@@ -372,45 +376,82 @@ class MathTex(SingleStringMathTex):
372
376
 
373
377
  return VGroup(*(m for m in self.submobjects if test(tex, m.get_tex_string())))
374
378
 
375
- def get_part_by_tex(self, tex, **kwargs):
379
+ def get_part_by_tex(self, tex: str, **kwargs: Any) -> MathTex | None:
376
380
  all_parts = self.get_parts_by_tex(tex, **kwargs)
377
381
  return all_parts[0] if all_parts else None
378
382
 
379
- def set_color_by_tex(self, tex, color, **kwargs):
383
+ def set_color_by_tex(
384
+ self, tex: str, color: ParsableManimColor, **kwargs: Any
385
+ ) -> Self:
380
386
  parts_to_color = self.get_parts_by_tex(tex, **kwargs)
381
387
  for part in parts_to_color:
382
388
  part.set_color(color)
383
389
  return self
384
390
 
385
- def set_color_by_tex_to_color_map(self, texs_to_color_map, **kwargs):
391
+ def set_opacity_by_tex(
392
+ self,
393
+ tex: str,
394
+ opacity: float = 0.5,
395
+ remaining_opacity: float | None = None,
396
+ **kwargs: Any,
397
+ ) -> Self:
398
+ """
399
+ Sets the opacity of the tex specified. If 'remaining_opacity' is specified,
400
+ then the remaining tex will be set to that opacity.
401
+
402
+ Parameters
403
+ ----------
404
+ tex
405
+ The tex to set the opacity of.
406
+ opacity
407
+ Default 0.5. The opacity to set the tex to
408
+ remaining_opacity
409
+ Default None. The opacity to set the remaining tex to.
410
+ If None, then the remaining tex will not be changed
411
+ """
412
+ if remaining_opacity is not None:
413
+ self.set_opacity(opacity=remaining_opacity)
414
+ for part in self.get_parts_by_tex(tex):
415
+ part.set_opacity(opacity)
416
+ return self
417
+
418
+ def set_color_by_tex_to_color_map(
419
+ self, texs_to_color_map: dict[str, ParsableManimColor], **kwargs: Any
420
+ ) -> Self:
386
421
  for texs, color in list(texs_to_color_map.items()):
387
422
  try:
388
423
  # If the given key behaves like tex_strings
389
424
  texs + ""
390
- self.set_color_by_tex(texs, color, **kwargs)
425
+ self.set_color_by_tex(texs, ManimColor(color), **kwargs)
391
426
  except TypeError:
392
427
  # If the given key is a tuple
393
428
  for tex in texs:
394
- self.set_color_by_tex(tex, color, **kwargs)
429
+ self.set_color_by_tex(tex, ManimColor(color), **kwargs)
395
430
  return self
396
431
 
397
- def index_of_part(self, part):
432
+ def index_of_part(self, part: MathTex) -> int:
398
433
  split_self = self.split()
399
434
  if part not in split_self:
400
435
  raise ValueError("Trying to get index of part not in MathTex")
401
436
  return split_self.index(part)
402
437
 
403
- def index_of_part_by_tex(self, tex, **kwargs):
438
+ def index_of_part_by_tex(self, tex: str, **kwargs: Any) -> int:
404
439
  part = self.get_part_by_tex(tex, **kwargs)
440
+ if part is None:
441
+ return -1
405
442
  return self.index_of_part(part)
406
443
 
407
- def sort_alphabetically(self):
444
+ def sort_alphabetically(self) -> None:
408
445
  self.submobjects.sort(key=lambda m: m.get_tex_string())
409
446
 
410
447
 
411
448
  class Tex(MathTex):
412
449
  r"""A string compiled with LaTeX in normal mode.
413
450
 
451
+ The color can be set using
452
+ the ``color`` argument. Any parts of the ``tex_string`` that are colored by the
453
+ TeX commands ``\color`` or ``\textcolor`` will retain their original color.
454
+
414
455
  Tests
415
456
  -----
416
457
 
@@ -422,7 +463,11 @@ class Tex(MathTex):
422
463
  """
423
464
 
424
465
  def __init__(
425
- self, *tex_strings, arg_separator="", tex_environment="center", **kwargs
466
+ self,
467
+ *tex_strings: str,
468
+ arg_separator: str = "",
469
+ tex_environment: str | None = "center",
470
+ **kwargs: Any,
426
471
  ):
427
472
  super().__init__(
428
473
  *tex_strings,
@@ -433,7 +478,8 @@ class Tex(MathTex):
433
478
 
434
479
 
435
480
  class BulletedList(Tex):
436
- """
481
+ """A bulleted list.
482
+
437
483
  Examples
438
484
  --------
439
485
 
@@ -451,18 +497,20 @@ class BulletedList(Tex):
451
497
 
452
498
  def __init__(
453
499
  self,
454
- *items,
455
- buff=MED_LARGE_BUFF,
456
- dot_scale_factor=2,
457
- tex_environment=None,
458
- **kwargs,
500
+ *items: str,
501
+ buff: float = MED_LARGE_BUFF,
502
+ dot_scale_factor: float = 2,
503
+ tex_environment: str | None = None,
504
+ **kwargs: Any,
459
505
  ):
460
506
  self.buff = buff
461
507
  self.dot_scale_factor = dot_scale_factor
462
508
  self.tex_environment = tex_environment
463
509
  line_separated_items = [s + "\\\\" for s in items]
464
510
  super().__init__(
465
- *line_separated_items, tex_environment=tex_environment, **kwargs
511
+ *line_separated_items,
512
+ tex_environment=tex_environment,
513
+ **kwargs,
466
514
  )
467
515
  for part in self:
468
516
  dot = MathTex("\\cdot").scale(self.dot_scale_factor)
@@ -470,10 +518,14 @@ class BulletedList(Tex):
470
518
  part.add_to_back(dot)
471
519
  self.arrange(DOWN, aligned_edge=LEFT, buff=self.buff)
472
520
 
473
- def fade_all_but(self, index_or_string, opacity=0.5):
521
+ def fade_all_but(self, index_or_string: int | str, opacity: float = 0.5) -> None:
474
522
  arg = index_or_string
475
523
  if isinstance(arg, str):
476
- part = self.get_part_by_tex(arg)
524
+ part: VGroup | VMobject | None = self.get_part_by_tex(arg)
525
+ if part is None:
526
+ raise Exception(
527
+ f"Could not locate part by provided tex string '{arg}'."
528
+ )
477
529
  elif isinstance(arg, int):
478
530
  part = self.submobjects[arg]
479
531
  else:
@@ -486,7 +538,8 @@ class BulletedList(Tex):
486
538
 
487
539
 
488
540
  class Title(Tex):
489
- """
541
+ """A mobject representing an underlined title.
542
+
490
543
  Examples
491
544
  --------
492
545
  .. manim:: TitleExample
@@ -504,13 +557,12 @@ class Title(Tex):
504
557
 
505
558
  def __init__(
506
559
  self,
507
- *text_parts,
508
- include_underline=True,
509
- match_underline_width_to_text=False,
510
- underline_buff=MED_SMALL_BUFF,
511
- **kwargs,
560
+ *text_parts: str,
561
+ include_underline: bool = True,
562
+ match_underline_width_to_text: bool = False,
563
+ underline_buff: float = MED_SMALL_BUFF,
564
+ **kwargs: Any,
512
565
  ):
513
-
514
566
  self.include_underline = include_underline
515
567
  self.match_underline_width_to_text = match_underline_width_to_text
516
568
  self.underline_buff = underline_buff