mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Have mobjects track family and parents more directly
This commit is contained in:
parent
b825b36b60
commit
ea59950b2c
13 changed files with 106 additions and 79 deletions
|
@ -137,7 +137,7 @@ class ShowIncreasingSubsets(Animation):
|
|||
self.update_submobject_list(index)
|
||||
|
||||
def update_submobject_list(self, index):
|
||||
self.mobject.submobjects = self.all_submobs[:index]
|
||||
self.mobject.set_submobjects(self.all_submobs[:index])
|
||||
|
||||
|
||||
class ShowSubmobjectsOneByOne(ShowIncreasingSubsets):
|
||||
|
@ -152,9 +152,9 @@ class ShowSubmobjectsOneByOne(ShowIncreasingSubsets):
|
|||
def update_submobject_list(self, index):
|
||||
# N = len(self.all_submobs)
|
||||
if index == 0:
|
||||
self.mobject.submobjects = []
|
||||
self.mobject.set_submobjects([])
|
||||
else:
|
||||
self.mobject.submobjects = self.all_submobs[index - 1]
|
||||
self.mobject.set_submobjects(self.all_submobs[index - 1])
|
||||
|
||||
|
||||
# TODO, this is broken...
|
||||
|
|
|
@ -74,7 +74,6 @@ class PiCreature(SVGMobject):
|
|||
self.flip()
|
||||
if self.start_corner is not None:
|
||||
self.to_corner(self.start_corner)
|
||||
self.unlock_triangulation()
|
||||
|
||||
def align_data(self, mobject):
|
||||
# This ensures that after a transform into a different mode,
|
||||
|
@ -260,10 +259,16 @@ class PiCreature(SVGMobject):
|
|||
for alpha_range in (self.right_arm_range, self.left_arm_range)
|
||||
])
|
||||
|
||||
def prepare_for_animation(self):
|
||||
self.unlock_triangulation()
|
||||
|
||||
def cleanup_from_animation(self):
|
||||
self.lock_triangulation()
|
||||
|
||||
|
||||
def get_all_pi_creature_modes():
|
||||
result = []
|
||||
prefix = "%s_" % PiCreature.CONFIG["file_name_prefix"]
|
||||
prefix = PiCreature.CONFIG["file_name_prefix"] + "_"
|
||||
suffix = ".svg"
|
||||
for file in os.listdir(PI_CREATURE_DIR):
|
||||
if file.startswith(prefix) and file.endswith(suffix):
|
||||
|
|
|
@ -54,6 +54,8 @@ class Mobject(Container):
|
|||
def __init__(self, **kwargs):
|
||||
Container.__init__(self, **kwargs)
|
||||
self.submobjects = []
|
||||
self.parents = []
|
||||
self.family = [self]
|
||||
self.color = Color(self.color)
|
||||
if self.name is None:
|
||||
self.name = self.__class__.__name__
|
||||
|
@ -80,21 +82,64 @@ class Mobject(Container):
|
|||
# Typically implemented in subclass, unless purposefully left blank
|
||||
pass
|
||||
|
||||
# Family matters
|
||||
def __getitem__(self, value):
|
||||
self_list = self.split()
|
||||
if isinstance(value, slice):
|
||||
GroupClass = self.get_group_class()
|
||||
return GroupClass(*self_list.__getitem__(value))
|
||||
return self_list.__getitem__(value)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.split())
|
||||
|
||||
def __len__(self):
|
||||
return len(self.split())
|
||||
|
||||
def split(self):
|
||||
result = [self] if len(self.points) > 0 else []
|
||||
return result + self.submobjects
|
||||
|
||||
def assemble_family(self):
|
||||
sub_families = [sm.get_family() for sm in self.submobjects]
|
||||
self.family = [self, *it.chain(*sub_families)]
|
||||
for parent in self.parents:
|
||||
parent.assemble_family()
|
||||
return self
|
||||
|
||||
def get_family(self):
|
||||
return self.family
|
||||
|
||||
def family_members_with_points(self):
|
||||
return [m for m in self.get_family() if m.get_num_points() > 0]
|
||||
|
||||
def add(self, *mobjects):
|
||||
if self in mobjects:
|
||||
raise Exception("Mobject cannot contain self")
|
||||
self.submobjects = list_update(self.submobjects, mobjects)
|
||||
return self
|
||||
|
||||
def add_to_back(self, *mobjects):
|
||||
self.remove(*mobjects)
|
||||
self.submobjects = list(mobjects) + self.submobjects
|
||||
for mobject in mobjects:
|
||||
if mobject not in self.submobjects:
|
||||
self.submobjects.append(mobject)
|
||||
if self not in mobject.parents:
|
||||
mobject.parents.append(self)
|
||||
self.assemble_family()
|
||||
return self
|
||||
|
||||
def remove(self, *mobjects):
|
||||
for mobject in mobjects:
|
||||
if mobject in self.submobjects:
|
||||
self.submobjects.remove(mobject)
|
||||
if self in mobject.parents:
|
||||
mobject.parents.remove(self)
|
||||
self.assemble_family()
|
||||
return self
|
||||
|
||||
def add_to_back(self, *mobjects):
|
||||
self.set_submobjects(list_update(mobjects, self.sub_mobjects))
|
||||
return self
|
||||
|
||||
def set_submobjects(self, submobject_list):
|
||||
self.remove(*self.submobjects)
|
||||
self.add(*submobject_list)
|
||||
return self
|
||||
|
||||
def get_array_attrs(self):
|
||||
|
@ -107,7 +152,7 @@ class Mobject(Container):
|
|||
in the submobjects list.
|
||||
"""
|
||||
mobject_attrs = [x for x in list(self.__dict__.values()) if isinstance(x, Mobject)]
|
||||
self.submobjects = list_update(self.submobjects, mobject_attrs)
|
||||
self.set_submobjects(list_update(self.submobjects, mobject_attrs))
|
||||
return self
|
||||
|
||||
def apply_over_attr_arrays(self, func):
|
||||
|
@ -138,10 +183,12 @@ class Mobject(Container):
|
|||
|
||||
copy_mobject = copy.copy(self)
|
||||
copy_mobject.points = np.array(self.points)
|
||||
copy_mobject.submobjects = [
|
||||
submob.copy() for submob in self.submobjects
|
||||
]
|
||||
copy_mobject.parents = []
|
||||
copy_mobject.submobjects = []
|
||||
copy_mobject.add(*[sm.copy() for sm in self.submobjects])
|
||||
copy_mobject.updaters = list(self.updaters)
|
||||
|
||||
# Make sure any mobject or numpy array attributes are copied
|
||||
family = self.get_family()
|
||||
for attr, value in list(self.__dict__.items()):
|
||||
if isinstance(value, Mobject) and value in family and value is not self:
|
||||
|
@ -817,7 +864,7 @@ class Mobject(Container):
|
|||
|
||||
def get_pieces(self, n_pieces):
|
||||
template = self.copy()
|
||||
template.submobjects = []
|
||||
template.set_submobjects([])
|
||||
alphas = np.linspace(0, 1, n_pieces + 1)
|
||||
return Group(*[
|
||||
template.copy().pointwise_become_partial(
|
||||
|
@ -893,34 +940,10 @@ class Mobject(Container):
|
|||
self.set_coord(point[dim], dim, direction)
|
||||
return self
|
||||
|
||||
# Family matters
|
||||
def __getitem__(self, value):
|
||||
self_list = self.split()
|
||||
if isinstance(value, slice):
|
||||
GroupClass = self.get_group_class()
|
||||
return GroupClass(*self_list.__getitem__(value))
|
||||
return self_list.__getitem__(value)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.split())
|
||||
|
||||
def __len__(self):
|
||||
return len(self.split())
|
||||
|
||||
def get_group_class(self):
|
||||
return Group
|
||||
|
||||
def split(self):
|
||||
result = [self] if len(self.points) > 0 else []
|
||||
return result + self.submobjects
|
||||
|
||||
def get_family(self):
|
||||
sub_families = [sm.get_family() for sm in self.submobjects]
|
||||
return [self, *it.chain(*sub_families)]
|
||||
|
||||
def family_members_with_points(self):
|
||||
return [m for m in self.get_family() if m.get_num_points() > 0]
|
||||
|
||||
# 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)
|
||||
|
@ -971,12 +994,10 @@ class Mobject(Container):
|
|||
|
||||
# Alignment
|
||||
def align_data(self, mobject):
|
||||
self.null_point_align(mobject)
|
||||
self.null_point_align(mobject) # Needed?
|
||||
self.align_submobjects(mobject)
|
||||
self.align_points(mobject)
|
||||
# Recurse
|
||||
for m1, m2 in zip(self.submobjects, mobject.submobjects):
|
||||
m1.align_data(m2)
|
||||
for mob1, mob2 in zip(self.get_family(), mobject.get_family()):
|
||||
mob1.align_points(mob2)
|
||||
|
||||
def align_points(self, mobject):
|
||||
count1 = self.get_num_points()
|
||||
|
@ -997,6 +1018,9 @@ class Mobject(Container):
|
|||
n2 = len(mob2.submobjects)
|
||||
mob1.add_n_more_submobjects(max(0, n2 - n1))
|
||||
mob2.add_n_more_submobjects(max(0, n1 - n2))
|
||||
# Recurse
|
||||
for sm1, sm2 in zip(mob1.submobjects, mob2.submobjects):
|
||||
sm1.align_submobjects(sm2)
|
||||
return self
|
||||
|
||||
def null_point_align(self, mobject):
|
||||
|
@ -1013,7 +1037,7 @@ class Mobject(Container):
|
|||
|
||||
def push_self_into_submobjects(self):
|
||||
copy = self.deepcopy()
|
||||
copy.submobjects = []
|
||||
copy.submobjects.set_submobjects([])
|
||||
self.reset_points()
|
||||
self.add(copy)
|
||||
return self
|
||||
|
@ -1025,15 +1049,12 @@ class Mobject(Container):
|
|||
curr = len(self.submobjects)
|
||||
if curr == 0:
|
||||
# If empty, simply add n point mobjects
|
||||
self.submobjects = [
|
||||
self.set_submobjects([
|
||||
self.copy().scale(0)
|
||||
for k in range(n)
|
||||
]
|
||||
])
|
||||
return
|
||||
|
||||
target = curr + n
|
||||
# TODO, factor this out to utils so as to reuse
|
||||
# with VMobject.insert_n_curves
|
||||
repeat_indices = (np.arange(target) * curr) // target
|
||||
split_factors = [
|
||||
(repeat_indices == i).sum()
|
||||
|
@ -1044,12 +1065,9 @@ class Mobject(Container):
|
|||
new_submobs.append(submob)
|
||||
for k in range(1, sf):
|
||||
new_submobs.append(submob.copy().fade(1))
|
||||
self.submobjects = new_submobs
|
||||
self.set_submobjects(new_submobs)
|
||||
return self
|
||||
|
||||
def repeat_submobject(self, submob):
|
||||
return submob.copy()
|
||||
|
||||
def interpolate(self, mobject1, mobject2,
|
||||
alpha, path_func=straight_path):
|
||||
"""
|
||||
|
@ -1082,7 +1100,7 @@ class Mobject(Container):
|
|||
Edit points, colors and submobjects to be idential
|
||||
to another mobject
|
||||
"""
|
||||
self.align_data(mobject)
|
||||
self.align_submobjects(mobject)
|
||||
for sm1, sm2 in zip(self.get_family(), mobject.get_family()):
|
||||
sm1.set_points(sm2.points)
|
||||
sm1.interpolate_color(sm1, sm2, 1)
|
||||
|
|
|
@ -122,7 +122,7 @@ class DecimalNumber(VMobject):
|
|||
new_decimal.match_style(self)
|
||||
|
||||
old_family = self.get_family()
|
||||
self.submobjects = new_decimal.submobjects
|
||||
self.set_submobjects(new_decimal.submobjects)
|
||||
for mob in old_family:
|
||||
# Dumb hack...due to how scene handles families
|
||||
# of animated mobjects
|
||||
|
|
|
@ -97,7 +97,7 @@ class BraceLabel(VMobject):
|
|||
self.label.scale(self.label_scale)
|
||||
|
||||
self.brace.put_at_tip(self.label)
|
||||
self.submobjects = [self.brace, self.label]
|
||||
self.set_submobjects([self.brace, self.label])
|
||||
|
||||
def creation_anim(self, label_anim=FadeIn, brace_anim=GrowFromCenter):
|
||||
return AnimationGroup(brace_anim(self.brace), label_anim(self.label))
|
||||
|
@ -128,7 +128,7 @@ class BraceLabel(VMobject):
|
|||
copy_mobject = copy.copy(self)
|
||||
copy_mobject.brace = self.brace.copy()
|
||||
copy_mobject.label = self.label.copy()
|
||||
copy_mobject.submobjects = [copy_mobject.brace, copy_mobject.label]
|
||||
copy_mobject.set_submobjects([copy_mobject.brace, copy_mobject.label])
|
||||
|
||||
return copy_mobject
|
||||
|
||||
|
|
|
@ -184,14 +184,14 @@ class TexMobject(SingleStringTexMobject):
|
|||
# For cases like empty tex_strings, we want the corresponing
|
||||
# part of the whole TexMobject to be a VectorizedPoint
|
||||
# positioned in the right part of the TexMobject
|
||||
sub_tex_mob.submobjects = [VectorizedPoint()]
|
||||
sub_tex_mob.set_submobjects([VectorizedPoint()])
|
||||
last_submob_index = min(curr_index, len(self.submobjects) - 1)
|
||||
sub_tex_mob.move_to(self.submobjects[last_submob_index], RIGHT)
|
||||
else:
|
||||
sub_tex_mob.submobjects = self.submobjects[curr_index:new_index]
|
||||
sub_tex_mob.set_submobjects(self.submobjects[curr_index:new_index])
|
||||
new_submobjects.append(sub_tex_mob)
|
||||
curr_index = new_index
|
||||
self.submobjects = new_submobjects
|
||||
self.set_submobjects(new_submobjects)
|
||||
return self
|
||||
|
||||
def get_parts_by_tex(self, tex, substring=True, case_sensitive=True):
|
||||
|
|
|
@ -137,7 +137,7 @@ class PMobject(Mobject):
|
|||
arrays = list(map(self.get_merged_array, attrs))
|
||||
for attr, array in zip(attrs, arrays):
|
||||
setattr(self, attr, array)
|
||||
self.submobjects = []
|
||||
self.set_submobjects([])
|
||||
return self
|
||||
|
||||
def get_color(self):
|
||||
|
|
|
@ -568,6 +568,10 @@ class VMobject(Mobject):
|
|||
self.make_smooth()
|
||||
return self
|
||||
|
||||
def flip(self):
|
||||
super().flip()
|
||||
self.refresh_triangulation()
|
||||
|
||||
#
|
||||
def consider_points_equals(self, p0, p1):
|
||||
return np.allclose(
|
||||
|
|
|
@ -153,7 +153,7 @@ class GeneralizedPascalsTriangle(VMobject):
|
|||
def fill_with_n_choose_k(self):
|
||||
if not hasattr(self, "coords_to_n_choose_k"):
|
||||
self.generate_n_choose_k_mobs()
|
||||
self.submobjects = []
|
||||
self.set_submobjects([])
|
||||
self.add(*[
|
||||
self.coords_to_n_choose_k[n][k]
|
||||
for n, k in self.coords
|
||||
|
|
|
@ -62,11 +62,11 @@ def fractalification_iteration(vmobject, dimension=1.05, num_inserted_anchors_ra
|
|||
new_anchors += [p1] + inserted_points
|
||||
new_anchors.append(original_anchors[-1])
|
||||
vmobject.set_points_as_corners(new_anchors)
|
||||
vmobject.submobjects = [
|
||||
vmobject.set_submobjects([
|
||||
fractalification_iteration(
|
||||
submob, dimension, num_inserted_anchors_range)
|
||||
for submob in vmobject.submobjects
|
||||
]
|
||||
])
|
||||
return vmobject
|
||||
|
||||
|
||||
|
@ -87,9 +87,9 @@ class SelfSimilarFractal(VMobject):
|
|||
def init_points(self):
|
||||
order_n_self = self.get_order_n_self(self.order)
|
||||
if self.order == 0:
|
||||
self.submobjects = [order_n_self]
|
||||
self.set_submobjects([order_n_self])
|
||||
else:
|
||||
self.submobjects = order_n_self.submobjects
|
||||
self.set_submobjects(order_n_self.submobjects)
|
||||
return self
|
||||
|
||||
def get_order_n_self(self, order):
|
||||
|
|
|
@ -67,9 +67,9 @@ class SwitchOff(LaggedStartMap):
|
|||
if (not isinstance(light, AmbientLight) and not isinstance(light, Spotlight)):
|
||||
raise Exception(
|
||||
"Only AmbientLights and Spotlights can be switched off")
|
||||
light.submobjects = light.submobjects[::-1]
|
||||
light.set_submobjects(light.submobjects[::-1])
|
||||
LaggedStartMap.__init__(self, FadeOut, light, **kwargs)
|
||||
light.submobjects = light.submobjects[::-1]
|
||||
light.set_submobjects(light.submobjects[::-1])
|
||||
|
||||
|
||||
class Lighthouse(SVGMobject):
|
||||
|
@ -182,7 +182,7 @@ class Spotlight(VMobject):
|
|||
return self.source_point.get_location()
|
||||
|
||||
def init_points(self):
|
||||
self.submobjects = []
|
||||
self.set_submobjects([])
|
||||
|
||||
self.add(self.source_point)
|
||||
|
||||
|
@ -493,7 +493,7 @@ class LightSource(VMobject):
|
|||
)
|
||||
new_ambient_light.apply_matrix(self.rotation_matrix())
|
||||
new_ambient_light.move_source_to(self.get_source_point())
|
||||
self.ambient_light.submobjects = new_ambient_light.submobjects
|
||||
self.ambient_light.set_submobjects(new_ambient_light.submobjects)
|
||||
|
||||
def get_source_point(self):
|
||||
return self.source_point.get_location()
|
||||
|
|
|
@ -393,16 +393,16 @@ class Scene(Container):
|
|||
mobject.unlock_shader_data()
|
||||
|
||||
def begin_animations(self, animations):
|
||||
curr_mobjects = self.get_mobject_family_members()
|
||||
for animation in animations:
|
||||
# Begin animation
|
||||
animation.begin()
|
||||
# Anything animated that's not already in the
|
||||
# scene gets added to the scene
|
||||
# scene gets added to the scene. Note, for
|
||||
# animated mobjects that are in the family of
|
||||
# those on screen, this can result in a restructuring
|
||||
# of the scene.mobjects list, which is usually desired.
|
||||
mob = animation.mobject
|
||||
if mob not in curr_mobjects:
|
||||
if mob not in self.mobjects:
|
||||
self.add(mob)
|
||||
curr_mobjects += mob.get_family()
|
||||
|
||||
def progress_through_animations(self, animations):
|
||||
last_t = 0
|
||||
|
|
|
@ -162,7 +162,7 @@ class SpecialThreeDScene(ThreeDScene):
|
|||
for piece in new_pieces:
|
||||
piece.shade_in_3d = True
|
||||
new_pieces.match_style(axis.pieces)
|
||||
axis.pieces.submobjects = new_pieces.submobjects
|
||||
axis.pieces.set_submobjects(new_pieces.submobjects)
|
||||
for tick in axis.tick_marks:
|
||||
tick.add(VectorizedPoint(
|
||||
1.5 * tick.get_center(),
|
||||
|
|
Loading…
Add table
Reference in a new issue