26
26
import itertools as it
27
27
import operator as op
28
28
import re
29
- from collections .abc import Iterable
29
+ from collections .abc import Iterable , Sequence
30
30
from functools import reduce
31
31
from textwrap import dedent
32
32
from typing import Any
33
33
34
+ from typing_extensions import Self
35
+
34
36
from manim import config , logger
35
37
from manim .constants import *
36
38
from manim .mobject .geometry .line import Line
39
41
from manim .utils .tex import TexTemplate
40
42
from manim .utils .tex_file_writing import tex_to_svg_file
41
43
42
- tex_string_to_mob_map = {}
43
-
44
44
45
45
class SingleStringMathTex (SVGMobject ):
46
46
"""Elementary building block for rendering text with LaTeX.
@@ -74,9 +74,8 @@ def __init__(
74
74
self .tex_environment = tex_environment
75
75
if tex_template is None :
76
76
tex_template = config ["tex_template" ]
77
- self .tex_template = tex_template
77
+ self .tex_template : TexTemplate = tex_template
78
78
79
- assert isinstance (tex_string , str )
80
79
self .tex_string = tex_string
81
80
file_name = tex_to_svg_file (
82
81
self ._get_modified_expression (tex_string ),
@@ -106,7 +105,7 @@ def __init__(
106
105
if self .organize_left_to_right :
107
106
self ._organize_submobjects_left_to_right ()
108
107
109
- def __repr__ (self ):
108
+ def __repr__ (self ) -> str :
110
109
return f"{ type (self ).__name__ } ({ repr (self .tex_string )} )"
111
110
112
111
@property
@@ -115,7 +114,7 @@ def font_size(self) -> float:
115
114
return self .height / self .initial_height / SCALE_FACTOR_PER_FONT_POINT
116
115
117
116
@font_size .setter
118
- def font_size (self , font_val : float ):
117
+ def font_size (self , font_val : float ) -> None :
119
118
if font_val <= 0 :
120
119
raise ValueError ("font_size must be greater than 0." )
121
120
elif self .height > 0 :
@@ -126,13 +125,13 @@ def font_size(self, font_val: float):
126
125
# font_size does not depend on current size.
127
126
self .scale (font_val / self .font_size )
128
127
129
- def _get_modified_expression (self , tex_string ) :
128
+ def _get_modified_expression (self , tex_string : str ) -> str :
130
129
result = tex_string
131
130
result = result .strip ()
132
131
result = self ._modify_special_strings (result )
133
132
return result
134
133
135
- def _modify_special_strings (self , tex ) :
134
+ def _modify_special_strings (self , tex : str ) -> str :
136
135
tex = tex .strip ()
137
136
should_add_filler = reduce (
138
137
op .or_ ,
@@ -185,7 +184,7 @@ def _modify_special_strings(self, tex):
185
184
tex = ""
186
185
return tex
187
186
188
- def _remove_stray_braces (self , tex ) :
187
+ def _remove_stray_braces (self , tex : str ) -> str :
189
188
r"""
190
189
Makes :class:`~.MathTex` resilient to unmatched braces.
191
190
@@ -203,14 +202,14 @@ def _remove_stray_braces(self, tex):
203
202
num_rights += 1
204
203
return tex
205
204
206
- def _organize_submobjects_left_to_right (self ):
205
+ def _organize_submobjects_left_to_right (self ) -> Self :
207
206
self .sort (lambda p : p [0 ])
208
207
return self
209
208
210
- def get_tex_string (self ):
209
+ def get_tex_string (self ) -> str :
211
210
return self .tex_string
212
211
213
- def init_colors (self , propagate_colors = True ):
212
+ def init_colors (self , propagate_colors : bool = True ) -> Self :
214
213
for submobject in self .submobjects :
215
214
# needed to preserve original (non-black)
216
215
# TeX colors of individual submobjects
@@ -221,6 +220,7 @@ def init_colors(self, propagate_colors=True):
221
220
submobject .init_colors ()
222
221
elif config .renderer == RendererType .CAIRO :
223
222
submobject .init_colors (propagate_colors = propagate_colors )
223
+ return self
224
224
225
225
226
226
class MathTex (SingleStringMathTex ):
@@ -256,21 +256,22 @@ def construct(self):
256
256
257
257
def __init__ (
258
258
self ,
259
- * tex_strings ,
259
+ * tex_strings : str ,
260
260
arg_separator : str = " " ,
261
261
substrings_to_isolate : Iterable [str ] | None = None ,
262
- tex_to_color_map : dict [str , ManimColor ] = None ,
262
+ tex_to_color_map : dict [str , ManimColor ] | None = None ,
263
263
tex_environment : str = "align*" ,
264
- ** kwargs ,
264
+ ** kwargs : Any ,
265
265
):
266
266
self .tex_template = kwargs .pop ("tex_template" , config ["tex_template" ])
267
267
self .arg_separator = arg_separator
268
268
self .substrings_to_isolate = (
269
269
[] if substrings_to_isolate is None else substrings_to_isolate
270
270
)
271
- self .tex_to_color_map = tex_to_color_map
272
- if self .tex_to_color_map is None :
273
- self .tex_to_color_map = {}
271
+ if tex_to_color_map is None :
272
+ self .tex_to_color_map : dict [str , ManimColor ] = {}
273
+ else :
274
+ self .tex_to_color_map = tex_to_color_map
274
275
self .tex_environment = tex_environment
275
276
self .brace_notation_split_occurred = False
276
277
self .tex_strings = self ._break_up_tex_strings (tex_strings )
@@ -302,12 +303,14 @@ def __init__(
302
303
if self .organize_left_to_right :
303
304
self ._organize_submobjects_left_to_right ()
304
305
305
- def _break_up_tex_strings (self , tex_strings ) :
306
+ def _break_up_tex_strings (self , tex_strings : Sequence [ str ]) -> list [ str ] :
306
307
# Separate out anything surrounded in double braces
307
308
pre_split_length = len (tex_strings )
308
- tex_strings = [re .split ("{{(.*?)}}" , str (t )) for t in tex_strings ]
309
- tex_strings = sum (tex_strings , [])
310
- 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 :
311
314
self .brace_notation_split_occurred = True
312
315
313
316
# Separate out any strings specified in the isolate
@@ -325,19 +328,19 @@ def _break_up_tex_strings(self, tex_strings):
325
328
pattern = "|" .join (patterns )
326
329
if pattern :
327
330
pieces = []
328
- for s in tex_strings :
331
+ for s in tex_strings_combined :
329
332
pieces .extend (re .split (pattern , s ))
330
333
else :
331
- pieces = tex_strings
334
+ pieces = tex_strings_combined
332
335
return [p for p in pieces if p ]
333
336
334
- def _break_up_by_substrings (self ):
337
+ def _break_up_by_substrings (self ) -> Self :
335
338
"""
336
339
Reorganize existing submobjects one layer
337
340
deeper based on the structure of tex_strings (as a list
338
341
of tex_strings)
339
342
"""
340
- new_submobjects = []
343
+ new_submobjects : list [ VMobject ] = []
341
344
curr_index = 0
342
345
for tex_string in self .tex_strings :
343
346
sub_tex_mob = SingleStringMathTex (
@@ -359,8 +362,10 @@ def _break_up_by_substrings(self):
359
362
self .submobjects = new_submobjects
360
363
return self
361
364
362
- def get_parts_by_tex (self , tex , substring = True , case_sensitive = True ):
363
- 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 :
364
369
if not case_sensitive :
365
370
tex1 = tex1 .lower ()
366
371
tex2 = tex2 .lower ()
@@ -371,19 +376,25 @@ def test(tex1, tex2):
371
376
372
377
return VGroup (* (m for m in self .submobjects if test (tex , m .get_tex_string ())))
373
378
374
- def get_part_by_tex (self , tex , ** kwargs ) :
379
+ def get_part_by_tex (self , tex : str , ** kwargs : Any ) -> MathTex | None :
375
380
all_parts = self .get_parts_by_tex (tex , ** kwargs )
376
381
return all_parts [0 ] if all_parts else None
377
382
378
- 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 :
379
386
parts_to_color = self .get_parts_by_tex (tex , ** kwargs )
380
387
for part in parts_to_color :
381
388
part .set_color (color )
382
389
return self
383
390
384
391
def set_opacity_by_tex (
385
- self , tex : str , opacity : float = 0.5 , remaining_opacity : float = None , ** kwargs
386
- ):
392
+ self ,
393
+ tex : str ,
394
+ opacity : float = 0.5 ,
395
+ remaining_opacity : float | None = None ,
396
+ ** kwargs : Any ,
397
+ ) -> Self :
387
398
"""
388
399
Sets the opacity of the tex specified. If 'remaining_opacity' is specified,
389
400
then the remaining tex will be set to that opacity.
@@ -404,7 +415,9 @@ def set_opacity_by_tex(
404
415
part .set_opacity (opacity )
405
416
return self
406
417
407
- def set_color_by_tex_to_color_map (self , texs_to_color_map , ** kwargs ):
418
+ def set_color_by_tex_to_color_map (
419
+ self , texs_to_color_map : dict [str , ManimColor ], ** kwargs : Any
420
+ ) -> Self :
408
421
for texs , color in list (texs_to_color_map .items ()):
409
422
try :
410
423
# If the given key behaves like tex_strings
@@ -416,17 +429,19 @@ def set_color_by_tex_to_color_map(self, texs_to_color_map, **kwargs):
416
429
self .set_color_by_tex (tex , color , ** kwargs )
417
430
return self
418
431
419
- def index_of_part (self , part ) :
432
+ def index_of_part (self , part : MathTex ) -> int :
420
433
split_self = self .split ()
421
434
if part not in split_self :
422
435
raise ValueError ("Trying to get index of part not in MathTex" )
423
436
return split_self .index (part )
424
437
425
- def index_of_part_by_tex (self , tex , ** kwargs ) :
438
+ def index_of_part_by_tex (self , tex : str , ** kwargs : Any ) -> int :
426
439
part = self .get_part_by_tex (tex , ** kwargs )
440
+ if part is None :
441
+ return - 1
427
442
return self .index_of_part (part )
428
443
429
- def sort_alphabetically (self ):
444
+ def sort_alphabetically (self ) -> None :
430
445
self .submobjects .sort (key = lambda m : m .get_tex_string ())
431
446
432
447
@@ -482,11 +497,11 @@ def construct(self):
482
497
483
498
def __init__ (
484
499
self ,
485
- * items ,
486
- buff = MED_LARGE_BUFF ,
487
- dot_scale_factor = 2 ,
488
- tex_environment = None ,
489
- ** kwargs ,
500
+ * items : str ,
501
+ buff : float = MED_LARGE_BUFF ,
502
+ dot_scale_factor : float = 2 ,
503
+ tex_environment : str = "" ,
504
+ ** kwargs : Any ,
490
505
):
491
506
self .buff = buff
492
507
self .dot_scale_factor = dot_scale_factor
@@ -501,12 +516,12 @@ def __init__(
501
516
part .add_to_back (dot )
502
517
self .arrange (DOWN , aligned_edge = LEFT , buff = self .buff )
503
518
504
- def fade_all_but (self , index_or_string , opacity = 0.5 ):
519
+ def fade_all_but (self , index_or_string : int | str , opacity : float = 0.5 ) -> None :
505
520
arg = index_or_string
506
521
if isinstance (arg , str ):
507
522
part = self .get_part_by_tex (arg )
508
523
elif isinstance (arg , int ):
509
- part = self .submobjects [arg ]
524
+ part = self .submobjects [arg ] # type: ignore[assignment]
510
525
else :
511
526
raise TypeError (f"Expected int or string, got { arg } " )
512
527
for other_part in self .submobjects :
@@ -536,11 +551,11 @@ def construct(self):
536
551
537
552
def __init__ (
538
553
self ,
539
- * text_parts ,
540
- include_underline = True ,
541
- match_underline_width_to_text = False ,
542
- underline_buff = MED_SMALL_BUFF ,
543
- ** kwargs ,
554
+ * text_parts : str ,
555
+ include_underline : bool = True ,
556
+ match_underline_width_to_text : bool = False ,
557
+ underline_buff : float = MED_SMALL_BUFF ,
558
+ ** kwargs : Any ,
544
559
):
545
560
self .include_underline = include_underline
546
561
self .match_underline_width_to_text = match_underline_width_to_text
0 commit comments