diff --git a/active_projects/fourier.py b/active_projects/fourier.py index f51029f9..395d834c 100644 --- a/active_projects/fourier.py +++ b/active_projects/fourier.py @@ -2455,18 +2455,57 @@ class WhiteComplexExponentialExpression(DrawFrequencyPlot): self.generate_center_of_mass_dot_update_anim() self.add(graph, pol_graph, wps_label) + self.set_variables_as_attrs(pol_graph, wps_label) + self.time_axes_group = VGroup(self.time_axes, graph) def show_winding_with_both_coordinates(self): - #TODO, tie dashed lines to dot + com_dot = self.center_of_mass_dot + plane = self.circle_plane + v_line = Line(ORIGIN, UP) + h_line = Line(ORIGIN, RIGHT) + lines = VGroup(v_line, h_line) + lines.highlight(PINK) + def lines_update(lines): + point = com_dot.get_center() + x, y = plane.point_to_coords(point) + h_line.put_start_and_end_on( + plane.coords_to_point(0, y), point + ) + v_line.put_start_and_end_on( + plane.coords_to_point(x, 0), point + ) + lines_update_anim = UpdateFromFunc(lines, lines_update) + lines_update_anim.update(0) + self.add(lines) self.change_frequency( - 2.0, run_time = 15, + 2.04, + added_anims = [ + lines_update_anim, + self.center_of_mass_dot_anim, + ], + run_time = 15, rate_func = bezier([0, 0, 1, 1]) ) self.wait() + self.dot_component_anim = lines_update_anim + def show_plane_as_complex_plane(self): - pass + to_fade = VGroup( + self.time_axes_group, self.pol_graph, self.wps_label + ) + plane = self.circle_plane + complex_plane_title = TextMobject("Complex plane") + complex_plane_title.add_background_rectangle() + complex_plane_title.to_edge(UP) + coordinate_labels = plane.get_coordinate_labels() + + self.play(FadeOut(to_fade)) + self.play(Write(complex_plane_title)) + self.play(Write(coordinate_labels)) + self.wait() + def show_eulers_formula(self): pass diff --git a/helpers.py b/helpers.py index 079b0b5e..85ad584e 100644 --- a/helpers.py +++ b/helpers.py @@ -252,14 +252,14 @@ def get_all_descendent_classes(Class): result.append(Child) return result -def filtered_locals(local_args): - result = local_args.copy() +def filtered_locals(caller_locals): + result = caller_locals.copy() ignored_local_args = ["self", "kwargs"] for arg in ignored_local_args: - result.pop(arg, local_args) + result.pop(arg, caller_locals) return result -def digest_config(obj, kwargs, local_args = {}): +def digest_config(obj, kwargs, caller_locals = {}): """ Sets init args and CONFIG values as local variables @@ -268,19 +268,25 @@ def digest_config(obj, kwargs, local_args = {}): be easily passed into instantiation, and is attached as an attribute of the object. """ - ### Assemble list of CONFIGs from all super classes + + # Assemble list of CONFIGs from all super classes classes_in_hierarchy = [obj.__class__] - configs = [] + static_configs = [] while len(classes_in_hierarchy) > 0: Class = classes_in_hierarchy.pop() classes_in_hierarchy += Class.__bases__ if hasattr(Class, "CONFIG"): - configs.append(Class.CONFIG) + static_configs.append(Class.CONFIG) #Order matters a lot here, first dicts have higher priority - all_dicts = [kwargs, filtered_locals(local_args), obj.__dict__] - all_dicts += configs + caller_locals = filtered_locals(caller_locals) + all_dicts = [kwargs, caller_locals, obj.__dict__] + all_dicts += static_configs + all_new_dicts = [kwargs, caller_locals] + static_configs obj.__dict__ = merge_config(all_dicts) + #Keep track of the configuration of objects upon + #instantiation + obj.initial_config = merge_config(all_new_dicts) def merge_config(all_dicts): all_config = reduce(op.add, [d.items() for d in all_dicts]) @@ -295,6 +301,15 @@ def merge_config(all_dicts): config[key] = merge_config([config[key], value]) return config +def soft_dict_update(d1, d2): + """ + Adds key values pairs of d2 to d1 only when d1 doesn't + already have that key + """ + for key, value in d2.items(): + if key not in d1: + d1[key] = value + def digest_locals(obj, keys = None): caller_locals = filtered_locals( inspect.currentframe().f_back.f_locals diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index ec1da178..21194a71 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -85,15 +85,17 @@ class VMobject(Mobject): return self def match_style(self, vmobject): - #TODO: Should this be smart about matching the - #style of the family members, if they happen to - #be different? self.set_style_data( stroke_color = vmobject.get_stroke_color(), stroke_width = vmobject.get_stroke_width(), fill_color = vmobject.get_fill_color(), fill_opacity = vmobject.get_fill_opacity(), + family = False ) + #TODO: This behaviro may not be optimal when submobject + #lists dont' have the same length + for sm1, sm2 in zip(self.submobjects, vmobject.submobjects): + sm1.match_style(sm2) return def fade(self, darkness = 0.5): diff --git a/topics/numerals.py b/topics/numerals.py index d3601839..10f7c51c 100644 --- a/topics/numerals.py +++ b/topics/numerals.py @@ -11,7 +11,7 @@ class DecimalNumber(VMobject): "num_decimal_points" : 2, "digit_to_digit_buff" : 0.05, "show_ellipsis" : False, - "unit" : None + "unit" : None, } def __init__(self, number, **kwargs): digest_config(self, kwargs, locals()) @@ -27,7 +27,9 @@ class DecimalNumber(VMobject): if self.show_ellipsis: self.add(TexMobject("\\dots")) - + if self.unit is not None: + self.add(TexMobject(self.unit)) + self.arrange_submobjects( buff = self.digit_to_digit_buff, aligned_edge = DOWN @@ -39,20 +41,8 @@ class DecimalNumber(VMobject): self.submobjects[1], LEFT, buff = self.digit_to_digit_buff ) - - - if self.unit != None: - unit_sign = TexMobject(self.unit) - unit_sign.next_to(self.submobjects[-1],RIGHT, - buff = self.digit_to_digit_buff) - - if self.unit == "^\\circ": - unit_sign.align_to(self,UP) - else: - unit_sign.align_to(self,DOWN) - self.add(unit_sign) - - + if self.unit == "\\circ": + self[-1].align_to(self, UP) class Integer(VGroup): CONFIG = { @@ -72,20 +62,18 @@ class ChangingDecimal(Animation): CONFIG = { "num_decimal_points" : None, "show_ellipsis" : None, - "spare_parts" : 2, "position_update_func" : None, "tracked_mobject" : None } def __init__(self, decimal_number_mobject, number_update_func, **kwargs): digest_config(self, kwargs, locals()) - if self.num_decimal_points is None: - self.num_decimal_points = decimal_number_mobject.num_decimal_points - if self.show_ellipsis is None: - self.show_ellipsis = decimal_number_mobject.show_ellipsis - decimal_number_mobject.add(*[ - VectorizedPoint(decimal_number_mobject.get_corner(DOWN+LEFT)) - for x in range(self.spare_parts)] + self.decimal_number_config = dict( + decimal_number_mobject.initial_config ) + for attr in "num_decimal_points", "show_ellipsis": + value = getattr(self, attr) + if value is not None: + self.decimal_number_config[attr] = value if self.tracked_mobject: dmc = decimal_number_mobject.get_center() tmc = self.tracked_mobject.get_center() @@ -100,13 +88,11 @@ class ChangingDecimal(Animation): decimal = self.decimal_number_mobject new_number = self.number_update_func(alpha) new_decimal = DecimalNumber( - new_number, - num_decimal_points = self.num_decimal_points, - show_ellipsis = self.show_ellipsis, - unit = self.decimal_number_mobject.unit + new_number, **self.decimal_number_config ) - new_decimal.replace(decimal, dim_to_match = 1) - new_decimal.highlight(decimal.get_color()) + new_decimal.match_height(decimal) + new_decimal.move_to(decimal) + new_decimal.match_style(decimal) decimal.submobjects = new_decimal.submobjects decimal.number = new_number