Refactor MTex

This commit is contained in:
YishiMichael 2022-03-27 14:44:50 +08:00
parent e44a2fc8c6
commit 3b01ec48e6
No known key found for this signature in database
GPG key ID: EC615C0C5A86BC80

View file

@ -6,7 +6,7 @@ import itertools as it
from types import MethodType from types import MethodType
from typing import Iterable, Union, Sequence from typing import Iterable, Union, Sequence
from manimlib.constants import WHITE from manimlib.constants import BLACK, WHITE
from manimlib.mobject.svg.svg_mobject import SVGMobject from manimlib.mobject.svg.svg_mobject import SVGMobject
from manimlib.mobject.types.vectorized_mobject import VGroup from manimlib.mobject.types.vectorized_mobject import VGroup
from manimlib.utils.color import color_to_int_rgb from manimlib.utils.color import color_to_int_rgb
@ -32,6 +32,9 @@ SCALE_FACTOR_PER_FONT_POINT = 0.001
class _TexSVG(SVGMobject): class _TexSVG(SVGMobject):
CONFIG = { CONFIG = {
"height": None, "height": None,
"svg_default": {
"fill_color": BLACK,
},
"stroke_width": 0, "stroke_width": 0,
"stroke_color": WHITE, "stroke_color": WHITE,
"path_string_config": { "path_string_config": {
@ -80,25 +83,11 @@ class MTex(_TexSVG):
self.use_plain_tex self.use_plain_tex
) )
def get_file_path(self) -> str: def get_file_path(self, use_plain_file: bool = False) -> str:
self.init_parser()
self.use_plain_file = any([
self.use_plain_tex,
self.color_cmd_repl_items,
self.base_color != WHITE
])
return self.get_file_path_(use_plain_file=self.use_plain_file)
def get_file_path_(self, use_plain_file: bool) -> str:
if use_plain_file: if use_plain_file:
content = "".join([ content = self.plain_string
"{{",
self.get_color_command(self.color_to_int(self.base_color)),
self.string,
"}}"
])
else: else:
content = self.get_labelled_string() content = self.labelled_string
full_tex = self.get_tex_file_body(content) full_tex = self.get_tex_file_body(content)
with display_during_execution(f"Writing \"{self.string}\""): with display_during_execution(f"Writing \"{self.string}\""):
@ -128,27 +117,26 @@ class MTex(_TexSVG):
def generate_mobject(self) -> None: def generate_mobject(self) -> None:
super().generate_mobject() super().generate_mobject()
glyphs = self.submobjects if not self.submobjects:
if not glyphs:
return return
if self.use_plain_file:
file_path = self.get_file_path_(use_plain_file=False)
labelled_svg_glyphs = _TexSVG(file_path)
predefined_colors = [
labelled_glyph.get_fill_color()
for labelled_glyph in self.submobjects
]
else:
labelled_svg_glyphs = self
predefined_colors = [self.base_color] * len(glyphs)
glyph_labels = [ glyph_labels = [
self.color_to_label(labelled_glyph.get_fill_color()) self.color_to_label(glyph.get_fill_color())
for labelled_glyph in labelled_svg_glyphs for glyph in self.submobjects
] ]
for glyph, glyph_color in zip(glyphs, predefined_colors):
glyph.set_fill(glyph_color) if any([
self.use_plain_tex,
self.color_cmd_repl_items,
self.base_color in (BLACK, WHITE)
]):
file_path = self.get_file_path(use_plain_file=True)
glyphs = _TexSVG(file_path).submobjects
for glyph, plain_glyph in zip(self.submobjects, glyphs):
glyph.set_fill(plain_glyph.get_fill_color())
else:
glyphs = self.submobjects
self.set_fill(self.base_color)
# Simply pack together adjacent mobjects with the same label. # Simply pack together adjacent mobjects with the same label.
submob_labels, glyphs_lists = self.group_neighbours( submob_labels, glyphs_lists = self.group_neighbours(
@ -171,20 +159,18 @@ class MTex(_TexSVG):
## Static methods ## Static methods
@staticmethod @staticmethod
def color_to_int(color: ManimColor) -> int: def color_to_label(color: ManimColor) -> int:
r, g, b = color_to_int_rgb(color) r, g, b = color_to_int_rgb(color)
rg = r * 256 + g rg = r * 256 + g
return rg * 256 + b rgb = rg * 256 + b
if rgb == 16777215: # white
@staticmethod
def color_to_label(color: ManimColor) -> int:
result = MTex.color_to_int(color)
if result == 16777215: # white
return -1 return -1
return result return rgb
@staticmethod @staticmethod
def get_color_command(label: int) -> str: def get_color_command(label: int) -> str:
if label == -1:
label = 16777215 # white
rg, b = divmod(label, 256) rg, b = divmod(label, 256)
r, g = divmod(rg, 256) r, g = divmod(rg, 256)
return "".join([ return "".join([
@ -223,40 +209,83 @@ class MTex(_TexSVG):
groups.append(new_group) groups.append(new_group)
return group_labels, groups return group_labels, groups
@staticmethod
def find_region_index(val: int, seq: list[int]) -> int:
# Returns an integer in `range(len(seq) + 1)` satisfying
# `seq[result - 1] <= val < seq[result]`
if not seq:
return 0
if val >= seq[-1]:
return len(seq)
result = 0
while val >= seq[result]:
result += 1
return result
@staticmethod
def lstrip(index: int, skipped_spans: list[tuple[int, int]]) -> int:
index_seq = list(it.chain(*skipped_spans))
region_index = MTex.find_region_index(index, index_seq)
if region_index % 2 == 1:
return index_seq[region_index]
return index
@staticmethod
def rstrip(index: int, skipped_spans: list[tuple[int, int]]) -> int:
index_seq = list(it.chain(*skipped_spans))
region_index = MTex.find_region_index(index - 1, index_seq)
if region_index % 2 == 1:
return index_seq[region_index - 1]
return index
@staticmethod
def strip(
tex_span: tuple[int, int], skipped_spans: list[tuple[int, int]]
) -> tuple[int, int] | None:
result = (
MTex.lstrip(tex_span[0], skipped_spans),
MTex.rstrip(tex_span[1], skipped_spans)
)
if result[0] >= result[1]:
return None
return result
@staticmethod
def lslide(index: int, slid_spans: list[tuple[int, int]]) -> int:
slide_dict = dict(slid_spans)
while index in slide_dict.keys():
index = slide_dict[index]
return index
@staticmethod
def rslide(index: int, slid_spans: list[tuple[int, int]]) -> int:
slide_dict = dict([
slide_span[::-1] for slide_span in slid_spans
])
while index in slide_dict.keys():
index = slide_dict[index]
return index
@staticmethod
def slide(
tex_span: tuple[int, int], slid_spans: list[tuple[int, int]]
) -> tuple[int, int] | None:
result = (
MTex.lslide(tex_span[0], slid_spans),
MTex.rslide(tex_span[1], slid_spans)
)
if result[0] >= result[1]:
return None
return result
## Parser ## Parser
def init_parser(self) -> None: @property
self.additional_substrings = self.get_additional_substrings() def full_span(self) -> tuple[int, int]:
self.full_span = self.get_full_span()
self.backslash_indices = self.get_backslash_indices()
self.left_brace_indices, self.right_brace_indices = \
self.get_left_and_right_indices()
self.script_char_spans = self.get_script_char_spans()
self.skipped_indices = self.get_skipped_indices()
self.script_spans = self.get_script_spans()
self.script_content_spans = self.get_script_content_spans()
self.double_braces_spans = self.get_double_braces_spans()
self.stripped_substrings = self.get_stripped_substrings()
self.specified_substrings = self.get_specified_substrings()
self.specified_spans, self.extended_specified_spans = \
self.get_specified_spans()
self.tex_span_list = self.get_tex_span_list()
self.extended_tex_span_list = self.get_extended_tex_span_list()
self.isolated_substrings = self.get_isolated_substrings()
self.containing_labels_dict = self.get_containing_labels_dict()
self.color_cmd_repl_items = self.get_color_cmd_repl_items()
self.span_repl_dict = self.get_span_repl_dict()
def get_additional_substrings(self) -> list[str]:
return list(it.chain(
self.tex_to_color_map.keys(),
self.isolate
))
def get_full_span(self) -> tuple[int, int]:
return (0, len(self.string)) return (0, len(self.string))
def get_backslash_indices(self) -> list[int]: @property
def backslash_indices(self) -> list[int]:
# Newlines (`\\`) don't count. # Newlines (`\\`) don't count.
return [ return [
match_obj.end() - 1 match_obj.end() - 1
@ -264,7 +293,9 @@ class MTex(_TexSVG):
if len(match_obj.group()) % 2 == 1 if len(match_obj.group()) % 2 == 1
] ]
def get_left_and_right_indices(self) -> list[tuple[int, int]]: def get_left_and_right_brace_indices(
self
) -> tuple[list[tuple[int, int]], list[tuple[int, int]]]:
string = self.string string = self.string
indices = list(filter( indices = list(filter(
lambda index: index - 1 not in self.backslash_indices, lambda index: index - 1 not in self.backslash_indices,
@ -287,60 +318,69 @@ class MTex(_TexSVG):
right_brace_indices.append(index) right_brace_indices.append(index)
if left_brace_indices_stack: if left_brace_indices_stack:
raise ValueError("Missing '}' inserted") raise ValueError("Missing '}' inserted")
# `right_brace_indices` is already sorted.
return left_brace_indices, right_brace_indices return left_brace_indices, right_brace_indices
def get_script_char_spans(self) -> list[tuple[int, int]]: @property
def left_brace_indices(self) -> list[tuple[int, int]]:
return self.get_left_and_right_brace_indices()[0]
@property
def right_brace_indices(self) -> list[tuple[int, int]]:
return self.get_left_and_right_brace_indices()[1]
@property
def skipped_spans(self) -> list[tuple[int, int]]:
return [ return [
match_obj.span() match_obj.span()
for match_obj in re.finditer(r"(\s*)[_^]\s*", self.string) for match_obj in re.finditer(r"\s*([_^])\s*|(\s+)", self.string)
if match_obj.group(1) if match_obj.group(2) is not None
or match_obj.start() - 1 not in self.backslash_indices or match_obj.start(1) - 1 not in self.backslash_indices
] ]
def get_skipped_indices(self) -> list[int]: def lstrip_span(self, index: int) -> int:
return sorted(remove_list_redundancies([ return self.lstrip(index, self.skipped_spans)
match_obj.start()
for match_obj in re.finditer(r"\s", self.string)
] + list(it.chain(*[
range(*script_char_span)
for script_char_span in self.script_char_spans
]))))
def get_script_spans(self) -> list[tuple[int, int]]: def rstrip_span(self, index: int) -> int:
string = self.string return self.rstrip(index, self.skipped_spans)
def strip_span(self, index: int) -> int:
return self.strip(index, self.skipped_spans)
@property
def script_char_spans(self) -> list[tuple[int, int]]:
return list(filter(
lambda tex_span: self.string[slice(*tex_span)].strip(),
self.skipped_spans
))
@property
def script_content_spans(self) -> list[tuple[int, int]]:
result = [] result = []
brace_indices_dict = dict(zip( brace_indices_dict = dict(zip(
self.left_brace_indices, self.right_brace_indices self.left_brace_indices, self.right_brace_indices
)) ))
for char_begin, span_begin in self.script_char_spans: for _, span_begin in self.script_char_spans:
if span_begin in brace_indices_dict.keys(): if span_begin in brace_indices_dict.keys():
span_end = brace_indices_dict[span_begin] + 1 span_end = brace_indices_dict[span_begin] + 1
else: else:
pattern = re.compile(r"[a-zA-Z0-9]|\\[a-zA-Z]+") pattern = re.compile(r"[a-zA-Z0-9]|\\[a-zA-Z]+")
match_obj = pattern.match(string, pos=span_begin) match_obj = pattern.match(self.string, pos=span_begin)
if not match_obj: if not match_obj:
script_name = { script_name = {
"_": "subscript", "_": "subscript",
"^": "superscript" "^": "superscript"
}[script_char] }[script_char]
log.warning( raise ValueError(
f"Unclear {script_name} detected while parsing. " f"Unclear {script_name} detected while parsing. "
"Please use braces to clarify" "Please use braces to clarify"
) )
continue
span_end = match_obj.end() span_end = match_obj.end()
result.append((char_begin, span_end)) result.append((span_begin, span_end))
return result return result
def get_script_content_spans(self) -> list[tuple[int, int]]: @property
return [ def double_braces_spans(self) -> list[tuple[int, int]]:
(script_char_span[1], script_span[1])
for script_char_span, script_span in zip(
self.script_char_spans, self.script_spans
)
]
def get_double_braces_spans(self) -> list[tuple[int, int]]:
# Match paired double braces (`{{...}}`). # Match paired double braces (`{{...}}`).
result = [] result = []
reversed_brace_indices_dict = dict(zip( reversed_brace_indices_dict = dict(zip(
@ -348,7 +388,7 @@ class MTex(_TexSVG):
)) ))
skip = False skip = False
for prev_right_index, right_index in self.get_neighbouring_pairs( for prev_right_index, right_index in self.get_neighbouring_pairs(
sorted(reversed_brace_indices_dict.keys()) self.right_brace_indices
): ):
if skip: if skip:
skip = False skip = False
@ -363,74 +403,64 @@ class MTex(_TexSVG):
skip = True skip = True
return result return result
def get_stripped_substrings(self) -> list[str]: @property
result = remove_list_redundancies([ def additional_substrings(self) -> list[str]:
substr.strip() result = remove_list_redundancies(list(it.chain(
for substr in self.additional_substrings self.tex_to_color_map.keys(),
]) self.isolate
)))
if "" in result: if "" in result:
result.remove("") result.remove("")
return result return result
def get_specified_substrings(self) -> list[str]: def get_tex_span_lists(
return remove_list_redundancies([
self.string[slice(*double_braces_span)]
for double_braces_span in self.double_braces_spans
] + list(filter(
lambda s: s in self.string,
self.stripped_substrings
)))
def get_specified_spans(
self self
) -> tuple[list[tuple[int, int]], list[tuple[int, int]]]: ) -> tuple[list[tuple[int, int]], list[tuple[int, int]]]:
tex_spans = sorted(remove_list_redundancies([
self.full_span,
*self.double_braces_spans,
*[
match_obj.span()
for substr in self.stripped_substrings
for match_obj in re.finditer(re.escape(substr), self.string)
]
]), key=lambda t: (t[0], -t[1]))
result = [] result = []
extended_result = [] extended_result = []
script_spans_dict = dict(self.script_spans) script_content_spans = self.script_content_spans
reversed_script_spans_dict = dict([ script_spans = [
script_span[::-1] for script_span in self.script_spans (script_char_span[0], script_content_span[1])
for script_char_span, script_content_span in zip(
self.script_char_spans, script_content_spans
)
]
tex_spans = remove_list_redundancies([
self.full_span,
*self.double_braces_spans,
*filter(lambda stripped_span: stripped_span is not None, [
self.strip_span(match_obj.span())
for substr in self.additional_substrings
for match_obj in re.finditer(re.escape(substr), self.string)
]),
*script_content_spans
]) ])
for tex_span in tex_spans: for tex_span in tex_spans:
if tex_span in self.script_content_spans: if tex_span in script_content_spans:
result.append(tex_span)
extended_result.append(tex_span)
continue continue
span_begin, span_end = tex_span span_begin, span_end = tex_span
extended_span_end = span_end shrinked_span = (span_begin, self.rslide(span_end, script_spans))
while span_end in reversed_script_spans_dict.keys(): extended_span = (span_begin, self.lslide(span_end, script_spans))
span_end = reversed_script_spans_dict[span_end] if shrinked_span[0] >= shrinked_span[1]:
while extended_span_end in script_spans_dict.keys():
extended_span_end = script_spans_dict[extended_span_end]
specified_span = (span_begin, span_end)
extended_specified_span = (span_begin, extended_span_end)
if span_begin >= span_end:
continue continue
if extended_specified_span in result: if shrinked_span in result:
continue continue
result.append(specified_span) result.append(shrinked_span)
extended_result.append(extended_specified_span) extended_result.append(extended_span)
return result, extended_result return result, extended_result
def get_tex_span_list(self) -> list[tuple[int, int]]: @property
return self.specified_spans + self.script_content_spans def tex_span_list(self) -> list[tuple[int, int]]:
return self.get_tex_span_lists()[0]
def get_extended_tex_span_list(self) -> list[tuple[int, int]]: @property
return self.extended_specified_spans + self.script_content_spans def extended_tex_span_list(self) -> list[tuple[int, int]]:
return self.get_tex_span_lists()[1]
def get_isolated_substrings(self) -> list[str]: @property
return remove_list_redundancies([ def containing_labels_dict(self) -> dict[tuple[int, int], list[int]]:
self.string[slice(*tex_span)]
for tex_span in self.tex_span_list
])
def get_containing_labels_dict(self) -> dict[tuple[int, int], list[int]]:
tex_span_list = self.tex_span_list tex_span_list = self.tex_span_list
result = { result = {
tex_span: [] tex_span: []
@ -449,40 +479,46 @@ class MTex(_TexSVG):
"Partially overlapping substrings detected: " "Partially overlapping substrings detected: "
f"'{string_0}' and '{string_1}'" f"'{string_0}' and '{string_1}'"
) )
result[self.full_span] = list(range(len(tex_span_list))) result[self.full_span] = list(range(-1, len(tex_span_list)))
return result return result
def get_color_cmd_repl_items(self) -> list[tuple[tuple[int, int], str]]: @property
color_related_command_items = [ def color_cmd_repl_items(self) -> list[tuple[tuple[int, int], str]]:
("color", 1, ""), color_related_command_dict = {
("textcolor", 1, ""), "color": (1, False),
("pagecolor", 1, "\\pagecolor{white}"), "textcolor": (1, False),
("colorbox", 1, "\\colorbox{white}"), "pagecolor": (1, True),
("fcolorbox", 2, "\\fcolorbox{white}{white}"), "colorbox": (1, True),
] "fcolorbox": (2, True),
}
result = [] result = []
string = self.string
backslash_indices = self.backslash_indices backslash_indices = self.backslash_indices
left_indices = self.left_brace_indices right_brace_indices = self.right_brace_indices
brace_indices_dict = dict(zip( pattern = "".join([
self.left_brace_indices, self.right_brace_indices r"\\",
)) "(",
for cmd_name, n_braces, repl_str in color_related_command_items: "|".join(color_related_command_dict.keys()),
pattern = cmd_name + r"(?![a-zA-Z])" ")",
for match_obj in re.finditer(pattern, string): r"(?![a-zA-Z])"
span_begin, span_end = match_obj.span() ])
span_begin -= 1 for match_obj in re.finditer(pattern, self.string):
if span_begin not in backslash_indices: span_begin, cmd_end = match_obj.span()
continue if span_begin not in backslash_indices:
for _ in range(n_braces): continue
left_index = min(filter( cmd_name = match_obj.group(1)
lambda index: index >= span_end, left_indices n_braces, substitute_cmd = color_related_command_dict[cmd_name]
)) span_end = right_brace_indices[self.find_region_index(
span_end = brace_indices_dict[left_index] + 1 cmd_end, right_brace_indices
result.append(((span_begin, span_end), repl_str)) ) + n_braces - 1] + 1
if substitute_cmd:
repl_str = "\\" + cmd_name + n_braces * "{white}"
else:
repl_str = ""
result.append(((span_begin, span_end), repl_str))
return result return result
def get_span_repl_dict(self) -> dict[tuple[int, int], str]: @property
def span_repl_dict(self) -> dict[tuple[int, int], str]:
indices, _, _, cmd_strings = zip(*sorted([ indices, _, _, cmd_strings = zip(*sorted([
( (
tex_span[flag], tex_span[flag],
@ -490,7 +526,7 @@ class MTex(_TexSVG):
-tex_span[1 - flag], -tex_span[1 - flag],
("{{" + self.get_color_command(label), "}}")[flag] ("{{" + self.get_color_command(label), "}}")[flag]
) )
for label, tex_span in enumerate(self.tex_span_list) for label, tex_span in enumerate(self.extended_tex_span_list)
for flag in range(2) for flag in range(2)
])) ]))
result = { result = {
@ -502,7 +538,17 @@ class MTex(_TexSVG):
result.update(self.color_cmd_repl_items) result.update(self.color_cmd_repl_items)
return result return result
def get_labelled_string(self) -> str: @property
def plain_string(self) -> str:
return "".join([
"{{",
self.get_color_command(self.color_to_label(self.base_color)),
self.string,
"}}"
])
@property
def labelled_string(self) -> str:
if not self.span_repl_dict: if not self.span_repl_dict:
return self.string return self.string
@ -545,7 +591,6 @@ class MTex(_TexSVG):
ordered_containing_labels[1:], ordered_span_begins[1:] ordered_containing_labels[1:], ordered_span_begins[1:]
) )
] ]
string_span_begins.insert(0, ordered_span_begins[0])
string_span_ends = [ string_span_ends = [
next_begin if next_label in containing_labels else curr_end next_begin if next_label in containing_labels else curr_end
for next_begin, next_label, containing_labels, curr_end in zip( for next_begin, next_label, containing_labels, curr_end in zip(
@ -553,49 +598,72 @@ class MTex(_TexSVG):
ordered_containing_labels[:-1], ordered_span_ends[:-1] ordered_containing_labels[:-1], ordered_span_ends[:-1]
) )
] ]
string_span_ends.append(ordered_span_ends[-1]) string_spans = list(zip(
(ordered_span_begins[0], *string_span_begins),
(*string_span_ends, ordered_span_ends[-1])
))
string = self.string string = self.string
left_indices = self.left_brace_indices left_brace_indices = self.left_brace_indices
right_indices = self.right_brace_indices right_brace_indices = self.right_brace_indices
skipped_indices = sorted(it.chain( slid_spans = self.skipped_spans + [
self.skipped_indices, (index, index + 1)
left_indices, for index in left_brace_indices + right_brace_indices
right_indices ]
))
result = [] result = []
for span_begin, span_end in zip(string_span_begins, string_span_ends): for str_span in string_spans:
while span_begin in skipped_indices: str_span = self.strip_span(str_span)
span_begin += 1 if str_span is None:
if span_begin >= span_end:
result.append("")
continue continue
while span_end - 1 in skipped_indices: str_span = self.slide(str_span, slid_spans)
span_end -= 1 if str_span is None:
unclosed_left_brace = 0 continue
unclosed_right_brace = 0 unclosed_left_braces = 0
for index in range(span_begin, span_end): unclosed_right_braces = 0
if index in left_indices: for index in range(*str_span):
unclosed_left_brace += 1 if index in left_brace_indices:
elif index in right_indices: unclosed_left_braces += 1
if unclosed_left_brace == 0: elif index in right_brace_indices:
unclosed_right_brace += 1 if unclosed_left_braces == 0:
unclosed_right_braces += 1
else: else:
unclosed_left_brace -= 1 unclosed_left_braces -= 1
result.append("".join([ result.append("".join([
unclosed_right_brace * "{", unclosed_right_braces * "{",
string[span_begin:span_end], string[slice(*str_span)],
unclosed_left_brace * "}" unclosed_left_braces * "}"
])) ]))
return result return result
@property
def specified_substrings(self) -> list[str]:
return remove_list_redundancies([
self.string[slice(*double_braces_span)]
for double_braces_span in self.double_braces_spans
] + list(filter(
lambda s: s in self.string,
self.additional_substrings
)))
def get_specified_substrings(self) -> list[str]:
return self.specified_substrings
@property
def isolated_substrings(self) -> list[str]:
return remove_list_redundancies([
self.string[slice(*tex_span)]
for tex_span in self.tex_span_list
])
def get_isolated_substrings(self) -> list[str]:
return self.isolated_substrings
## Selector ## Selector
def find_span_components_of_custom_span( def find_span_components_of_custom_span(
self, self,
custom_span: tuple[int, int] custom_span: tuple[int, int]
) -> list[tuple[int, int]] | None: ) -> list[tuple[int, int]] | None:
skipped_indices = self.skipped_indices
tex_span_choices = sorted(filter( tex_span_choices = sorted(filter(
lambda tex_span: all([ lambda tex_span: all([
tex_span[0] >= custom_span[0], tex_span[0] >= custom_span[0],
@ -606,13 +674,13 @@ class MTex(_TexSVG):
# Choose spans that reach the farthest. # Choose spans that reach the farthest.
tex_span_choices_dict = dict(tex_span_choices) tex_span_choices_dict = dict(tex_span_choices)
span_begin, span_end = custom_span
result = [] result = []
span_begin, span_end = custom_span
span_begin = self.rstrip_span(span_begin)
span_end = self.rstrip_span(span_end)
while span_begin != span_end: while span_begin != span_end:
span_begin = self.lstrip_span(span_begin)
if span_begin not in tex_span_choices_dict.keys(): if span_begin not in tex_span_choices_dict.keys():
if span_begin in skipped_indices:
span_begin += 1
continue
return None return None
next_begin = tex_span_choices_dict[span_begin] next_begin = tex_span_choices_dict[span_begin]
result.append((span_begin, next_begin)) result.append((span_begin, next_begin))
@ -640,7 +708,7 @@ class MTex(_TexSVG):
return VGroup(*[ return VGroup(*[
self.get_part_by_custom_span(match_obj.span()) self.get_part_by_custom_span(match_obj.span())
for match_obj in re.finditer( for match_obj in re.finditer(
re.escape(tex.strip()), self.string re.escape(tex), self.string
) )
]) ])