diff --git a/animation/numbers.py b/animation/numbers.py new file mode 100644 index 00000000..6fed7b26 --- /dev/null +++ b/animation/numbers.py @@ -0,0 +1,61 @@ +from __future__ import absolute_import + +from constants import * + +from animation.animation import Animation +from mobject.numbers import DecimalNumber +from utils.bezier import interpolate +from utils.config_ops import digest_config + +class ChangingDecimal(Animation): + CONFIG = { + "num_decimal_points" : None, + "show_ellipsis" : None, + "position_update_func" : None, + "tracked_mobject" : None, + } + def __init__(self, decimal_number_mobject, number_update_func, **kwargs): + digest_config(self, kwargs, locals()) + 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 hasattr(self.decimal_number_mobject, "background_rectangle"): + self.decimal_number_config["include_background_rectangle"] = True + if self.tracked_mobject: + dmc = decimal_number_mobject.get_center() + tmc = self.tracked_mobject.get_center() + self.diff_from_tracked_mobject = dmc - tmc + Animation.__init__(self, decimal_number_mobject, **kwargs) + + def update_mobject(self, alpha): + self.update_number(alpha) + self.update_position() + + def update_number(self, alpha): + decimal = self.decimal_number_mobject + new_number = self.number_update_func(alpha) + new_decimal = DecimalNumber( + new_number, **self.decimal_number_config + ) + new_decimal.match_height(decimal) + new_decimal.move_to(decimal) + new_decimal.match_style(decimal) + + decimal.submobjects = new_decimal.submobjects + decimal.number = new_number + + def update_position(self): + if self.position_update_func is not None: + self.position_update_func(self.decimal_number_mobject) + elif self.tracked_mobject is not None: + self.decimal_number_mobject.move_to(self.tracked_mobject.get_center() + self.diff_from_tracked_mobject) + +class ChangeDecimalToValue(ChangingDecimal): + def __init__(self, decimal_number_mobject, target_number, **kwargs): + start_number = decimal_number_mobject.number + func = lambda alpha : interpolate(start_number, target_number, alpha) + ChangingDecimal.__init__(self, decimal_number_mobject, func, **kwargs) diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index bb1ef166..06de7344 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -20,6 +20,7 @@ from animation.composition import * from animation.creation import * from animation.indication import * from animation.movement import * +from animation.numbers import * from animation.rotation import * from animation.specialized import * from animation.transform import * @@ -31,11 +32,13 @@ from camera.moving_camera import * from continual_animation.continual_animation import * from continual_animation.from_animation import * +from continual_animation.numbers import * from continual_animation.update import * from mobject.mobject import * from mobject.frame import * from mobject.geometry import * +from mobject.numbers import * from mobject.shape_matchers import * from mobject.value_tracker import * from mobject.svg.brace import * @@ -68,7 +71,6 @@ from topics.functions import * from topics.graph_scene import * from topics.matrix import * from topics.number_line import * -from topics.numerals import * from topics.probability import * from topics.three_dimensions import * from topics.vector_space_scene import * diff --git a/continual_animation/numbers.py b/continual_animation/numbers.py new file mode 100644 index 00000000..42679af6 --- /dev/null +++ b/continual_animation/numbers.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import + +from continual_animation.from_animation import NormalAnimationAsContinualAnimation +from animation.numbers import ChangingDecimal + +class ContinualChangingDecimal(NormalAnimationAsContinualAnimation): + def __init__(self, *args, **kwargs): + NormalAnimationAsContinualAnimation.__init__( + self, ChangingDecimal(*args, **kwargs) + ) diff --git a/mobject/numbers.py b/mobject/numbers.py new file mode 100644 index 00000000..bb50c070 --- /dev/null +++ b/mobject/numbers.py @@ -0,0 +1,85 @@ +from __future__ import absolute_import + +from constants import * + +from mobject.svg.tex_mobject import TexMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject +from mobject.shape_matchers import BackgroundRectangle + +class DecimalNumber(VMobject): + CONFIG = { + "num_decimal_points" : 2, + "digit_to_digit_buff" : 0.05, + "show_ellipsis" : False, + "unit" : None, #Aligned to bottom unless it starts with "^" + "include_background_rectangle" : False, + } + def __init__(self, number, **kwargs): + VMobject.__init__(self, **kwargs) + self.number = number + ndp = self.num_decimal_points + + #Build number string + if isinstance(number, complex): + num_string = '%.*f%s%.*fi'%( + ndp, number.real, + "-" if number.imag < 0 else "+", + ndp, abs(number.imag) + ) + else: + num_string = '%.*f'%(ndp, number) + negative_zero_string = "-%.*f"%(ndp, 0.) + if num_string == negative_zero_string: + num_string = num_string[1:] + self.add(*[ + TexMobject(char, **kwargs) + for char in num_string + ]) + + #Add non-numerical bits + if self.show_ellipsis: + self.add(TexMobject("\\dots")) + + + if num_string.startswith("-"): + minus = self.submobjects[0] + minus.next_to( + self.submobjects[1], LEFT, + buff = self.digit_to_digit_buff + ) + + if self.unit != None: + self.unit_sign = TexMobject(self.unit) + self.add(self.unit_sign) + + self.arrange_submobjects( + buff = self.digit_to_digit_buff, + aligned_edge = DOWN + ) + + #Handle alignment of parts that should be aligned + #to the bottom + for i, c in enumerate(num_string): + if c == "-" and len(num_string) > i+1: + self[i].align_to(self[i+1], alignment_vect = UP) + if self.unit and self.unit.startswith("^"): + self.unit_sign.align_to(self, UP) + # + if self.include_background_rectangle: + self.add_background_rectangle() + + def add_background_rectangle(self): + #TODO, is this the best way to handle + #background rectangles? + self.background_rectangle = BackgroundRectangle(self) + self.submobjects = [ + self.background_rectangle, + VGroup(*self.submobjects) + ] + return self + +class Integer(DecimalNumber): + CONFIG = { + "num_decimal_points" : 0, + } diff --git a/topics/numerals.py b/topics/numerals.py deleted file mode 100644 index 0f8bc901..00000000 --- a/topics/numerals.py +++ /dev/null @@ -1,169 +0,0 @@ - -from animation.animation import Animation -from constants import * -from continual_animation.continual_animation import ContinualAnimation -from mobject.svg.tex_mobject import TexMobject -from mobject.types.vectorized_mobject import VGroup -from mobject.types.vectorized_mobject import VMobject -from mobject.types.vectorized_mobject import VectorizedPoint -from scene.scene import Scene -from mobject.shape_matchers import BackgroundRectangle -from utils.bezier import interpolate -from utils.config_ops import digest_config - -class DecimalNumber(VMobject): - CONFIG = { - "num_decimal_points" : 2, - "digit_to_digit_buff" : 0.05, - "show_ellipsis" : False, - "unit" : None, #Aligned to bottom unless it starts with "^" - "include_background_rectangle" : False, - } - def __init__(self, number, **kwargs): - VMobject.__init__(self, **kwargs) - self.number = number - ndp = self.num_decimal_points - - #Build number string - if isinstance(number, complex): - num_string = '%.*f%s%.*fi'%( - ndp, number.real, - "-" if number.imag < 0 else "+", - ndp, abs(number.imag) - ) - else: - num_string = '%.*f'%(ndp, number) - negative_zero_string = "-%.*f"%(ndp, 0.) - if num_string == negative_zero_string: - num_string = num_string[1:] - self.add(*[ - TexMobject(char, **kwargs) - for char in num_string - ]) - - #Add non-numerical bits - if self.show_ellipsis: - self.add(TexMobject("\\dots")) - - - if num_string.startswith("-"): - minus = self.submobjects[0] - minus.next_to( - self.submobjects[1], LEFT, - buff = self.digit_to_digit_buff - ) - - if self.unit != None: - self.unit_sign = TexMobject(self.unit) - self.add(self.unit_sign) - - self.arrange_submobjects( - buff = self.digit_to_digit_buff, - aligned_edge = DOWN - ) - - #Handle alignment of parts that should be aligned - #to the bottom - for i, c in enumerate(num_string): - if c == "-" and len(num_string) > i+1: - self[i].align_to(self[i+1], alignment_vect = UP) - if self.unit and self.unit.startswith("^"): - self.unit_sign.align_to(self, UP) - # - if self.include_background_rectangle: - self.add_background_rectangle() - - def add_background_rectangle(self): - #TODO, is this the best way to handle - #background rectangles? - self.background_rectangle = BackgroundRectangle(self) - self.submobjects = [ - self.background_rectangle, - VGroup(*self.submobjects) - ] - return self - -class Integer(DecimalNumber): - CONFIG = { - "num_decimal_points" : 0, - } - -class ChangingDecimal(Animation): - CONFIG = { - "num_decimal_points" : None, - "show_ellipsis" : None, - "position_update_func" : None, - "tracked_mobject" : None, - } - def __init__(self, decimal_number_mobject, number_update_func, **kwargs): - digest_config(self, kwargs, locals()) - 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 hasattr(self.decimal_number_mobject, "background_rectangle"): - self.decimal_number_config["include_background_rectangle"] = True - if self.tracked_mobject: - dmc = decimal_number_mobject.get_center() - tmc = self.tracked_mobject.get_center() - self.diff_from_tracked_mobject = dmc - tmc - Animation.__init__(self, decimal_number_mobject, **kwargs) - - def update_mobject(self, alpha): - self.update_number(alpha) - self.update_position() - - def update_number(self, alpha): - decimal = self.decimal_number_mobject - new_number = self.number_update_func(alpha) - new_decimal = DecimalNumber( - new_number, **self.decimal_number_config - ) - new_decimal.match_height(decimal) - new_decimal.move_to(decimal) - new_decimal.match_style(decimal) - - decimal.submobjects = new_decimal.submobjects - decimal.number = new_number - - def update_position(self): - if self.position_update_func is not None: - self.position_update_func(self.decimal_number_mobject) - elif self.tracked_mobject is not None: - self.decimal_number_mobject.move_to(self.tracked_mobject.get_center() + self.diff_from_tracked_mobject) - -class ChangeDecimalToValue(ChangingDecimal): - def __init__(self, decimal_number_mobject, target_number, **kwargs): - start_number = decimal_number_mobject.number - func = lambda alpha : interpolate(start_number, target_number, alpha) - ChangingDecimal.__init__(self, decimal_number_mobject, func, **kwargs) - -class ContinualChangingDecimal(ContinualAnimation): - def __init__(self, decimal_number_mobject, number_update_func, **kwargs): - self.anim = ChangingDecimal(decimal_number_mobject, number_update_func, **kwargs) - ContinualAnimation.__init__(self, decimal_number_mobject, **kwargs) - - def update_mobject(self, dt): - self.anim.update(self.internal_time) - - - - - - - - - - - - - - - - - - -