diff --git a/manimlib/mobject/svg/labelled_string.py b/manimlib/mobject/svg/labelled_string.py index dedb7591..18ecd556 100644 --- a/manimlib/mobject/svg/labelled_string.py +++ b/manimlib/mobject/svg/labelled_string.py @@ -211,24 +211,6 @@ class LabelledString(SVGMobject, ABC): val_ranges = LabelledString.get_neighbouring_pairs(indices) return list(zip(unique_vals, val_ranges)) - @staticmethod - def sort_obj_pairs_by_spans( - obj_pairs: list[tuple[Span, tuple[T, T]]] - ) -> list[tuple[int, T]]: - return sorted([ - (index, obj) - for (index, _), obj in [ - *sorted([ - (span[::-1], end_obj) - for span, (_, end_obj) in reversed(obj_pairs) - ], key=lambda t: (t[0][0], -t[0][1])), - *sorted([ - (span, begin_obj) - for span, (begin_obj, _) in obj_pairs - ], key=lambda t: (t[0][0], -t[0][1])) - ] - ], key=lambda t: t[0]) - @staticmethod def span_contains(span_0: Span, span_1: Span) -> bool: return span_0[0] <= span_1[0] and span_0[1] >= span_1[1] @@ -246,13 +228,11 @@ class LabelledString(SVGMobject, ABC): (*span_ends, universal_span[1]) )) - def replace_string(self, span: Span, repl_items: list[Span, str]): + def replace_substr(self, span: Span, repl_items: list[Span, str]): if not repl_items: return self.get_substr(span) - repl_spans, repl_strs = zip(*sorted( - repl_items, key=lambda t: t[0] - )) + repl_spans, repl_strs = zip(*sorted(repl_items, key=lambda t: t[0])) pieces = [ self.get_substr(piece_span) for piece_span in self.get_complement_spans(span, repl_spans) @@ -278,7 +258,6 @@ class LabelledString(SVGMobject, ABC): cmd_spans = self.get_cmd_spans() cmd_substrs = [self.get_substr(span) for span in cmd_spans] flags = [self.get_substr_flag(substr) for substr in cmd_substrs] - specified_items = self.get_specified_items( self.get_cmd_span_pairs(cmd_spans, flags) ) @@ -303,13 +282,6 @@ class LabelledString(SVGMobject, ABC): ] self.check_overlapping() - #self.original_content = self.get_content( - # cmd_repl_items_for_content, split_items, is_labelled=False - #) - #self.labelled_content = self.get_content( - # cmd_repl_items_for_content, split_items, is_labelled=True - #) - @abstractmethod def get_cmd_spans(self) -> list[Span]: return [] @@ -318,6 +290,14 @@ class LabelledString(SVGMobject, ABC): def get_substr_flag(self, substr: str) -> int: return 0 + @abstractmethod + def get_repl_substr_for_content(self, substr: str) -> str: + return "" + + @abstractmethod + def get_repl_substr_for_matching(self, substr: str) -> str: + return "" + @staticmethod def get_cmd_span_pairs( cmd_spans: list[Span], flags: list[int] @@ -329,11 +309,11 @@ class LabelledString(SVGMobject, ABC): begin_cmd_spans_stack.append(cmd_span) elif flag == -1: if not begin_cmd_spans_stack: - raise ValueError("Missing '{' inserted") + raise ValueError("Missing open command") begin_cmd_span = begin_cmd_spans_stack.pop() result.append((begin_cmd_span, cmd_span)) if begin_cmd_spans_stack: - raise ValueError("Missing '}' inserted") + raise ValueError("Missing close command") return result @abstractmethod @@ -394,14 +374,6 @@ class LabelledString(SVGMobject, ABC): f"'{self.get_substr(span_0)}' and '{self.get_substr(span_1)}'" ) - @abstractmethod - def get_repl_substr_for_content(self, substr: str) -> str: - return "" - - @abstractmethod - def get_repl_substr_for_matching(self, substr: str) -> str: - return "" - @staticmethod @abstractmethod def get_cmd_str_pair( @@ -423,16 +395,27 @@ class LabelledString(SVGMobject, ABC): )) for label, (span, attr_dict) in enumerate(self.split_items) ] + inserted_str_items = sorted([ + (index, s) + for (index, _), s in [ + *sorted([ + (span[::-1], end_str) + for span, (_, end_str) in reversed(inserted_str_pairs) + ], key=lambda t: (t[0][0], -t[0][1])), + *sorted([ + (span, begin_str) + for span, (begin_str, _) in inserted_str_pairs + ], key=lambda t: (t[0][0], -t[0][1])) + ] + ], key=lambda t: t[0]) repl_items = self.cmd_repl_items_for_content + [ ((index, index), inserted_str) - for index, inserted_str in self.sort_obj_pairs_by_spans( - inserted_str_pairs - ) + for index, inserted_str in inserted_str_items ] prefix, suffix = self.get_content_prefix_and_suffix(is_labelled) return "".join([ prefix, - self.replace_string(self.full_span, repl_items), + self.replace_substr(self.full_span, repl_items), suffix ]) @@ -483,7 +466,7 @@ class LabelledString(SVGMobject, ABC): ) ] group_substrs = [ - re.sub(r"\s+", "", self.replace_string( + re.sub(r"\s+", "", self.replace_substr( span, [ (cmd_span, repl_str) for cmd_span, repl_str in self.cmd_repl_items_for_matching diff --git a/manimlib/mobject/svg/mtex_mobject.py b/manimlib/mobject/svg/mtex_mobject.py index 3e06a5f8..6245149e 100644 --- a/manimlib/mobject/svg/mtex_mobject.py +++ b/manimlib/mobject/svg/mtex_mobject.py @@ -81,6 +81,12 @@ class MTex(LabelledString): def get_substr_flag(self, substr: str) -> int: return {"{": 1, "}": -1}.get(substr, 0) + def get_repl_substr_for_content(self, substr: str) -> str: + return substr + + def get_repl_substr_for_matching(self, substr: str) -> str: + return substr if substr.startswith("\\") else "" + def get_specified_items( self, cmd_span_pairs: list[tuple[Span, Span]] ) -> list[tuple[Span, dict[str, str]]]: @@ -108,12 +114,6 @@ class MTex(LabelledString): ] return [(span, {}) for span in specified_spans] - def get_repl_substr_for_content(self, substr: str) -> str: - return substr - - def get_repl_substr_for_matching(self, substr: str) -> str: - return substr if substr.startswith("\\") else "" - @staticmethod def get_color_cmd_str(rgb_hex: str) -> str: rgb = MTex.hex_to_int(rgb_hex) diff --git a/manimlib/mobject/svg/text_mobject.py b/manimlib/mobject/svg/text_mobject.py index a7839288..3bfdf0d2 100644 --- a/manimlib/mobject/svg/text_mobject.py +++ b/manimlib/mobject/svg/text_mobject.py @@ -247,6 +247,34 @@ class MarkupText(LabelledString): return -1 return 0 + def get_repl_substr_for_content(self, substr: str) -> str: + if substr.startswith("<") and substr.endswith(">"): + return "" + return { + "<": "<", + ">": ">", + "&": "&", + "\"": """, + "'": "'" + }.get(substr, substr) + + def get_repl_substr_for_matching(self, substr: str) -> str: + if substr.startswith("<") and substr.endswith(">"): + return "" + if substr.startswith("&#") and substr.endswith(";"): + if substr.startswith("&#x"): + char_reference = int(substr[3:-1], 16) + else: + char_reference = int(substr[2:-1], 10) + return chr(char_reference) + return { + "<": "<", + ">": ">", + "&": "&", + """: "\"", + "'": "'" + }.get(substr, substr) + def get_specified_items( self, cmd_span_pairs: list[tuple[Span, Span]] ) -> list[tuple[Span, dict[str, str]]]: @@ -290,34 +318,6 @@ class MarkupText(LabelledString): ] ] - def get_repl_substr_for_content(self, substr: str) -> str: - if substr.startswith("<") and substr.endswith(">"): - return "" - return { - "<": "<", - ">": ">", - "&": "&", - "\"": """, - "'": "'" - }.get(substr, substr) - - def get_repl_substr_for_matching(self, substr: str) -> str: - if substr.startswith("<") and substr.endswith(">"): - return "" - if substr.startswith("&#") and substr.endswith(";"): - if substr.startswith("&#x"): - char_reference = int(substr[3:-1], 16) - else: - char_reference = int(substr[2:-1], 10) - return chr(char_reference) - return { - "<": "<", - ">": ">", - "&": "&", - """: "\"", - "'": "'" - }.get(substr, substr) - @staticmethod def get_cmd_str_pair( attr_dict: dict[str, str], label_hex: str | None