From 073a62bf03dec7863b49736001608919b38a59ad Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 19 Dec 2022 17:28:22 -0800 Subject: [PATCH 1/6] Factor rgb_to_hex, hex_to_int and int_to_hex away from StringMobject and to utils/color --- manimlib/mobject/svg/mtex_mobject.py | 6 ++++-- manimlib/mobject/svg/string_mobject.py | 23 ++++++----------------- manimlib/mobject/svg/text_mobject.py | 6 ++++-- manimlib/utils/color.py | 12 ++++++++++++ 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/manimlib/mobject/svg/mtex_mobject.py b/manimlib/mobject/svg/mtex_mobject.py index fdb40cd3..1a545666 100644 --- a/manimlib/mobject/svg/mtex_mobject.py +++ b/manimlib/mobject/svg/mtex_mobject.py @@ -3,6 +3,8 @@ from __future__ import annotations import re from manimlib.mobject.svg.string_mobject import StringMobject +from manimlib.utils.color import color_to_hex +from manimlib.utils.color import hex_to_int from manimlib.utils.tex_file_writing import tex_content_to_svg_file from typing import TYPE_CHECKING @@ -159,7 +161,7 @@ class MTex(StringMobject): @staticmethod def get_color_command(rgb_hex: str) -> str: - rgb = MTex.hex_to_int(rgb_hex) + rgb = hex_to_int(rgb_hex) rg, b = divmod(rgb, 256) r, g = divmod(rg, 256) return f"\\color[RGB]{{{r}, {g}, {b}}}" @@ -181,7 +183,7 @@ class MTex(StringMobject): suffix_lines = [] if not is_labelled: prefix_lines.append(self.get_color_command( - self.color_to_hex(self.base_color) + color_to_hex(self.base_color) )) if self.alignment: prefix_lines.append(self.alignment) diff --git a/manimlib/mobject/svg/string_mobject.py b/manimlib/mobject/svg/string_mobject.py index 0bda1488..a21ed9b4 100644 --- a/manimlib/mobject/svg/string_mobject.py +++ b/manimlib/mobject/svg/string_mobject.py @@ -10,8 +10,9 @@ from manimlib.constants import WHITE from manimlib.logger import log from manimlib.mobject.svg.svg_mobject import SVGMobject from manimlib.mobject.types.vectorized_mobject import VGroup -from manimlib.utils.color import color_to_rgb -from manimlib.utils.color import rgb_to_hex +from manimlib.utils.color import color_to_hex +from manimlib.utils.color import hex_to_int +from manimlib.utils.color import int_to_hex from typing import TYPE_CHECKING @@ -117,7 +118,7 @@ class StringMobject(SVGMobject, ABC): for submob, labelled_svg_submob in zip( self.submobjects, labelled_svg.submobjects ): - label = self.hex_to_int(self.color_to_hex( + label = hex_to_int(color_to_hex( labelled_svg_submob.get_fill_color() )) if label >= labels_count: @@ -129,7 +130,7 @@ class StringMobject(SVGMobject, ABC): "Unrecognizable color labels detected (%s). " + \ "The result could be unexpected.", ", ".join( - self.int_to_hex(color) + int_to_hex(color) for color in unrecognizable_colors ) ) @@ -196,18 +197,6 @@ class StringMobject(SVGMobject, ABC): def span_contains(span_0: Span, span_1: Span) -> bool: return span_0[0] <= span_1[0] and span_0[1] >= span_1[1] - @staticmethod - def color_to_hex(color: ManimColor) -> str: - return rgb_to_hex(color_to_rgb(color)) - - @staticmethod - def hex_to_int(rgb_hex: str) -> int: - return int(rgb_hex[1:], 16) - - @staticmethod - def int_to_hex(rgb_int: int) -> str: - return f"#{rgb_int:06x}".upper() - # Parsing def parse(self) -> None: @@ -380,7 +369,7 @@ class StringMobject(SVGMobject, ABC): lambda label, flag, attr_dict: self.get_command_string( attr_dict, is_end=flag < 0, - label_hex=self.int_to_hex(label) if is_labelled else None + label_hex=int_to_hex(label) if is_labelled else None ) ) prefix, suffix = self.get_content_prefix_and_suffix( diff --git a/manimlib/mobject/svg/text_mobject.py b/manimlib/mobject/svg/text_mobject.py index 002c8a48..f504cc01 100644 --- a/manimlib/mobject/svg/text_mobject.py +++ b/manimlib/mobject/svg/text_mobject.py @@ -15,6 +15,8 @@ from manimlib.constants import NORMAL from manimlib.logger import log from manimlib.mobject.svg.string_mobject import StringMobject from manimlib.utils.customization import get_customization +from manimlib.utils.color import color_to_hex +from manimlib.utils.color import int_to_hex from manimlib.utils.directories import get_downloads_dir from manimlib.utils.directories import get_text_dir from manimlib.utils.simple_functions import hash_string @@ -366,7 +368,7 @@ class MarkupText(StringMobject): self, is_labelled: bool ) -> tuple[str, str]: global_attr_dict = { - "foreground": self.color_to_hex(self.base_color), + "foreground": color_to_hex(self.base_color), "font_family": self.font, "font_style": self.slant, "font_weight": self.weight, @@ -394,7 +396,7 @@ class MarkupText(StringMobject): self.get_command_string( global_attr_dict, is_end=is_end, - label_hex=self.int_to_hex(0) if is_labelled else None + label_hex=int_to_hex(0) if is_labelled else None ) for is_end in (False, True) ) diff --git a/manimlib/utils/color.py b/manimlib/utils/color.py index e0078bfd..b78ce258 100644 --- a/manimlib/utils/color.py +++ b/manimlib/utils/color.py @@ -62,6 +62,18 @@ def color_to_int_rgba(color: ManimColor, opacity: float = 1.0) -> np.ndarray[int return np.array([*color_to_int_rgb(color), alpha], dtype=np.uint8) +def color_to_hex(color: ManimColor) -> str: + return rgb_to_hex(color_to_rgb(color)) + + +def hex_to_int(rgb_hex: str) -> int: + return int(rgb_hex[1:], 16) + + +def int_to_hex(rgb_int: int) -> str: + return f"#{rgb_int:06x}".upper() + + def color_gradient( reference_colors: Iterable[ManimColor], length_of_output: int From 75a98a8936d4bcc9a11176a91891e2a96dec56ea Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 19 Dec 2022 18:03:53 -0800 Subject: [PATCH 2/6] Change color_to_hex implementation --- manimlib/utils/color.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/utils/color.py b/manimlib/utils/color.py index b78ce258..f6b18e2e 100644 --- a/manimlib/utils/color.py +++ b/manimlib/utils/color.py @@ -63,7 +63,7 @@ def color_to_int_rgba(color: ManimColor, opacity: float = 1.0) -> np.ndarray[int def color_to_hex(color: ManimColor) -> str: - return rgb_to_hex(color_to_rgb(color)) + return Color(color).hex.upper() def hex_to_int(rgb_hex: str) -> int: From d8deec8f813166b9bf42960acb0f6da4349712e7 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 19 Dec 2022 18:17:25 -0800 Subject: [PATCH 3/6] Revert back from the needs_new_unit_normal change in favor of recomputation --- manimlib/mobject/types/vectorized_mobject.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/manimlib/mobject/types/vectorized_mobject.py b/manimlib/mobject/types/vectorized_mobject.py index 44b15e4d..3221beaf 100644 --- a/manimlib/mobject/types/vectorized_mobject.py +++ b/manimlib/mobject/types/vectorized_mobject.py @@ -100,7 +100,6 @@ class VMobject(Mobject): self.flat_stroke = flat_stroke self.needs_new_triangulation = True - self.needs_new_unit_normal = True self.triangulation = np.zeros(0, dtype='i4') super().__init__(**kwargs) @@ -759,10 +758,9 @@ class VMobject(Mobject): if not self.has_points(): return np.zeros(3) - nppc = self.n_points_per_curve points = self.get_points() - p0 = points[0::nppc] - p1 = points[nppc - 1::nppc] + p0 = points[:-1] + p1 = points[1:] # Each term goes through all edges [(x1, y1, z1), (x2, y2, z2)] return 0.5 * np.array([ @@ -772,7 +770,7 @@ class VMobject(Mobject): ]) def get_unit_normal(self, recompute: bool = False) -> Vect3: - if not self.needs_new_unit_normal and not recompute: + if not recompute: return self.data["unit_normal"][0] if self.get_num_points() < 3: @@ -789,12 +787,11 @@ class VMobject(Mobject): points[2] - points[1], ) self.data["unit_normal"][:] = normal - self.needs_new_unit_normal = False return normal def refresh_unit_normal(self): for mob in self.get_family(): - mob.needs_new_unit_normal = True + mob.get_unit_normal(recompute=True) return self # Alignment @@ -1026,9 +1023,10 @@ class VMobject(Mobject): super().set_points(points) return self - @triggers_refreshed_triangulation def append_points(self, points: Vect3Array): super().append_points(points) + self.refresh_unit_normal() + self.refresh_triangulation() return self @triggers_refreshed_triangulation From c605ac1c835926d67a9cebae1e1ff58cdafb9df8 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 19 Dec 2022 20:34:42 -0800 Subject: [PATCH 4/6] Better height, width, unit_size behavior for CoordinateSystems --- manimlib/mobject/coordinate_systems.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/manimlib/mobject/coordinate_systems.py b/manimlib/mobject/coordinate_systems.py index cbc11826..c2620c47 100644 --- a/manimlib/mobject/coordinate_systems.py +++ b/manimlib/mobject/coordinate_systems.py @@ -410,13 +410,15 @@ class Axes(VGroup, CoordinateSystem): axis_config: dict = dict(), x_axis_config: dict = dict(), y_axis_config: dict = dict(), - height: float = FRAME_HEIGHT - 2, - width: float = FRAME_WIDTH - 2, + height: float | None = None, + width: float | None = None, + unit_size: float = 1.0, **kwargs ): CoordinateSystem.__init__(self, x_range, y_range, **kwargs) VGroup.__init__(self, **kwargs) + axis_config = dict(**axis_config, unit_size=unit_size) self.x_axis = self.create_axis( self.x_range, axis_config=merge_dicts_recursively( @@ -435,7 +437,7 @@ class Axes(VGroup, CoordinateSystem): axis_config, y_axis_config ), - length=height + length=height, ) self.y_axis.rotate(90 * DEGREES, about_point=ORIGIN) # Add as a separate group in case various other @@ -449,7 +451,7 @@ class Axes(VGroup, CoordinateSystem): self, range_terms: RangeSpecifier, axis_config: dict, - length: float + length: float | None ) -> NumberLine: axis = NumberLine(range_terms, width=length, **axis_config) axis.shift(-axis.n2p(0)) @@ -576,8 +578,6 @@ class NumberPlane(Axes): self, x_range: RangeSpecifier = (-8.0, 8.0, 1.0), y_range: RangeSpecifier = (-4.0, 4.0, 1.0), - height: float = 8.0, - width: float = 16.0, background_line_style: dict = dict( stroke_color=BLUE_D, stroke_width=2, @@ -589,12 +589,7 @@ class NumberPlane(Axes): make_smooth_after_applying_functions: bool = True, **kwargs ): - super().__init__( - x_range, y_range, - height=height, - width=width, - **kwargs - ) + super().__init__(x_range, y_range, **kwargs) self.background_line_style = dict(background_line_style) self.faded_line_style = dict(faded_line_style) self.faded_line_ratio = faded_line_ratio From 5c7caee902c0cce3bf56b13eed310563a63225f5 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 19 Dec 2022 21:14:29 -0800 Subject: [PATCH 5/6] Go back to writing a new file for insert_embed, but edit the module's __file__ attribute --- manimlib/config.py | 20 ++++++++++++-------- manimlib/scene/scene.py | 1 - 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/manimlib/config.py b/manimlib/config.py index da5b08fd..8d98e498 100644 --- a/manimlib/config.py +++ b/manimlib/config.py @@ -184,7 +184,7 @@ def get_manim_dir(): return os.path.abspath(os.path.join(manimlib_dir, "..")) -def get_module(file_name): +def get_module(file_name: str | None): if file_name is None: return None module_name = file_name.replace(os.sep, ".").replace(".py", "") @@ -255,14 +255,14 @@ def insert_embed_line(file_name: str, scene_name: str, line_marker: str): inserted_line = " " * n_spaces + "self.embed()\n" new_lines = list(lines) new_lines.insert(prev_line_num + 1, inserted_line) + new_file = file_name.replace(".py", "_insert_embed.py") - with open(file_name, 'w') as fp: + with open(new_file, 'w') as fp: fp.writelines(new_lines) try: - yield file_name + yield new_file finally: - with open(file_name, 'w') as fp: - fp.writelines(lines) + os.remove(new_file) def get_custom_config(): @@ -326,7 +326,7 @@ def get_configuration(args): elif not os.path.exists(__config_file__): log.info(f"Using the default configuration file, which you can modify in `{global_defaults_file}`") log.info( - "If you want to create a local configuration file, you can create a file named" + "If you want to create a local configuration file, you can create a file named" + \ f" `{__config_file__}`, or run `manimgl --config`" ) @@ -367,11 +367,15 @@ def get_configuration(args): "quiet": args.quiet, } - module = get_module(args.file) - if args.embed is not None: with insert_embed_line(args.file, args.scene_names[0], args.embed) as alt_file: module = get_module(alt_file) + # Slightly hacky, this is to pretend the module imported + # from the edited lines of code actually comes from the + # original file. + module.__file__ = args.file + else: + module = get_module(args.file) config = { "module": module, diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index 9af75153..8d6a500a 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -624,7 +624,6 @@ class Scene(object): break self.refresh_static_mobjects() self.post_play() - return self def hold_loop(self): while self.hold_on_wait: From a26fe605b3ae96be293f40916b06f051d9d71d5a Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 19 Dec 2022 21:17:44 -0800 Subject: [PATCH 6/6] Slight refactor for inserted_embed_line --- manimlib/config.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/manimlib/config.py b/manimlib/config.py index 8d98e498..cb697a6b 100644 --- a/manimlib/config.py +++ b/manimlib/config.py @@ -198,8 +198,9 @@ def get_indent(line: str): return len(line) - len(line.lstrip()) -@contextmanager -def insert_embed_line(file_name: str, scene_name: str, line_marker: str): +def get_module_with_inserted_embed_line( + file_name: str, scene_name: str, line_marker: str +): """ This is hacky, but convenient. When user includes the argument "-e", it will try to recreate a file that inserts the line `self.embed()` into the end of the scene's @@ -259,10 +260,15 @@ def insert_embed_line(file_name: str, scene_name: str, line_marker: str): with open(new_file, 'w') as fp: fp.writelines(new_lines) - try: - yield new_file - finally: - os.remove(new_file) + + module = get_module(new_file) + # This is to pretend the module imported from the edited lines + # of code actually comes from the original file. + module.__file__ = file_name + + os.remove(new_file) + + return module def get_custom_config(): @@ -368,12 +374,9 @@ def get_configuration(args): } if args.embed is not None: - with insert_embed_line(args.file, args.scene_names[0], args.embed) as alt_file: - module = get_module(alt_file) - # Slightly hacky, this is to pretend the module imported - # from the edited lines of code actually comes from the - # original file. - module.__file__ = args.file + module = get_module_with_inserted_embed_line( + args.file, args.scene_names[0], args.embed + ) else: module = get_module(args.file)