mirror of
https://github.com/3b1b/manim.git
synced 2025-07-31 05:52:34 +08:00
Introduced update_submobject to Animation. I'd be surprised if this didn't break something.
This commit is contained in:
@ -18,6 +18,9 @@ class Animation(object):
|
||||
"name" : None,
|
||||
#Does this animation add or remove a mobject form the screen
|
||||
"remover" : False,
|
||||
#Options are lagged_start, smoothed_lagged_start,
|
||||
#one_at_a_time, all_at_once
|
||||
"submobject_mode" : "lagged_start",
|
||||
}
|
||||
def __init__(self, mobject, **kwargs):
|
||||
mobject = instantiate(mobject)
|
||||
@ -31,8 +34,6 @@ class Animation(object):
|
||||
self.update(0)
|
||||
|
||||
def update_config(self, **kwargs):
|
||||
if "path_arc" in kwargs:
|
||||
kwargs["path_arc"]
|
||||
digest_config(self, kwargs)
|
||||
return self
|
||||
|
||||
@ -49,6 +50,40 @@ class Animation(object):
|
||||
alpha = 1.0
|
||||
self.update_mobject(self.rate_func(alpha))
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
families = self.get_all_families_zipped()
|
||||
for i, mobs in enumerate(families):
|
||||
sub_alpha = self.get_sub_alpha(alpha, i, len(families))
|
||||
self.update_submobject(*list(mobs) + [sub_alpha])
|
||||
return self
|
||||
|
||||
def update_submobject(self, submobject, starting_sumobject, alpha):
|
||||
#Typically ipmlemented by subclass
|
||||
pass
|
||||
|
||||
def get_all_families_zipped(self):
|
||||
"""
|
||||
Ordering must match the ording of arguments to update_submobject
|
||||
"""
|
||||
return zip(
|
||||
self.mobject.submobject_family(),
|
||||
self.starting_mobject.submobject_family()
|
||||
)
|
||||
|
||||
def get_sub_alpha(self, alpha, index, num_submobjects):
|
||||
if self.submobject_mode in ["lagged_start", "smoothed_lagged_start"]:
|
||||
prop = float(index)/num_submobjects
|
||||
if self.submobject_mode is "smoothed_lagged_start":
|
||||
prop = smooth(prop)
|
||||
return np.clip(2*alpha - prop, 0, 1)
|
||||
elif self.submobject_mode == "one_at_a_time":
|
||||
lower = float(index)/num_submobjects
|
||||
upper = float(index+1)/num_submobjects
|
||||
return np.clip((alpha-lower)/(upper-lower), 0, 1)
|
||||
elif self.submobject_mode == "all_at_once":
|
||||
return alpha
|
||||
raise Exception("Invalid submobject mode")
|
||||
|
||||
def filter_out(self, *filter_functions):
|
||||
self.filter_functions += filter_functions
|
||||
return self
|
||||
@ -71,11 +106,7 @@ class Animation(object):
|
||||
|
||||
def set_name(self, name):
|
||||
self.name = name
|
||||
return self
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
#Typically ipmlemented by subclass
|
||||
pass
|
||||
return self
|
||||
|
||||
def is_remover(self):
|
||||
return self.remover
|
||||
|
@ -18,14 +18,12 @@ class Rotating(Animation):
|
||||
"rate_func" : None,
|
||||
"in_place" : True,
|
||||
}
|
||||
def update_submobject(self, submobject, starting_mobject, alpha):
|
||||
submobject.points = starting_submobject.points
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
Animation.update_mobject(self, alpha)
|
||||
axes = [self.axis] if self.axis is not None else self.axes
|
||||
families = [
|
||||
self.mobject.submobject_family(),
|
||||
self.starting_mobject.submobject_family()
|
||||
]
|
||||
for mob, start in zip(*families):
|
||||
mob.points = np.array(start.points)
|
||||
if self.in_place:
|
||||
method = self.mobject.rotate_in_place
|
||||
else:
|
||||
@ -34,14 +32,9 @@ class Rotating(Animation):
|
||||
|
||||
|
||||
class ShowPartial(Animation):
|
||||
CONFIG = {
|
||||
"submobject_mode" : None
|
||||
}
|
||||
def update_mobject(self, alpha):
|
||||
self.mobject.become_partial(
|
||||
self.starting_mobject,
|
||||
*self.get_bounds(alpha),
|
||||
submobject_partial_creation_mode = self.submobject_mode
|
||||
def update_submobject(self, submobject, starting_submobject, alpha):
|
||||
submobject.pointwise_become_partial(
|
||||
starting_submobject, *self.get_bounds(alpha)
|
||||
)
|
||||
|
||||
def get_bounds(self, alpha):
|
||||
@ -83,32 +76,6 @@ class ShowPassingFlash(ShowPartial):
|
||||
return (lower, upper)
|
||||
|
||||
|
||||
class Flash(Animation):
|
||||
CONFIG = {
|
||||
"color" : "white",
|
||||
"slow_factor" : 0.01,
|
||||
"run_time" : 0.1,
|
||||
"rate_func" : None,
|
||||
}
|
||||
def __init__(self, mobject, **kwargs):
|
||||
self.intermediate = Mobject(color = self.color)
|
||||
self.intermediate.add_points([
|
||||
point + (x, y, 0)
|
||||
for point in self.mobject.points
|
||||
for x in [-1, 1]
|
||||
for y in [-1, 1]
|
||||
])
|
||||
Animation.__init__(self, mobject, **kwargs)
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
#Makes alpha go from 0 to slow_factor to 0 instead of 0 to 1
|
||||
alpha = self.slow_factor * (1.0 - 4 * (alpha - 0.5)**2)
|
||||
self.mobject.interpolate(
|
||||
self.starting_mobject,
|
||||
self.intermediate,
|
||||
alpha
|
||||
)
|
||||
|
||||
class MoveAlongPath(Animation):
|
||||
def __init__(self, mobject, vmobject, **kwargs):
|
||||
digest_config(self, kwargs, locals())
|
||||
@ -131,16 +98,12 @@ class Homotopy(Animation):
|
||||
digest_config(self, kwargs)
|
||||
Animation.__init__(self, mobject, **kwargs)
|
||||
|
||||
def update_submobject(self, submob, start, alpha):
|
||||
submob.points = start.points
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
pairs = zip(
|
||||
self.mobject.submobject_family(),
|
||||
self.starting_mobject.submobject_family()
|
||||
)
|
||||
for mob, start_mob in pairs:
|
||||
mob.become_partial(start_mob, 0, 1)
|
||||
self.mobject.apply_function(
|
||||
self.function_at_time_t(alpha)
|
||||
)
|
||||
Animation.update_mobject(self, alpha)
|
||||
submob.apply_function(self.function_at_time_t(alpha))
|
||||
|
||||
|
||||
class PhaseFlow(Animation):
|
||||
|
@ -39,14 +39,15 @@ class Transform(Animation):
|
||||
else:
|
||||
self.path_func = path_along_arc(self.path_arc)
|
||||
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
families = map(
|
||||
def get_all_families_zipped(self):
|
||||
return zip(*map(
|
||||
Mobject.submobject_family,
|
||||
[self.mobject, self.starting_mobject, self.ending_mobject]
|
||||
)
|
||||
for m, start, end in zip(*families):
|
||||
m.interpolate(start, end, alpha, self.path_func)
|
||||
))
|
||||
|
||||
def update_submobject(self, submob, start, end, alpha):
|
||||
submob.interpolate(start, end, alpha, self.path_func)
|
||||
return self
|
||||
|
||||
|
||||
class ClockwiseTransform(Transform):
|
||||
@ -81,6 +82,9 @@ class ShrinkToCenter(Transform):
|
||||
)
|
||||
|
||||
class ApplyMethod(Transform):
|
||||
CONFIG = {
|
||||
"submobject_mode" : "all_at_once"
|
||||
}
|
||||
def __init__(self, method, *args, **kwargs):
|
||||
"""
|
||||
Method is a method of Mobject. *args is for the method,
|
||||
@ -88,7 +92,11 @@ class ApplyMethod(Transform):
|
||||
|
||||
Relies on the fact that mobject methods return the mobject
|
||||
"""
|
||||
assert(inspect.ismethod(method))
|
||||
if not inspect.ismethod(method):
|
||||
raise Exception(
|
||||
"Whoops, looks like you accidentally invoked " + \
|
||||
"the method you want to animate"
|
||||
)
|
||||
assert(isinstance(method.im_self, Mobject))
|
||||
Transform.__init__(
|
||||
self,
|
||||
@ -119,10 +127,7 @@ class FadeIn(Transform):
|
||||
if isinstance(mobject, VMobject):
|
||||
mobject.set_stroke(width = 0)
|
||||
Transform.__init__(self, mobject, target, **kwargs)
|
||||
# self.mobject.rgbs = self.starting_mobject.rgbs * alpha
|
||||
# if self.mobject.points.shape != self.starting_mobject.points.shape:
|
||||
# self.mobject.points = self.starting_mobject.points
|
||||
# #TODO, Why do you need to do this? Shouldn't points always align?
|
||||
|
||||
|
||||
class ShimmerIn(DelayByOrder):
|
||||
def __init__(self, mobject, **kwargs):
|
||||
|
130
eola/chapter3.py
130
eola/chapter3.py
@ -288,16 +288,144 @@ class RotateIHat(LinearTransformationScene):
|
||||
self.setup()
|
||||
i_hat, j_hat = self.get_basis_vectors()
|
||||
i_label, j_label = self.get_basis_vector_labels()
|
||||
self.play(ShowCreation(i_hat))
|
||||
self.add_vector(i_hat)
|
||||
self.play(Write(i_label, run_time = 1))
|
||||
self.dither()
|
||||
self.play(FadeOut(i_label))
|
||||
self.apply_transposed_matrix([[0, 1], [-1, 0]])
|
||||
self.dither()
|
||||
self.play(Write(j_label, run_time = 1))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
class TransformationsAreFunctions(Scene):
|
||||
def construct(self):
|
||||
title = TextMobject([
|
||||
"""Linear transformations are a
|
||||
special kind of""",
|
||||
"function"
|
||||
])
|
||||
title_start, function = title.split()
|
||||
function.highlight(YELLOW)
|
||||
title.to_edge(UP)
|
||||
|
||||
equation = TexMobject([
|
||||
"L",
|
||||
"(",
|
||||
"\\vec{\\textbf{v}}",
|
||||
") = ",
|
||||
"\\vec{\\textbf{w}}",
|
||||
])
|
||||
L, lp, _input, equals, _output = equation.split()
|
||||
L.highlight(YELLOW)
|
||||
_input.highlight(MAROON_C)
|
||||
_output.highlight(BLUE)
|
||||
equation.scale(2)
|
||||
equation.next_to(title, DOWN, buff = 1)
|
||||
|
||||
starting_vector = TextMobject("Starting vector")
|
||||
starting_vector.shift(DOWN+3*LEFT)
|
||||
starting_vector.highlight(MAROON_C)
|
||||
ending_vector = TextMobject("The vector where it lands")
|
||||
ending_vector.shift(DOWN).to_edge(RIGHT)
|
||||
ending_vector.highlight(BLUE)
|
||||
|
||||
func_arrow = Arrow(function.get_bottom(), L.get_top(), color = YELLOW)
|
||||
start_arrow = Arrow(starting_vector.get_top(), _input.get_bottom(), color = MAROON_C)
|
||||
ending_arrow = Arrow(ending_vector, _output, color = BLUE)
|
||||
|
||||
|
||||
self.add(title)
|
||||
self.play(
|
||||
Write(equation),
|
||||
ShowCreation(func_arrow)
|
||||
)
|
||||
for v, a in [(starting_vector, start_arrow), (ending_vector, ending_arrow)]:
|
||||
self.play(Write(v), ShowCreation(a), run_time = 1)
|
||||
self.dither()
|
||||
|
||||
|
||||
class UsedToThinkinfOfFunctionsAsGraphs(VectorScene):
|
||||
def construct(self):
|
||||
self.show_graph()
|
||||
self.show_inputs_and_output()
|
||||
|
||||
def show_graph(self):
|
||||
axes = self.add_axes()
|
||||
graph = FunctionGraph(lambda x : x**2, x_min = -2, x_max = 2)
|
||||
name = TexMobject("f(x) = x^2")
|
||||
name.next_to(graph, RIGHT).to_edge(UP)
|
||||
point = Dot(graph.point_from_proportion(0.8))
|
||||
point_label = TexMobject("(x, x^2)")
|
||||
point_label.next_to(point, DOWN+RIGHT, buff = 0.1)
|
||||
|
||||
self.play(ShowCreation(graph))
|
||||
self.play(Write(name, run_time = 1))
|
||||
self.play(
|
||||
ShowCreation(point),
|
||||
Write(point_label),
|
||||
run_time = 1
|
||||
)
|
||||
self.dither()
|
||||
|
||||
def collapse_func(p):
|
||||
return np.dot(p, [RIGHT, RIGHT, OUT]) + (SPACE_HEIGHT+1)*DOWN
|
||||
self.play(
|
||||
ApplyPointwiseFunction(
|
||||
collapse_func, axes,
|
||||
submobject_mode = "all_at_once",
|
||||
),
|
||||
ApplyPointwiseFunction(collapse_func, graph),
|
||||
ApplyMethod(point.shift, 10*DOWN),
|
||||
ApplyMethod(point_label.shift, 10*DOWN),
|
||||
ApplyFunction(lambda m : m.center().to_edge(UP), name),
|
||||
run_time = 1
|
||||
)
|
||||
self.clear()
|
||||
self.add(name)
|
||||
self.dither()
|
||||
|
||||
def show_inputs_and_output(self):
|
||||
numbers = range(-3, 4)
|
||||
inputs = VMobject(*map(TexMobject, map(str, numbers)))
|
||||
inputs.arrange_submobjects(DOWN, buff = 0.5, aligned_edge = RIGHT)
|
||||
arrows = VMobject(*[
|
||||
Arrow(LEFT, RIGHT).next_to(mob)
|
||||
for mob in inputs.split()
|
||||
])
|
||||
outputs = VMobject(*[
|
||||
TexMobject(str(num**2)).next_to(arrow)
|
||||
for num, arrow in zip(numbers, arrows.split())
|
||||
])
|
||||
everyone = VMobject(inputs, arrows, outputs)
|
||||
everyone.center().to_edge(UP, buff = 1.5)
|
||||
|
||||
self.play(Write(inputs, run_time = 1))
|
||||
self.dither()
|
||||
self.play(
|
||||
Transform(inputs.copy(), outputs),
|
||||
ShowCreation(arrows)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -46,11 +46,11 @@ class VectorScene(Scene):
|
||||
self.add(axes)
|
||||
self.freeze_background()
|
||||
|
||||
def add_vector(self, vector, animate = True, color = YELLOW):
|
||||
def add_vector(self, vector, color = YELLOW, animate = True):
|
||||
if not isinstance(vector, Arrow):
|
||||
vector = Vector(vector, color = color)
|
||||
if animate:
|
||||
self.play(ShowCreation(vector, submobject_mode = "one_at_a_time"))
|
||||
self.play(ShowCreation(vector))
|
||||
self.add(vector)
|
||||
return vector
|
||||
|
||||
@ -66,12 +66,11 @@ class VectorScene(Scene):
|
||||
]
|
||||
]
|
||||
|
||||
def get_basis_vector_labels(self, animate = False, **kwargs):
|
||||
def get_basis_vector_labels(self, **kwargs):
|
||||
i_hat, j_hat = self.get_basis_vectors()
|
||||
return [
|
||||
self.label_vector(
|
||||
self.get_vector_label(
|
||||
vect, label, color = color,
|
||||
animate = animate,
|
||||
label_scale_val = 1,
|
||||
**kwargs
|
||||
)
|
||||
@ -81,11 +80,12 @@ class VectorScene(Scene):
|
||||
]
|
||||
]
|
||||
|
||||
def label_vector(self, vector, label, animate = True,
|
||||
direction = "left", rotate = False,
|
||||
color = WHITE, add_to_vector = False,
|
||||
buff_factor = 2,
|
||||
label_scale_val = VECTOR_LABEL_SCALE_VAL):
|
||||
def get_vector_label(self, vector, label,
|
||||
direction = "left",
|
||||
rotate = False,
|
||||
color = WHITE,
|
||||
buff_factor = 2,
|
||||
label_scale_val = VECTOR_LABEL_SCALE_VAL):
|
||||
if len(label) == 1:
|
||||
label = "\\vec{\\textbf{%s}}"%label
|
||||
label = TexMobject(label)
|
||||
@ -103,9 +103,11 @@ class VectorScene(Scene):
|
||||
boundary_point = label.get_critical_point(boundary_dir)
|
||||
label.shift(buff_factor*boundary_point)
|
||||
label.shift(vector_vect/2.)
|
||||
return label
|
||||
|
||||
if add_to_vector:
|
||||
vector.add(label)
|
||||
|
||||
def label_vector(self, vector, label, animate = True, **kwargs):
|
||||
label = self.get_vector_label(vector, label, **kwargs)
|
||||
if animate:
|
||||
self.play(Write(label, run_time = 1))
|
||||
self.add(label)
|
||||
@ -268,8 +270,8 @@ class LinearTransformationScene(VectorScene):
|
||||
self.plane = NumberPlane(**self.foreground_plane_kwargs)
|
||||
self.add_transformable_mobject(self.plane)
|
||||
if self.show_basis_vectors:
|
||||
self.add_vector((1, 0), self.i_hat_color)
|
||||
self.add_vector((0, 1), self.j_hat_color)
|
||||
self.add_vector((1, 0), self.i_hat_color, animate = False)
|
||||
self.add_vector((0, 1), self.j_hat_color, animate = False)
|
||||
|
||||
def add_background_mobject(self, *mobjects):
|
||||
for mobject in mobjects:
|
||||
@ -283,9 +285,9 @@ class LinearTransformationScene(VectorScene):
|
||||
self.transformable_mobject.append(mobject)
|
||||
self.add(mobject)
|
||||
|
||||
def add_vector(self, vector, color = YELLOW, animate = False):
|
||||
def add_vector(self, vector, color = YELLOW, **kwargs):
|
||||
vector = VectorScene.add_vector(
|
||||
self, vector, color = color, animate = animate
|
||||
self, vector, color = color, **kwargs
|
||||
)
|
||||
self.moving_vectors.append(vector)
|
||||
return vector
|
||||
|
@ -22,10 +22,6 @@ class Mobject(object):
|
||||
"name" : None,
|
||||
"dim" : 3,
|
||||
"target" : None,
|
||||
#Options are lagged_start, smoothed_lagged_start,
|
||||
#one_at_a_time, all_at_once
|
||||
"submobject_partial_creation_mode" : "lagged_start",
|
||||
#TODO, probably make this Animations's responsibility?
|
||||
}
|
||||
def __init__(self, *submobjects, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
@ -548,7 +544,7 @@ class Mobject(object):
|
||||
def interpolate_color(self, mobject1, mobject2, alpha):
|
||||
raise Exception("Not implemented")
|
||||
|
||||
def become_partial(self, mobject, a, b, submobject_partial_creation_mode = None):
|
||||
def become_partial(self, mobject, a, b):
|
||||
"""
|
||||
Set points in such a way as to become only
|
||||
part of mobject.
|
||||
@ -557,29 +553,6 @@ class Mobject(object):
|
||||
"""
|
||||
|
||||
#TODO, color?
|
||||
spcm = submobject_partial_creation_mode or self.submobject_partial_creation_mode
|
||||
pairs = zip(
|
||||
self.family_members_with_points(),
|
||||
mobject.family_members_with_points()
|
||||
)
|
||||
for i, (self_sub, mob_sub) in enumerate(pairs):
|
||||
if spcm in ["lagged_start", "smoothed_lagged_start"]:
|
||||
prop = float(i)/len(pairs)
|
||||
if spcm is "smoothed_lagged_start":
|
||||
prop = smooth(prop)
|
||||
sub_a = np.clip(2*a - prop, 0, 1)
|
||||
sub_b = np.clip(2*b - prop, 0, 1)
|
||||
elif spcm == "one_at_a_time":
|
||||
lower = float(i)/len(pairs)
|
||||
upper = float(i+1)/len(pairs)
|
||||
sub_a = np.clip((a-lower)/(upper-lower), 0, 1)
|
||||
sub_b = np.clip((b-lower)/(upper-lower), 0, 1)
|
||||
elif spcm == "all_at_once":
|
||||
sub_a, sub_b = a, b
|
||||
else:
|
||||
raise Exception("Invalid submobject partial creation mode")
|
||||
self_sub.pointwise_become_partial(mob_sub, sub_a, sub_b)
|
||||
return self
|
||||
|
||||
def pointwise_become_partial(self, mobject, a, b):
|
||||
raise Exception("Not implemented")
|
||||
|
@ -41,24 +41,26 @@ class VMobject(Mobject):
|
||||
fill_color = None,
|
||||
fill_opacity = None,
|
||||
family = True):
|
||||
if stroke_color is not None:
|
||||
setattr(self, "stroke_rgb", color_to_rgb(stroke_color))
|
||||
if stroke_width is not None:
|
||||
setattr(self, "stroke_width", stroke_width)
|
||||
if fill_color is not None:
|
||||
setattr(self, "fill_rgb", color_to_rgb(fill_color))
|
||||
if fill_opacity is not None:
|
||||
setattr(self, "fill_opacity", fill_opacity)
|
||||
if family:
|
||||
kwargs = locals()
|
||||
kwargs.pop("self")
|
||||
for mob in self.submobjects:
|
||||
mob.set_style_data(**kwargs)
|
||||
mobs = self.submobject_family() if family else [self]
|
||||
for mob in mobs:
|
||||
if stroke_color is not None:
|
||||
mob.stroke_rgb = color_to_rgb(stroke_color)
|
||||
if stroke_width is not None:
|
||||
mob.stroke_width = stroke_width
|
||||
if fill_color is not None:
|
||||
mob.fill_rgb = color_to_rgb(fill_color)
|
||||
if fill_opacity is not None:
|
||||
mob.fill_opacity = fill_opacity
|
||||
probably_meant_to_change_opacity = reduce(op.and_, [
|
||||
fill_color is not None,
|
||||
fill_opacity is None,
|
||||
mob.fill_opacity == 0
|
||||
])
|
||||
if probably_meant_to_change_opacity:
|
||||
mob.fill_opacity = 1
|
||||
return self
|
||||
|
||||
def set_fill(self, color = None, opacity = None, family = True):
|
||||
if self.fill_opacity == 0 and opacity is None:
|
||||
opacity = 1
|
||||
return self.set_style_data(
|
||||
fill_color = color,
|
||||
fill_opacity = opacity,
|
||||
@ -73,11 +75,7 @@ class VMobject(Mobject):
|
||||
)
|
||||
|
||||
def highlight(self, color, family = True):
|
||||
self.set_fill(
|
||||
color = color,
|
||||
opacity = self.get_fill_opacity(),
|
||||
family = family
|
||||
)
|
||||
self.set_fill(color = color, family = family)
|
||||
self.set_stroke(color = color, family = family)
|
||||
return self
|
||||
|
||||
|
@ -133,7 +133,6 @@ class Arrow(Line):
|
||||
"buff" : 0.3,
|
||||
"propogate_style_to_family" : False,
|
||||
"preserve_tip_size_when_scaling" : True,
|
||||
"submobject_partial_creation_mode" : "one_at_a_time",
|
||||
}
|
||||
def __init__(self, *args, **kwargs):
|
||||
if len(args) == 1:
|
||||
|
Reference in New Issue
Block a user