diff --git a/animation/simple_animations.py b/animation/simple_animations.py index fac20e9d..9cae9ffd 100644 --- a/animation/simple_animations.py +++ b/animation/simple_animations.py @@ -4,6 +4,8 @@ import itertools as it from helpers import * from mobject import Mobject +from mobject.vectorized_mobject import VMobject +from mobject.tex_mobject import DecimalNumber from animation import Animation @@ -36,24 +38,11 @@ class ShowPartial(Animation): "submobject_mode" : None } def update_mobject(self, alpha): - pairs = zip( - self.starting_mobject.submobject_family(), - self.mobject.submobject_family() + self.mobject.become_partial( + self.starting_mobject, + *self.get_bounds(alpha), + submobject_partial_creation_mode = self.submobject_mode ) - for i, (start, mob) in enumerate(pairs): - if self.submobject_mode == "lagged_start": - sub_alpha = 2*alpha - float(i)/len(pairs) - sub_alpha = max(0, sub_alpha) - sub_alpha = min(1, sub_alpha) - elif self.submobject_mode == "one_at_a_time": - lower = float(i)/len(pairs) - upper = float(i+1)/len(pairs) - sub_alpha = (alpha-lower)/(upper-lower) - sub_alpha = max(0, sub_alpha) - sub_alpha = min(1, sub_alpha) - else: - sub_alpha = alpha - mob.become_partial(start, *self.get_bounds(sub_alpha)) def get_bounds(self, alpha): raise Exception("Not Implemented") @@ -175,6 +164,55 @@ class MoveAlongPath(Animation): point = self.path.points[int(alpha*n)] self.mobject.shift(point-self.mobject.get_center()) +class RangingValues(Animation): + CONFIG = { + "num_decimal_points" : 2, + "rate_func" : None, + "tracking_function" : None, + "tracked_mobject" : None, + "tracked_mobject_next_to_kwargs" : {}, + } + def __init__(self, start_val, end_val, **kwargs): + digest_config(self, kwargs, locals()) + Animation.__init__(self, self.get_mobject_at_alpha(0), **kwargs) + + def update_mobject(self, alpha): + pairs = zip( + self.mobject.family_members_with_points(), + self.get_mobject_at_alpha(alpha).family_members_with_points() + ) + for old, new in pairs: + ##TODO, figure out a better way + old.__dict__.update(new.__dict__) + + def get_number(self, alpha): + return interpolate(self.start_val, self.end_val, alpha) + + def get_mobject_at_alpha(self, alpha): + mob = DecimalNumber( + self.get_number(alpha), + num_decimal_points=self.num_decimal_points + ) + if self.tracking_function: + self.tracking_function(alpha, mob) + elif self.tracked_mobject: + mob.next_to( + self.tracked_mobject, + **self.tracked_mobject_next_to_kwargs + ) + return mob + + def set_tracking_function(self, func): + """ + func shoudl be of the form func(alpha, mobject), and + should dictate where to place running number during an + animation + """ + self.tracking_function = func + + + + ### Animation modifiers ### class ApplyToCenters(Animation): diff --git a/mobject/mobject.py b/mobject/mobject.py index 977563e9..3fcdd6c7 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -21,7 +21,9 @@ class Mobject(object): "stroke_width" : DEFAULT_POINT_THICKNESS, "name" : None, "dim" : 3, - "target" : None + "target" : None, + #Options are lagged_start, one_at_a_time, all_at_once + "submobject_partial_creation_mode" : "lagged_start", } def __init__(self, *submobjects, **kwargs): digest_config(self, kwargs) @@ -219,6 +221,13 @@ class Mobject(object): self.shift(target_point - anchor_point + buff*direction) return self + def shift_onto_screen(self): + space_lengths = [SPACE_WIDTH, SPACE_HEIGHT] + for vect in UP, DOWN, LEFT, RIGHT: + dim = np.argmax(np.abs(vect)) + if abs(self.get_edge_center(vect)[dim]) > space_lengths[dim]: + self.to_edge(vect) + def stretch_to_fit(self, length, dim, stretch = True): old_length = self.length_over_dim(dim) if old_length == 0: @@ -423,12 +432,9 @@ class Mobject(object): self.submobject_family() ) - def arrange_submobjects(self, - direction = RIGHT, - buff = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, - center = True): + def arrange_submobjects(self, direction = RIGHT, center = True, **kwargs): for m1, m2 in zip(self.submobjects, self.submobjects[1:]): - m2.next_to(m1, direction, buff = buff) + m2.next_to(m1, direction, **kwargs) if center: self.center() return self @@ -533,13 +539,37 @@ class Mobject(object): def interpolate_color(self, mobject1, mobject2, alpha): raise Exception("Not implemented") - def become_partial(self, mobject, a, b): + def become_partial(self, mobject, a, b, submobject_partial_creation_mode = None): """ Set points in such a way as to become only part of mobject. Inputs 0 <= a < b <= 1 determine what portion of mobject to become. """ + self.pointwise_become_partial(mobject, a, b) + #TODO, color + # self.interpolate_color(self, mobject, b) + + spcm = submobject_partial_creation_mode or self.submobject_partial_creation_mode + pairs = zip(self.submobjects, mobject.submobjects) + for i, (self_sub, mob_sub) in enumerate(pairs): + if spcm == "lagged_start": + prop = float(i)/len(pairs) + 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.become_partial(mob_sub, sub_a, sub_b) + return self + + def pointwise_become_partial(self, mobject, a, b): raise Exception("Not implemented") @@ -557,3 +587,4 @@ class Mobject(object): + diff --git a/mobject/point_cloud_mobject.py b/mobject/point_cloud_mobject.py index 1d2fef54..dc5e9a5a 100644 --- a/mobject/point_cloud_mobject.py +++ b/mobject/point_cloud_mobject.py @@ -129,7 +129,7 @@ class PMobject(Mobject): mobject1.rgbs, mobject2.rgbs, alpha ) - def become_partial(self, mobject, a, b): + def pointwise_become_partial(self, mobject, a, b): lower_index, upper_index = [ int(x * mobject.get_num_points()) for x in a, b diff --git a/mobject/tex_mobject.py b/mobject/tex_mobject.py index 755454fd..cd7b9b4a 100644 --- a/mobject/tex_mobject.py +++ b/mobject/tex_mobject.py @@ -7,7 +7,7 @@ TEXT_MOB_SCALE_VAL = 0.05 class TexSymbol(VMobjectFromSVGPathstring): - def become_partial(self, mobject, a, b): + def pointwise_become_partial(self, mobject, a, b): #TODO, this assumes a = 0 if b < 0.5: b = 2*b @@ -17,7 +17,7 @@ class TexSymbol(VMobjectFromSVGPathstring): width = 2 - 2*b opacity = 2*b - 1 b = 1 - VMobjectFromSVGPathstring.become_partial( + VMobjectFromSVGPathstring.pointwise_become_partial( self, mobject, 0, b ) self.set_stroke(width = width) @@ -64,10 +64,7 @@ class TexMobject(SVGMobject): def handle_list_expression(self): #TODO, next_to not sufficient? subs = [ - # TexMobject(expr) - self.__class__( - expr - ) + TexMobject(expr) for expr in self.expression ] self.initial_scale_val = 1 @@ -109,6 +106,27 @@ class Brace(TexMobject): self.shift(left - self.get_corner(UP+LEFT) + self.buff*DOWN) for mob in mobject, self: mob.rotate(angle) + +class DecimalNumber(TexMobject): + CONFIG = { + "num_decimal_points" : 2, + "digit_to_digit_buff" : 0.05 + } + def __init__(self, float_num, **kwargs): + digest_config(self, kwargs) + num_string = '%.*f' % (self.num_decimal_points, float_num) + TexMobject.__init__(self, list(num_string)) + self.arrange_submobjects( + buff = self.digit_to_digit_buff, + aligned_edge = DOWN + ) + if float_num < 0: + minus = self.submobjects[0] + minus.next_to( + self.submobjects[1], LEFT, + buff = self.digit_to_digit_buff + ) + def tex_hash(expression, template_tex_file): return str(hash(expression + template_tex_file)) diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index bc71d2de..47df0a32 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -300,7 +300,7 @@ class VMobject(Mobject): # print getattr(mobject2, attr) setattr(self, attr, getattr(mobject2, attr)) - def become_partial(self, mobject, a, b): + def pointwise_become_partial(self, mobject, a, b): assert(isinstance(mobject, VMobject)) #Partial curve includes three portions: #-A middle section, which matches the curve exactly diff --git a/topics/geometry.py b/topics/geometry.py index 2885ea4e..1daeddf3 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -133,6 +133,7 @@ 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: