From 120d26defac667171536effab0dbf8a5823113a9 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Mar 2021 17:32:45 -0700 Subject: [PATCH 01/30] If chosen monitor is not available, choose one that does exist --- manimlib/config.py | 4 +++- manimlib/window.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/manimlib/config.py b/manimlib/config.py index cde35a16..5c0e88eb 100644 --- a/manimlib/config.py +++ b/manimlib/config.py @@ -234,7 +234,9 @@ def get_configuration(args): # Default to making window half the screen size # but make it full screen if -f is passed in - monitor = get_monitors()[custom_config["window_monitor"]] + monitors = get_monitors() + mon_index = custom_config["window_monitor"] + monitor = monitors[min(mon_index, len(monitors) - 1)] window_width = monitor.width if not args.full_screen: window_width //= 2 diff --git a/manimlib/window.py b/manimlib/window.py index ddf0d8fe..b6dc40db 100644 --- a/manimlib/window.py +++ b/manimlib/window.py @@ -38,7 +38,9 @@ class Window(PygletWindow): def find_initial_position(self, size): custom_position = get_customization()["window_position"] - monitor = get_monitors()[get_customization()["window_monitor"]] + monitors = get_monitors() + mon_index = get_customization()["window_monitor"] + monitor = monitors[min(mon_index, len(monitors) - 1)] window_width, window_height = size # Position might be specified with a string of the form # x,y for integers x and y From 3c240478b8ad78db48fddc14de173b65c6f55c06 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Mar 2021 17:33:20 -0700 Subject: [PATCH 02/30] Tiny format change --- manimlib/utils/directories.py | 1 + 1 file changed, 1 insertion(+) diff --git a/manimlib/utils/directories.py b/manimlib/utils/directories.py index dfc9aaec..c82a5368 100644 --- a/manimlib/utils/directories.py +++ b/manimlib/utils/directories.py @@ -23,6 +23,7 @@ def get_text_dir(): def get_mobject_data_dir(): return guarantee_existence(os.path.join(get_temp_dir(), "mobject_data")) + def get_downloads_dir(): return guarantee_existence(os.path.join(get_temp_dir(), "manim_downloads")) From ed2e3e80d9c3d431cb9cc0545f29b583c57d9868 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Mar 2021 17:34:16 -0700 Subject: [PATCH 03/30] Updates to VectorField --- manimlib/mobject/vector_field.py | 35 +++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/manimlib/mobject/vector_field.py b/manimlib/mobject/vector_field.py index 1405ec3f..7aaa6044 100644 --- a/manimlib/mobject/vector_field.py +++ b/manimlib/mobject/vector_field.py @@ -62,10 +62,13 @@ def move_submobjects_along_vector_field(mobject, func): return mobject -def move_points_along_vector_field(mobject, func): +def move_points_along_vector_field(mobject, func, coordinate_system): + cs = coordinate_system + origin = cs.get_origin() + def apply_nudge(self, dt): - self.mobject.apply_function( - lambda p: p + func(p) * dt + mobject.apply_function( + lambda p: p + (cs.c2p(*func(*cs.p2c(p))) - origin) * dt ) mobject.add_updater(apply_nudge) return mobject @@ -162,19 +165,21 @@ class StreamLines(VGroup): self.init_style() def point_func(self, point): - return self.coordinate_system.c2p( - *self.func(*self.coordinate_system.p2c(point)) - ) + in_coords = self.coordinate_system.p2c(point) + out_coords = self.func(*in_coords) + return self.coordinate_system.c2p(*out_coords) def draw_lines(self): lines = [] + origin = self.coordinate_system.get_origin() for point in self.get_start_points(): points = [point] total_arc_len = 0 - # for t in np.arange(0, self.virtual_time, self.dt): + time = 0 for x in range(self.max_time_steps): + time += self.dt last_point = points[-1] - new_point = last_point + self.dt * self.point_func(last_point) + new_point = last_point + self.dt * (self.point_func(last_point) - origin) points.append(new_point) total_arc_len += get_norm(new_point - last_point) if get_norm(last_point) > self.cutoff_norm: @@ -182,8 +187,10 @@ class StreamLines(VGroup): if total_arc_len > self.arc_len: break line = VMobject() + line.virtual_time = time step = max(1, int(len(points) / self.n_samples_per_line)) - line.set_points_smoothly(points[::step]) + line.set_points_as_corners(points[::step]) + line.make_approximately_smooth() lines.append(line) self.set_submobjects(lines) @@ -220,7 +227,7 @@ class StreamLines(VGroup): rgbas[:, 3] = self.stroke_opacity line.set_rgba_array(rgbas, "stroke_rgba") else: - self.set_stroke(self.stroke_color) + self.set_stroke(self.stroke_color, opacity=self.stroke_opacity) if self.taper_stroke_width: width = [0, self.stroke_width, 0] @@ -234,7 +241,7 @@ class AnimatedStreamLines(VGroup): "lag_range": 4, "line_anim_class": VShowPassingFlash, "line_anim_config": { - "run_time": 4, + # "run_time": 4, "rate_func": linear, "time_width": 0.5, }, @@ -244,7 +251,11 @@ class AnimatedStreamLines(VGroup): super().__init__(**kwargs) self.stream_lines = stream_lines for line in stream_lines: - line.anim = self.line_anim_class(line, **self.line_anim_config) + line.anim = self.line_anim_class( + line, + run_time=line.virtual_time, + **self.line_anim_config, + ) line.anim.begin() line.time = -self.lag_range * random.random() self.add(line.anim.mobject) From 0e326c7ac5aad544f35114de44beeafeae3de1c9 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Mar 2021 17:34:36 -0700 Subject: [PATCH 04/30] Return stroke_width as 1d array --- manimlib/mobject/types/vectorized_mobject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/mobject/types/vectorized_mobject.py b/manimlib/mobject/types/vectorized_mobject.py index 874047ce..c1c698f1 100644 --- a/manimlib/mobject/types/vectorized_mobject.py +++ b/manimlib/mobject/types/vectorized_mobject.py @@ -244,7 +244,7 @@ class VMobject(Mobject): return self.data['stroke_rgba'][:, 3] def get_stroke_widths(self): - return self.data['stroke_width'] + return self.data['stroke_width'][:, 0] # TODO, it's weird for these to return the first of various lists # rather than the full information From 15f3b359aeeed7a37bddcc1cffd5f2ab1ebd635f Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Mar 2021 17:34:57 -0700 Subject: [PATCH 05/30] Added Text.get_parts_by_text --- manimlib/mobject/svg/text_mobject.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/manimlib/mobject/svg/text_mobject.py b/manimlib/mobject/svg/text_mobject.py index 6e6f3761..63b8ba62 100644 --- a/manimlib/mobject/svg/text_mobject.py +++ b/manimlib/mobject/svg/text_mobject.py @@ -10,6 +10,7 @@ import manimpango from manimlib.constants import * from manimlib.mobject.geometry import Dot from manimlib.mobject.svg.svg_mobject import SVGMobject +from manimlib.mobject.types.vectorized_mobject import VGroup from manimlib.utils.config_ops import digest_config from manimlib.utils.customization import get_customization from manimlib.utils.directories import get_downloads_dir, get_text_dir @@ -100,6 +101,19 @@ class Text(SVGMobject): index = self.text.find(word, index + len(word)) return indexes + def get_parts_by_text(self, word): + return VGroup(*( + self[i:j] + for i, j in self.find_indexes(word) + )) + + def get_part_by_text(word): + parts = self.get_parts_by_text() + if len(parts) > 0: + return parts[0] + else: + return None + def full2short(self, config): for kwargs in [config, self.CONFIG]: if kwargs.__contains__('line_spacing_height'): @@ -212,6 +226,7 @@ class Text(SVGMobject): self.text, ) + @contextmanager def register_font(font_file: typing.Union[str, Path]): """Temporarily add a font file to Pango's search path. From 933b7fd3da0edffa125e2ef566cc669380749e41 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Mar 2021 17:35:23 -0700 Subject: [PATCH 06/30] Use Text not TexText for Brace --- manimlib/mobject/svg/brace.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/manimlib/mobject/svg/brace.py b/manimlib/mobject/svg/brace.py index 5fe36d19..6d46d3e3 100644 --- a/manimlib/mobject/svg/brace.py +++ b/manimlib/mobject/svg/brace.py @@ -8,6 +8,7 @@ from manimlib.animation.growing import GrowFromCenter from manimlib.mobject.svg.tex_mobject import Tex from manimlib.mobject.svg.tex_mobject import SingleStringTex from manimlib.mobject.svg.tex_mobject import TexText +from manimlib.mobject.svg.text_mobject import Text from manimlib.mobject.types.vectorized_mobject import VMobject from manimlib.utils.config_ops import digest_config from manimlib.utils.space_ops import get_norm @@ -61,8 +62,8 @@ class Brace(SingleStringTex): mob.shift(self.get_direction() * shift_distance) return self - def get_text(self, *text, **kwargs): - text_mob = TexText(*text) + def get_text(self, text, **kwargs): + text_mob = Text(text) self.put_at_tip(text_mob, **kwargs) return text_mob From 611ac7f44801dd71491d942462f32ca00572c394 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Mar 2021 17:36:46 -0700 Subject: [PATCH 07/30] Update to Cross to make it default to variable stroke width --- manimlib/mobject/shape_matchers.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/manimlib/mobject/shape_matchers.py b/manimlib/mobject/shape_matchers.py index f9a6ba51..9b320740 100644 --- a/manimlib/mobject/shape_matchers.py +++ b/manimlib/mobject/shape_matchers.py @@ -64,16 +64,17 @@ class BackgroundRectangle(SurroundingRectangle): class Cross(VGroup): CONFIG = { "stroke_color": RED, - "stroke_width": 6, + "stroke_width": [0, 6, 0], } def __init__(self, mobject, **kwargs): - VGroup.__init__(self, - Line(UP + LEFT, DOWN + RIGHT), - Line(UP + RIGHT, DOWN + LEFT), - ) + super().__init__( + Line(UL, DR), + Line(UR, DL), + ) + self.insert_n_curves(2) self.replace(mobject, stretch=True) - self.set_stroke(self.stroke_color, self.stroke_width) + self.set_stroke(self.stroke_color, width=self.stroke_width) class Underline(Line): From 8345ca6160126ade1b577f32050a1f5936e43498 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Mar 2021 17:37:12 -0700 Subject: [PATCH 08/30] Small fixes to NumberLine --- manimlib/mobject/number_line.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/manimlib/mobject/number_line.py b/manimlib/mobject/number_line.py index e51e9f8f..73beb534 100644 --- a/manimlib/mobject/number_line.py +++ b/manimlib/mobject/number_line.py @@ -144,7 +144,7 @@ class NumberLine(Line): direction=direction, buff=buff ) - if x < 0 and self.line_to_number_direction[0] == 0: + if x < 0 and direction[0] == 0: # Align without the minus sign num_mob.shift(num_mob[0].get_width() * LEFT / 2) return num_mob @@ -155,10 +155,11 @@ class NumberLine(Line): kwargs["font_size"] = font_size + if excluding is None: + excluding = self.numbers_to_exclude + numbers = VGroup() for x in x_values: - if x in self.numbers_to_exclude: - continue if excluding is not None and x in excluding: continue numbers.add(self.get_number_mobject(x, **kwargs)) From 5126dd1f522ef988c31018a19687af950cace8db Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Mar 2021 17:42:19 -0700 Subject: [PATCH 09/30] New defaults for FullScreenRectangle --- manimlib/mobject/frame.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/manimlib/mobject/frame.py b/manimlib/mobject/frame.py index 4ea2260d..7523ab51 100644 --- a/manimlib/mobject/frame.py +++ b/manimlib/mobject/frame.py @@ -20,6 +20,9 @@ class ScreenRectangle(Rectangle): class FullScreenRectangle(ScreenRectangle): CONFIG = { "height": FRAME_HEIGHT, + "fill_color": GREY_E, + "fill_opacity": 1, + "stroke_width": 0, } From f6ff070a8ebfaaee3db92525cc697e55c5b92193 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Mar 2021 17:42:47 -0700 Subject: [PATCH 10/30] Add FlashAround and FlashUnder --- manimlib/animation/indication.py | 80 ++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/manimlib/animation/indication.py b/manimlib/animation/indication.py index 453330ff..0c425b7f 100644 --- a/manimlib/animation/indication.py +++ b/manimlib/animation/indication.py @@ -14,6 +14,7 @@ from manimlib.mobject.types.vectorized_mobject import VMobject from manimlib.mobject.geometry import Circle from manimlib.mobject.geometry import Dot from manimlib.mobject.shape_matchers import SurroundingRectangle +from manimlib.mobject.shape_matchers import Underline from manimlib.mobject.types.vectorized_mobject import VGroup from manimlib.mobject.geometry import Line from manimlib.utils.bezier import interpolate @@ -156,45 +157,50 @@ class ShowPassingFlash(ShowPartial): class VShowPassingFlash(Animation): CONFIG = { "time_width": 0.3, - "taper_width": 0.1, + "taper_width": 0.02, "remover": True, } def begin(self): self.mobject.align_stroke_width_data_to_points() + # Compute an array of stroke widths for each submobject + # which tapers out at either end + self.submob_to_anchor_widths = dict() + for sm in self.mobject.get_family(): + original_widths = sm.get_stroke_widths() + anchor_widths = np.array([*original_widths[0::3], original_widths[-1]]) + + def taper_kernel(x): + if x < self.taper_width: + return x + elif x > 1 - self.taper_width: + return 1.0 - x + return 1.0 + + taper_array = list(map(taper_kernel, np.linspace(0, 1, len(anchor_widths)))) + self.submob_to_anchor_widths[hash(sm)] = anchor_widths * taper_array super().begin() def interpolate_submobject(self, submobject, starting_sumobject, alpha): - original_widths = starting_sumobject.get_stroke_widths() - # anchor_widths = np.array([*original_widths[0::3, 0], original_widths[-1, 0]]) - anchor_widths = np.array([0, *original_widths[3::3, 0], 0]) - n_anchors = len(anchor_widths) - time_width = self.time_width - # taper_width = self.taper_width + anchor_widths = self.submob_to_anchor_widths[hash(submobject)] # Create a gaussian such that 3 sigmas out on either side - # will equals time_width * (number of points) - sigma = time_width / 6 - mu = interpolate(-time_width / 2, 1 + time_width / 2, alpha) - offset = math.exp(-4.5) # 3 sigmas out + # will equals time_width + tw = self.time_width + sigma = tw / 6 + mu = interpolate(-tw / 2, 1 + tw / 2, alpha) - def kernel_func(x): - result = math.exp(-0.5 * ((x - mu) / sigma)**2) - offset - result = max(result, 0) - # if x < taper_width: - # result *= x / taper_width - # elif x > 1 - taper_width: - # result *= (1 - x) / taper_width - return result + def gauss_kernel(x): + if abs(x - mu) > 3 * sigma: + return 0 + z = (x - mu) / sigma + return math.exp(-0.5 * z * z) - kernel_array = np.array([ - kernel_func(n / (n_anchors - 1)) - for n in range(n_anchors) - ]) + kernel_array = list(map(gauss_kernel, np.linspace(0, 1, len(anchor_widths)))) scaled_widths = anchor_widths * kernel_array new_widths = np.zeros(submobject.get_num_points()) new_widths[0::3] = scaled_widths[:-1] - new_widths[1::3] = (scaled_widths[:-1] + scaled_widths[1:]) / 2 new_widths[2::3] = scaled_widths[1:] + new_widths[1::3] = (new_widths[0::3] + new_widths[2::3]) / 2 submobject.set_stroke(width=new_widths) def finish(self): @@ -203,6 +209,32 @@ class VShowPassingFlash(Animation): submob.match_style(start) +class FlashAround(VShowPassingFlash): + CONFIG = { + "stroke_width": 4.0, + "color": YELLOW, + "buff": SMALL_BUFF, + "time_width": 1.0, + "n_inserted_curves": 20, + } + + def __init__(self, mobject, **kwargs): + digest_config(self, kwargs) + path = self.get_path(mobject) + path.insert_n_curves(self.n_inserted_curves) + path.set_points(path.get_points_without_null_curves()) + path.set_stroke(self.color, self.stroke_width) + super().__init__(path, **kwargs) + + def get_path(self, mobject): + return SurroundingRectangle(mobject, buff=self.buff) + + +class FlashUnder(FlashAround): + def get_path(self, mobject): + return Underline(mobject, buff=self.buff) + + class ShowCreationThenDestruction(ShowPassingFlash): CONFIG = { "time_width": 2.0, From 288983e7b95eb491f756f32ae8e003c68e2af0f5 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Mar 2021 17:43:15 -0700 Subject: [PATCH 11/30] Make sure mobject data gets unlocked after animations --- manimlib/animation/creation.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/manimlib/animation/creation.py b/manimlib/animation/creation.py index 40ffd235..6a69815f 100644 --- a/manimlib/animation/creation.py +++ b/manimlib/animation/creation.py @@ -25,6 +25,10 @@ class ShowPartial(Animation): if not self.should_match_start: self.mobject.lock_matching_data(self.mobject, self.starting_mobject) + def finish(self): + super().finish() + self.mobject.unlock_data() + def interpolate_submobject(self, submob, start_submob, alpha): submob.pointwise_become_partial( start_submob, *self.get_bounds(alpha) From fd18e4a21f534f777818f515e45f802da9632edf Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Fri, 19 Mar 2021 10:55:04 -0700 Subject: [PATCH 12/30] Fixed missing arg in self.get_parts_by_text --- manimlib/mobject/svg/text_mobject.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manimlib/mobject/svg/text_mobject.py b/manimlib/mobject/svg/text_mobject.py index 63b8ba62..e0161f23 100644 --- a/manimlib/mobject/svg/text_mobject.py +++ b/manimlib/mobject/svg/text_mobject.py @@ -107,8 +107,8 @@ class Text(SVGMobject): for i, j in self.find_indexes(word) )) - def get_part_by_text(word): - parts = self.get_parts_by_text() + def get_part_by_text(self, word): + parts = self.get_parts_by_text(word) if len(parts) > 0: return parts[0] else: From 52baf5b7c2c9284bfbe6b192358c790f80affb77 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 23 Mar 2021 08:46:13 -0700 Subject: [PATCH 13/30] Change matrix entry alignment default --- manimlib/mobject/matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/mobject/matrix.py b/manimlib/mobject/matrix.py index a4c95f18..9541ddc0 100644 --- a/manimlib/mobject/matrix.py +++ b/manimlib/mobject/matrix.py @@ -63,7 +63,7 @@ class Matrix(VMobject): "include_background_rectangle": False, "element_to_mobject": Tex, "element_to_mobject_config": {}, - "element_alignment_corner": DR, + "element_alignment_corner": DOWN, } def __init__(self, matrix, **kwargs): From 6c3e4b94eadaf818afa2c3da5d259e8611a54461 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 23 Mar 2021 08:50:10 -0700 Subject: [PATCH 14/30] Add min_scale_factor to keep Mobjects from shrinking to 0, and hence losing all shape information, unless its purposeful --- manimlib/mobject/mobject.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manimlib/mobject/mobject.py b/manimlib/mobject/mobject.py index 17dcca11..729cc51b 100644 --- a/manimlib/mobject/mobject.py +++ b/manimlib/mobject/mobject.py @@ -561,7 +561,7 @@ class Mobject(object): ) return self - def scale(self, scale_factor, **kwargs): + def scale(self, scale_factor, min_scale_factor=1e-8, **kwargs): """ Default behavior is to scale about the center of the mobject. The argument about_edge can be a vector, indicating which side of @@ -571,6 +571,7 @@ class Mobject(object): Otherwise, if about_point is given a value, scaling is done with respect to that point. """ + scale_factor = max(scale_factor, min_scale_factor) self.apply_points_function( lambda points: scale_factor * points, works_on_bounding_box=True, From 01d989ba2390cfab6174f6824b1fc5115b5ea0fd Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 24 Mar 2021 13:58:18 -0700 Subject: [PATCH 15/30] Fix a bug for off-center vector fields --- manimlib/mobject/vector_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/mobject/vector_field.py b/manimlib/mobject/vector_field.py index 7aaa6044..0776a3cc 100644 --- a/manimlib/mobject/vector_field.py +++ b/manimlib/mobject/vector_field.py @@ -131,7 +131,7 @@ class VectorField(VGroup): origin, _output, buff=0, **vector_config ) - vect.shift(_input) + vect.shift(_input - origin) vect.set_rgba_array([[*self.value_to_rgb(norm), self.opacity]]) return vect From 09579fcd3eb94be7f0c6d3963afa2bce74941ecf Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 24 Mar 2021 13:58:52 -0700 Subject: [PATCH 16/30] Not a great long-term fix, but flipping should always refresh the triangulation --- manimlib/mobject/types/vectorized_mobject.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/manimlib/mobject/types/vectorized_mobject.py b/manimlib/mobject/types/vectorized_mobject.py index c1c698f1..1394e78b 100644 --- a/manimlib/mobject/types/vectorized_mobject.py +++ b/manimlib/mobject/types/vectorized_mobject.py @@ -848,8 +848,8 @@ class VMobject(Mobject): old_points = self.get_points() func(self, *args, **kwargs) if not np.all(self.get_points() == old_points): - self.refresh_triangulation() self.refresh_unit_normal() + self.refresh_triangulation() return wrapper @triggers_refreshed_triangulation @@ -870,9 +870,10 @@ class VMobject(Mobject): self.make_approximately_smooth() return self - @triggers_refreshed_triangulation def flip(self, *args, **kwargs): super().flip(*args, **kwargs) + self.refresh_unit_normal() + self.refresh_triangulation() return self # For shaders From 88f0c24c69152a718735debf0449f658c56f8ebc Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 24 Mar 2021 14:00:46 -0700 Subject: [PATCH 17/30] Decompose ellipse manipulations --- manimlib/mobject/svg/svg_mobject.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/manimlib/mobject/svg/svg_mobject.py b/manimlib/mobject/svg/svg_mobject.py index a61a6eac..deb902a6 100644 --- a/manimlib/mobject/svg/svg_mobject.py +++ b/manimlib/mobject/svg/svg_mobject.py @@ -169,7 +169,11 @@ class SVGMobject(VMobject): else 0.0 for key in ("cx", "cy", "rx", "ry") ] - return Circle().scale(rx * RIGHT + ry * UP).shift(x * RIGHT + y * DOWN) + result = Circle() + result.stretch(rx, 0) + result.stretch(ry, 1) + result.shift(x * RIGHT + y * DOWN) + return result def rect_to_mobject(self, rect_element): fill_color = rect_element.getAttribute("fill") From 8999ebb5565e289c75d76909632b6555f884d209 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 27 Mar 2021 11:55:58 -0700 Subject: [PATCH 18/30] Also look for jpegs --- manimlib/utils/images.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/utils/images.py b/manimlib/utils/images.py index 40b6e0d1..e302c3b2 100644 --- a/manimlib/utils/images.py +++ b/manimlib/utils/images.py @@ -10,7 +10,7 @@ def get_full_raster_image_path(image_file_name): return find_file( image_file_name, directories=[get_raster_image_dir()], - extensions=[".jpg", ".png", ".gif", ""] + extensions=[".jpg", ".jpeg", ".png", ".gif", ""] ) From d3e61b962b232eb339753609fe218fddd422a30c Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 27 Mar 2021 11:56:36 -0700 Subject: [PATCH 19/30] Have DecimalNumber match full family style when setting a new value --- manimlib/mobject/numbers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/manimlib/mobject/numbers.py b/manimlib/mobject/numbers.py index 94b8eec3..34d1fa6c 100644 --- a/manimlib/mobject/numbers.py +++ b/manimlib/mobject/numbers.py @@ -128,10 +128,11 @@ class DecimalNumber(VMobject): def set_value(self, number): move_to_point = self.get_edge_center(self.edge_to_fix) - style = self.get_style() + old_submobjects = self.submobjects self.set_submobjects_from_number(number) self.move_to(move_to_point, self.edge_to_fix) - self.set_style(**style) + for sm1, sm2 in zip(self.submobjects, old_submobjects): + sm1.match_style(sm2) return self def scale(self, scale_factor, **kwargs): From 7f47815230fd6dd9b231f1dbdbb6b42e589c71e5 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 27 Mar 2021 11:56:58 -0700 Subject: [PATCH 20/30] Change some defaults and add Matrix.get_rows method --- manimlib/mobject/matrix.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/manimlib/mobject/matrix.py b/manimlib/mobject/matrix.py index 9541ddc0..adcbf664 100644 --- a/manimlib/mobject/matrix.py +++ b/manimlib/mobject/matrix.py @@ -57,8 +57,8 @@ class Matrix(VMobject): CONFIG = { "v_buff": 0.8, "h_buff": 1.3, - "bracket_h_buff": MED_SMALL_BUFF, - "bracket_v_buff": MED_SMALL_BUFF, + "bracket_h_buff": 0.2, + "bracket_v_buff": 0.25, "add_background_rectangles_to_entries": False, "include_background_rectangle": False, "element_to_mobject": Tex, @@ -132,6 +132,12 @@ class Matrix(VMobject): for i in range(len(self.mob_matrix[0])) ]) + def get_rows(self): + return VGroup(*[ + VGroup(*row) + for row in self.mob_matrix + ]) + def set_column_colors(self, *colors): columns = self.get_columns() for color, column in zip(colors, columns): From 6997cc95010eff7405d9601aeb1c116329053713 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 27 Mar 2021 11:57:50 -0700 Subject: [PATCH 21/30] Have Mobject.match_points return self --- manimlib/mobject/mobject.py | 1 + 1 file changed, 1 insertion(+) diff --git a/manimlib/mobject/mobject.py b/manimlib/mobject/mobject.py index 729cc51b..3edec5d8 100644 --- a/manimlib/mobject/mobject.py +++ b/manimlib/mobject/mobject.py @@ -176,6 +176,7 @@ class Mobject(object): def match_points(self, mobject): self.set_points(mobject.get_points()) + return self def get_points(self): return self.data["points"] From e95aa69c4c093940dee034dd742c75c938d41e2c Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 31 Mar 2021 23:22:54 -0700 Subject: [PATCH 22/30] Change arg_separator default --- manimlib/mobject/svg/tex_mobject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/mobject/svg/tex_mobject.py b/manimlib/mobject/svg/tex_mobject.py index a5b579b6..0818c4b1 100644 --- a/manimlib/mobject/svg/tex_mobject.py +++ b/manimlib/mobject/svg/tex_mobject.py @@ -152,7 +152,7 @@ class SingleStringTex(VMobject): class Tex(SingleStringTex): CONFIG = { - "arg_separator": " ", + "arg_separator": "", # Note, use of isolate is largely rendered # moot by the fact that you can surround such strings in # {{ and }} as needed. From ca9f4357fa80f466513f67b08ba8b18f131f41d0 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 31 Mar 2021 23:23:34 -0700 Subject: [PATCH 23/30] Allow configuration in Brace.get_text --- manimlib/mobject/svg/brace.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/manimlib/mobject/svg/brace.py b/manimlib/mobject/svg/brace.py index 6d46d3e3..cd686fbb 100644 --- a/manimlib/mobject/svg/brace.py +++ b/manimlib/mobject/svg/brace.py @@ -63,8 +63,9 @@ class Brace(SingleStringTex): return self def get_text(self, text, **kwargs): - text_mob = Text(text) - self.put_at_tip(text_mob, **kwargs) + buff = kwargs.pop("buff", SMALL_BUFF) + text_mob = Text(text, **kwargs) + self.put_at_tip(text_mob, buff=buff) return text_mob def get_tex(self, *tex, **kwargs): From de9ecbd7666ee02a154484d5d6d5e542d26ee571 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 31 Mar 2021 23:24:55 -0700 Subject: [PATCH 24/30] Remove unnecessary import --- manimlib/mobject/number_line.py | 1 - 1 file changed, 1 deletion(-) diff --git a/manimlib/mobject/number_line.py b/manimlib/mobject/number_line.py index 73beb534..0346399c 100644 --- a/manimlib/mobject/number_line.py +++ b/manimlib/mobject/number_line.py @@ -5,7 +5,6 @@ from manimlib.mobject.types.vectorized_mobject import VGroup from manimlib.utils.bezier import interpolate from manimlib.utils.config_ops import digest_config from manimlib.utils.config_ops import merge_dicts_recursively -from manimlib.utils.iterables import list_difference_update from manimlib.utils.simple_functions import fdiv from manimlib.utils.space_ops import normalize From 82658e1db3fe4d0b40c16fefe154dd913cfdb0f7 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 31 Mar 2021 23:25:43 -0700 Subject: [PATCH 25/30] Change default element alignment for integer matrices --- manimlib/mobject/matrix.py | 1 + 1 file changed, 1 insertion(+) diff --git a/manimlib/mobject/matrix.py b/manimlib/mobject/matrix.py index adcbf664..4800aa01 100644 --- a/manimlib/mobject/matrix.py +++ b/manimlib/mobject/matrix.py @@ -169,6 +169,7 @@ class DecimalMatrix(Matrix): class IntegerMatrix(Matrix): CONFIG = { "element_to_mobject": Integer, + "element_alignment_corner": UP, } From 0c61c908b2259b24103b9e09d2ef4847353ea592 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 31 Mar 2021 23:26:10 -0700 Subject: [PATCH 26/30] Small fixes to Axes defaults --- manimlib/mobject/coordinate_systems.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/manimlib/mobject/coordinate_systems.py b/manimlib/mobject/coordinate_systems.py index b1518593..f02706b7 100644 --- a/manimlib/mobject/coordinate_systems.py +++ b/manimlib/mobject/coordinate_systems.py @@ -25,8 +25,8 @@ class CoordinateSystem(): """ CONFIG = { "dimension": 2, - "x_range": np.array([-8, 8, 1.0]), - "y_range": np.array([-4, 4, 1.0]), + "x_range": np.array([-8.0, 8.0, 1.0]), + "y_range": np.array([-4.0, 4.0, 1.0]), "width": None, "height": None, "num_sampled_graph_points_per_tick": 5, @@ -343,11 +343,13 @@ class Axes(VGroup, CoordinateSystem): class ThreeDAxes(Axes): CONFIG = { "dimension": 3, - "x_range": np.array([-6, 6, 1]), - "y_range": np.array([-5, 5, 1]), - "z_range": np.array([-4, 4, 1]), + "x_range": np.array([-6.0, 6.0, 1.0]), + "y_range": np.array([-5.0, 5.0, 1.0]), + "z_range": np.array([-4.0, 4.0, 1.0]), "z_axis_config": {}, "z_normal": DOWN, + "height": None, + "width": None, "depth": None, "num_axis_pieces": 20, "gloss": 0.5, From df657c06c250aaf9412e9e65770036e42bc56036 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 31 Mar 2021 23:26:35 -0700 Subject: [PATCH 27/30] Add (admitedly silly) RADIANS constant --- manimlib/constants.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/manimlib/constants.py b/manimlib/constants.py index b884fa13..6c82bba2 100644 --- a/manimlib/constants.py +++ b/manimlib/constants.py @@ -50,6 +50,9 @@ RIGHT_SIDE = FRAME_X_RADIUS * RIGHT PI = np.pi TAU = 2 * PI DEGREES = TAU / 360 +# Nice to have a constant for readability +# when juxtaposed with expressions like 30 * DEGREES +RADIANS = 1 FFMPEG_BIN = "ffmpeg" From 322f138490c75453f587ebff3b90a7769f56b1a4 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 31 Mar 2021 23:27:12 -0700 Subject: [PATCH 28/30] Add CameraFrame.reorient for quicker changes to frame angle --- manimlib/camera/camera.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index 80695a83..41c432b4 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -77,16 +77,24 @@ class CameraFrame(Mobject): self.set_euler_angles(theta, phi, gamma) return self - def set_euler_angles(self, theta=None, phi=None, gamma=None): + def set_euler_angles(self, theta=None, phi=None, gamma=None, units=RADIANS): if theta is not None: - self.data["euler_angles"][0] = theta + self.data["euler_angles"][0] = theta * units if phi is not None: - self.data["euler_angles"][1] = phi + self.data["euler_angles"][1] = phi * units if gamma is not None: - self.data["euler_angles"][2] = gamma + self.data["euler_angles"][2] = gamma * units self.refresh_rotation_matrix() return self + def reorient(self, theta_degrees=None, phi_degrees=None, gamma_degrees=None): + """ + Shortcut for set_euler_angles, defaulting to taking + in angles in degrees + """ + self.set_euler_angles(theta_degrees, phi_degrees, gamma_degrees, units=DEGREES) + return self + def set_theta(self, theta): return self.set_euler_angles(theta=theta) From 42d8888f8e48046dee41997b7b7f10de588a755b Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 31 Mar 2021 23:28:49 -0700 Subject: [PATCH 29/30] Allow any VMobject to be passed into TransformMatchingTex, so that slicing into Tex works --- manimlib/animation/transform_matching_parts.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/manimlib/animation/transform_matching_parts.py b/manimlib/animation/transform_matching_parts.py index d68d65d0..3ee228a9 100644 --- a/manimlib/animation/transform_matching_parts.py +++ b/manimlib/animation/transform_matching_parts.py @@ -10,7 +10,6 @@ from manimlib.mobject.mobject import Mobject from manimlib.mobject.mobject import Group from manimlib.mobject.types.vectorized_mobject import VGroup from manimlib.mobject.types.vectorized_mobject import VMobject -from manimlib.mobject.svg.tex_mobject import Tex from manimlib.utils.config_ops import digest_config @@ -129,7 +128,7 @@ class TransformMatchingShapes(TransformMatchingParts): class TransformMatchingTex(TransformMatchingParts): CONFIG = { - "mobject_type": Tex, + "mobject_type": VMobject, "group_type": VGroup, } From cf63dfddf90d9a1858996b92b5a71297450403d7 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 8 Apr 2021 14:14:32 -0700 Subject: [PATCH 30/30] Fix Lighthouse --- manimlib/once_useful_constructs/light.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/manimlib/once_useful_constructs/light.py b/manimlib/once_useful_constructs/light.py index b10a8883..5ec132b9 100644 --- a/manimlib/once_useful_constructs/light.py +++ b/manimlib/once_useful_constructs/light.py @@ -74,12 +74,14 @@ class SwitchOff(LaggedStartMap): class Lighthouse(SVGMobject): CONFIG = { - "file_name": "lighthouse", "height": LIGHTHOUSE_HEIGHT, "fill_color": WHITE, "fill_opacity": 1.0, } + def __init__(self, **kwargs): + super().__init__("lighthouse", **kwargs) + def move_to(self, point): self.next_to(point, DOWN, buff=0)