Small cleanup/reorganizing of Mobject methods

This commit is contained in:
Grant Sanderson 2021-01-14 15:07:35 -10:00
parent 7b67f4556b
commit f314054081

View file

@ -1,7 +1,5 @@
from functools import reduce
import copy import copy
import itertools as it import itertools as it
import operator as op
import random import random
import sys import sys
import moderngl import moderngl
@ -31,9 +29,6 @@ from manimlib.shader_wrapper import ShaderWrapper
from manimlib.shader_wrapper import get_colormap_code from manimlib.shader_wrapper import get_colormap_code
# TODO: Explain array_attrs
# TODO: Incorporate shader defaults
class Mobject(object): class Mobject(object):
""" """
Mathematical Object Mathematical Object
@ -298,6 +293,84 @@ class Mobject(object):
self.set_submobjects(list_update(self.submobjects, mobject_attrs)) self.set_submobjects(list_update(self.submobjects, mobject_attrs))
return self 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 # Copying
def copy(self): def copy(self):
@ -373,16 +446,16 @@ class Mobject(object):
self.has_updaters = False self.has_updaters = False
self.updating_suspended = 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: if not self.has_updaters or self.updating_suspended:
return self return self
for updater in self.time_based_updaters: for updater in self.time_based_updaters:
updater(self, dt) updater(self, dt)
for updater in self.non_time_updaters: for updater in self.non_time_updaters:
updater(self) updater(self)
if recursive: if recurse:
for submob in self.submobjects: for submob in self.submobjects:
submob.update(dt, recursive) submob.update(dt, recurse)
return self return self
def get_time_based_updaters(self): def get_time_based_updaters(self):
@ -419,13 +492,13 @@ class Mobject(object):
updater_list.remove(update_function) updater_list.remove(update_function)
return self return self
def clear_updaters(self, recursive=True): def clear_updaters(self, recurse=True):
self.time_based_updaters = [] self.time_based_updaters = []
self.non_time_updaters = [] self.non_time_updaters = []
if recursive: if recurse:
for submob in self.submobjects: for submob in self.submobjects:
submob.clear_updaters() submob.clear_updaters()
self.suspend_updating(recursive) self.suspend_updating(recurse)
return self return self
def match_updaters(self, mobject): def match_updaters(self, mobject):
@ -434,22 +507,22 @@ class Mobject(object):
self.add_updater(updater) self.add_updater(updater)
return self return self
def suspend_updating(self, recursive=True): def suspend_updating(self, recurse=True):
self.updating_suspended = True self.updating_suspended = True
if recursive: if recurse:
for submob in self.submobjects: for submob in self.submobjects:
submob.suspend_updating(recursive) submob.suspend_updating(recurse)
return self return self
def resume_updating(self, recursive=True, call_updater=True): def resume_updating(self, recurse=True, call_updater=True):
self.updating_suspended = False self.updating_suspended = False
if recursive: if recurse:
for submob in self.submobjects: for submob in self.submobjects:
submob.resume_updating(recursive) submob.resume_updating(recurse)
for parent in self.parents: for parent in self.parents:
parent.resume_updating(recursive=False, call_updater=False) parent.resume_updating(recurse=False, call_updater=False)
if call_updater: if call_updater:
self.update(dt=0, recursive=recursive) self.update(dt=0, recurse=recurse)
return self return self
def refresh_has_updater_status(self): def refresh_has_updater_status(self):
@ -744,30 +817,6 @@ class Mobject(object):
self.shift(start - curr_start) self.shift(start - curr_start)
return self 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 # Color functions
def set_rgba_array(self, color=None, opacity=None, name="rgbas", recurse=True): def set_rgba_array(self, color=None, opacity=None, name="rgbas", recurse=True):
@ -857,6 +906,30 @@ class Mobject(object):
mob.uniforms["shadow"] = shadow mob.uniforms["shadow"] = shadow
return self 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 # Getters
def get_bounding_box_point(self, direction): def get_bounding_box_point(self, direction):
@ -1037,84 +1110,6 @@ class Mobject(object):
def get_group_class(self): def get_group_class(self):
return Group 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 # Alignment
def align_data_and_family(self, mobject): def align_data_and_family(self, mobject):