diff --git a/manimlib/mobject/mobject.py b/manimlib/mobject/mobject.py index 14b2b027..34ab6dc8 100644 --- a/manimlib/mobject/mobject.py +++ b/manimlib/mobject/mobject.py @@ -1,7 +1,5 @@ -from functools import reduce import copy import itertools as it -import operator as op import random import sys import moderngl @@ -31,9 +29,6 @@ from manimlib.shader_wrapper import ShaderWrapper from manimlib.shader_wrapper import get_colormap_code -# TODO: Explain array_attrs -# TODO: Incorporate shader defaults - class Mobject(object): """ Mathematical Object @@ -298,6 +293,84 @@ class Mobject(object): self.set_submobjects(list_update(self.submobjects, mobject_attrs)) return self + # Submobject organization + + def arrange(self, direction=RIGHT, center=True, **kwargs): + for m1, m2 in zip(self.submobjects, self.submobjects[1:]): + m2.next_to(m1, direction, **kwargs) + if center: + self.center() + return self + + def arrange_in_grid(self, n_rows=None, n_cols=None, + buff=None, + h_buff=None, + v_buff=None, + buff_ratio=None, + h_buff_ratio=0.5, + v_buff_ratio=0.5, + aligned_edge=ORIGIN, + fill_rows_first=True): + submobs = self.submobjects + if n_rows is None and n_cols is None: + n_rows = int(np.sqrt(len(submobs))) + if n_rows is None: + n_rows = len(submobs) // n_cols + if n_cols is None: + n_cols = len(submobs) // n_rows + + if buff is not None: + h_buff = buff + v_buff = buff + else: + if buff_ratio is not None: + v_buff_ratio = buff_ratio + h_buff_ratio = buff_ratio + if h_buff is None: + h_buff = h_buff_ratio * self[0].get_width() + if v_buff is None: + v_buff = v_buff_ratio * self[0].get_height() + + x_unit = h_buff + max([sm.get_width() for sm in submobs]) + y_unit = v_buff + max([sm.get_height() for sm in submobs]) + + for index, sm in enumerate(submobs): + if fill_rows_first: + x, y = index % n_cols, index // n_cols + else: + x, y = index // n_rows, index % n_rows + sm.move_to(ORIGIN, aligned_edge) + sm.shift(x * x_unit * RIGHT + y * y_unit * DOWN) + self.center() + return self + + def get_grid(self, n_rows, n_cols, height=None, **kwargs): + """ + Returns a new mobject containing multiple copies of this one + arranged in a grid + """ + grid = self.get_group_class()( + *(self.copy() for n in range(n_rows * n_cols)) + ) + grid.arrange_in_grid(n_rows, n_cols, **kwargs) + if height is not None: + grid.set_height(height) + return grid + + def sort(self, point_to_num_func=lambda p: p[0], submob_func=None): + if submob_func is not None: + self.submobjects.sort(key=submob_func) + else: + self.submobjects.sort(key=lambda m: point_to_num_func(m.get_center())) + return self + + def shuffle(self, recurse=False): + if recurse: + for submob in self.submobjects: + submob.shuffle(recurse=True) + random.shuffle(self.submobjects) + return self + # Copying def copy(self): @@ -373,16 +446,16 @@ class Mobject(object): self.has_updaters = False self.updating_suspended = False - def update(self, dt=0, recursive=True): + def update(self, dt=0, recurse=True): if not self.has_updaters or self.updating_suspended: return self for updater in self.time_based_updaters: updater(self, dt) for updater in self.non_time_updaters: updater(self) - if recursive: + if recurse: for submob in self.submobjects: - submob.update(dt, recursive) + submob.update(dt, recurse) return self def get_time_based_updaters(self): @@ -419,13 +492,13 @@ class Mobject(object): updater_list.remove(update_function) return self - def clear_updaters(self, recursive=True): + def clear_updaters(self, recurse=True): self.time_based_updaters = [] self.non_time_updaters = [] - if recursive: + if recurse: for submob in self.submobjects: submob.clear_updaters() - self.suspend_updating(recursive) + self.suspend_updating(recurse) return self def match_updaters(self, mobject): @@ -434,22 +507,22 @@ class Mobject(object): self.add_updater(updater) return self - def suspend_updating(self, recursive=True): + def suspend_updating(self, recurse=True): self.updating_suspended = True - if recursive: + if recurse: for submob in self.submobjects: - submob.suspend_updating(recursive) + submob.suspend_updating(recurse) return self - def resume_updating(self, recursive=True, call_updater=True): + def resume_updating(self, recurse=True, call_updater=True): self.updating_suspended = False - if recursive: + if recurse: for submob in self.submobjects: - submob.resume_updating(recursive) + submob.resume_updating(recurse) for parent in self.parents: - parent.resume_updating(recursive=False, call_updater=False) + parent.resume_updating(recurse=False, call_updater=False) if call_updater: - self.update(dt=0, recursive=recursive) + self.update(dt=0, recurse=recurse) return self def refresh_has_updater_status(self): @@ -744,30 +817,6 @@ class Mobject(object): self.shift(start - curr_start) return self - # Background rectangle - - def add_background_rectangle(self, color=BLACK, opacity=0.75, **kwargs): - # TODO, this does not behave well when the mobject has points, - # since it gets displayed on top - from manimlib.mobject.shape_matchers import BackgroundRectangle - self.background_rectangle = BackgroundRectangle( - self, color=color, - fill_opacity=opacity, - **kwargs - ) - self.add_to_back(self.background_rectangle) - return self - - def add_background_rectangle_to_submobjects(self, **kwargs): - for submobject in self.submobjects: - submobject.add_background_rectangle(**kwargs) - return self - - def add_background_rectangle_to_family_members_with_points(self, **kwargs): - for mob in self.family_members_with_points(): - mob.add_background_rectangle(**kwargs) - return self - # Color functions def set_rgba_array(self, color=None, opacity=None, name="rgbas", recurse=True): @@ -857,6 +906,30 @@ class Mobject(object): mob.uniforms["shadow"] = shadow return self + # Background rectangle + + def add_background_rectangle(self, color=BLACK, opacity=0.75, **kwargs): + # TODO, this does not behave well when the mobject has points, + # since it gets displayed on top + from manimlib.mobject.shape_matchers import BackgroundRectangle + self.background_rectangle = BackgroundRectangle( + self, color=color, + fill_opacity=opacity, + **kwargs + ) + self.add_to_back(self.background_rectangle) + return self + + def add_background_rectangle_to_submobjects(self, **kwargs): + for submobject in self.submobjects: + submobject.add_background_rectangle(**kwargs) + return self + + def add_background_rectangle_to_family_members_with_points(self, **kwargs): + for mob in self.family_members_with_points(): + mob.add_background_rectangle(**kwargs) + return self + # Getters def get_bounding_box_point(self, direction): @@ -1037,84 +1110,6 @@ class Mobject(object): def get_group_class(self): return Group - # Submobject organization - - def arrange(self, direction=RIGHT, center=True, **kwargs): - for m1, m2 in zip(self.submobjects, self.submobjects[1:]): - m2.next_to(m1, direction, **kwargs) - if center: - self.center() - return self - - def arrange_in_grid(self, n_rows=None, n_cols=None, - buff=None, - h_buff=None, - v_buff=None, - buff_ratio=None, - h_buff_ratio=0.5, - v_buff_ratio=0.5, - aligned_edge=ORIGIN, - fill_rows_first=True): - submobs = self.submobjects - if n_rows is None and n_cols is None: - n_rows = int(np.sqrt(len(submobs))) - if n_rows is None: - n_rows = len(submobs) // n_cols - if n_cols is None: - n_cols = len(submobs) // n_rows - - if buff is not None: - h_buff = buff - v_buff = buff - else: - if buff_ratio is not None: - v_buff_ratio = buff_ratio - h_buff_ratio = buff_ratio - if h_buff is None: - h_buff = h_buff_ratio * self[0].get_width() - if v_buff is None: - v_buff = v_buff_ratio * self[0].get_height() - - x_unit = h_buff + max([sm.get_width() for sm in submobs]) - y_unit = v_buff + max([sm.get_height() for sm in submobs]) - - for index, sm in enumerate(submobs): - if fill_rows_first: - x, y = index % n_cols, index // n_cols - else: - x, y = index // n_rows, index % n_rows - sm.move_to(ORIGIN, aligned_edge) - sm.shift(x * x_unit * RIGHT + y * y_unit * DOWN) - self.center() - return self - - def get_grid(self, n_rows, n_cols, height=None, **kwargs): - """ - Returns a new mobject containing multiple copies of this one - arranged in a grid - """ - grid = self.get_group_class()( - *(self.copy() for n in range(n_rows * n_cols)) - ) - grid.arrange_in_grid(n_rows, n_cols, **kwargs) - if height is not None: - grid.set_height(height) - return grid - - def sort(self, point_to_num_func=lambda p: p[0], submob_func=None): - if submob_func is not None: - self.submobjects.sort(key=submob_func) - else: - self.submobjects.sort(key=lambda m: point_to_num_func(m.get_center())) - return self - - def shuffle(self, recurse=False): - if recurse: - for submob in self.submobjects: - submob.shuffle(recurse=True) - random.shuffle(self.submobjects) - return self - # Alignment def align_data_and_family(self, mobject):