Merge pull request #1941 from 3b1b/video-work

Small bug fixes and refactors
This commit is contained in:
Grant Sanderson 2022-12-19 21:20:01 -08:00 committed by GitHub
commit 67912e26d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 60 additions and 56 deletions

View file

@ -184,7 +184,7 @@ def get_manim_dir():
return os.path.abspath(os.path.join(manimlib_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: if file_name is None:
return None return None
module_name = file_name.replace(os.sep, ".").replace(".py", "") module_name = file_name.replace(os.sep, ".").replace(".py", "")
@ -198,8 +198,9 @@ def get_indent(line: str):
return len(line) - len(line.lstrip()) return len(line) - len(line.lstrip())
@contextmanager def get_module_with_inserted_embed_line(
def insert_embed_line(file_name: str, scene_name: str, line_marker: str): file_name: str, scene_name: str, line_marker: str
):
""" """
This is hacky, but convenient. When user includes the argument "-e", it will try 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 to recreate a file that inserts the line `self.embed()` into the end of the scene's
@ -255,14 +256,19 @@ def insert_embed_line(file_name: str, scene_name: str, line_marker: str):
inserted_line = " " * n_spaces + "self.embed()\n" inserted_line = " " * n_spaces + "self.embed()\n"
new_lines = list(lines) new_lines = list(lines)
new_lines.insert(prev_line_num + 1, inserted_line) 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) fp.writelines(new_lines)
try:
yield file_name module = get_module(new_file)
finally: # This is to pretend the module imported from the edited lines
with open(file_name, 'w') as fp: # of code actually comes from the original file.
fp.writelines(lines) module.__file__ = file_name
os.remove(new_file)
return module
def get_custom_config(): def get_custom_config():
@ -326,7 +332,7 @@ def get_configuration(args):
elif not os.path.exists(__config_file__): 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(f"Using the default configuration file, which you can modify in `{global_defaults_file}`")
log.info( 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`" f" `{__config_file__}`, or run `manimgl --config`"
) )
@ -367,11 +373,12 @@ def get_configuration(args):
"quiet": args.quiet, "quiet": args.quiet,
} }
module = get_module(args.file)
if args.embed is not None: if args.embed is not None:
with insert_embed_line(args.file, args.scene_names[0], args.embed) as alt_file: module = get_module_with_inserted_embed_line(
module = get_module(alt_file) args.file, args.scene_names[0], args.embed
)
else:
module = get_module(args.file)
config = { config = {
"module": module, "module": module,

View file

@ -410,13 +410,15 @@ class Axes(VGroup, CoordinateSystem):
axis_config: dict = dict(), axis_config: dict = dict(),
x_axis_config: dict = dict(), x_axis_config: dict = dict(),
y_axis_config: dict = dict(), y_axis_config: dict = dict(),
height: float = FRAME_HEIGHT - 2, height: float | None = None,
width: float = FRAME_WIDTH - 2, width: float | None = None,
unit_size: float = 1.0,
**kwargs **kwargs
): ):
CoordinateSystem.__init__(self, x_range, y_range, **kwargs) CoordinateSystem.__init__(self, x_range, y_range, **kwargs)
VGroup.__init__(self, **kwargs) VGroup.__init__(self, **kwargs)
axis_config = dict(**axis_config, unit_size=unit_size)
self.x_axis = self.create_axis( self.x_axis = self.create_axis(
self.x_range, self.x_range,
axis_config=merge_dicts_recursively( axis_config=merge_dicts_recursively(
@ -435,7 +437,7 @@ class Axes(VGroup, CoordinateSystem):
axis_config, axis_config,
y_axis_config y_axis_config
), ),
length=height length=height,
) )
self.y_axis.rotate(90 * DEGREES, about_point=ORIGIN) self.y_axis.rotate(90 * DEGREES, about_point=ORIGIN)
# Add as a separate group in case various other # Add as a separate group in case various other
@ -449,7 +451,7 @@ class Axes(VGroup, CoordinateSystem):
self, self,
range_terms: RangeSpecifier, range_terms: RangeSpecifier,
axis_config: dict, axis_config: dict,
length: float length: float | None
) -> NumberLine: ) -> NumberLine:
axis = NumberLine(range_terms, width=length, **axis_config) axis = NumberLine(range_terms, width=length, **axis_config)
axis.shift(-axis.n2p(0)) axis.shift(-axis.n2p(0))
@ -576,8 +578,6 @@ class NumberPlane(Axes):
self, self,
x_range: RangeSpecifier = (-8.0, 8.0, 1.0), x_range: RangeSpecifier = (-8.0, 8.0, 1.0),
y_range: RangeSpecifier = (-4.0, 4.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( background_line_style: dict = dict(
stroke_color=BLUE_D, stroke_color=BLUE_D,
stroke_width=2, stroke_width=2,
@ -589,12 +589,7 @@ class NumberPlane(Axes):
make_smooth_after_applying_functions: bool = True, make_smooth_after_applying_functions: bool = True,
**kwargs **kwargs
): ):
super().__init__( super().__init__(x_range, y_range, **kwargs)
x_range, y_range,
height=height,
width=width,
**kwargs
)
self.background_line_style = dict(background_line_style) self.background_line_style = dict(background_line_style)
self.faded_line_style = dict(faded_line_style) self.faded_line_style = dict(faded_line_style)
self.faded_line_ratio = faded_line_ratio self.faded_line_ratio = faded_line_ratio

View file

@ -3,6 +3,8 @@ from __future__ import annotations
import re import re
from manimlib.mobject.svg.string_mobject import StringMobject 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 manimlib.utils.tex_file_writing import tex_content_to_svg_file
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@ -159,7 +161,7 @@ class MTex(StringMobject):
@staticmethod @staticmethod
def get_color_command(rgb_hex: str) -> str: 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) rg, b = divmod(rgb, 256)
r, g = divmod(rg, 256) r, g = divmod(rg, 256)
return f"\\color[RGB]{{{r}, {g}, {b}}}" return f"\\color[RGB]{{{r}, {g}, {b}}}"
@ -181,7 +183,7 @@ class MTex(StringMobject):
suffix_lines = [] suffix_lines = []
if not is_labelled: if not is_labelled:
prefix_lines.append(self.get_color_command( prefix_lines.append(self.get_color_command(
self.color_to_hex(self.base_color) color_to_hex(self.base_color)
)) ))
if self.alignment: if self.alignment:
prefix_lines.append(self.alignment) prefix_lines.append(self.alignment)

View file

@ -10,8 +10,9 @@ from manimlib.constants import WHITE
from manimlib.logger import log from manimlib.logger import log
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_rgb from manimlib.utils.color import color_to_hex
from manimlib.utils.color import rgb_to_hex from manimlib.utils.color import hex_to_int
from manimlib.utils.color import int_to_hex
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@ -117,7 +118,7 @@ class StringMobject(SVGMobject, ABC):
for submob, labelled_svg_submob in zip( for submob, labelled_svg_submob in zip(
self.submobjects, labelled_svg.submobjects 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() labelled_svg_submob.get_fill_color()
)) ))
if label >= labels_count: if label >= labels_count:
@ -129,7 +130,7 @@ class StringMobject(SVGMobject, ABC):
"Unrecognizable color labels detected (%s). " + \ "Unrecognizable color labels detected (%s). " + \
"The result could be unexpected.", "The result could be unexpected.",
", ".join( ", ".join(
self.int_to_hex(color) int_to_hex(color)
for color in unrecognizable_colors for color in unrecognizable_colors
) )
) )
@ -196,18 +197,6 @@ class StringMobject(SVGMobject, ABC):
def span_contains(span_0: Span, span_1: Span) -> bool: def span_contains(span_0: Span, span_1: Span) -> bool:
return span_0[0] <= span_1[0] and span_0[1] >= span_1[1] 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 # Parsing
def parse(self) -> None: def parse(self) -> None:
@ -380,7 +369,7 @@ class StringMobject(SVGMobject, ABC):
lambda label, flag, attr_dict: self.get_command_string( lambda label, flag, attr_dict: self.get_command_string(
attr_dict, attr_dict,
is_end=flag < 0, 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( prefix, suffix = self.get_content_prefix_and_suffix(

View file

@ -15,6 +15,8 @@ from manimlib.constants import NORMAL
from manimlib.logger import log from manimlib.logger import log
from manimlib.mobject.svg.string_mobject import StringMobject from manimlib.mobject.svg.string_mobject import StringMobject
from manimlib.utils.customization import get_customization 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_downloads_dir
from manimlib.utils.directories import get_text_dir from manimlib.utils.directories import get_text_dir
from manimlib.utils.simple_functions import hash_string from manimlib.utils.simple_functions import hash_string
@ -366,7 +368,7 @@ class MarkupText(StringMobject):
self, is_labelled: bool self, is_labelled: bool
) -> tuple[str, str]: ) -> tuple[str, str]:
global_attr_dict = { global_attr_dict = {
"foreground": self.color_to_hex(self.base_color), "foreground": color_to_hex(self.base_color),
"font_family": self.font, "font_family": self.font,
"font_style": self.slant, "font_style": self.slant,
"font_weight": self.weight, "font_weight": self.weight,
@ -394,7 +396,7 @@ class MarkupText(StringMobject):
self.get_command_string( self.get_command_string(
global_attr_dict, global_attr_dict,
is_end=is_end, 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) for is_end in (False, True)
) )

View file

@ -100,7 +100,6 @@ class VMobject(Mobject):
self.flat_stroke = flat_stroke self.flat_stroke = flat_stroke
self.needs_new_triangulation = True self.needs_new_triangulation = True
self.needs_new_unit_normal = True
self.triangulation = np.zeros(0, dtype='i4') self.triangulation = np.zeros(0, dtype='i4')
super().__init__(**kwargs) super().__init__(**kwargs)
@ -759,10 +758,9 @@ class VMobject(Mobject):
if not self.has_points(): if not self.has_points():
return np.zeros(3) return np.zeros(3)
nppc = self.n_points_per_curve
points = self.get_points() points = self.get_points()
p0 = points[0::nppc] p0 = points[:-1]
p1 = points[nppc - 1::nppc] p1 = points[1:]
# Each term goes through all edges [(x1, y1, z1), (x2, y2, z2)] # Each term goes through all edges [(x1, y1, z1), (x2, y2, z2)]
return 0.5 * np.array([ return 0.5 * np.array([
@ -772,7 +770,7 @@ class VMobject(Mobject):
]) ])
def get_unit_normal(self, recompute: bool = False) -> Vect3: 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] return self.data["unit_normal"][0]
if self.get_num_points() < 3: if self.get_num_points() < 3:
@ -789,12 +787,11 @@ class VMobject(Mobject):
points[2] - points[1], points[2] - points[1],
) )
self.data["unit_normal"][:] = normal self.data["unit_normal"][:] = normal
self.needs_new_unit_normal = False
return normal return normal
def refresh_unit_normal(self): def refresh_unit_normal(self):
for mob in self.get_family(): for mob in self.get_family():
mob.needs_new_unit_normal = True mob.get_unit_normal(recompute=True)
return self return self
# Alignment # Alignment
@ -1026,9 +1023,10 @@ class VMobject(Mobject):
super().set_points(points) super().set_points(points)
return self return self
@triggers_refreshed_triangulation
def append_points(self, points: Vect3Array): def append_points(self, points: Vect3Array):
super().append_points(points) super().append_points(points)
self.refresh_unit_normal()
self.refresh_triangulation()
return self return self
@triggers_refreshed_triangulation @triggers_refreshed_triangulation

View file

@ -624,7 +624,6 @@ class Scene(object):
break break
self.refresh_static_mobjects() self.refresh_static_mobjects()
self.post_play() self.post_play()
return self
def hold_loop(self): def hold_loop(self):
while self.hold_on_wait: while self.hold_on_wait:

View file

@ -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) return np.array([*color_to_int_rgb(color), alpha], dtype=np.uint8)
def color_to_hex(color: ManimColor) -> str:
return Color(color).hex.upper()
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( def color_gradient(
reference_colors: Iterable[ManimColor], reference_colors: Iterable[ManimColor],
length_of_output: int length_of_output: int